IOException.de

Icon

Ausgewählter Nerdkram von Informatikstudenten der Uni Ulm

Few words about… The seek for a WhatsApp alternative

Since WhatsApp was sold for 19 billion dollar to Facebook, lots of blogs and news seek for alternatives. In this short comment, I will point out why we all need alternatives, why we all need more than one alternative, why this works and what features our new alternative must have.

Threema, Textsecure or Telegram are just a few new so called WhatsApp competitor nowadays. But before we go out and look for alternatives, we must understand what’s the problem with WhatsApp and Facebook. And before we consider that, we must understand why Zuckerberg payed 19 billion dollar for WhatsApp. I intentionally do not say that WhatsApp is worth that much money. It’s only that much worth for Facebook. The big deal shows us what really matters in the information age. Surprise, it’s information itself. Facebook itself is free, so where comes all the money? Facebook can afford buying WhatsApp, despite Facebook has not a single paying user. This tells us that information is very important and also very expensive. Important for advertising, marketing research or insurance companies. Or intelligence agencies. Information about us. Companies make billions of dollars by selling information they know about us!

The bad thing about this is, that we only understand why this can be a problem when it’s too late. When knowledge about us is used against us and we suddendly recognize it. Before that, we all agree using our personal information. And that’s bad.

So we note that information is important and we must take care of it.

For example by not giving a single company that much information. But there is more. It’s power. Facebook not only has our personal information, it has the power of more than one billion users. And there is almost no business competition.

So we note that using one centralized service supports monopolism and helps aggregating information.

So far, we’ve learned about the disadvantages of an information collecting centralized service. Now let’s have a look at why WhatsApp has so many users despite there are a lot of alternatives. When we read about apps having the potential to compete with WhatsApp, we always stumble upon the word usability. One of the main reason why WhatsApp is so successful, is because everyone can use it. You do not even have to register (explicitly). Registering is done almost instantly and implicitly

So we note that providing a real alternative to people, we must make the barrier of using our product very, very low by optimizing its usability. Features like group-chats or the ability to send multimedia files would increase the acceptance too. Platform support is also very important.

Let’s recap that. A chat system should protect our information. This can be done partially by using the right encryption. Partially, because meta data can be very difficult to encrypt. That means, data between two chatters can be strongly encrypted, but it’s hard to encrypt the information about who talks to each other (meta data). If we store the whole meta information collection at a single place (or company), we can hide what we are talking but not when, to who, where, how often and so on. For the latter, we must take a look at network topologies first. All communication in WhatsApp or Facebook end up at one server or server-cluster (see figure 1). A better alternative is using multiple independent servers. A decentralized system (see figure 2).

network topology: centralized network

Figure 1: Centralized network topology.

Here, each server can be owned by another person or company. Communication is still possible between them because the Internet is designed that way. Think about email for example. Here we have the freedom of choice which provider we want to use. On top of that, we could use TOR (a network for the anonymization of connection data) to disguise even more of our meta data.

network topology: decentral network

Figure 2: Decentralized network topology.

Another network topology we consider is the peer-to-peer architecture (see figure 3). Skype used to have this before Microsoft took it over. But Skype also fails somewhere else. At first, meta data is centralized. Second, it is owned by is a network for the anonymization of connection data one company (Microsoft). Third, it fails on it’s closed source nature. We cannot control or see what’s going on inside the system.
So we note that using an open source decentralized system is good. Also note that this is where most of the recently discussed alternatives fail completely.

network topology: peer to peer

Figure 3: Peer-to-peer network topology.

Another problem with closed source is the denial of choice. For example the choice of crypto algorithms. In an open system, we can use any end-to-end encryption we want. And we want that choice because weak encryption is not considerable for us. We also want encryption that guarantees us deniability and perfect forward secrecy. Deniability means that nobody can proof that your conversation actually took place. Perfect forward secrecy means that if someone comes into possession of your password or encryption keys, your conversation cannot be decrypted afterwards. So we note that we need a system that allows us to use our own clients and our own encryption. Let’s summarize this. Our chat system must be decentralized, support any client and any end-to-end encryption,
be easy to use and support all available platforms. To make it short here, it already exists. It’s called XMPP and was developed in 1999.

Building node-webkit programs with Grunt

The days before Christmas were busy as usual: It’s not just that everyone is hunting gifts for family and friends. There’s also the annual German Youth Team Championship of Chess beginning on 25th December. Together with some friends I’m trying to broadcast this big event with more than 500 young participants to their families left at home, waiting for results.

In the last years, we used a simple mechanism to provide near to live results: The arbiters got a special email adress where they could send their tournament files to. On the server side there was a small node.js mail server (as mentioned in “Einfacher SMTP-Server zur Aufgabenverarbeitung” [german]) that took the proprietary file format, converted it and imported the results of already finished games. Although being a huge progress to the past where the results were imported once all games has been finished, this approach needed an arbiter constantly sending mails around.

Therefore I wanted to try another way: A program that keeps an eye on the tournament file and uploads it once it was changed and automatically triggers the import of new game results. Having just some days for its development it was necessary to stay with the same technology stack we used before for the mail server and tournament file converter: node.js.

As the tournament arbiters aren’t all familiar with command line tools, a graphical user interface was necessary. Strongloop published a blog post about node-webkit, which allows writing native apps in HTML and Javascript, some days before. This blog post is a good entry to the topic. Nettuts+ wrote a nice introduction recently too. Different from their approach I used the plugin for Grunt grunt-node-webkit-builder, which takes on the whole building process. Here’s my project’s setup:

/
├── dist
├── Gruntfile.js
├── package.json
└── src
    ├── index.html
    ├── package.json
    ├── js
    │   └── index.js
    └── css
        └── style.css

By using the grunt-node-webkit-builder it is necessary to keep the source of the building tool (all in the root directory) separate from the source code of the node-webkit program. Otherwise it may happen that the building tools (Grunt, you know?) get bundled in the node-webkit program as well which leads to high file sizes and slow execution times.

So it’s clear we specify in /package.json only the dependencies that are necessary for the building process:

{
  "name": "do-my-build",
  "version": "0.0.1",
  "description": "Using Grunt to build my little program",
  "author": "Falco Nogatz <fnogatz@gmail.com>",
  "private": true,
  "dependencies": {
    "grunt": "~0.4.2",
    "grunt-node-webkit-builder": "~0.1.14"
  }
}

We also have to create the Gruntfile.js:

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('src/package.json'),
    nodewebkit: {
      options: {
        build_dir: './dist',
        // specifiy what to build
        mac: false,
        win: true,
        linux32: false,
        linux64: true
      },
      src: './src/**/*'
    },
  });

  grunt.loadNpmTasks('grunt-node-webkit-builder');

  grunt.registerTask('default', ['nodewebkit']);
};

The real node-webkit program can be written now in the /src directory. As also mentioned in the tutorials linked above, the /src/package.json should be filled with some node-webkit related fields:

{
  "name": "my-program",
  ...
  "main": "index.html",
  "window": {
    "toolbar": false,
    "width": 800,
    "height": 600
  }
}

To build the node-webkit program for the architectures specified in /package.json you simply have to call the command:

grunt

This downloads the up-to-date binaries necessary for the specified architectures and builds an executable program. The result for Windows is simply a .exe file, for Linux an executable file. It contains all needed to run the program, so the user neither has to install node.js nor Chrome. The builds are located in /dist/releases.

By using this setup it was possible to automate the building process and develop the application within some days. The node-webkit runtime extends some native browser properties, for example it is possible to get the full path of a file selected by an <input type="file">. With that it was possible to create a graphical user interface to select tournament files and watch for their changes, which would trigger the update process.

Simple cURL based activity monitor for websites

I wrote a simple activity monitor to notify me about changes on certain websites. The script is executed as a cronjob on a dedicated server and regularly fetches certain URIs. It then executes diff on the last file fetched from this URI. If there is a difference, it is mailed to me. To describe some use cases:

  • Product in Online-Shop is out of stock, I want to be notified when it is available again.
  • University lecture. Want to be notified when news/exercises/etc are put on the lecture website.
  • Usenet Search. Want to be notified when certain files are available and can be found via a Usenet search site.
  • Event registration. I monitored a Barcamp website to get notified, once the registration was available.
  • Monitor changes on certain Wiki pages.
  • Regularly check if something can be ordered now.
  • Monitor changes to a certain Etherpad instance.
  • Monitor changes on databases. Some projects offer a HTTP API (e.g. OpenStreetMap). Regularly exporting the database file and running diffs against it shows changes.
  • Monitor newly created mailinglists via the lists interface offered by our university.
  • Monitor the delivery status of packages, by fetching the transport providers tracking page.

Generally the idea is simply to monitor content changes on HTTP resources and thus the script can easily be used for anything that supports HTTP. A simple monitor is set-up like this:
./check-for-changes.sh "alice@mail.de" "http://some-resource.net/"

If you want to monitor only a certain part of a website you can use a preprocess file to filter out content you don’t want to monitor:
./check-for-changes.sh "alice@mail.de" "http://some-resource.net/" "./preprocess/only-body.sh"

I have released the project under a MIT license via GitHub.
differenziert.net: Plattform zur Meinungsbildung

Gitlab: Freie GitHub-Alternative für geschlossene Projekte

Wer einmal mit einer Versionsverwaltung wie Git oder Subversion gearbeitet hat, möchte es für die eigene Arbeit nicht mehr missen: Nie mehr verzweifeln, weil der Texteditor nur die letzten X Änderungen über die allseits beliebte Tastenkombination Strg+Z zurücknehmen lässt. Kein leidiges Abgleichen von Dateien per Hand, wenn mehrere Leute am gleichen Projekt arbeiten.
Für die meisten dieser Versionskontrollsysteme ist ein zentraler Server, auf dem die Datenstände und Versionen allen Projektmitgliedern bereitgestellt werden können, zwingend erforderlich oder zumindest sehr empfehlenswert. In der Open-Source-Welt hat sich hierfür GitHub etabliert, was das kostenfreie Git-Hosting für öffentliche Projekte erlaubt. Wer – wie wir für unser Softwareprojekt (Sopra) im Rahmen des Informatikstudiums – im Team an einem privaten Repository arbeiten möchte, muss bei GitHub schon etwas Geld in die Hand nehmen: Zwar wäre es uns die sieben Dollar im Monat wert gewesen, da ich aber die Vorteile von Git auch für ein paar andere eigene (nicht öffentliche) Projekte verwenden wollte und einen vServer quasi brachliegen hatte, sah ich mich nach selbstgehosteten Alternativen um – und stieß auf Gitlab.

Gleich vorweg: Wenn man Git alleine nutzt, braucht man natürlich überhaupt keinen Server. Und wem grafische Oberfläche ohnehin ein Fremdwort ist, der kann seinen Server sicher auch nur als reinen Git-Server betreiben. Für unser Team waren in der Programmentwicklung jedoch die Features, die auch GitHub bietet, sehr wichtig: Wir wollten Issues online erfassen, Commits direkt im Code kommentieren. Ja, vielleicht sogar MergeRequests direkt über die Web-Oberfläche abarbeiten. All dies wurde durch Gitlab ermöglicht, wenn auch zum Teil erst nach ein paar Versionssprüngen.

Installation und Updates

Versionssprünge? – Ja, Gitlab steckt so gesehen noch in den Kinderschuhen. Oder anders formuliert: Es wird stetig verbessert. Wer sich ein wenig mit Ruby auskennt oder einfach ein paar neue Ideen einbringen will, kann ja mal bei Gitlab auf GitHub vorbeischauen.
Wir fingen im März unter Nutzung der Version 2.2 an, mit Gitlab zu arbeiten. Mittlerweile läuft auf meinem Server die 2.6er-Version, in den kommenden Tagen müsste das Update auf 2.7 veröffentlicht werden. Anfangs weigerte ich mich, das System zu aktualisieren, um das Sopra nicht unnötig zu gefährden. Es zeigte sich jedoch schnell, dass jedes einzelne Update sehr nützliche Features mit sich brachte – so kamen seit Beginn unserer Arbeit die Möglichkeit der Milestones, Taggen von Issues und das Annehmen von MergeRequests über die Web-Oberfläche dazu.

Die Installation auf dem eigenen Server geht relativ einfach von der Hand. Eine Schritt-für-Schritt-Anleitung dazu gibt es im Gitlab-Wiki, die ich eigentlich nur abarbeiten musste. Offiziell unterstützt wird nur Ubuntu, es finden sich im Internet aber mittlerweile genügend Hilfen, wie man Gitlab auch auf CentOS- oder anderen Servern zum Laufen bekommt. Einmal installiert gehen Updates so einfach wie nur möglich von der Hand: Ein einfaches git pull und rake db:migrate reichen in aller Regel aus, um das System auf den neuesten Stand zu bringen.

Features

Wie oben schon geschrieben: Im Prinzip bringt Gitlab alles mit, was man auch von GitHub kennt. Das große Vorbild schwingt in allen Diskussionen um neue Features mit. So birgt die Oberfläche von Gitlab erstmal auch kaum Überraschungen.
Da ich noch nie auf GitHub in einem Team gearbeitet habe, kann ich das leider nicht vergleichen. In Gitlab gibt es eine ganze Reihe an verschiedenen Rollen, vom “Guest” bis “Master”. Die Rollenverteilung auch im Team umzusetzen, erwies sich bereits nach wenigen Tagen gemeinsamer Arbeit am Sopra als sehr nützlich: Durch die Unterscheidung zwischen “Developer” und “Master” konnten nur noch zwei Mitglieder in unseren Master-Branch pushen und waren dementsprechend für das Mergen und Schließen der Issues hauptverantwortlich.

Letztlich nutzten wir im Team mit Ausnahme der sogenannten “Wall” alle Mittel, die uns Gitlab an die Hand gab. Am anschaulichsten wird das vielleicht, wenn man betrachtet, wie sich unser Workflow seit Benutzung von Gitlab geändert hat:

  • Gerade am Anfang war die intensive Arbeit mit Git für uns alle noch ziemlich neu. Das Wiki eignet sich sehr gut, um auch gemeinsam die wichtigsten Kommandos zusammenzufassen. Aber etwa auch um nochmal die Programmierkonventionen zu benennen.
  • Code wird nicht mehr per Skype oder Google Hangout unter Nutzung der Bildschirmübertragung diskutiert, sondern einfach über die Weboberfläche – und zwar gerne auch direkt mit Kommentaren im Code oder Commit.
  • Issues und Arbeitsaufträge werden nicht mehr über Rundmails verteilt, sondern einfach als neue Einträge unter Issues hinterlegt.
  • “Bei mir trat gerade ein Fehler in Deinem Modul auf.” – “Schick mir mal die Fehlermeldung per Skype”. Solche Dialoge gibt es nicht mehr. Wir nutzten Skype zwar natürlich weiter intensiv zur Teamdiskussion, lange Fehlermeldungen haben im Chat aber nichts zu suchen. Und wanderten somit in die Snippets, wo man ihnen eine Expire-Zeit zuweisen kann.
  • Jegliche Dokumente wie die Benutzerdokumentation werden auch im Gitlab hinterlegt. Und nicht wie zuvor über Dropbox verteilt.
  • Unverändert blieb jedoch die Nutzung von GoogleDocs. Für Dokumente, die sich häufig verändern, wie etwa das Projekttagebuch, ist das doch noch einfacher, da Gitlab im Gegensatz zu GitHub (noch?) nicht das Ändern von Dateien via Browser unterstützt.

Grundsätzlich gab es also nichts, was uns noch gefehlt hätte. Nervig waren hin und wieder lediglich die kleinen Aussetzer, auf die ich im nächsten Abschnitt mal kurz eingehen werde.

Limits

Auch wenn sich Gitlab und GitHub nicht nur optisch sehr ähneln, verfolgen beide Systeme eigentlich unterschiedliche Ziele: Gitlab beschränkt sich komplett auf das Hosting von privaten Repositories. Es ist also nicht möglich, Gitlab als selbst gehosteten Ersatz für Github zu nutzen, und seine Open-Source-Projekte damit zu verwalten. Klar, prinzipiell geht das natürlich auch, aber dann kann eben niemand den Code sehen. Im Gegensatz zu GitHub bietet der Klon nämlich keine Möglichkeit, auf Projekte ohne Registrierung zuzugreifen. Und neue Benutzer anlegen kann nur der Admin. Auch wenn in sehr regelmäßigen Abständen von etwa 2 Wochen ein neuer Issue an die Entwickler gerichtet wird, dies doch umzustellen, bleiben diese ihrer Linie treu und grenzen sich damit ganz klar von Github ab. Und das auch ganz explizit mit der Aussage, dass Open-Source-Projekte eben am besten zentral auf GitHub gehostet werden.

Das Bearbeiten von MergeRequests direkt über die Weboberfläche ist zwar sehr komfortabel, sollte naturgemäß aber nur bei einfachen Änderungen benutzt werden. Ohnehin prüft Gitlab, bevor es die Möglichkeit anbietet, ob es zu irgendwelchen Merge-Konflikten kommt. Doch auch wenn dem nicht so ist, habe ich Abstand davon genommen, umfangreiche Änderungen über diesen Weg zu übernehmen. Letztlich schien mir der traditionelle Weg über git merge und ggf. git mergetool doch immer noch am sichersten.

Etwas Schluckauf kann man Gitlab im Moment auch noch durch den intensiven Gebrauch von Umlauten bereiten: Commit-Messages, die Umlaute beinhalten, können mitunter dafür sorgen, dass ein ganzer Dateibaum in der Weboberfläche nicht mehr verfügbar ist. Umlaute in Dateinamen sorgen dafür, dass die ansonsten sehr hilfreiche Funktion, um ein komplettes Repository als *.tar.gz herunterzuladen, plötzlich nicht mehr funktioniert. Schade ist, dass das System in diesen Fällen keine ordentliche Fehlerseite liefert, sondern schlicht mit 404 quittiert. Und man dann händisch versuchen muss, die Sachen in der Datenbank zu korrigieren.
Generell hat die Zahl der Fehler mit jedem Update aber gut abgenommen, sodass man sich einfach auf die Vermeidung von Umlauten einlassen sollte und ein sehr gutes und stabiles System bekommt.

Screenshots

Eine Demo gibt es ebenfalls auf den Seiten von Gitlab.

HTTP-Methoden und ihre Semantiken

HTTP ist vermutlich das erfolgreichste Anwendungsprotokoll überhaupt. Ohne HTTP wäre das WWW nicht denkbar, und ein wesentlicher Teil des gesamten Internet-Traffics ist auf HTTP zurückzuführen. HTTP ist ein zustandsloses, Request-Response basiertes Protokoll, das heißt es gibt ein (einziges) Muster, über das die beteiligten Kommunikationspartner Daten austauschen: Clients initieren immer die Kommunikation, fordern eine Resource per Request an, und Server antworten wiederum per Response. Je nach Art der Anfrage, können sowohl Request als auch Response Nutzdaten, sogenannte Entities enthalten. Im überwiegenden Fall sind das HTML-Dokumente oder Mediendateien, die bei der Response übermittelt werden.

Die eigentliche Semantik des Aufrufs im Sinne von HTTP bestimmt die verwendete Methode, die Bestandteil des Requests ist. Das absolute Arbeitstier des Webs ist GET, allerdings existieren noch weitere Methoden: POST, HEAD, PUT, DELETE und OPTIONS. Deren Bedeutung und korrekte Verwendung ist allgemein jedoch weniger bekannt. Deswegen möchte ich diese Methoden kurz vorstellen und ihre Funktionen erklären. Dafür gehe ich nun zunächst auf die verschiedenen Merkmale ein, die Methoden besitzen nach RFC 2616 können.

Eine Methode gilt als sicher, wenn sie keine Seiteneffekte erzeugt. Mögliche Seiteneffekte wären zum Beispiel das Ändnern einer Resource oder das Durchführen eines Logouts. Zu einem gewissen Grad sind auch Seiteneffekte bei sicheren Methoden erlaubt. Allerdings nur dann, wenn sie nicht explizit vom Benutzer intendiert sind. Darunter fallen zum Beispiel Logfiles, die Zugriffe aufzeichnen oder Zähler, die Zugriffe aufsummieren.

Als idempotent bezeichnet man eine HTTP Methode dann, wenn die mehrfache Ausführung eines Requests die gleichen Seiteneffekte besitzt wie ein einmaliges Ausführen. Ob ein Request für das Löschen einer Resource einfach oder mehrfach ausgeführt wird, das Resultat bleibt ist identisch, die Resource wurde gelöscht. Idempotenz ist für verteilten Systemten eine interessante Aufrufsemantik, da sie es im Fehlerfall ermöglicht, einen Request zu wiederholen (At-least-once). Bei nicht idempotenten Methoden ist dies nicht möglich. Ein Beispiel hierfür wäre eine Onlinebanking-Sitzung, die zur Übermittlung einer Überweisung eine nicht-idempotente Methode verwendet. Bricht nun während des Requests die Verbindung ab, so ist unklar, ob eine erneute Ausführung zu mehrfachen Überweisungen führt oder nicht (in der Praxis wird dies z.B. durch Tokens auf Anwendungsebene verhindert).

Eine dritte Eigenschaft einer Methode ist Möglichkeit, ob bei ihrer Verwendung Responses gecachet werden können. Es ist offensichtlich, dass dies nur für Methoden ohne Seiteneffekte funktioneren kann. Ebenfalls ist Caching nur dann sinnvoll und erlaubt, wenn die Responses entsprechende Caching-Informationen bereitstellen. Dies ist unter anderem möglich durch die Angabe von Expire-Zeiten, Datum der letzten Änderungen oder Entity-Tag-Angaben. Sie beschreiben, wie lange das Resultat einer Response weiterverwendet werden kann, ohne einen erneuten Request zu versenden. Beziehungsweise geben sie an, wie bei einem konditionellen Request übermittelt werden kann, was die bekannte Version des Entities ist. Sofern sich die Resource nicht verändert hat, muss sie somit nicht erneut übertragen werden.

safe idempotent cachable
HEAD (✓)
GET (✓)
PUT
DELETE
POST

GET
GET ist die meistverwendete Methode und dient zur Abfrage einer Resource. Sie ist sowohl sicher als auch idempotent, und erlaubt Caching. Erweiterte Funktionen von GET erlauben konditionale Abfragen, sowie partielle Abfragen. Ersteres erlaubt eine effiziente Benutzung von Caching, um nur dann eine Resource erneut zu laden, wenn sie sich auch wirklich verändert hat oder aktualisiert wurde. Letzteres dient dazu, nur Teile einer Resource abzurufen. Das kann zum Beispiel bei einer Resource, die eine Videodatei darstellt, hilfreich sein und erlaubt das Springen im Bytestream des Videos.

HEAD
HEAD besitzt prinzipiell die gleiche Semantik wie GET, allerdings wird keine Repräsentation der Resource mitgesendet, das heißt die Response enthält kein Entity. Somit ist HEAD nützlich, um Metadaten über die Resource abzufragen, oder überhaupt des Existenz zu überprüfen.

PUT
Mit PUT kann eine Resource erstellt oder ersetzt werden. Hierfür ist im Request ein Entity enthalten, dass eine neue Repräsentation der Resource enthält. Die zu erstellende/ändernde Resource wird durch die URI eindeutig beschrieben.

DELETE
DELETE entfernt die durch die URI identifizierte Resource.

POST
Als letzte Methode wird POST für alle weiteren Aufrufe verwendet, insbesondere wenn keinerlei Zugeständnisse bezüglich der Semantiken gemacht werden (können), insbesondere Idempotenz. In der Praxis dient POST ebenfalls zum Erzeugen und Ändern von Resourcen, aber auch für viele weitere Aktionen, die via POST quasi ‘getunnelt’ werden.

Für weitere Informationen empfiehlt es sich, RFC 2616 zu lesen.

Benachrichtigungen für die Piratenpads skripten

Die Piratenpads sind eine bequeme, kostenfreie Möglichkeit um kollaborativ an Texten zu arbeiten oder etwa Ideen zu sammeln. In letzter Zeit nutze ich den Service mit einigen anderen Leuten sehr exzessiv. Über die verschiedenen Pads in verschiedenen Teams habe ich aber inzwischen einfach den Überblick verloren. Um herauszufinden, ob es irgendwo Neuerungen gibt muss ich alles manuell abklappern und mich bei jedem einzelnen Team einloggen. Ich bin der Ansicht, dass man genau solche Aufgaben nach Möglichkeit immer automatisieren sollte.

Deswegen habe ich einen Notificationservice für die Piratenpads geschrieben. Das Skript loggt sich ein, fragt die aktuelle Version des Dokuments ab und speichert diese lokal zwischen. Beim nächsten Überprüfen (idealerweise als Cronjob) wird die lokale Kopie mit dem online-Original verglichen. Gibt es eine Differenz wird eine E-Mailbenachrichtigung mit dem diff versandt.

Um das Ganze zu Skripten habe ich node.js mit der Bibliothek request verwendet:

Request is designed to be the simplest way possible to make HTTP calls. It support HTTPS and follows redirects by default.

Tolle Sache! Ohne HTTPS-Support kommen wir eh nicht weit, bei einigen Pads ist der Zugriff auf HTTPS eingeschränkt. Außerdem muss man sich dank der Bibliothek nicht um das mühsame Parsen von “Set-Cookie” Feldern kümmern. request übernimmt die Cookies standardmäßig einfach global für zukünftige Requests.

Um die Session zu initialisieren, also sich vor dem Login einen Cookie zu holen, sieht der Code etwa so aus:

var request = require('request'); 
var base = 'https://foo.piratenpad.de';

(function initiateSession() {
	request.get(base, function (error, response, body) {
		if (!error && response.statusCode == 200) {
			login();
		}
	});
})();

Die Loginfunktion habe ich zusammengebaut, nachdem ich den kompletten Skriptablauf im Firefox durchgespielt habe und alle Requests mittels des (unheimlich praktischen) Live HTTP Headers Add-ons aufgezeichnet habe.

function login() {
	var options = {
			url: base + '/ep/account/sign-in', 
			form: {'email': 'john@doe.de', 'password' : 'mypw'}
	};
	
	request.post(options, function (err, res, body) {	
			request.get(base + '/' + acc.padId, function (err, resp, body) {
				// get the latest version of the pad document
				var linkToLatestVersion = body.match(/[\w\d\/\-\.]*(latest')/gi);
				linkToLatestVersion = linkToLatestVersion[0].replace("'", '');

				getLatestDocument(linkToLatestVersion);
			});			
		}
	);	
}

Die aktuelle Version des Dokuments lässt sich dann mit einem einfachen GET-Request abfragen:

function getLatestDocument(linkToLatestVersion) {
	request.get(base + linkToLatestVersion, function (err, resp, body) {
		var start = body.search('id="padcontent">') + 'id="padcontent">'.length;
		var end = body.search("<!-- /padeditor -->");
		var padContent = body.substring(start, end);
		
		// strip all tags and entities
		padContent = padContent.replace(/(<[^>]+>)|(&[#\w]+;)/gi, '');
		
		console.log(padContent.trim());
	});
}

Das Ganze zusammengefasst als ordentliches, sauber konfigurierbares, Projekt gibt es hier auf GitHub. Das Skript kann sehr einfach für ähnliche Aufgaben wiederverwendet werden. Als Anregung: Beispielsweise wäre es möglich das Uni Hochschulportal anzuzapfen um E-Mailbenachrichtigungen zu versenden, wenn neue Prüfungsergebnisse eingetragen sind.

Update: Ich habe noch die Möglichkeit hinzugefügt, Benachrichtigungen für Änderungen an dem Inhalt hinter einer URL zu konfigurieren (im Ordner simple-webpage/). Ich benutze das als simple Lösung für Seiten, die keinen RSS-Feed bereitstellen.

Sopra-Projekt: Routenplaner für das Gelände der Uni Ulm

An der Uni Ulm muss jeder Bachelorstudent in einem Informatik / Informatik verwandten Studiengang das Sopra absolvieren. Das ist ein umfangreiches Softwareprojekt, das sich über zwei Semester erstreckt. Im ersten Semester steht dabei die Erfassung von Anforderungen, die Planung und das Pflichtenheft im Vordergrund. Das passiert in einem Team aus drei Studenten. Im zweiten Semester werden zwei Teams zusammengelegt, so dass fortan sechs Leute an der Implementierung arbeiten.

Alle Teams, die am Sopra teilnehmen, müssen dasselbe Projekt implementieren, wobei die Projekte jedes Jahr wechseln. Dieses Jahr war die Aufgabe von Grund auf einen Routenplaner für die Universität zur erstellen. Die Ausgangsbasis waren die Baupläne der Uni, die vom Bauamt zur Verfügung gestellt wurden.

Einige Problemstellungen des Projekts waren eine besondere Herausforderungen. Beispielsweise müssen handelsübliche Straßen-Routenplaner nicht mit fünf Stockwerken die übereinander liegen zurecht kommen.

Zum Routing: Algorithmen für Routingprobleme (Dijkstra, Bellman-Ford, etc.) verwenden üblicherweise einen gewichteten Graphen. Unser Team hat sich dafür entschieden eine graphenbasierte NoSQL-Datenbank zu verwenden: Neo4J. Da wir vorhatten einen Routeplaner zu entwickeln bot es sich einfach an eine Datenbank zu verwenden die inhärent schon auf einer Graphenstruktur aufbaut und somit viele Probleme — wie das Routing zwischen Knoten — schon im Vornherein löst.

Routingalgorithmen auf relationalen Datenbanken können recht hässlich werden. Für einige andere Teams war das eines der Kernprobleme.

Man sollte Technologien nie deswegen auswählen, weil es eben die einzige Technologie ist die man kennt.
Stattdessen sollte eine Technologie, ein Werkzeug, gewählt werden, weil es sich am Besten für die Aufgabe eignet.

Der Routenplaner sollte webbasiert realisiert werden. Wir haben uns für Vaadin als Web-Framework entschieden. Vaadin setzt auf GWT auf und ermöglicht es Web-Applikationen in Java zu schreiben, als würde man eine Swing-Applikation entwickeln. Java Code wird durch einen Cross-Compiler in JavaScript, HTML & CSS übersetzt. Die Arbeit mit dem Framework hat ziemlich gut funktioniert, es lassen sich sehr schnell vorzeigbare Ergebnisse erstellen.

Wir haben außerdem eine Desktop-Applikation für das Bearbeiten und Hochladen von Kartenmaterial geschrieben. Da wir die Aufgaben im Vornherein aufgeteilt hatten war ich hier aber nur beteiligt als es um die Synchronisation mit der Webapp ging. Dafür haben wir Git verwendet (siehe auch den Blogbeitrag “Git als Update-Mechanismus“).

Weitere erwähnenswerte Technologien: Für den PDF-Druck haben wir LaTeX verwendet. Aus einer generierten *.tex Datei wird eine PDF erstellt und an den Browser zurückgegeben. Wir haben mittels node.js das Uni-Adressbuch gescraped um so vernünftig einen vollständigen Datensatz in die Datenbank zu bekommen.

Die erstellen Projekte werden nicht produktiv eingesetzt werden, es wird aber an einer Musterimplementierung gearbeitet. Diese wird später eventuell auch auf den Terminals, die auf dem Unigelände verteilt sind, laufen.
Allgemein war es ziemlich interessant mal in einem größeren Team an einem umfrangreicheren Projekt zu arbeiten. Man lernt einige Dinge über die Arbeit im Team und verschiedene Technologien. Insbesondere lernet man aber wo die eigenen Stärken liegen.

 

Einfache Visualisierung von Geodaten – Teil 2: Leaflet & jquery.couch.js

Im vorherigen Teil haben wir gesehen, wie man Geodaten mithilfe von CouchDB abspeichern kann. Da diese Datenbank zugleich ein Webserver ist und die Daten im JSON-Format gespeichert werden, eignet sie CouchDB auch gut für AJAX-Abfragen. Hierfür gibt es eine auf jQuery aufbauende Library namens jquery.couch.js, die von den AJAX-Requests abstrahiert und direkt browser-seitige Interaktionen mit der Datenbank ermöglicht.

Im diesem Beitrag soll gezeigt werden, wie man mit der offenen Karten-Library Leaflet und jquery.couch.js geographische Daten aus CouchDB heraus auf einer Karten anzeigen kann.

Beispiel-Visualisierung von ulmapi.de, ebenfalls basierend auf CouchDB und Leaflet.

Wir verwenden die CouchApp aus dem ersten Teil weiter, und fügen zu den bisherigen Map/Reduce Views noch statische HTML- und Javascript-Dateien hinzu (im _attachments Ordner), die dann im Browser abgerufen werden können. Beim Aufruf dieser Webseite wird ein HTML-Grundgerüst übertragen, sowie eine JavaScript-Datei, die beim Aufruf die eigentlichen Datensätze via jquery.couch.js aus der CouchDB nachlädt.

Als Mapping-Library verwenden wir Leaflet, eine Open-Source Bibliothek für Kartendarstellungen im Browser. Leaflet abstrahiert von verschiedenen Kartenprovidern und erlaubt es somit, unterschiedliche Datenquellen zu verwenden, wie zum Beispiel auch Bing Maps oder Cloudmade. Letzteres ist ein Service, der auf Basis der Open Street Map Daten Kartenkacheln mit individuellen Stilen rendert und hostet – für Visualisierungen oft sehr hilfreich, da reguläre Karten meist zu viele Karteninformationen enthalten oder farblich überladen sind. In unserem Fall haben wir einen einfach Graustufenkarte gewählt. Leaflet selbst lässt sich relativ leicht verwenden, es muss eine CSS-Datei sowie eine JavaScript-Datei importiert werden, und ein div-Block im HTML enthalten sein, worin später die Karte gerendert werden soll. Somit sieht unser HTML-Gerüst zu Beginn so aus:

<!doctype html>
<html>
<head>
	<link rel="stylesheet" href="style/leaflet.css" />
	<script type="text/javascript" src="js/jquery.min.js"></script>
	<script type="text/javascript" src="js/jquery.couch.js"></script>
	<script type="text/javascript" src="js/leaflet.js"></script>
	<script type="text/javascript" src="js/maploader.js"></script>
</head>
<body>
	<div id="map"></div>
</body>
</html>

Es werden jQuery, jquery.couch.js und die Leaflet-Libs geladen, und die letzte importierte JavaScript-Datei soll nun unseren Code zum initialisieren der Karte und dem Laden der Daten aus der CouchDB enthalten. Zunächst erstellen wir eine Karte und rendern sie, sobald die Seite vollständig geladen wurde (jQuery Callback für document.ready):

$(document).ready(function(){

		var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/[YOUR_API_KEY]/33481/256/{z}/{x}/{y}.png';
		var cloudmadeAttribution = 'UlmApi.de / Shape Files: Stadt Ulm (cc-by-sa), Map data &copy; 2011 OpenStreetMap contributors, Imagery &copy; 2011 CloudMade';
		var cloudmade = new L.TileLayer(
			cloudmadeUrl, {
				maxZoom : 18,
				attribution : cloudmadeAttribution
		});

		var map = new L.Map('map', {
			center : new L.LatLng(48.399976,9.995399),
			zoom : 12,
			layers : [ cloudmade ],
			zoomControl : false
		});
});

In der cloudmadeUrl muss für Cloudmade Karte ein korrekter API-Key angegeben werden, der nächste Parameter im Pfad identifiziert den Kartentyp. Beim Initialisieren der Karte wird dann die ID des divs angeben, bei uns ‘map’. Nun sollte unsere Karte bereits dargestellt werden, nachdem wir die CouchApp neu deployen (außerhalb des Fokus dieses Artikels, mehr dazu auf couchapp.org).

Was nun noch fehlt, ist das Nachladen der Geodaten aus der CouchDB und die Anzeige auf der Karte. Hierfür verwenden wir jquery.couch.js als Wrapper für die AJAX-Requests gegen CouchDB und die GeoJSON-Funktionalität von Leaflet:

$.couch.db('database_name').view('design_doc_name/view_name', {

	success: function(data){
		if(data && data.rows && data.rows.length){

			var geoJsonLayer = new L.GeoJSON();

			for(var i = 0;i<data.rows.length;i++){
				geoJsonLayer.addGeoJSON(data.rows[i].value.geometry);
			}

			map.addLayer(geoJsonLayer);
		}
	}
});

Das obige Snippet sollte im vorherigen Code hinter der Erzeugung der Karte eingefügt werden. Es ruft von der Datenbank ‘database_name’ den View ‘view_name’ des Design-Dokuments ‘design_doc_name’ auf, und iteriert bei erfolgreicher Abfrage über alle Zeilen. Von jeder Zeile wird dabei die geometry-Property zu einem GeoJSON-Layer hinzugefügt, der am Ende an die Karte übergeben wird. Da unser View aus Teil 1 bereits GeoJSON generiert, und Leaflet nativ GeoJSON lesen und darstellen kann, ist das Hinzufügen von Geodaten auf die Karte sehr einfach.

Hier noch ein paar weiterführende Links mit vertiefenden Inhalten zu den einzelnen Themen:

Einfache Visualisierung von Geodaten – Teil 1: CouchDB/GeoCouch

Die Hochschulgruppe Open Data Ulm hat es sich zur Aufgabe gemacht, offene und öffentliche Daten rund um die Region Ulm zu aggregieren, aufzuarbeiten und zu visualisieren. Näheres zu diesem Projekt sowie bereits gesammelte Datensätze gibt es unter UlmApi.de

Für unser Vorhaben habe ich als Persistenzlösung die dokumentenorientierte Datenbank CouchDB gewählt, da sie für uns mehrere interessante Features bietet:

  • schemalos: Anders als relationale Datenbanken benötigen schemalose Datenbanken keine im Voraus fest definierte Struktur der Einträge. Für unsere Geodaten ist dies sehr hilfreich, da außer einer ID und den Geodaten noch beliebige zusätzliche Daten pro Eintrag mitgespeichert werden können.
  • JSON: Für die Speicherung strukturierter Daten stellt dieses Format eine leichtgewichtige Alternative zu XML dar.
  • webbasiert: Die Datenbank ist zugleich ein Webserver und der Zugriff auf die Daten läuft somit über HTTP.
  • verteilt/replizierend: Ein wichtiges Konzept von CouchDB ist die einfache aber mächtige Replikation zwischen verschiedenen Instanzen. Im Kontext unserer offenen Datensammlungen ermöglicht dies, dezentralte Kopien der Daten anzulegen, diese lokal zu editieren oder erweitern und wieder auf unsere Hauptdatenbank zu laden.
  • Attachments: Neben strukturierten Daten lassen sich auch ganze Dateien speichern. Dies ist vor allem für archivierte Rohdaten in proprietären Formaten interessant.
  • CouchApps: Neben der Speicherung der Daten sind vor allem einfache Anwendungen interessant, die diese visualisieren oder aufbereiten. Das Konzept der CouchApps ermöglicht es uns, simple Webanwendungen direkt auf der Datenbank zu deployen und verfügbar zu machen.
  • räumliche Indizes: Dank Volker Mische besitzt CouchDB einen zusätzlichen Index (GeoCouch), der statt eindimensionaler B-Bäume zweidimensionale R-Bäume benutzt. Damit lassen sich Dokumente mit räumlichen Daten abspeichern, indizieren und effizient abfragen.

Von der Stadt Ulm haben wir als ersten Datensatz Shapefiles der Ulmer Stadtteile und Stadtviertel bekommen. Diese wurden zunächst vom Ausgangsformat (Gauss-Krueger-Shapefiles) in das GeoJSON-Format mit WGS84-Koordinaten konvertiert. Mithilfe eines kleinen node.js Skriptes wurden die einzelnen Shapes dann als Dokumente auf die Couch geladen.

Ein Dokument hat hierbei folgende Form (Originaldokument):

{
   "_id": "ul-st14",
   "_rev": "1-797187e292d93b6d661ca8f7fec3f6c9",
   "type": "stadtteil",
   "name": "Weststadt"
   "geometry": {
       "type": "Feature",
       "properties": {
           "identifier": "ST 14",
           "name": "Weststadt"
       },
       "geometry": {
           "type": "Polygon",
           "coordinates": [
               [
                   [
                       9.981459,
                       48.395751
                   ],
                   …
              ]
           ]
       }
   },
   "creator": "Stadt Ulm",
   "license": {
       "name": "Creative Commons - Namensnennung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland (CC BY-SA 3.0)",
       "link": "http://creativecommons.org/licenses/by-sa/3.0/de/"
   },
}

Die _id bestimmt das Dokument eindeutig, das _rev Feld ist für die Versionskontrolle verantwortlich. Als ID haben wir einen internen Identifier der Stadt genommen und noch mit einem “ul” Präfix versehen. Der Rest des Dokuments kann frei strukturiert werden. In unserem Fall verwenden wir noch ein type Feld, durch das wir später Dokumente unterschiedlichen Typs unterscheiden können (z.B. stadtteil oder stadtviertel). Das geometry Feld (hier gekürzt) enthält die geografischen Daten im GeoJSON-Format. Die sonstigen Felder beschreiben noch den Urheber und die Lizenz der Daten sowie den Namen des Eintrags.

Nun ist die Datenbank gefüllt, später sollen aber die Daten auch wieder abgefragt werden können. Als “NoSQL” Datenbank bietet CouchDB hierfür aber keine SQL-Statements an, stattdessen muss man mithilfe von MapReduce beschreiben, wie aus den Daten Indizes gebildet werden sollen:

function(doc) {
	if (doc.type) {
		if(doc.type === 'stadtviertel'){
		    emit(['stadtviertel', doc._id], {
		    	'geometry' : doc.geometry,
		    	'label' : "<b>Stadtviertel "+doc.name+"</b><br/>ID: "+doc._id+"<br/>(Stadteil "+doc.stadtteil+")"
		    });
		}
		else if(doc.type === 'stadtteil'){
		    emit(['stadtteil', doc._id], {
		    	'geometry' : doc.geometry,
		    	'label' : "<b>Stadtteil "+doc.name+"</b><br/>ID: "+doc._id
		    });
		}
	}
};

Damit erzeugen wir eine sortiere Liste von Schlüssel-Wert-Paaren. Der Schlüssel ist selbst wieder komplex und besteht aus zwei Teilen. Der erste Teil ist der Typ, der zweite Teil die ID des Dokuments. Damit kann man später durch die sogenannte View Collation Abfragen durchführen, die sich auf einen bestimmtem Typ beschränken (zur Wiederholung: ohne SQL gibt es hier auch kein WHERE Statement). In diesem Fall werden bisher nur Dokumente des Typs stadtteil oder stadtviertel eingetragen, und als Wert eines Eintrages wird bereits die spätere Nutzung auf einer Karte vorbereitet – es werden die Geodaten sowie ein Label indiziert. Damit lassen sich nun schon Stadtteile/Stadtviertel abfragen.

Ergänzt man dies noch um einen räumlichen Index, so werden auch räumliche Abfragen ermöglicht. Hierfür werden in den Index als Schlüssel die Geodaten (unverändert im GeoJSON Format) eingetragen, den Wert selbst bliebt leer, da das Feld _id sowieso eingetragen wird und vorest keine weiteren Daten mehr benötigt werden:

function(doc){
	if(doc.geometry && doc.geometry.geometry){
		emit(doc.geometry.geometry, null);
	}
};

(Das etwas merkwürdig anmutende doc.geometry.geometry entstand einerseits dadruch, dass unser Feld mit dem GeoJSON-Objekt geometry heißt, das GeoJSON-Objekt selbst aber komplex ist und nur in einem Teil davon die eigentlichen Geodaten hinterlegt sind.)

Mithilfe dieses Index lässt sich nun bei einem gegebenen geografischen Raum überprüfen, welche Objekte darin enthalten sind. Also zum Beispiel ausgehend von einer Koordinate, ob sie sich in Ulm befindet und wenn ja, in welchem Stadtteil/Stadtviertel.

Im nächsten Teil wird näher betrachtet, wie die nun abgespeicherten und indizierten Geodaten im Browser auf einer Karte dargestellt werden können, und zwar direkt aus CouchDB heraus.

Präsentieren mit HTML5-Foliensätzen

Kurzversion: HTML5 Foliensatz, basierend auf einem Google Template, das zusätzlich Notizen und einen Presenter Mode bietet: Demo / Code

Nicht nur die Frage, wie man richtig präsentiert (Stichwort Zen vs. Death by PowerPoint), sondern auch die Frage, mit welchen Anwendungen man präsentiert, ist oft umstritten. Ich persönlich konnte mich bisher mit Powerpoint und Konsorten eher wenig anfreunden – vor allem Typografie und Einschränkungen bei der Gestaltung waren problematisch. Als Alternative habe ich bisher oft LaTeX Beamer verwendet, was allerdings je nach visueller Komplexität auch oft relativ zeitaufwendig ist, sich aber zumindest bei Grafiken in Vektorformaten und mathematischen Inhalten auch schnell auszahlt.

HTML5-basierte Foliensätze

Mit dem Aufkommen von HTML5 entstand eine zusätzliche Möglichkeit. Dank der neuen Multimedia-Tags wie <audio> und <video> sowie mächtigeren CSS Stilen bietet HTML nun die Grundlagen für browser-basierte Präsentationen. Mittlerweile gibt es hierfür auch schon mehrere Templates:

Noch mehr Alternativen gibt es in dieser Auflistung. Ein weiteres sehr schönes Beispiel ist ein Foliensatz zu HTML5, der selbst quasi eine Technologiedemonstration enthält: http://slides.html5rocks.com

Der Vorteil von HTML-basierten Präsentationen ist die hohe Anzahl von Medien (u.a. auch SVG, Flash Videos oder ganze Webseiten als IFrames), die man einbetten kann. Ein einfaches, weitläufig bekanntes Markup ermöglicht das schnelle Erstellen von Folien, und mit einer Kombination aus HTML, CSS und JavaScript lassen sich dennoch auch komplexe Spezialfunktionen realisieren.

Mir persönlich hat das html5slides Template ganz gut gefallen, das Google entwickelt und für die Google I/O Slides eingesetzt hat. Da das Template unter einer Apache License veröffentlicht wurde, habe ich zunächst damit begonnen, es an das Uni Ulm Corporate Design anzupassen. Außerdem hatte ich ein paar kleine Änderungen am Code vorgenommen, um zum Beispiel eine Nummerierung der Folien zu ermöglichen.

Presenter Mode?

Prinzipiell war das Ergebnis schon mal akzeptabel, allerdings wurden die oft genannten Probleme bei solchen HTML-Foliensätzen schnell offensichtlich – fehlende Notizen für den Vortragenden und nur eine Ausgabe.

Eher durch Zufall bin ich auf ein anderes Konstrukt gestoßen, dass seit HTML5 Cross-Frame-Communication erlaubt, also den Austausch von Nachrichten zwischen zwei verschiedenen Frames (mit einigen Einschränkungen): window.postMessage()

Die Möglichkeit, zwischen Frames zu kommunizieren, ist natürlich auch ideal dafür, Daten zwischen Frames zu synchronisieren. Übertragen auf zwei verschiedene Präsenationsframes ermöglicht dies beim Weiterschalten der Folien in einem Frame, den zweiten Frame zu aktualisieren. Schematisch sieht das so aus:

(CC-BY-NC) Icons by picol.org / w3.org

Im Hauptfenster kann per Tastendruck ein zusätzliches Popup geöffnet werden (1). Das neue Popup öffnet die gleiche URI der Präsentation und wird auf dem Bildschirm des Vortragenden platziert. Schaltet der Vortragende nun im Hauptfenster weiter zu nächsten Folie, so erzeugt dies ein Nachricht an das zweite Frame (2), das dann ebenfalls weiterschaltet.

Eine weitere Ergänzung war die Unterstützung von Notizen als Overlay über die Folien. Kombiniert mit dem Dual-Screen-Ansatz ermöglicht dies, dem Publikum die Folien zu zeigen, dem Vortragenden auf einem zweiten Bildschirm die Folien plus den verfügbaren Notizen.

Ausführliche Beispiele mit Code gibt es in einer Beispielpräsentation, den kompletten Code auf github: https://github.com/berb/html5slides-uulm

Auf der Feature Wishlist steht noch ein alternativer CSS-Stylesheet für den Druckexport. Außerdem ein Tool, dass externe Daten wie Bilder per Base64 encoding als Data URI integriert und JavaScript sowie Stylesheets inline einbindet, sodass die Präsentation als einzelne HTML5 Datei ohne externe Abhängigkeiten abgespeichert werden kann.

ioexception.de

Benjamin Erb [] studiert seit 2006 Medieninformatik und interessiert sich insbesondere für Java, Web-Technologien, Ubiquitous Computing, Cloud Computing, verteilte Systeme und Informationsdesign.


Raimar Wagner studiert seit 2005 Informatik mit Anwendungsfach Medizin und interessiert sich für C++ stl, boost & Qt Programmierung, Scientific Visualization, Computer Vision und parallele Rechenkonzepte.


David Langer studiert seit 2006 Medieninformatik und interessiert sich für Web-Entwicklung, jQuery, Business Process Management und Java.


Sebastian Schimmel studiert seit 2006 Informatik mit Anwendungsfach Medizin und interessiert sich für hardwarenahe Aspekte, Robotik, webOs, C/C++ und UNIX/Linux.


Timo Müller studiert seit 2006 Medieninformatik. Er interessiert sich allen voran für Mobile and Ubiquitous Computing, systemnahe Enwticklung und verteilte Systeme, sowie Computer Vision.


Achim Strauß studiert seit 2006 Medieninformatik. Seine Interessen liegen in Themen der Mensch-Computer Interaktion sowie Webentwicklung und UNIX/Linux.


Tobias Schlecht studiert seit 2006 Medieninformatik und interessiert sich vor allem für Software Engineering, Model Driven Architecture, Requirements Engineering, Usability Engineering, Web-Technologien, UML2 und Java.


Fabian Groh studiert seit 2006 Medieninformatik. Seine Interessengebiete sind Computer Graphics, Computer Vision, Computational Photography sowie Ubiquitos Computing.


Matthias Matousek studiert seit 2007 Medieninformatik und interessiert sich besonders für Skriptsprachen, Echtzeitsysteme und Kommunikation.


Michael Müller [] studiert seit 2009 Medieninformatik. Er interessiert sich vor allem für Web-Technologien, Ubiquitous Computing, User-Interfaces, UNIX und Creative Coding.


Falco Nogatz [] studiert seit 2010 Informatik mit Anwendungsfach Mathematik. Er interessiert sich für Web-Technologien, Programmierparadigmen und theoretische Grundlagen.

Archiv

Februar 2015
M D M D F S S
« Mrz    
 1
2345678
9101112131415
16171819202122
232425262728