Friday, September 28, 2018

Adresse Empfänger in Bescheinigungen

Wow, it took me some time to find the explanation for the following problem (#2554):

  • (melanie) Inhalt der Bescheinigungen: Adressfeld ist versetzt/verschwunden! Layout nicht ok!

Lag daran, dass etgen.html seit Thursday, March 8, 2018 lxml-Elemente benutzt und die Syntax class_="Recipient" deshalb ersetzt werden muss durch die neue Syntax **{'class':"Recipient"}. Das heißt auch, dass die *.odt-Dateien jetzt nicht mehr gesyncht werden sollten, also muss ich die initdb_preview_from_prod.sh lokal anpassen. Das erwies sich aber als eher aufwändig, deshalb muss ich einfach dran denken, nach dem Synch die Datei excerpts/Default.odt manuell anzupassen.

Ich habe das gefunden, nachdem ich den Fall in Usage of database excerpts in Lino Welfare dokumentiert hatte. Dazu habe ich dem Roten Kreuz in den Demo-Daten eine Adresse verpasst.

Formatierung Tabellen in Budgets Schuldnerberatung

The debts/Budget/Default.odt template uses the story() function to insert a stream of “story chunks” produced by methods on the Budget object. These chunks are processed by story2odt which calls insert_table for each table in the stream. insert_table dynamically creates odf tables and respects the widths it gets from the request’s get_field_info, which returns col.width or col.preferred_width for each column.

I played a bit with the appy renderer in order to remind me how it all works internally. (Yes, I wrote it myself, but this was years ago.)

>>> from lino import startup
>>> startup('lino_welfare.projects.eupen.settings.doctests')
>>> from lino.api.doctest import *
>>> from etgen.html import tostring
>>> from lino_xl.lib.appypod.appy_renderer import AppyRenderer
>>> from lxml import etree
>>> ar = rt.login('robin')
>>> ctx = {}
>>> rnd = AppyRenderer(ar, "empty.odt", ctx, "out.odt")

Appy is not very testing friendly. Above line requires a file empty.odt just some empty but valid LO document, and every test run will create a temporary directory.

>>> obj = debts.Budget.objects.get(pk=1)
>>> story = obj.data_story(ar)
>>> odf = rnd.insert_story(story)
>>> # print(odf)

The story contains a sequence of <header> and <table> elements, but no root element. That’s a problem for the parser. So we wrap the story for them.

>>> odf = "<foo>" + odf + "</foo>"
>>> # from io import BytesIO
>>> # stream = BytesIO(odf.encode("utf-8"))
>>> # tree = etree.parse(stream)
>>> # root = tree.getroot()
>>> root = etree.fromstring(odf)
>>> children = list(root)
>>> table = children[1]
>>> table  
<Element {urn:oasis:names:tc:opendocument:xmlns:table:1.0}table at ...>
>>> columns, headers, rows = tuple(table)
>>> for col in columns:
...    etree.tostring(col)
...    print(col.get("table:style-name"))
>>> for child in root:
...     print(child.tag)
>>> print(odf)
>>> tree = etree.fromstring(odf)
>>> tree

I finally simply added width specifiers to the column_names in the tables that are getting printed there. Including a new constant AMOUNT_WIDTH in lino_welfare.modlib.debts.choicelists. Currently it is set to ":15" which seems to give good results. I also decreased the right page margin in debts/Budget/Default.odt from 20 to 10 mm.

Django 2 support is advancing

Hamza and I had a session where we fixed a failure on Travis:

  • 2018-09-28 20:15 in book: be32b6b (fixing failures py2to3 ajax.rst django.request logger level)

Django 2 has become more eager about logging every request, including failed ones. This is of course disturbing when we want a testable doc that works for both Django 1 and 2. So in Refusing permission to an anonymous request we simply disable logging.