Markus studiert!
Was ich an Django so mag
Nachdem ich im dritten Semester zum ersten Mal intensiveren Kontakt mit Python hatte, habe ich die Sprache in den vergangenen Semestern immer stärker schätzen gelernt.
In diesem Semester setzen wir für die Server-Komponente unseres Mobile-Computing-Projekts ebenfalls auf Python, und zwar in Form des Web-Frameworks Django. Und das ist wirklich ein Genuss.
Zur Implementierung des Servers lieferte Django bereits alle notwendigen Komponenten. Das Framework bietet den besonderen Vorteil, dass die typischen Aufgaben einer Webanwendung, deren Hauptaufgabe die Verwaltung von Datensätzen ist, also deren Auflisten, Anzeigen, Finden und Editieren (CRUD), nach Erstellung von nur wenig eigenem Code bereits komfortabel zur Verfügung stehen. Es muss lediglich die Datenstruktur in Form von Models definiert werden. Anhand dieser Informationen kann Djongo schon die nötige Datenbankstruktur erzeugen und passende Formulare zum Editieren der Datensätze im mitgelieferten Admin-Bereich erzeugen.
Zur Implementierung der öffentlichen Schnittstellen der Anwendung reichen auch lediglich wenige Zeile Code aus. In Django werden diese mithilfe der Views erstellt, wobei man hier nur Ausnahmen behandeln muss, die über das Anzeigen von einzelnen Datensätzen oder Listen von Datensätzen hinaus gehen. Die für die App zur Verfügung gestellt API wird ebenfalls mit Hilfe der Views erzeugt, wobei dann das Antwort-Format nicht HTML ist, sondern JSON. Die Unterscheidung wird anhand das Accept-Headers in der Anfrage getroffen. Um die Implementierung eines eigenen Controllers muss man sich nicht kümmern, hier übernimmt das Framework bereits alle nötigen Aufgaben, sofern man die vorgegebenen Konventionen befolgt und die bereitgestellten Hilfsfunktionen verwendet.
Auch die Möglichkeit zum Testen seiner Anwendung sind genial, das Test-Framework kümmert sich um alle Aufgaben, lediglich die Unit-Tests muss man selber schreiben - was elegant von der Hand geht, dank einem mitgelieferten Test-Client der URL-Request simuliert.
Django nimmt einem durch viele Konventionen unheimlich viel Arbeit ab, und man kann sich auf das Wesentliche konzentrieren. Die Geschwindigkeit, mit der man in Django entwickeln kann ist beeindruckend und ich bin schwer am Überlegen, es auch für meine Thesis ein zu setzen...
< 27. January 2012, 08:45 Uhr
Tags: #python #mobile computing
What The Foto: WorkPackages
Über die Einführung der WorkPackages hatte ich ja schon an anderer Stelle gesprochen.
Hier möchte ich noch kurz das Python3-Script vorstellen, dass ich verwendet habe, um die Wiki-Seite mit den WorkPackages zu erstellen.
Prinzipiell habe ich dazu einen eigenen Ticket-Typ workpackage erstellt. In diesen Tickets wurde dann speziell formatierte Angaben ausgelesen.
Aus diesen ganzen Angaben werden dann übersichtlich die Infos zu allen WorkPackages erzeugt:
Standard-Felder mit besonderer Bedeutung
- Status
- Der status des Tickets bestimmt die Einordnung in aktive WorkPackages (accepted, assigned,
reopened), ausstehende WorkPackages (new) und erledigte Workpackages (closed). - Owner, CC
- Der Verantwortliche für ein WorkPackage ist der owner des Tickets, die Mitarbeiter werden aus dem
Feld CC ausgelesen, wobei die Usernamen dort mit der Liste der Entwickler
abgeglichen wird. Der Owner eines Workpackages ist für dessen Umsetzung verantwortlich, die Personen im Feld CC
sind ebenfalls mit der Umsetzung beauftragt. - Kommentare
- Die Kommentare, die in einem WorkPackage-Ticket hinterlassen werden, sind das Protokoll über
den Verlauf des WorkPackages.
@-tags in der Beschreibung
@-tags werden in der Beschreibung des Tickets eingetragen.
- @start und @end
- Diese beiden Tags beschreiben den Beginn und das Ende eines WorkPackages. Sie müssen im Format YYYY-MM-DD sein und werden verwendet um die WorkPackages nach Beginn
zu sortieren. - @status
- Die Ampel des WorkPackages wird durch den Tag @status bestimmt, der standardmäßig auf grün steht. Mögliche weitere Werte sind rot und gelb. Die einzelnen Stati
bedeuten dabei:
grün: Alles in Ordnung, Zeitplan wird eingehalten, keine Probleme, Fragen, offene Abhängigkeiten, etc
gelb: es gibt kleinere Probleme
rot: Akutes dringendes Problem, das (sofort) gelöst werden muss - @task
- Mit @task beschreibt man, welche Person welche Aufgabe für dieses WorkPackage hatte. Die Beschreibung sollte dabei stichpunktartig sein. Das Format ist
@task [person] [Beschreibung der Aufgabe]
wobei [person] das Kürzel aus der Liste der Entwickler ist und [Beschreibung der Aufgabe] der beschreibende Text.
Zuordnung von Tickets zu WorkPackages
Tickets ordnet man einem Workpackage zu, in dem man in den Keywords des Tickets das Keyword des Workpackages einträgt, z.B. workpackage-115.
< 01. August 2011, 04:00 Uhr
What The Foto: API-Dokumentation
Ein nicht unerheblicher Teil meiner Arbeit am Projekt ist in die Erstellung einer übersichtlichen API-Dokumentation geflossen.
Wichtig war mir dabei, dass beide Teams einen guten Überblick darüber bekommen, welche Schnittstellen es gibt, und welche bereits implementiert sind — gerade letztere Information war für das Frontend-Team wichtig, damit sie wissen, welche Funktionen serverseitig schon unterstützt werden und mit welcher Funktion man dann dementsprechend im Frontend weiter machen kann.
XML als Basis
Ich hatte bereits aus früheren Projekten mit der Dokumentation von APIs zu tun, und habe mich daher als Basis für eine XML-Datei entschieden.
Zum Einen kann diese manuell leicht gepflegt werden, und mit einer passenden XSD bekommen Entwickler in einer IDE wie Eclipse auch direkt Fehlermeldungen angezeigt, wenn Sie Angaben eintragen, die nicht korrekt sind.
Zum Anderen — und dazu ist es leider nicht gekommen — kann die XML-Datei auch mittels Reflection aus bestehendem Quellcode generiert werden. Im Idealfall hat man im Verlaufe eines Projektes ein sauber dokumentiertes API-Package, dessen JavaDoc in das in der XML verwendete Format transformiert werden kann. Zeitlich sind wir leider nicht soweit gekommen, und so haben die Teams die XML-Datei manuell gepflegt.
Aufbau der XML-Datei
Ziel war es, die Schnittstellen-Kommunikation vollständig zu beschreiben, somit werden im ersten Abschnitt der XML-Datei Datentypen definiert.
Zuerst werden einfache Datentypen definiert.
<simpletype name="String">
<description>Repräsentiert ein String</description>
<example>Lorem ipsum</example>
</simpletype>
<simpletype name="DateTime">
<description>Repräsentiert ein Datum mit Uhrzeit. Das Format ist YYYY-MM-DDTHH:MM:SS+ZZZZ</description>
<example>2011-03-28T17:58:23+0001</example>
</simpletype>
Gefolgt von Enums, die in unserem Fall aus jedem der definierten einfachen Datentypen bestehen dürfen.
<enum name="Sex">
<description>Repräsentiert das Geschlecht einer Person</description>
<example>M</example>
<items>
<item value="M">
<description>männlich</description>
</item>
<item value="F">
<description>weiblich</description>
</item>
</items>
</enum>
Anschließend folgen die komplexen Datentypen, die in der jeweiligen Implementierung ValueObjects oder Models entsprechen.
<complextype name="Lightbox" type="Object">
<description>Repräsentiert eine Lightbox</description></p>
<property name="id" type="Integer" description="ID der Lightbox">
<example>28388</example>
</property>
<property name="name" type="String" description="Name der Lightbox">
<example>Projekt2011</example>
</property>
<property name="description" type="String" description="Beschreibung der Lightbox">
<example>Das ist die Beschreibung der Lightbox.</example>
</property>
<property name="state" type="LightboxState" description="Aktueller Zustand der Lightbox"/>
<property name="created" type="DateTime" description="Das Erstellungsdatum"/>
<property name="modified" type="DateTime" description="Das Datum der letzten Änderung"/>
</complextype>
Wie man sehen kann, habe ich sehr viel Wert auf eine ausführliche Dokumentation der Elemente wert gelegt. Es gibt zu jedem Typ mindestens eine Beschreibung und dort wo es nötig ist auch ein Beispiel für den Inhalt.</p>
Aus diesen Angaben lässt sich dann eine schöne Übersicht mit allen in der Kommunikation verwendeten Datentypen erzeugen.
Zu guter letzt werden die Schnittstellen-Methoden definiert. Diese sind zuerst thematisch gruppiert, in unserem Fall nach den Bezeichnungen der Entitäten aus dem Domänenmodell, z.B. Agency, Photo, Lightbox, User usw.
Innerhalb dieser Gruppe werden dann die dort verfügbaren Methoden definiert. Es werden alle Parameter einer Anfrage beschrieben, wie bei den Datentypen ebenfalls mit ausführlicher Beschreibung und bei Bedarf einen Beispiel, sowie die Art der Antwort.
Da wir mit ActiveMQ ein asynchrones Messaging verwenden und es auch Situationen gibt, in denen Ereignisse eintreten, ohne, dass der Client dazu eine Anfrage gestellt hat, werden auch bei manchen Methoden die dazugehörigen Notifications beschrieben.
<group name="Lightbox">
<description>Enthält Methoden die für Lightboxen benötigt werden.</description>
<action name="enter" inServer="true" inClient="true" messageType="LB_ENTER">
<description>Sobald ein Nutzer eine Lightbox öffnet, tritt er ihr bei.</description>
<request></p>
<property name="session" type="String" description="Die Session-ID des Benutzers">
<example>yvwH8WrBWqz3mabw2TDDTYDUSwDC54HC</example>
</property>
<property name="lightboxID" type="Integer" description="Die ID der Lightbox">
<example>34532</example>
</property>
</request>
<response></p>
<property name="result" type="SuccessMessage" description="Liefert Aussage, ob das Eintreten erfolgreich war."/>
</response>
<notification></p>
<property name="userID" type="Integer" description="userID des Nutzers, der die Lightbox betreten hat">
<example>234544</example>
</property>
<property name="lightboxID" type="Integer" description="Die ID der Lightbox">
<example>34532</example>
</property>
</notification>
</action>
</group>
Daraus lässt sich dann im Wiki eine übersichtliche Darstellung der Methode erzeugen. Durch die vorher definierten Datentypen und die korrekte Einhaltung der Reihenfolge in der XML-Datei, kann der Datentyp eines Parameters direkt mit seiner Definition verlinkt werden.
Zusätzlich können noch mit Hilfe der Ticket-Query-Funktionen von Trac dazugehörige Tickets angezeigt werden.
Die schwarze und weiße Fahne markiert übrigens, dass die Methode von Server und Client fertig implementiert ist. Diese Information ist dann auch in der Seitenleiste der Wiki-Seite (die man hier in voller Größe bewundern kann) einsehbar und bietet so einen schnellen Überblick über den Stand der Implementierung.
Python-Script zum Anlegen der Wiki-Seite
Die Wiki-Seite selber wird mit Hilfe eines Cronjobs über die XMLRPC-API von Trac regelmäßig erzeugt.
Dazu liest dieses Python-Script die XML-Datei aus und erzeugt daraus die Wiki-Seite.
Fazit
Die API-Dokumentation wurde vom gesamten Team sehr stark genutzt auch als sehr positiv empfunden. Leider haben wir in der Design-Phase des Projektes versäumt, explizit den Aufbau der Schnittstellen-Klassen zu definieren, wäre diese früher geschehen, hätte man mit Hilfe der XML-Datei einen automatisierten Satz Unit-Tests schreiben können, der so überprüfen kann, ob die dokumentierte Version der Schnittstelle auch mit den tatsächlichen Gegebenheiten übereinstimmt. Dies hätte auch verhindert, dass im Server eine Schnittstelle still geändert wird und der jeweilige Entwickler keine Möglichkeit hat, seine Änderung auf Auswirkungen bezüglich der Schnittstelle zu prüfen — allerdings hätte dann auch mehr Arbeitsleistung in das reine Testen der Schnittstelle fließen müssen — und das auf beiden Seiten.
< 19. July 2011, 15:36 Uhr
Drucker automatisch passend zum Raum setzen
Dieses Python-Script ermittelt anhand des Hostnamens des Rechners, an dem man sich im Fachbereich der Medieninformatik an der Hochschule RheinMain einloggt, den passenden Drucker.
Installation
wget --no-check-certificate https://github.com/tacker/hsrm-mi-utils/raw/master/printer-autoselect.py -O ~/printer-autoselect.py
chmod +x ~/printer-autoselect.py
echo '~/printer-autoselect.py > /dev/null' >> ~/.bashrc
Anschließend wird das Script nach jedem Login ausgeführt.
< 17. May 2011, 09:49 Uhr
Tags: #allgemein #python
Dropbox synct jetzt via WebDAV
Ein Update am Ilias über Ostern hatte zur Folge, dass mein Synchronisations-Script, dass sich mit wget --mirror die Daten zieht, nicht mehr funktionierte.
Heute habe ich das Script dann vollständig auf Python umgestellt und verwende dazu diese WebDAV-Bibliothek.
Wer mag, kann sich den Quellcode meines Scripts auf github ansehen: WebdavMirror.py.