Friday, April 9, 2021¶
The Mystery of the failing Welfare test suite¶
I am exploring the failures in Lino Welfare. I picked
docs/specs/b2c.rst (the source file for b2c : SEPA BankToCustomer) as an
example. It fails also when I go back to Django 3.1.7 (the version I had before
Note that I also have to run
pm prep in the gerd demo
project after every change. This indicates that the issues are caused by demo
data being generated differently because sorting behaviour has changed.
The failure in
docs/specs/b2c.rst comes because in
lino_welfare.modlib.sepa.fixtures.demo the following line failed with
qs = Company.objects.filter(sepa_accounts__iban__gt='').distinct()
I changed it to:
qs = Company.objects.exclude(sepa_accounts__iban='').distinct()
This change caused the companies to be sorted differently.
The special sqlite sorting hack in
lino_welfare.lib.welfare.models.customize_sqlite() seems also related to
these issues. In Django 3.2 the
stricmp() function caused a NameError:
name ‘cmp’ is not defined and I have no explanation why this error didn’t come
already earlier. The built-in
cmp() function has been removed in Python 3
but it was still being used, and nobody complained. Which made me suspect that
the hack wasn’t active.
Maybe the following documented change is responsible: The django.db.models.Field equality operator now correctly distinguishes inherited field instances across models. Additionally, the ordering of such fields is now defined.
lino_xl.lib.contacts.Company model had no Meta.ordering specified.
ordering = [‘name’]
The docs about Meta.ordering
have a warning “Ordering is not a free operation. Each field you add to the
ordering incurs a cost to your database. Each foreign key you add will
implicitly include all of its default orderings as well.” Not sure how to
understand this. Does it mean that we should rather use only
I thought that the issue comes because querysets are no longer automatically sorted by id (when Meta.ordering) is not specified. So I replaced, in welfare/**fixtures/*.py, all occurrences of .objects.all() by .objects.order_by(‘id’). And added an .order_by(‘id’) to all calls to .objects.filter(…) that didn’t specify any ordering.
I now at least understand the problem: certain models don’t have a default ordering. And the demo fixtures in welfare happily created coachings and other things based on non-ordered data. That was no problem because unordered data was in some constant way unordered. Until Django 3.2 where this ordering changes.
But that didn’t help, either.
docs/specs/newcomers.rst fixture helped me to understand more:
$ dt docs/specs/newcomers.rst Failed example: rt.show('newcomers.Competences') #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE Expected: ==== ================= ================================ ========= ID Benutzer Fachbereich Aufwand ---- ----------------- -------------------------------- --------- 1 Alicia Allmanns Eingliederungseinkommen (EiEi) 10 2 Hubert Huppertz DSBE 5 3 Mélanie Mélard Ausländerbeihilfe 4 4 Alicia Allmanns Finanzielle Begleitung 6 5 Hubert Huppertz Laufende Beihilfe 2 6 Mélanie Mélard Eingliederungseinkommen (EiEi) 10 7 Alicia Allmanns DSBE 5 **42** ==== ================= ================================ ========= <BLANKLINE> Got: ==== ================= ================================ ========= ID Benutzer Fachbereich Aufwand ---- ----------------- -------------------------------- --------- 1 Mélanie Mélard Eingliederungseinkommen (EiEi) 10 2 Hubert Huppertz DSBE 5 3 Alicia Allmanns Ausländerbeihilfe 4 4 Mélanie Mélard Finanzielle Begleitung 6 5 Hubert Huppertz Laufende Beihilfe 2 6 Alicia Allmanns Eingliederungseinkommen (EiEi) 10 7 Mélanie Mélard DSBE 5 **42** ==== ================= ================================ ========= <BLANKLINE>
Here is the (simplified) code that generates this database content:
FACULTIES = Cycler(newcomers.Faculty.objects.all()) USERS = Cycler(User.objects.all()) for i in range(7): yield newcomers.Competence(user=USERS.pop(), faculty=FACULTIES.pop())
>>> users.User.objects.all().ordered True >>> newcomers.Faculty.objects.all().ordered False
Some models have a default ordering:
>>> from lino.api.shell import * >>> "ORDER BY" in str(contacts.Person.objects.all().query) True >>> jobs.Job.objects.all().ordered True >>> debts.Account.objects.all().ordered True >>> jobs.ContractType.objects.all().ordered True >>> art61.ContractType.objects.all().ordered True >>> immersion.ContractType.objects.all().ordered True >>> immersion.Goal.objects.all().ordered True
But many models don’t have have it:
>>> from lino.api.shell import * >>> "ORDER BY" in str(contacts.Company.objects.all().query) False >>> pcsw.Client.objects.all().ordered False >>> "ORDER BY" in str(clients.ClientContactType.objects.all().query) False >>> "ORDER BY" in str(aids.Granting.objects.all().query) False >>> "ORDER BY" in str(households.Household.objects.all().query) False >>> "ORDER BY" in str(cv.StudyType.objects.all().query) False >>> isip.ContractEnding.objects.all().ordered False >>> aids.AidType.objects.all().ordered False >>> isip.ContractType.objects.all().ordered False >>> isip.Contract.objects.all().ordered False >>> immersion.Contract.objects.all().ordered False
Getting notified when long-running command terminates¶
pm prep takes a minute or more to complete (on my computer), I run
it as follows to get an acoustic notification when it is done:
$ pm prep --noinput ; espeak done
The –noinput option is useful here because more than once I launched the process and forget to hit ENTER in order to confirm the question:
We are going to flush your database (.../gerd/settings/default.db). Are you sure (y/n) ? [Y,n]?
But typing “; espeak done” each time is a bit tedious. Can’t we optimize this?
$ sudo apt install libnotify-bin