Tag Archives: django

hacks

Last-minute door hack

Im Rahmen eines Großprojekts wird auf einige Türen und die Steuerbarkeit selbiger vergessen. Und schon sitzt man in einer großen Runde und diskutiert unter Zeit- und Kostendruck viele umständliche Lösungen. Was hier folgt, ist die wahrscheinlich größte Kosteneinsparung pro Zeiteinheit Softwareentwickler in der Geschichte des Häuslbauens – mein unvorstellbarer Kaffeekonsum bereits berücksichtigt… 

Man sollte meinen, eine Tür über einen Taster zu öffnen sei trivial. Doch was ist in einem Gebäude mit 1.500 Mitarbeitenden und 6.000 Türen schon trivial? Insbesondere wenn man nicht Türtaster auf jeden Schreibtisch stellen kann, und das Gebäude wenige Wochen vor Eröffnung ansich bereits fertig verkabelt sein sollte.

Elektriker öffnen Türen üblicherweise über potentialfreie Kontakte, also mit viel Kupfer zwischen Tastern und den Türen, aber eigentlich auch nur deswegen, weil die law of the instrument greift:

“if all you have is a hammer, everything looks like a nail”

Ich glaube, diesmal war dieses Gesetz auf meiner Seite, bzw. war ich eben in diesem Moment derjenige mit dem richtigen Hammer. Nailed it!

Türen via TCP/IP öffnen

Im Rahmen des Projekts rund um die elektronische Schließanlage haben wir uns eine Programmierschnittstelle (API) ausbedungen, welche unter anderem auch Türen öffnen soll. An einer Universität bestehen hierfür zahlreiche Anwendungsfälle, beispielsweise beim Öffnen von Lehrräumen während gebuchter Veranstaltungen. (An der “alten” WU habe ich das bereits mit einem heldenhaften reverse engineering des Kaba-Systems exos 9300 zustande gebracht. Aber damals gab’s diesen Blog noch nicht und ich hatte auch noch deutlich mehr Zeit zum Programmieren.)

Der richtige Client

Die Türöffnung via Klick auf einer Webseite auszulösen, drängt sich aus technischer Perspektive auf. Doch was ist mit Personen, die keinen  Rechner besitzen, oder gerade wieder mal auf das elendslange Einspielen diverser Windows Patches warten müssen? Eine Türöffnung sollte immerhin augenblicklich erfolgen.

Von einer vor der Tür stehenden Besucherin wird man angerufen, man ist also bereits am Telefon. Aus funktionaler Sicht liegt es folglich nahe, die Türöffnung auf dem Telefon zu bedienen. Cisco sei dank, gibt es für deren Call Manager eine umfangreiche Schnittstelle zur Steuerung des Telefons via XML.

ooo
Über XML eingespieltes Menü für mein Cisco Telefon.

Der Quellcode zur Darstellung des Menüs auf dem Foto:

<?xml version="1.0" encoding="utf-8"?>
<CiscoIPPhoneMenu>
  <Title>Bach Door Service</Title>
  <Prompt>Security FTW</Prompt>
  <MenuItem>
    <Name>IT Front Office</Name>
    <URL>https://bach.wu.ac.at/dd/playground/dooropener/open/1</URL>
  </MenuItem>
  <MenuItem>
    <Name>IT Back Office</Name>
    <URL>https://bach.wu.ac.at/dd/playground/dooropener/open/2</URL>
  </MenuItem>
</CiscoIPPhoneMenu>

gfg
Tür offen!

HTTP und Security

Sind alle Komponenten fertig, gilt es diese über HTTP zur Verfügung zu stellen. (Bevor noch jemand meckert: Telefone landen in einem eigenen VLAN – Türen gehen nur bei Anfragen aus dessen Adressbereich auf.)

e drfr fdw
Web-Interface zur Verwaltung von Telefonen, Türen, Personen und Organisationseinheiten. Die beiden letzteren werden automatisch aus SAP übernommen.
fdfdf
Bei Tastendruck am Telefon wird ein reverse lookup durchgeführt, der entsprechend zugeordnete Türen beim Telefon, bei der eingeloggten Benutzerin und deren Organisationseinheiten sucht. Zur Kontrolle gibt’s die Funktion auch im Web.

tltr;

Etwas Wagemut, acht Stunden Python-Hack und schon ist ein ganzes (Um-)Bauprojekt überflüssig.

hacks

Visualizations i love

In the making of our room booking software we’ve invested a huge amount of time in interactive visualizations. Enjoy!

fdg
Visualizing supply and demand during the semester’s planning phase (blogged).

 

fdg
Schedules of selected room and possible alternatives. This booking page can be controlled via text input as well as drag&drop – both at the same time interacting with each other!

 

qwer
Densely aggregated occupancy stats for each room per semester. (Thanks to mbostock’s calendar view)

Lessons learned

  • I will no longer chose colors by myself, since there’s a lot of psychology behind colors and our perception thereof. Try: colorbrewer, colorpicker
  • Thoughtfully design and then stick to your own APIs. Interacting with visualizations dangerously leads to a messy codebase since there are (too) many technologies involved.
  • Force users to use a modern browser.
hacks

Tetris 3D!

Vor kurzem habe ich über die Tetris-Maschine, einen Algorithmus zum Finden optimaler Raum-Zeit-Kombinationen in Reservierungssystemen, gebloggt. Diesmal ist die Aufgabe dieselbe, der Lösungsweg allerdings ein gänzlich anderer.

März oder Oktober – die WU plant – wie andere Universitäten ebenso – Lehrveranstaltungstermine des jeweils kommenden Semesters. Der Prozess gleicht dem Einräumen meines Kühlschranks nach dem Großeinkauf: Zuerst müssen große Veranstaltungen fixiert werden, kleinere müssen sich an die Gegebenheiten anpassen – also nehmen, was noch übrig ist. Mit zunehmender Fragmentierung durch Blockveranstaltungen oder Prüfungstermine wird es somit immer schwieriger, einen fixen Raum oder auch nur eine stabile Beginn- und Endzeit für Veranstaltungen zu finden. In diesem Fall kann eine Visualisierung der Belegung sinnvoll sein.

Doch wie soll man knapp 20.000 Veranstaltungen in 150 Räumen pro Semester übersichtlich darstellen?

Die Referenz

Anbei ein Screenshot vom aktuellen Buchungssystem: Wochentagsweise werden die Belegungen dargestellt – pro Raum gibt es eine derartige Auswertung. Die einzelnen Kürzel codieren die Veranstaltungen, ein Überblick über den gesamten Campus ist allerdings so nicht möglich.

Belegung eines Raums über ein ganzes Semester nach Wochentagen. ASCII-Art rules!

Der Re-Write

Trotz des geekigen Charmes der obigen ASCII-Darstellung lautete der Plan, die Belegungen interaktiv zu visualisieren. Typische Anwendungsfälle sind etwa:

  • “Ich benötige kommendes Semester einen 60er-Raum am Donnerstag Nachmittag.”
  • “Ich benötige in der ersten Semesterhälfte einen 30er-Raum am Montag und Mittwoch zur selben Beginnzeit.”
  • “Ich benötige für Termine 1, 2, 3, 4 und 5 einen Hörsaal.”

Die Idee: Die Belegung sollte Raum, Uhrzeit und Datum dreidimensional darstellen, also Datum über Datum legen. Mit semitransparenten Balken pro Belegungen könnte die Farbintensität somit die Dichte der Buchungen codieren. Durch Zu- und Wegschalten einzelner Tage, wäre es außerdem möglich, schnell in der Datenmenge zu navigieren.

Mock-up Dez. 2011: Meine ursprüngliche Idee, wie man dreidimensional durch die Buchungen am Campus blicken sollte.
Low-Fidelity Prototype auf Papier. Die Achsen für Uhrzeiten und Räume sind nun vertauscht, denn eine variable Anzahl an Räumen expandiert besser nach unten als nach rechts.

Nach einigen Spielereien mit absoluter Positionierung von <div>-Elementen im DOM fiel die Wahl doch recht schnell auf das weitaus effektivere d3.js. Diese Javascript-Library ermöglicht das Erstellen interaktiver SVG-Dokumente. Meine Hassliebe zu Javascript war jedenfalls überwunden, als das method chaining der Library endlich von den Fingern ins Gehirn übergegangen war:

function actually_draw(data) {

  d3.select("#bookings")
    .append("g")
      .attr("id", "day" + data.day)
    .selectAll("rect.booking")
    .data(data.bookings)
    .enter()
    .append("rect")
      .attr("class", "booking")
      .attr("width", function(d) {
        return linear_scale(time_to_float(d.end)) - linear_scale( time_to_float(d.start))
       })
      .attr("height", R_AXIS.incr - 2)
      .attr("x", function(d) { return linear_scale( time_to_float(d.start) ) } )
      .attr("y", function(d) { return  room_y(d.room)});  
}

Exzessives method chaining gepaart mit einigen wenig intuitiven Methoden wie .selectAll() (macht man, obwohl noch nichts vorhanden ist) oder .data(data).enter() (“steigt” hinein in die Daten). Ist man da mal durch, ist plötzlich alles möglich!

Endergebnis: Über 2000 Termine auf einer Seite visualisiert, interaktiv mit dem Kalender zu steuern. Echte Daten der WU im Sommersemester 2013.

Auch wenn der Screenshot noch nicht toll aussieht: Der Browser holt sich mehrere tausend Termine und rendert diese ohne Performance-Probleme bei jedem Klick im Kalender. Sourcecode und Live-Demo sind verfügbar unter:

http://www.zieglergasse.at/stuff/2012/bookingviz/view.html

 

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. (…)