2010-09-14

Optimierungen an tim2lino.py

  • Rief unnötigerweise lino.lino_site.setup()

  • Wenn was schief geht, dann schreibt er die betreffende Zeile jetzt in eine Datei changelog.failed.json und macht weiter.

Release

Den heutigen Stand nenne ich Lino 0.8.5 und Lino-DSBE 0.1.4 und markiere das mit tags im Repository.

0.8.4 und 0.1.3 sind nie released worden, weil ich eher aus Versehen einen Fehler in meiner bisherigen Release-Methode bemerkt habe: die neue Versionsnummer muss natürlich vor jedem Release entschieden werden. In den Zwischenversionen nach dem Release bleibt die Nummer des letzten Releases stehen. Ein “+” um das zu kennzeichnen halte ich bis auf weiteres für unnötig.

Gedanken zum logging

Ich bin noch weiter am suchen, wie ich das Logging verwalten muss.

Die Standard-Konfiguration in lino/__init__.py ist wohl noch nicht das non plus ultra…

Interessante Lektüre:

Was will ich eigentlich? Ich will ja eigentlich nur, dass ich von überall aus mit einem einfachen lino.log.info() oder lino.log.debug() loggen kann.

Die lino/__init__.py macht ja vor allem:

import logging
log = logging.getLogger('lino')

Diese beiden Zeilen wären mir eben schon zu viel, wenn ich sie in jedem Modul schreiben müsste.

Reicht eigentlich ein einziger Logger? Müsste ich nicht einen zusätzlichen Logger vorsehen für Meldungen, die die Daten selber betreffen? Zum Beispiel detaillierter changelog (falls ein Benutzer das will). Also einen weiteren Logger lino.data, der auch in den zentralen Log geht (stimmt das?), aber zusätzlich irgendwo separat geloggt wird für den Fall, dass man die Historie der Änderungen in der Datenbank konsultieren will.

Müsste es nicht doch immer eine lino.log geben, die in der settings.py angegeben wird

Ich habe drei Methoden, wie Lino gestartet wird:

  • manage.py, initdb.py, load_tim.py & Co. (interaktiv an der Kommandozeile)

  • apache.wsgi

  • tim2lino.py (Hintergrundprozess)

Experiment: Die eigentliche tim2lino.py habe ich von /dsbe/demo nach /dsbe verschoben. In /dsbe/demo steht jetzt ein Muster für eine lokale tim2lino.py, die das Logging konfiguriert und dann from dsbe.tim2lino import main macht und aufruft.

Noch Bugs in tim2lino.py

Okay, wie zu erwarten sind wir mit der Synchronhaltung der Daten noch nicht so schnell fertig.

  • Experimente, wie tim2lino.py am besten loggen soll. Momentan mach ich:

    import logging
    logger = logging.getLogger('lino.tim2lino')
    
  • Die Entscheidung, ob ein PAR eine Person oder eine Company ist, wird komplexer und deshalb in eine eigene Funktion tim2lino.is_company() ausgelagert: Wer eine Nationalregisternummer (PAR->NB2) hat ist eine Person, selbst wenn er auch eine MWSt-Nummer hat. Die MWSt-Nummer geht in so einem Fall verloren.

django-admin commands

Wenn tim2lino einen OperationalError “unable to open database file” kriegt, dann darf er natürlich nicht gleich aufgeben und die Zeile nach changelog.failed.json schreiben, sondern muss neu probieren. Denn dieser Fehler kommt ja nur, weil zu diesem Zeitpunkt der Webserver-Prozess die sqlite-Datenbank blockiert hat. Deshalb macht er jetzt im Fall eines Fehlers 10 Versuche, bevor er aufgibt.

Aber stimmt es, dass sqlite nicht erlaubt, dass zwei Prozesse gleichzeitig auf die Datenbank zugreifen?

An den Zugriffsrechten auf die Datenbank scheint es jedenfalls nicht zu liegen:

$ ls -al /usr/local/django/dsbe-eupen/data
total 3436
drwxrwsr-x 2 lsaffre www-data    4096 2010-09-14 10:09 .
drwxrwsr-x 3 lsaffre www-data    4096 2010-09-14 11:15 ..
-rw-rw-r-- 1 lsaffre www-data 3506176 2010-09-14 10:09 dsbe-eupen.db

Andererseits sieht es aus als ob der Server-Prozess die Datenbank ziemlich lange gesperrt hält, und dass tim2lino somit nie zum Zuge kommt, wenn der Server einmal aktiv geworden ist.

Außerdem kann ich von einem manage.py shell aus sehr wohl auf die Datenbank zugreifen und darin verändern, während der Serverprozess läuft:

$ python manage.py shell
Lino-DSBE 0.1.4 <http://dsbe.saffre-rumma.net>
Lino 0.8.5 <http://code.google.com/p/lino/>
Django 1.3 pre-alpha SVN-13818 <http://www.djangoproject.com>
Python 2.5.2 <http://www.python.org/>
ReportLab Toolkit 2.1 <http://www.reportlab.org/rl_toolkit.html>
PyYaml  <http://pyyaml.org/>
python-dateutil 1.4.1 <http://labix.org/python-dateutil>
Python 2.5.2 (r252:60911, Jan 24 2010, 17:44:40)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from dsbe.models import Person
>>> p = Person.objects.get(pk=20474)
>>> p.first_name += "TEST"
>>> p.save()
>>>

Außerdem kommt der OperationalError sogar dann, wenn ich Apache runtergefahren habe.

Also muss der OperationalError noch einen anderen Grund haben. Aber welchen? Scheinbar ist das ein Newbie Mistake, aber keiner der dort angeführten Lösungsvorschläge trifft zu.

Tilt! Nach stundenlanger Suche sehe ich, dass tim2lino.py irgendwie die falsche settings.py nimmt und deshalb versucht auf eine (nicht existierende) Datenbank in /var/snapshots/dsbe/demo/data zuzugreifen! Das ist ein Wink mit dem Zaunpfahl! Jetzt mache ich aus tim2lino einen django-admin command!

Die Aktion an sich hat kaum eine Stunde gedauert:

  • tim2lino.py ersetzt durch dsbe.management.commands.watch_tim.

  • initdb.py ersetzt durch dsbe.management.commands.initdb.

  • load_tim.py ersetzt durch dsbe.management.commands.initdb_tim.

Also wenn ich mich gestern noch über Djangos Macken geärgert habe, dann darf ich mich heute über seine Genialität freuen (und mich ärgern, dass ich meine Scripts nicht schon früher in django-admin commands konvertiert hatte)!

Den 10-mal-neu-versuchen-Loop hole ich dann auch wieder raus.

Weiter

  • dsbe.Person.civil_state ist jetzt ein CharField und nicht mehr ein SmallIntegerField. Weil ja choices auf Integer-Feldern noch nicht funktionieren.

  • Einige Felder waren noch nicht als read-only markiert für Personen die aus TIM importiert wurden.

  • Bankkonten und Gesdos-Nummer (PAR->NB1) werden jetzt aus TIM übernommen

  • Der doppelte Report für Personen ist wieder raus. Kunde ist einverstanden, dass kleinere Bildschirme und mehr Tabs besser sind.

  • Kolonnen sind jetzt wieder sortierbar.

  • Noch Übersetzungen

Release und initdb_tim auf dsbe-eupen. Eine Latte von Warnungen:

pharmacy 0000086213 not found
pharmacy 0000086121 not found
pharmacy 0000086372 not found