Category Archives: hacks

hacks

Von Algorithmen, Königreichen und Platzmangel

Obwohl ich mit dem vergangenen Post zum Thema Campus WU das Projekt für mich selbst abschließen wollte, muss ich hier noch einen Artikel nachschießen:

In der aktuellen Uni-Beilage von Der Standard komme ich als Vertreter der WU zu Wort. Der Artikel handelt vom Tool “more space”, das wir vorab zur Simulation der Gebäudeauslastung eingesetzt haben.

hgfd
“Der Standard”-Artikel über Raumeffizienz an den drei großen Wiener Universitäten.

tltr;

Unis verschwenden enorm viel Platz durch unflexible Stundenpläne und Besitzansprüche einzelner Mitarbeiter. An der WU läuft die Verteilung der Ressource Raum zentral und daher deutlich effizienter, als vermutlich anderswo.

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

Kiosk Adapter für Evolis Tattoo RW 2

Semi-professionelles 3D-Drucken ist bekanntlich gerade Hype im Web und somit aktuell der heißeste Scheiß für Hipsters, Startups und – ja – mich. Grund genug also, ein wenig Erfahrung zu sammeln. Großer Dank geht an Martin Kahr, der den Drucker und vor allem viel Zeit zur Verfügung gestellt hat. (Preise bei ihm auf Anfrage) 

fge
Das Endprodukt in Serienreife: Ein Kiosk Adapter für den Evolis Tattoo RW 2. Mit dem auf Bruchteile von Millimetern genau gefertigten Kartenschlitz wird aus dem Tischgerät ein für den Einbau geeignetes Unattended-Gerät.

Die Problemstellung

Das Herz des Projekts Octopus ist ein Evolis Tattoo RW2. Das ist ein MIFARE-Kartenleser, der auf speziell beschichteten Ausweiskarten Thermodrucke im ReWrite-Modus durchführen kann. An der WU Wirtschaftsuniversität Wien benötigen wir diesen ThermoReWrite-, oder kurz TRW-Druck für die semesterweise Aktualisierung der Studierendenausweise.

Wir waren schon sehr froh, als wir 2011 ein Gerät gefunden hatten, das unseren Ansprüchen hinsichtlich Verarbeitungsqualität, Support und v.a. Softwareschnittstellen genügte. Unser Wunsch nach einem Einbaugerät ging beim französischen Hersteller leider nicht in Erfüllung, und so platzierten wir den Drucker beim Terminalbau möglichst präzise hinter der Frontblende.

tattoo
Evolis Tattoo RW 2 – leider nur ein Tischgerät.

Die Methode funktioniert in fast allen Fällen, doch es gibt Ausweiskarten, die selbst diesen kleinen Übergang zwischen Front und dahinterliegender Hardware beinhart ausnützen: Eine dieser Killerkarten konnte ich kurz nach ihrer Straftat fotografieren. Kurzum: Wir hatten in den vergangenen Monaten immer wieder das lästige Problem, steckengebliebene Karten aus überforderten Auswurf-Mechanismen zu entfernen.

killer
Ein Killer-Ausweis, der den Auswurfmechanismus und somit das ganze Terminal lahmlegt.

Unser Tischler verkleinerte daraufhin das Kartenmaul, um der Karte bei Einzug weniger Spiel zu geben. Besonders ärgerlicher Effekt: Die fehlerhaft eingesteckten Karten konnten wir somit auf Null reduzieren. Nur konnte daraufhin der Drucker umso mehr Karten gar nicht mehr auswerfen.

grenzen
Der Tischler stößt beim Karteneinzug an seine Grenzen in Sachen Genauigkeit.

 

Außenseite
Außenseite des Terminals. Das Kartenmaul ist zu groß – die Karte hat demnach zu viel Spiel.

 

Rückseite
Innenansicht des Terminals. Die durchgesteckte Karte geht manchmal leider daneben.

 

eng
Der Drucker schließt beinahe nahtlos mit der Innenseite ab. Doch Spielräume <1mm reichen bereits aus, um Fehler zu produzieren.

3D-print your printer!

Für uns war somit klar, dass ein Kartenmaul aus Holz immer und immer wieder zu ungenau sein würde. Aufgrund der Natur des Werkstoffs (Melamin) und insbes. dessen Bearbeitungsform (Fräsen) kann nur bis zu einer gewissen Genauigkeit gearbeitet werden. Folglich musste ein Kartenmaul aus einem anderen Material her. Aber soll man ein komplettes Terminal neu designen, nur weil ein Bauteil nicht optimal funktioniert?

Kurzum: Die Idee des Kiosk Adapters war geboren und mit neuartiger 3D-Drucktechnologie war auch die Umsetzung nicht weit.

design
Design des Adapters mittels CAD. Die Außenform des Druckers wird vermessen und das Werkstück wird nahtlos angepasst.

 

nnnn
Druck des ersten Prototypen am MakerBot.

 

jhj
Nahaufnahme MakerBot: Erhitzter Kunststoff wird in hauchdünnen Schichten und somit dreidimensional aufgetragen.

 

proto
Der Adapter ist an die Außenform des Druckers angepasst.

 

fff
Erstes funktionierendes Modell. Das Kartenmaul ist nun auf Bruchteile von Millimetern genau, Kartenein- und auswurf funktionieren.

 

rt
An der Terminal-Innenseite angebrachter Kiosk Adapter. Der Drucker wird nur noch eingehängt.
rt
Der Unterschied zwischen Soft- und Hardware? Bei letzterer muss man jeden noch so kleinen Fehler sofort ausbessern;-)
hacks

Designing APIs that work

With something around 20 systems that need interconnection, building APIs might currently be my main task at work. The systems involved range from access control, digital signage, kiosk and wayfinding terminals, CO2-sensors, facility management, resource allocation, geo-information to payment for printing or catering. In the context of an awfully huge project virtually everything needs data from somewhere else with schedules being extremely tight and vendor-side flexibility sometimes not being present at all. This simply means interface hell.

So be prepared for some lessons learned from someone who’s already made every possible mistake by himself:

One of the first public interfaces I wrote was a course data export for a mobile app, then running on iOS. The app developer and I decided on JSON using a list for the various attributes (title, location, time, etc.) of every course record:

[
  ["09W", "1985", "Wirtschaft im rechtlichen Kontext ", "H. 3.33 (B)", "UZA 1", "19:00", "21:00", 
    [
      [5803, "Lastname M."],
      [9651, "Lastname2 D."]
    ], "course", "20091212"
  ], 
  ...
]

As a matter of fact my first call came with two design failures attached: a badly chosen data structure and an unnecessary invention of something that was widely available anyway.

Use key-value pairs

First, using a list was a bad idea since position mattered and extending the interface immediately became a challenge since we quickly had 3.000 client-side installations out in the field. When using key-value pairs you retain the possibility of extending the data structure.

[
  {"semester": "09W",
   "course_id": "1985",
   "title": "Wirtschaft im rechtlichen Kontext ",
   ...
   }
]

Use wide-spread formats

Second, I suffered from the NIH anti-pattern since there was a decent exchange format available anyway: iCalendar. With a rewrite to the latter format data became usable even outside our app. People started importing personalized course lists into their calendars of choice.

Add metadata

After some time I realized that my course export was not only useful in its intended context but also in many other places: on digital signage screens for example. The latter project needed metdata so I added a timestamp and nested the payload inside the top level data structure.  

{"last_update": "2013-03-17T13:19:18",
  "courses":
    [
      {"semester": "09W",
       "course_id": "1985",
       "title": "Wirtschaft im rechtlichen Kontext ",
       ...
       }
    ]
}

Serialize

With calls available for courses, organizations, employees, publications, rooms, etc. in different flavours – usually search(), list() and get() – the overall structure soon became messy. Especially inconsistent naming started to be a pain when programming the server backend: employees had a "first_name" as members of organizational units whereas the same people carried a "firstName" when they authored publications.

While the key-value approach was a fine idea on the public side of my API the backend urgently needed refurbishment. I steadily moved my code to a more object-oriented approach which meant that I used Employee() objects rather than simple data structures. Nevertheless the API’s output consists of simple data types – so the magic of turning objects into JSON strings is mainly done by a serialize() method which every class must have:

class Employee(object):
    """Main Employee class"""
    ...
    
    def serialize(self):
        return {
	    'tid': self.tid,
	    'first_name': self.first_name,
	    'last_name': self.last_name,
            ...
        }

Add options

With more and more applications using the calls the clients became more demanding. For our wayfinding kiosk application I was asked to add pagination and custom sorting to some of the calls. I decided to provide an API-wide feature.
The result should look like this while sorting, limit, offset, etc. can be passed as arguments to the call (as key-value pairs rather than positional arguments of course!):

{"last_update": "2013-03-17T13:19:18",
  "offset": 100,
  "limit": 25,
  "sorting": {
      "fields": ["semester", "title"],
      "reverse": false
  },
  "courses":
    [
      {"semester": "09W",
       "course_id": "1985",
       "title": "Wirtschaft im rechtlichen Kontext ",
       ...
       }
    ]
}

Instead of painfully implementing pagination and sorting in a hundred different places I wrote a decorator that enables the functionality in a single place. The available sorting parameters have to be passed to the decorator since they differ from call to call.
@search_option_factory(set(['semester', 'title']))
def search_courses(self, value):
    """A sophisticated course search"""
    ...
        
    return courses

The decorator itself looks a bit complicated since passing arguments to decorators is a bit tricky. You actually have to write a function that handles the input and returns a decorator. Thanks to stackoverflow it’s not that difficult after all.
    def search_option_factory(sorting_set):
        """A decorator factory that handles generic options for calls.
        
        sorting_set: set that defines sortable fields
        
        according to Robert's spec:
        
        paging-objekt (struct) mit folgenden keys verwenden:
        - offset (null/int)
        - limit (null/int)
        - order (null/string)
        - desc (null/false/true)        
        """
        def search_decorator(method):
            
            def wrapper(self, value, options):

                # call the method
                recs = method(self, value)

                # count the recs
                count = len(recs)

                ## parse the options
                if not options:
                    options = {}
                
                sorting = options.get('sorting')
                if sorting:            
                    reverse = False
                    if options.has_key('reverse'):
                        r = options['reverse']
                        assert(isinstance(r, bool))
                        reverse = r
                
                    assert isinstance(sorting, list)
                    assert set(sorting).issubset(sorting_set)
                
                    sortator = ','.join(['d.%s' % x for x in sorting])
                    recs = sorted(recs, key=lambda d: (eval(sortator)), reverse=reverse)
                      
                
                offset = options.get('offset')
                if offset:
                    assert isinstance(offset, int)
                    recs = recs[offset:]
            
            
                limit = options.get('limit')
                if limit:
                    assert isinstance(limit, int)
                    recs = recs[:limit]
                
                return {
                    'count': count,
                    'limit': limit,
                    'offset': offset,
                    'payload': recs,
                    'created_at': datetime.datetime.now().isoformat()
                }
            
            return wrapper
        return search_decorator

hacks

Project visualization with redesigned Gantt charts

When reading the Edward Tufte forum on Project Management Graphics I was surprised (and relieved) that a lot of people seem to have their problems with Gantt charts and their doubtful usefulness for project management.

After some thoughts on the topic I’d like to present my proposal for visualization of project flows. In case it matters: I’m currently part of a half a billion Euro construction project in Austria.

The real world example

The image below shows one Gantt chart of the project I’m currently involved in.

dfgh
Actual Gantt chart of “my” current project.

Aside from its professional look it transports surprisingly little content. My main points of criticism are:

  • On the one hand, with more than a hundred tasks on the left I get overwhelmed by things I’m not responsible for anyway.
  • On the other, my 15 tasks lack important detail.
  • Since it’s not possible to combine overview with sub-project specific detail I have to manage my tasks in parallel.
  • I easily lose track between tasks and the bars on the far right. Gridlines would be a bad idea as well since they add too much clutter.
  • Data is sparse and the chart needs too much space for the presentation thereof.
  • In my context of software development the chart’s implicit focus on sequential, waterfall-like project flows does not fit my reality of ever-changing project plans.
  • I definitely need to print it out to be able to use it.

What would I need?

First of all: Everybody can read a Gantt chart so let’s stick to some reasonable habits: bars that encode project duration by length and diamond symbols that mean milestones, i.e. important dates.

When I think of project management, the whole story is about one core task: Align your output and handover dates with those of others.

As a member of any project team I simply cannot be interested in the task of each individual. However the Gantt chart’s focus lies on the presentation of single tasks. I state that any visualization for project management should focus on people or teams, i.e. responsibilities, rather than tasks.

My second point revolves around overview and detail: My own tasks should be presented in greater detail than data of other teams. My only concern with them is deliverables when we approach mutual handover dates.

The Redesign

Here’s some example data taken from one of my schedules:

Responsible Task Start End
Construction Define location Sun Jan 01 2012 Sun Apr 15 2012
Construction Finish site Thu Aug 01 2013 Milestone
IT Develop prototype Sun Jan 15 2012 Sat Jun 02 2012
IT Testing Sun Jun 03 2012 Sun Jul 01 2012
Business Process Give feedback Sun Jun 03 2012 Sun Jul 01 2012
Supplier Deliver parts (prototype) Thu Mar 01 2012 Sun Aug 05 2012
IT Update integration Sun Jul 01 2012 Tue Aug 14 2012
IT Evaluate prototype (hands-on) Wed Aug 15 2012 Mon Oct 01 2012
IT Evaluate prototype (remote monitoring) Tue Oct 02 2012 Wed May 01 2013
IT Production Mon Aug 19 2013 Wed Jan 01 2014
Business Process Launch prototype environment Wed Aug 15 2012 Milestone
Business Process Go live Mon Aug 19 2013 Milestone
Procurement Procure Mon Apr 01 2013 Wed May 01 2013
Supplier Deliver parts (production) Wed May 01 2013 Mon Aug 12 2013

Classic Gantt chart

And here goes the corresponding Gantt chart:

srg
Gantt chart encoding the tabular data above. (Written in JavaScript).

My version, aka: Gantt90

My enhanced version is shown below. Instead of single tasks I only show the responsible team. Additionally I rotate by 90 degrees – thus Gantt90 – to shift the attention from process flow to responsibility, from a mechanical point of view to a more social one if you want to put it like this.

While the information conveyed is almost equal, the charts size is halved. Hence even the thumbnail becomes readable.

dsf
Proposed redesign of a Gantt chart: teams instead of tasks and rotation by 90 degrees.

Adding detail

Since I’m a member of IT, the two blue bars on the right should now convey more information than the others. I do that by highlighting my team and showing the specific tasks along the time axis. Enough detail for the team, and I manage to simultaneously keep track of other teams’ schedules.

sd
Overview and detail combined. The chart could even be made recursive with sub-teams of IT on the right. (…)

Thinking of my big example from the beginning it will be necessary to further divide the detail view on the right. Since projects – tasks as well as teams responsible – fit into hierarchical, tree-like data structures my chart would recursively expand to the right. (IT would then be divided into developers, testers, administrators, etc. and the new chart would resemble its left neighbour – both aligned with the common time axis.)

What about connecting lines?

You may have noticed that I skipped the connecting lines between the taks. I’m convinced that a proper visualization should signalize the project’s inherent structure on its own. Even if the lines are not there, you can see the dependencies between tasks and/or milestones anyway. Your brain does quite a good job at pattern recognition.

tldr: Like spreadsheets, Gantt charts are used to visualize business fiction rather than to manage projects. I’ve proposed a modified Gantt chart to better visualize projects by drawing attention to responsibility rather to single tasks.

Thanks to Elena Reitman’s tweet I stumbled upon the Tufte forum.

I highly recommend the book Envisioning information by Edward Tufte. The charts and the source code used can be found here.

hacks

Campus-Tetris revisited

Ich habe bereits über die Raumbuchung am Campus WU und eine damit verbundene Visualisierung der Belegung gebloggt. Hier in aller Kürze eine Visualisierung der Nachfrage nach Räumen.

Das Raumbuchungstool des Campus WU ist darauf ausgelegt, dass Mitarbeiter_innen wie Studierende – also etwa 30.000 Personen – selbständig und unbürokratisch Räume buchen können. Das dadurch drohende Chaos eines heillos belegten Gebäudes verhindern wir durch mehrere Parameter, wie etwa schrittweises Freigeben von Ressourcen, Speichern von Bedarfen in Raumgruppen anstatt in spezifischen Räumen, automatisierte Allokation, oder individuelle Berechtigungen. (Viele der hier genannten Dinge haben wir uns übrigens bei der Konkurrenz simulieren lassen.)

Anyway – wir sind nun seit genau einer Woche online und haben rund 8.000 Termine und somit bereits die Hälfte des kommenden Wintersemesters eingesammelt. Die Frage liegt also nahe, welche Räume (Raumgruppen) stark nachgefragt werden, und – am allerwichtigsten – wo etwaige Konflikte auftreten. Nach ein paar Stunden Python und Javascript kam dann mein Erfolgserlebnis, das ich auch gleich auf Twitter verewigen musste.

Programmieren mit d3.js (Data Driven Documents) macht also Spaß.

  d3.select("#applications")
    .append("g") 
      .attr("id", "application-frame")
    .selectAll("rect.application_frame")
    .data(data.slots)
    .enter()
    .append("rect")
      .attr("class", "application_frame")
      .attr("x", function(d) { return linear_scale(time_to_float(d.start) ) })
      .attr("y", function(d) { return POOL_POS[d.pool_id].y } )
      .attr("height", function(d) { return POOL_POS[d.pool_id].height } )
      .style("fill", function(d) { return occupancy_to_color(d.occupancy) })
      .transition()
        .attr("width",fifteen)
        .duration(500) // this is 1s
        .delay(function() { i = i +1; return i * 2; })         
  ; 

Ergebnis der Bastelarbeiten ist ein interaktives Diagramm, das mit dem Kalender-Widget oben gesteuert wird. Entlang der Tageszeit wird pro Raum-Pool (die Kapazität ist anhand der Fläche codiert) die Belegung inkl. der angefragten Termine visualisiert. Je stärker die Nachfrage, desto dunkler der Blauton. Liegt die Nachfrage über dem Angebot, wird das Blau zu einem gefährlichen Rot – hier gibt’s also dann etwas zu bereinigen.

sdsd
Visualisierung der angefragten und verfügbaren Kapazitäten pro Datum.

Das Ergebnis mit der traumhaft-schönen Animation auf YouTube. Das Diagramm ist erstaunlich responsive – immerhin werden mit jedem Klick eine Tabelle mit einer halben Million Einträge sechsmal durchsucht, der Tag in 60 Viertelstunden zerteilt und anschließend die Auslastung live berechnet.

Links

hacks

Terminals in Serienproduktion

Roald Amundsen schrieb beim Durchqueren der Nordwestpassage ins Tagebuch, das Schiff sei ein glückliches, weil jedes Crewmitglied einfach wisse, was gerade zu tun sei. Zwei Tage im Jänner 2013 segelte ich auf einem derart glücklichen Schiff, wo tausende Handgriffe am Ende ein wunderbares Ergebnis brachten – oder hat man als Softwareentwickler sonst einen Akkuschrauber in der Hand? Meinen Crewmitgliedern sei Dank!

test
Die Terminals gehen in Serie.

Einen kleinen Zusammenschnitt der beiden Arbeitstage gibt’s als Video (ordentliche Auflösung direkt bei YouTube):

Mehr zum Thema: The Octopus has landed!, Terminal-Update, Service Terminal Plus

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

 

hacks

Die DIY-Ökonomie

Bislang war im Blog entweder von IT-Hacks oder von politisch-ökonomischen Themen zu lesen. Diesmal wage ich während einer Reise durch Japan die Synthese meiner beiden Interessen mit der – katastrophal erfolglosen – Simulation eines künstlichen Wirtschaftssystems…

Doch alles der Reihe nach: Simulationen sind ein enorm spannender Bereich der Informatik. Im Grunde geht es dabei darum, Ergebnisse weniger anhand bekannter Algorithmen zu errechnen, sondern das Verhalten von Lösungsteilen zu modellieren, um sich später das Gesamtergebnis einfach anzusehen.

Mit solchen Simulationen lässt sich etwa die Kreiszahl Pi berechnen, man kann voraussagen, ob die Wasserqualität eines Badesees hält, oder aber man simuliert einen Verkehrsstau oder einen Gebäudebrand.

Bestückt mit Wirtschaftsstudium und – wichtiger – langjähriger Erfahrung als Bürgermeister von SimCity und zahlreichen Siedler-Inseln, oder einfach mangels naturwissenschaftlicher Expertise, lag die Aufgabenstellung in meinem Fall jedenfalls am Tisch: Die Simulation einer künstlichen Marktwirtschaft bestehend aus Produzenten, Konsumenten, Einwohnern, Arbeitgebern, Produkten, Preisen und Steuern.

Die Wirtschaftsmodelle der Wuselspiele haben mit freien Märkten nichts zu tun. Im Screenshot: Widelands, ein Open Source Siedler-Klon.

Software-Agenten statt homo oeconomicus

Den Wirtschaftswissenschaften wird oft vorgeworfen, an den homo oeconomicus, also das rein rationale Verhalten der Akteure zu glauben. Dass es diesen homo oeconomicus nicht gibt, erkennt man beispielsweise daran, dass Kinos trotz Breitbandanschlüssen noch nicht komplett leer stehen, oder dass mein Fitnesscenter einen gut frequentierten Aufzug besitzt.

Auch im Reisegepäck dabei: Kahnemann, Langsames Denken, schnelles Denken, 2011. Lesenswertes Buch über die Irrationalität der Menschen, Buch bei Amazon.

Wie auch immer, Wirtschaftsprognosen sind gerade auch deshalb so schwierig, weil das Verhalten vieler Akteure komplett unterschiedlich abläuft und die Interaktionen unüberschaubar komplex sind. Ohne das Problem verstehen zu wollen, kann man jedenfalls versuchen, das Problem mit Rechenleistung zu erschlagen.

Meine Annahme: Warum nicht eine Vielzahl künstlicher, unterschiedlich agierender Agenten erschaffen und sie anschließend Wirtschaft spielen lassen. Dabei könnten etwa Konsumenten vorkommen, die sich bis auf ein Maximum verschulden, andere würden eisern sparen. Manche Produzenten wären schneller mit Investitionen, andere vorsichtiger.

Meine auf der Flugzeugserviette entworfene Ökonomie sieht jedenfalls so aus:

              -------------
              Markt
              -------------
              - Jobangebote
              - Löhne
              -------------
                  |
  -----------     |   -----------
  Arbeitgeber  ---+->  Einwohner
  -----------         -----------
      |                    ^
      |                    |
      v                    |
  -----------         -----------
   Produzent   <--+--  Konsument
  -----------     |   -----------
                  |
               ------------
               Markt
               ------------
               - Produkte
               - Preise 
               - Steuern
               ------------

Nach einer schlaflosen Nacht zwischen Minsk und Vladivostok und einem weiteren High-Speed Hack im Shinkansen von Tokio nach Kyoto ist sie also fertig – meine künstliche Wirtschaft mit hundert Einwohnern und sieben produzierenden Firmen, gegossen in ein paar Python-Module und einen Volkswirtschaftssimulator, der über zehn Jahre hinweg Bevölkerungszahlen, BIP, Privat- und Firmenvermögen sowie Jobs und Arbeitslosenrate misst. Einwohner suchen sich Jobs, verdienen Geld und geben es nach Lust und Laune wieder aus.

Meine Volkswirtschaft in Python gegossen. Links die Vermögens- und Arbeitsmarktstatistiken meiner Modellwelt.

Klingt vielleicht beeindruckend, ist es aber nicht: Statt Antworten eröffnen sich volkswirtschaftliche Lücken.

Woher kommt eigentlich das Wachstum?

Meine modellhafte Volkswirtschaft kann zwar nicht alle hundert Einwohner ernähren, aber nach ein paar Monaten hat der Großteil der Leute einen Job gefunden. Die Arbeitslosenrate sinkt – auch dank der Verhungerten – auf Null und dann passiert… nichts. Die Firmen verdienen genau das Geld, das sie wiederum den Arbeitenden auszahlen. Das BIP stagniert, woher käme denn auch das Wachstum?

Zahlreiche ökonomische Theorien beruhen auf der Ansicht, Wachstum entstünde zwangsweise durch Ausbeutung von Ressourcen. Ganz egal, ob nun damit etwa die Natur (Physiokratie), Arbeiter (Marxismus), deren Kombination in Übersee (Globalisierungskritik) oder das Sparvermögen des Mittelstands (“Draghismus“) gemeint sind. Das Gegenstück zu den diversen Ausbeutungstheorien bildet Schumpeters Theorie der wirtschaftlichen Entwicklung. Diese besagt, dass Wachstum aus neuen Ideen entsteht, aus der Zerstörung des Alten und Erschaffung des Neuen.

Nun ja, im angesprochenen Programm sind alle Optionen für Wachstum absent: Die Bevölkerung arbeitet bereits unter Vollauslastung, es gibt keine unentdeckten Bodenschätze und disruptive Innovation ist irgendwie schwer zu simulieren…

Weitere Stunden später, in denen ich meine Liliput-Ökonomie weiter parametrisiert und einen etwas intelligenteren Marktmechanismus (Preise und Löhne) implementiert habe, gebe ich schließlich entnervt auf. Wirtschaft ist wohl nicht zu simulieren, denn die Komplexität ist nicht abbildbar. Während bei technischen Aufgabenstellung oft das Prinzip Divide and Conquer zum Erfolg führt, ist das hier nicht machbar: Entweder es ist alles berücksichtigt (Lohnverhandlungen, Preisbildung, Investitionen der Unternehmen, Kundenpräferenzen für deren Produkte, Steuern, Subventionen, Arbeitslosenunterstützung, Unternehmensgründung durch einstige Konsumenten, Währungen, Zinsen, Staaten, Banken, usw.) oder die Simulation bleibt ein netter Zeitvertreib – was es allerdings wirklich gewesen ist.

Was ich dennoch gelernt habe

  1. Am Schlimmsten sind die Sparer unter meinen Einwohnern: Denn deren Konsumverzicht häuft unproduktives Kapital an.
  2. Mein in der Folge chronischer Hungersnöte eingeführtes Grundeinkommen hat die Modellwirtschaft nachhaltig stabilisiert. Zum Teil finanziert sich mein Staat über die Umsatzsteuer, ebenso findet aber eine massive Umverteilung vom Staat in Richtung Unternehmen statt. Der Staat mach also in jeder Modellvariante Schulden, damit Arbeitslose von Produzenten kaufen können. Folglich werde ich Arbeitslosenunterstützung künftig auch als Subvention für die Wirtschaft begreifen.
  3. Ein Käufermarkt, wo sämtliche Grundbedürfnisse der Menschen vielfach befriedigt sind, und Geld über massives Marketing nicht-lebensnotwendiger Produkte gemacht wird, wäre wohl gar nicht mehr zu simulieren.
  4. Im Urlaub sollte man keinen Notebook dabei haben.