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 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.