Tag Archives: python

hacks

Terminal-Update

Ein kurzes Update zu unserem Projekt Octopus, einfach weil’s so geil läuft. (Und ich hasse normalerweise das Wort geil, aber in diesem Fall ist es angebracht!)

Unser heldenhafter Möbelprofi hat’s vollbracht: Alle Komponenten sind im Gehäuse sicher und für zukünftige Wartungen optimiert untergebracht. Außenrum bleibt das Interface “clean” und daher für die Benutzer übersichtlich. Der A4-Drucker ist dank Umbau des Papierauswurfs runter auf eine Fehlerrate von 1:300 und somit unterhalb der Quote, die wir mit den Originalteilen des Herstellers messen. Sämtliche Bedienelemente befinden sich in Greifhöhe für Rollstuhlfahrer. Die L-förmige Lichtleiste wird das Bedienkonzept einer Art “Farbleitung” ermöglichen. Aber was schreibe ich, wenn es Bilder gibt:

Der Mock-up ist beinahe fertig. Ursprüngliches Ziel - ein "cleanes" Interface - geschafft.

Materialkunde

Für jemand, der normalerweise mit Bits und Bytes zu tun hat, ist die Wahl der Materialen zugegebenermaßen eine Herausforderung. Immerhin sind Langlebigkeit, Kosten, Widerstandsfähigkeit gegen Reinigungsmittel und Vandalismus zu bedenken. Unser Ergebnis: Weiße Melaminplatte auf blankem Stahl – passend zum Gebäude, das mit Sichtbeton und schrägen Wänden auffallen wird.

Lokalaugenschein am künftigen Standort - ein echter Business-Termin.

Software: Python, Python, Python!

Abseits vom Möbelstück hat sich auch bei der Software unglaublich viel getan.

So wurde etwa beim Bezahlmodul eine proprietäre Library durch eine quelloffene Implementierung des Protokolls ZVT 700 in Python ersetzt. Damit können wir, von der Plattform unabhängig, den Leser der Card Complete einsetzen. “Abfallprodukt” dieser Entscheidung ist, dass wir künftig auch Zahlungen per Kreditkarte akzeptieren können.

Integration "Bankomat": Hard- und Software verschmelzen mit unserem Terminal.

Bei Webcam und den drei (!) Druckern lautete die Marschrichtung ebenso hin zu offenen Schnittstellen. Protokolle wie Apples CUPS machen die Ansteuerung der einzelnen Komponenten einfach(er), da viel der Komplexität versteckt wird. Anstatt des Modells “Treiber + lokal Anstecken” hat sich ein rein TCP/IP-basierter Aufbau durchgesetzt.

Als User Interface Library fiel die Wahl auf Nokias Qt - ein Wahnsinn in Sachen Flexibilität und Robustheit. (Nicht zuletzt darum tut mir der Einstieg von Windows Phone 7 bei Nokia im Herzen weh…) Die Python Bindings für Qt (PyQt) ermöglichen die Entwicklung in reinem Python-Code. Mit QWebview, einem für den User unsichtbar  in das UI eingebetteten WebKit (Google Chrome), werden wir einen Teil der Funktionalität mittels Web-Technologien implementieren. Diese hybride Art der Applikationsentwicklung wird ansonsten häufig bei mobilen Apps betrieben, um Apps auf Android, iOS und Co. mit nur einer Codebasis zu betreiben. Bei uns geht’s allerdings eher um die Entwicklungsgeschwindigkeit, da HTTP schlicht die lingua franca in der IT ist.

Richtig abgehoben wird das Projekt letztlich bei der Lichtsteuerung, einem Bedienkonzept, welches Benutzer anhand von Lichtimpulsen und Farben anstatt von Beschriftungen und Erklärungstexten leiten soll. Ein prototypisches Video kann bei Google Plus angesehen werden.

Photoshooter

Obwohl ich mich eher um die Projektabwicklung, als um die tatsächliche Implementierung kümmere, wollte ich eine Sache selbst hinbekommen: Die Software für den Photoshooter. Jeder WU-Student kennt das Ding, muss man doch am Tag der Inskription genau dort hinein lachen…

Erste Erfolge mit der Webcam.

Eines war mir jedenfalls nicht so ganz klar, als ich das Modul übernommen hatte: Dass ich eine long and winding road begehen würde bevor das Ziel auch nur halbwegs in Blickweite sein würde…

Kurzum, nach zahllosen gescheiterten Versuchen mit diversen APIs von VoIP-Clients bis hin zum Sourcecode der Kamera des Nokia N900 bin ich nun mit opencv glücklich. Diese Library für maschinelles Sehen tut aber nur am Rande, wonach ich hauptsächlich gesucht habe. Fokus liegt eher bei der Bilderkennung, und so war dann auch – das nächste “Abfallprodukt” – eine Gesichtserkennung schnell implementiert. Während man jetzt den Bildausschnitt mit dem Gesicht manuell auswählen muss, übernimmt das künftig die Software. Die überraschend geringe Fehlerrate bei der Gesichtserkennung trägt außerdem dazu bei, dass künftig nur noch erkennbare Bilder akzeptiert werden. Ein, wie ich, fauler Programmierer spart sich außerdem das ganze Gezoome und Gecroppe im UI.

Gesichterkennung mit Intels opencv: Das Gesicht kommt künftig automatisch erkannt auf den Studierendenausweis. Ergebnis sind ein einfacheres UI und eine indirekte Qualitätskontrolle, da der Algorithmus nur richtig belichtete Gesichter erkennt.

Linux statt Windows

Plattformunabhängigkeit, Python, offene Schnittstellen – all das dient letztlich dazu eine der letzten Domänen von Windows zu beenden: Jene der interaktiven Terminals. Unser Betriebssystem wird Ubuntu Linux 12.04 LTS. Mit TFTP-Boot und automatisiertem Checkout aus dem SVN-Repository ist auch hier die Reduktion der Wartung bereits Teil des Kernkonzepts.

Schon während der Entwicklung setzen wir auf continuous integration. Das bedeutet, dass zwei Testrechner laufend selbständig nach neuen Releases suchen und sich im Falle notwendiger Updates automatisch aktualisieren. Was jetzt also das Testen ungemein erleichtert, bedeutet im Produktivsystem einen stabilen Rollout-Mechanismus, der keinerlei manuelle Eingriffe benötigt.

Und noch so viel zu tun…

Nach einem guten halben Jahr Entwicklung sind wir nun an dem Punkt, wo alle Teile für sich alleine funktionieren. Nun heißt es, die einzelnen proofs-of-concept zu einem Ganzen zu verbinden und danach zu testen, zu testen und zu testen. Bis hierher sieht es also gut aus, aber die Geschichte bleibt spannend…

python

Memcached decorator for Python

Bad English, yet findable by search engines…

After returning from Europython 2011 I was specially excited about two things: Memcached and Python’s decorator syntax. Don’t know why this took so long but finally it was  time to try things out.

Memcached is a memory-based caching system that can be easily installed on various operating systems. Online documentation is abundant (try this) - so let’s focus on it’s purpose. Imagine some expensive function, e.g. a CPU-costly database calculation or some network-bound function. For the sake of simplicity my example just sleeps for 5 secs:

import time

def expensive_function(x):
    time.sleep(5)
    return x

expensive_function('some_input')

The function in my example always returns the same output given an input, thus it is said to be referentially transparent. Hence the expensive function can be replaced with it’s value. (This technique is either called caching or memoization but I don’t really care about the disambiguation.)

The first approach looks like this:

import memcache
import time

def expensive_function(x):
    mc = memcache.Client(['127.0.0.1:11211'], debug=0)
    # calculate a unique key
    key = 'expensive_function_%s' % (x,)    
    # check if key/value pair exists
    value = mc.get(key)
    if value:
        return value
    else:
        time.sleep(5)
        value = x
        # cache result for 5 mins
        mc.set(key, value, 60 * 5)
        return value

expensive_function('some_input')
expensive_function('some_input')

On the first invocation the function now runs for five seconds whereas the second will return immediately. A unique key is needed to store and retrieve the values. Since memcached is not bound to the Python runtime the speedup also works in a second process.

The return value depends on the functions’s input value. So the key must contain (some form of) the input. I will show a better solution below.

By now, it’s possible to cache results of a single function. In a bigger application context we run into two problems. First, we violate the DRY principle because we have to write the code getting and setting key-value pairs over and over again. Second, and more severe, we risk setting the same key for different combinations of functions and input parameters. Since this could lead to harmful errors hard to debug it seems advisable to place the caching logic in a single point of your code.

Thanks to Python’s decorators the solution is easy. (A decorator simply is a function that intercepts execution of another function or method. So every time we call expensive_function a wrapper function will look for cached results and decide whether to invoke as intended or return from the cache immediately.) The function can now be decorated. The code readability is preserved.

#!/usr/bin/env python

import time
import memcache
import hashlib

def memoize(f):

    def newfn(*args, **kwargs):
        mc = memcache.Client(['127.0.0.1:11211'], debug=0)
        # generate md5 out of args and function
        m = hashlib.md5()
        margs = [x.__repr__() for x in args]
        mkwargs = [x.__repr__() for x in kwargs.values()]
        map(m.update, margs + mkwargs)
        m.update(f.__name__)
        m.update(f.__class__.__name__)
        key = m.hexdigest()

        value = mc.get(key)
        if value:
            return value
        else:
            value = f(*args, **kwargs)
            mc.set(key, value, 60)
            return value
        return f(*args)

    return newfn

@memoize
def expensive_function(x):
    time.sleep(5)
    return x

if __name__ == '__main__':
    print expensive_function('abc')
    print expensive_function('abc')

The function name, class name and all parameters passed produce a hash key which will be used as key by memcached. I’m still not happy with the solution because uniqueness of the key is not guaranteed. (And all solutions I can think of revolve around some string concatenation magic…) A glimpse at more sophisticated implementations, e.g. Django’s cache decorator or memorised, work pretty much the same way though.

hacks

Redesign SAP Finanzbericht

Zur Thematik, dass erfolgreiche ERP-Systeme nicht zwangsweise durch einfach bedienbare User Interfaces glänzen, habe ich bereits gebloggt. Spätestens, seitdem ich meine eigene Kostenstelle verantworte, war’s nun aber höchste Zeit, den Bericht, der mir Überblick über meine Finanzen geben sollte, zu überarbeiten.

Zu allererst ein Blick auf den Status Quo

Nach einigem Herumgeklicke und der Erkenntnis, dass SAP-Jahre aus 16 Monaten bestehen, erreiche ich Woche für Woche den Überblick über meine Kostenstelle im Look & Feel von Teletext, aka SAP GUI.

Der Bericht listet alle von mir unterschriebenen Rechnungen mit Betrag, Datum, Belegtext sowie Kostenart. Um den Leser nicht mit Details zu verwirren (so interessant sind meine Zahlen nun auch wieder nicht), habe ich die Berichtsstruktur hervorgehoben:

Der Bericht zeigt auf Kontierungselemente (Kostenstellen) gebuchte Kosten aufgeschlüsselt nach Kostenarten (Konten). Die Logik des Berichts orientiert sich mMn zu stark am System.

Im Großen und Ganzen befinden sich auf der Seite zwei interessante Blöcke: eine Übersicht oben links, sowie eine Tabelle mit den Buchungen darunter.

Wenn ich mir nun überlege, welche Aufgaben ich mit dem Bericht abwickeln muss, dann sind das nach absteigender Wichtigkeit:

  1. Überblick über Planungs- sowie Ist-Stand meiner Finanzen
  2. Details zu einzelnen Rechnungen (z.B.: im Falle von Reklamationen)
  3. Analyse meiner Kostenstruktur nach Zeit und Kostenart

Traurig, aber wahr: bereits der erste Task ist mit dem Bericht nicht ohne Weiteres möglich. Die Transaktion, also der SAP-Name für eine Bildschirm-Maske, listet entweder Plan- oder Ist-Daten. Die jeweils andere Ansicht erreicht man durch Beenden und Neustart des Berichts nach anderen Auswahlkriterien. (…)

Finanzbericht Marke Eigenbau

Eine kleiner Entwurf auf einer McDonalds-Rechnung (ein so genannter Low-Fidelity Mockup) und ein paar Stunden Datenexport, HTML, SQL, Javascript und Python später ist die Alternative fertig: Eine Webseite, die die drei oben genannten Tasks auf einen Blick ermöglicht.

Der Kasten oben links bietet mir die Gesamtsummen von Ist und Plan als Zahlen, zusätzlich verdeutlicht ein Prozentwert das Verhältnis (Wieviel ist schon weg?).  Darüber ein einziges Auswahlfeld für das Berichtsjahr, kein Formular-Moloch. Rechts daneben befindet sich Säulendiagramm, das mir dieses Plan/Ist-Verhältnis nochmal grafisch verdeutlicht. Bei mehreren Jahren – bei mir nicht der Fall – sieht man so automatisch den Budgetverlauf.

Die Details zu einzelnen Rechnungen finden sich darunter. Die Tabelle verwendet das jQuery-Plugin DataTables, welches Such-, Sortierfunktion und Pagination bietet. So sind ein Sortieren nach Datum, oder das Suchen einer bestimmten Rechnung keine große Hexerei. Die rein Client-seitige Umsetzung sorgt dafür, dass das Ding responsive bleibt.

Alternativvorschlag: Ein Überblick über Plan und Ist (gleichzeitig!) oben links, eine Tabelle aller Buchungen mit Sortier- und Suchfunktion darunter. Oben rechts ein paar grafische Darstellungen eben jener Daten.

Den Zuckerguss stellen die drei Diagramme im TabbedView oben rechts dar. Neben dem bereits besprochenen Jahresüberblick, gibt es noch ein Liniendiagramm, welches den Planungsstand mit dem Iststand unterjährig vergleicht. Dadurch dass auf Jahresebene geplant wird, ist eine kumulative Auswertung sinnvoll.

Die wichtigste Frage auf einen Blick beantwortet: Bin ich drüber oder drunter?

Abschließend noch eine Auswertung nach Kostenarten, die eine hierarchische Datenstruktur mit variabler Tiefe darstellen. In diesem Fall habe ich mich für eine Treemap entschieden. Diese codiert zwei Variable (Größe des Rechtecks entspricht der Summe; Farbe ist die Abweichung) in eine klickbare Fläche. Das “Entdecken” der Kostenzusammensetzung macht so richtig Spaß.

Sehr schöne Visualisierung einer hierarchischen Datenstruktur: die Größe entspricht der Summe, die Farbe der Plan/Ist-Abweichung

Die hier dargestellte Kritik an aktuellen Berichten ist positiv gemeint und soll keineswegs als SAP-Bashing missverstanden werden. Dass die Software derart erfolgreich war, ist und vermutlich bleiben wird, hat schon seine guten Gründe. Ich bin selbst überrascht, wie sich mein Blick auf eben jenen Bericht verändert hat, seitdem ich auch wirklich ein Anwender bin. (…)