Campusmanagement

Campusmanagement an der FAU

Inhalt

Schnelle Anonymisierung in DB-Tabellen

Aufgabenstellung ist es, mölichst schnell und unkompliziert eine Tabelle mit typischen Benutzerdaten (z.B. Vor- und Nachname, Geburtsdatum) zu anonymisieren. Aufgrund der Größe der Tabelle mit ca. 35.000 Einträgen erachten wir es als ausreichend, die jeweiligen Spalten „durcheinanderzuwürfeln“, so dass andere Kombinationen aus Vor- und Nachnamen entstehen als in der Ausgangstabelle. Und am besten sollten keine externen Tools verwendet werden, also kein Perl, Java etc.

Ausgangstabelle:

1 Firstname1 Lastname1
2 Firstname2 Lastname2
3 Firstname3 Lastname3

Wir generieren zunächst 3 temporäre Tabellen, in die jeweils ein Attribut gespeichert wird:

CREATE TABLE id_temp
(
id serial NOT NULL,
source_id integer
);

CREATE TABLE firstname_temp
(
id serial NOT NULL,
firstname character varying(70)
);

CREATE TABLE lastname_temp
(
id serial NOT NULL,
lastname character varying(70)
);

und eine Zieltabelle mit derselben Strunktur wie die Ausgangstabelle:

CREATE TABLE shuffeled
(
id integer NOT NULL,
firstname character varying(70),
lastname character varying(70)
);

Dann speichern wir in die temporären Tabellen Werte aus der Ausgangstabelle und nutzen dabei die SQL-Random()-Funktion. Wichtig ist auch das Serial-Feld in den temporären Tabellen, das eine Nummerierung der Zeilen sicherstellt.

INSERT INTO id_temp(source_id) SELECT id from original ORDER BY RANDOM();
INSERT INTO firstname_temp(firstname) SELECT firstname from original ORDER BY RANDOM();
INSERT INTO lastname_temp(lastname) SELECT lastname from original ORDER BY RANDOM();

Das Resultat sind drei Tabellen, die zufällig angeordnete Abfolgen der Spalteninhalte der Ausgangstabelle beinhalten.

id_temp:

id source_id
1 2
2 1
3 3

firstname_temp:

id firstname
1 firstname3
2 firstname2
3 firstname1

lastname_temp:

id lastname
1 firstname2
2 lastname1
3 lastname3

Und als letzer Schritt werden diese zusammengeführt in die Zieltabelle:

INSERT INTO shuffeled (id, firstname, lastname)
SELECT id_temp.source_id AS id,
firstname_temp.firstname AS firstname,
lastname_temp.lastname AS lastname
FROM id_temp, firstname_temp, lastname_temp
WHERE id_temp.id=firstname_temp.id
AND id_temp.id=lastname_temp.id;

Et voila, wir erhalten eine Tabelle mit zufällig angeordneten Kombinationen:

1 Firstname2 Lastname3
2 Firstname3 Lastname1
3 Firstname1 Lastname2

Danach müssen nur noch die temporären Tabellen wieder entfernt werden. Statt in eine Zieltabelle sch schreiben, kann natürlich auch die Ausgangstabelle beschrieben werden.

In der tatsächlichen Anwendung – die noch einiges komplexer ist und ca. 36.000 Einträge hat – dauert das ganze auf einem Standard-PC ca. 6 Sekunden!

mein campus SSO Backend

Die Anbindung von mein campus an das SingleSignOn-System des RRZE soll in Kürze erfolgen. Die hiefür nötigen Implementierungsarbeiten im Backend-Bereich von mein campus wurden durchgeführt. Nun folgt der Aufbau der SSO-Serviceprovider-Infrastruktur (bei uns kommt die Shibboleth-Implementierung zum Einsatz) für mein campus und das Testen der neuen Funktionen.
Auch nach Freischaltung wird es wie bisher möglich sein, sich lokal bei mein campus anzumelden. Die zusätzlich mögliche SSO-Anmeldung ist zunächst auf die Benutzergruppe der Studierenden beschränkt.

HTTP-Requests, Hibernate Sessions und ThreadLocal

Kurz zum Hintergrund:
Hibernate kapselt Datenbank-Persistenz von der Anwendungslogik. Somit sind keine Low-Level-Datenbankoperationen mehr nötig, die entsprechenden SQL-Statements werden von Hibernate erzeugt. Im Hintergrund öffnet Hibernate die Verbindung zur DB, schickt Statements und stößt die Ausführung an (commit), dies alles im Kontext einer „Session“. Die Erzeugung einer Session ist keine teuere Operation, es wird in der Hibernate-Literatur empfohlen, möglichst kurze Sessions zu verwenden und bei Bedarf neue zu erzeugen.
In der ersten Implementierung der Veranstaltungsverwaltung gab es (sehr schlecht!!) nur eine Session pro Sitzung, d.h. beim Login wurde eine Session erzeugt, die erst bei der Abmeldung wieder freigegeben wurde. Um das Session-Objekt von den Objekten anderer angemeldeter Teilnehmer abzuschotten, wurde es als ThreadLocal an den aktuellen Thread gebunden. Folge war, dass in bestimmten Konstellationen Domänenobjekte veraltete Daten gespeichert hatten, denn ein neuer Request eines Browsers kann in einem anderen Thread abgesetzt werden als bisher.
Inzwischen ist ein Session-Per-Request implementiert, das heißt, dass für jeden Aufruf einer Seite eine neue Session erzeugt wird, die nach erfolgreichem Laden committed und beendet wird. Dies hat jedoch Auswirkungen:

  • Änderungen an der Datenbank (Update, Insert) werden erst ausgeführt, nachdem ein commit() auf die Transaktion der Session aufgerufen wurde. Bei Session-Per-Request kommt dieses commit eigentlich zu spät, nämlich erst nach der Darstellung des Inhalts der Seite. Daher muss das commit() bei Änderungsaktionen explizit angestoßen werden.
  • Objekte, die nicht an eine Session gebunden sind, verursachen Fehler, sobald sie auf ihre Felder zugreifen wollen (Hibernates Lazy Loading – DB-Aktionen werden erst ausgeführt, wenn das Objekt tatsächlich benötigt wird, an sich eine gute Sache) oder selbst wieder persistiert werden sollen („Hibernate: no session or session was closed“). Auch auf solche Fälle muss daher in Zukunft sorgfältig geachtet werden.

Eine Veranstaltung hat keine Teilnehmer

Bei der Konzeption der  Veranstaltungs-Anmeldung dachten wir zunächst daran, dass es eine Teilnehmerliste und eventuell eine Warteliste pro Veranstaltung geben geben sollte. Dies erwies sich jedoch bald als zu unflexibel:
Was zum Bespiel tun bei Anmeldeheuristiken wie RoundRobin? Hier meldet man sich für einen Course an, die Zuteilung zu einem bestimmten Track (zu diesen Begriffen siehe meinen letzen Blogpost) erfolgt aber durch das System. Ähnlich bei Losverfahren bei einem Course mit Tracks. Hier resultiert die Anmeldung darin, dass der Angemeldete zunächst in einer virtuellen Warteliste des Courses gespeichert ist; erst wenn die Verlosung der Plätze durchgeführt wird, erfolgt die Verteilung auf einzelne Tracks – die selbst aber keine Warteliste haben.
Wir haben uns für ein Subscription-Objekt entschieden, in dem die Verbindung zwischen Teilnehmer und Veranstaltung gespeichert wird:
Subscription(Course course, Track track, Participant participant, Subscriptiontype type)
Subscriptiontype := {SUBSCRIBED|WAITINGLIST|UNSUBSCRIBED}
Eine Veranstaltung hat somit eine Liste aus Subscriptions und über deren Type kann Teilnehmer- und Warteliste dynamisch extrahiert werden.
Also, wie im Titel des Posts: Eine Veranstaltung hat keine Teilnehmer, sondern Subscriptions mit Subscriptiontype=SUBSCRIBED
 

Veranstaltungen und Kurse

Die übliche Ausprägung einer Veranstaltung an einer Universität (egal ob Vorlesung, Übung, Seminar oder sonstiges) ist:

  • ein Veranstaltungsname
  • eine bestimmte Zeit und ein Ort für die Durchführung
  • ein Dozent

Jeder kennt jedoch Veranstaltungen, die wegen hoher Teilnehmerzahlen aufgeteilt werden – beispielsweise eine Übung im Grundstudium Informatik mit hunderten von Teilnehmern, bei der jede Übungsgruppe jedoch eine Maximalzahl an Teilnehmern hat. Als Student muss man nur eine der Ausprägungen besuchen, also zum Beispiel die Übung montags 14:00 bis 16:00 Uhr.
Die UnivIS-Nomenklatur hierfür ist „Veranstaltung“ und „Kurs“. Das heißt, eine Veranstaltung kann – muss aber nicht – mehrere untergeordnete Kurse haben. Der Besuch eines dieser Kurse bedeutet den Besuch der Veranstaltung.
Die Veranstaltungsverwaltung bildet diese Struktur natürlich ebenfalls ab, allerdings mit englischen Bezeichnern:

  • UnivIS-Veranstaltung = Course
  • UnivIS-Kurs = Track

Wegen der phonetischen Ähnlichkeit von „Course“ und „Kurs“ ist es geboten, sich an eine der Nomenklaturen zu halten und dies vor dem Gespräch abzuklären.
Ein Track erbt von seinem Course einige Stammdaten wie die Zuordnung zu einem Studiengangsmodul oder die Anzahl der Credits, die bei erfolgreichem Besuch vergeben werden. Tracks eines Courses können sich jedoch unterscheiden in Bezug auf

  • Dozenten
  • Zeit und Ort
  • und natürlich unterscheiden sie sich durch die Teilnehmer, es sei denn, ein Student möchte doppelte Arbeit leisten 😉

Erweiterung des CIT-Teams

Seit Mitte Januar ist das CIT-Team um zwei neue Mitglieder erweitert.  Eines davon bin ich, daher möchte ich mich in aller Kürze vorstellen:
Mein Name ist Peter Reiß, in Bamberg geboren und seit dem Studium (Computerlinguistik) in Erlangen. Nach dem Studium war ich sechs Jahre lang am Lehrstuhl Informatik 8 als wissenschaftlicher Mitarbeiter beschäftigt, wo ich im Sommer auch promoviert habe. Im CIT-Projekt werde ich mich vor allem um die Integration der Veranstaltungsverwaltung in das bisherige Framework kümmern – eine Aufgabe, auf die ich mich freue!