Fachhochschule Aachen Fachbereich 2 - Bauingenieurwesen Seminararbeit im Studiengang Scientific Programming Wintersemester 2011/2012 Betreuer: Prof. Dr. Jürgen Höttges Valerij Herber, B.Sc. Konzeption und Realisierung einer Datenbank zur Klausurenverwaltung mit PostgreSQL/Python Daniel Lütkehermölle Malteserstraße 20 52062 Aachen [email protected] 16. Dezember 2011 Inhaltsverzeichnis 1 Einleitung 1.1 Ausgangssituation und Motivation . . . . . . . . . . . . . . . . . . 1.2 Konventionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 3 2 Grundlagen 2.1 Programmiersprache Python 2.1.1 Allgemeines . . . . . . 2.1.2 PsycoPG . . . . . . . 2.2 Datenbanken . . . . . . . . . 2.2.1 Allgemeines . . . . . . 2.2.2 SQL . . . . . . . . . . 2.2.3 Relationale Algebra . 4 4 4 5 5 5 7 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Entwurf des Datenbankschemas 4 Umsetzung 4.1 Verwendete Software 4.2 Datenbank . . . . . . 4.3 Datenmigration . . . . 4.4 Python Schnittstellen . . . . . . . . . . . . . . . . . 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 11 12 12 5 Fazit 14 6 Ausblick 14 7 Anhang 7.1 Abbildungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2 Quelltextbeispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3 Literaturverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . 15 15 16 27 1 Einleitung 1.1 Ausgangssituation und Motivation Im Fachbereich 2 der Fachhochschule Aachen sind die Raumkapazitäten für Klausuren begrenzt. Um eine optimale Auslastung dieser Räumlichkeiten zu erreichen, ist eine sorgfältige Planung notwendig. Dabei spielen viele Faktoren eine Rolle, wie zum Beispiel die Anzahl der Studenten oder Krankheitsfälle unter Aufsichtsführenden, die sich auch kurzfristig noch ändern können und eine schnelle Änderung der Planung notwendig machen. Um diesen Planungsaufwand zu vereinfachen, wurde ein Datenbanksystem mit einer einfachen Benutzerschnittstelle entwickelt, die dabei helfen soll die Organisation von Klausuren durchzuführen und die Raumverteilung in Form von Papieraushängen zu publizieren. Da jedoch nicht viel Zeit in dieses System investiert wurde, erschien es an vielen Stellen unvollständig und verbesserungswürdig. Mit dieser Arbeit soll dieses System von Grund auf neu entwickelt werden. Dabei wurden auch durch Besprechungen mit dem Nutzerkreis gewünschte Funktionalitäten ergründet, die im alten System noch nicht umgesetzt waren. 1.2 Konventionen • Im Zusammenhang dieser Arbeit wird das Wort „Sprache“ synonym zu „Programmiersprache“ verwendet. • Diagramme der Datenbank sind gemäß des UML-Standards angefertigt. • Quellenangaben und Anmerkungen im Text werden in nummerierten Fußnoten angegeben. • Quelltextbeispiele werden durch eine andere Schriftart und Hervorhebung der Syntax gesondert gekennzeichnet. 3 2 Grundlagen 2.1 Programmiersprache Python 2.1.1 Allgemeines Die Programmiersprache Python wurde um 1990 mit dem Ziel entwickelt einfach benutzbar und lesbar zu sein und dabei dennoch eine umfangreiche Funktionalität zu besitzen1 Es handelt sich bei Python vordergründig um eine imperative Sprache, hält aber mittlwerweile auch objektorientierte Ansätze vor. So ist das Konzept der Polymorphie und Vererbung in Python umsetzbar. Die Möglichkeit mit verschiedenen Programmierparadigmen arbeiten zu können, macht Python zu einer sehr vielseitigen Sprache. Da unsere Abteilung hauptsächlich aus Auszubildenden und wiss. Hilfskräften besteht, gibt es eine hohe Fluktuation an Mitarbeiten, deren Beteiligung an Programmierprojekten selten drei Jahre überschreiten. Daher ist es wichtig, dass sich ein Programmierer leicht in bestehende Projekte einarbeiten kann, was nur durch gut lesbaren und dokumentierten Quelltext erleichtert werden kann. Python bringt die Eigenschaft von Haus aus mit und unterstützt schon beim Programmieren die Lesbarkeit durch die fest vorgegebene Syntax. Der Personalstruktur dienlich ist auch die aufgrund der geringen Anzahl von Schlüsselwörtern und der impliziten Typisierung von Datenstrukturen schnelle Erlernbarkeit für Programmieranfänger im Vergleich zu Programmiersprachen wie C/C++ oder Java. Ein weiterer Vorteil sind die Plattformunabhängigkeit, die die Sprache durch den Python-Interpreter erlangt, und die Unterstützung unterschiedlicher Programmierparadigmen, die es ermöglichen Python in einer Vielzahl von Projekten verwenden zu können. Schlussendlich bieten die Lizenzen, unter denen Python (PSF-Lizenz2 und die Bibliothek PsycoPG (LGPL-Lizenz) 3 entwickelt und veröffentlicht werden, die Möglichkeit einer freien Verwendung ohne rechtliche Einschränkungen. In dieser Arbeit wird Python in der Version 2.7.2 benutzt, welche die letzte Version aus dem Entwicklungsbaum von Python 2 darstellt. Das aktuellere Python 3 wurde nicht verwendet, da zum Zeitpunkt der Programmierung einige Bibliotheken (z.B. PsycoPG) noch nicht für die Version 3 umgesetzt waren. 1 Peter Kaiser, Johannes Ernesti: Python - Das umfassende Handbuch,Galileo Computing,Bonn,2008:S.28 2 http://docs.python.org/license.html, Dezember 2011 3 http://initd.org/psycopg/license/, Dezember 2011 4 2.1.2 PsycoPG Für die Anbindung an die Datenbank wird die Python-Bibliothek PsycoPG verwendet, die eine sehr komfortable Schnittstelle für SQL-Abfragen an die Datenbank bietet. 2.2 Datenbanken 2.2.1 Allgemeines Moderne Informationsverarbeitungssystem sind ohne ein DBMS (Datenbankmanagementsystem) kaum noch denkbar. Gegenüber einer Datenhaltung in einem einfachen Dateisystem hat die Datenbank erhebliche Vorteile. Informationen in Datenbanken lassen sich leicht in neue Relationen setzen, was mit isolierten Dateien nur unter Aufwand möglich ist und bietet so auch in bereits bestehenden System die Möglichkeit existierende Daten in vorher ungeplanten Zusammenhang zu stellen. Ein DBMS besteht aus drei Schichten: • Physische Ebene - Hier wird die eigentliche Datenspeicherung auf dem Datenträger (z. B. Festplatte) organisiert. • Logische Ebene - Diese Ebene beherbergt das Datenbankschema, in der die Daten in einen logischen Bezug gebracht werden. Das sogenannte Datenmodell versucht dabei den realen Bezug möglichst genau abzubilden. • Sichtebene - Die Schnittstelle zu den Benutzergruppen wird über - von der physischen Datenspeicherung nun völlig unabhängigen - Programme geleistet. Diese können die Informationen des Datenbankschemas auf die Wünsche und Rechte des jeweilgen Nutzers angepasst darstellen. Damit ein DBMS die oben beschriebenen Vorteile bietet, müssen die darauf ausführbaren Transaktionen die sogenannten ACID (dt. AKID)-Eigenschaften erfüllen. AKID ist die Abkürzung für die vier Eigenschaften, die eine Datenbank erfüllen muss, um überhaupt funktional zu sein und machen das aus, was sie von anderen Datenhaltungssystemen, wie der Speicherung in einem Dateisystem, abhebt. 5 • Atomizität - Diese Eigenschaft verlangt, dass eine Transaktion als kleinste, nicht mehr weiter zerlegbare Einheit behandelt wird. [. . . ]„alles-oder-nichts“Prinzip • Konsistenz - Eine Transaktion hinterlässt nach Beendigung einen konsistenten Datenbasisbestand. Andernfalls werden sie komplett [. . . ] zurückgesetzt. [. . . ] Der resultierende Endzustand muss die im Schema definierten Konsistenzbedigungen (z.B. referentielle Integrität) erfüllen. • Isolation - Nebenläufig [. . . ] ausgeführte Transaktionen [dürfen] sich nicht gegenseitig beeinflussen. Jede Transaktion muss - logisch gesehen - so ausgeführt werden, als wäre sie die einzige Transaktion. [. . . ] • Dauerhaftigkeit - Die Wirkung einer erfolgreich abgeschlossenen Transaktion bleibt dauerhaft in der Datenbank erhalten. 4 Die Eigenschaften der Dauerhaftigkeit wird werden bereits durch die eingesetzte PostgreSQL-Software sichergestellt und der Datenbankentwickler muss sich darum in der Regel nicht selbst sorgen. Ebenfalls kann SQL vor einer Inkonsistenz der Daten schützen, indem die Auswertung von Anfragen an die Datenbank, die eine Inkonsistenz hervorrufen würden, nicht ausgeführt werden. So warnt z.B. PostgreSQL bevor eine Spalte, die als Fremdschlüssel zu anderen Datensätzen verweist gelöscht werden soll und lässt die Löschung erst zu, wenn diese Abhängigkeit zuvor gelöst wurde. Atomizität und Isolation kann von SQL allerdings nur bedingt überprüfen und gewährleisten. Der Entwickler muss darauf achten, dass sein Datenbankkonzept diese Eigenschaften erfüllt, indem er sein entwickeltes Schema schrittweise normalisiert. Dabei helfen die sogenannten Normalformen. Normalformen sind auf einander aufbauende formale Anforderungen an ein Datenbankschema, die die Datenredundanz immer weiter verringern ohne dabei formal mathematisch bewiesen - zu Datenverlust zu führen. Insgesamt sind sechs Normalformen in der Literatur bekannt, allerdings haben sowohl die Boyce-CoddNormalform (BCNF), als auch die vierte und fünfte Normalform (4NF, 5NF) einen speziellen Charakter und werden nicht erläutert, da sie in dem später gezeigten Datenbankschema keine Verwendung finden.5 Auf die ersten drei Normalformen soll in der folgenden Tabelle eingegangen werden, in der zu jeder Normalform eine kurze Definition und die positiven Folgen für eine so normalisierte Tabelle skizziert sind. 4 Alfons Kemper, Andre Eickler: Datenbanksysteme - Eine Einführung, Oldenbourg Verlag,München,2009:S.283 5 http://de.wikipedia.org/wiki/Normalisierung_(Datenbank), Dezember 2011 6 Normalform Definition Resultat 1. Normalform (1NF) Alle Attribute müssen einen atomaren Wertebereich besitzen. Die Atomizität ist nun erfüllt. 2. Normalform (2NF) Nichtschlüsselattribute dürfen nur von einem einzigen Schlüssel direkt abhängen. Die Abhängigkeiten zwischen Attributen ist auf die logisch Nachvollziehbaren beschränkt. Die Tabelle erfüllt aber noch nicht alle Anforderungen der Isolationsbedingung. 3. Normalform (3NF) Nichtschlüsselattribute dürfen nicht transitiv von einem Schlüssel abhängen. Die Tabelle besitzt nun die Eigenschaft der Isolation. 2.2.2 SQL SQL steht für „Structured Query Language“ und wurde in den 70er Jahren des letzten Jahrhunderts bei IBM speziell dazu entwickelt Abfragen an Datenbanksystem stellen zu können. Das Programmierparadigma ist deklarativ. Da es sich bei SQL nicht um eine turingvollständige Sprache handelt, also nicht beliebige Programme damit entwickelt werden können, ist es oft nötig sie mit anderen Programmiersprachen (wie in diesem Fall mit Python) zu kombinieren. SQL ist eine reine Anragesprache, es ist also für den Entwickler bei der Programmierung mit SQL unwichtig, wie die Daten zusammengestellt werden, sondern nur welche Daten er erhält. Die vorher erwähnten relationalen Algebraoperation können mit dieser Sprache umgesetzt werden. Eine Besonderheit der Sprache ist, dass verschiedene Hersteller den SQL-Standard sehr unterschiedlich umsetzen. So ist es z.B. nicht direkt möglich Anfragen aus MySQL in PostgreSQL zu übertragen. Zwar sind grundlegende Abfragen in allen Derivaten des SQL-Standards grundsätzlich gleich, doch kann sich die Syntax und auch fortgeschrittenere Befehle in den Umsetzungen so stark unterscheiden, dass keine Kompatibilität untereinander mehr vorhanden ist. 2.2.3 Relationale Algebra Um später den Datenbestand sinnvoll nutzen zu können, muss man die Datenabfragen mathematisch sinnvoll gestalten können. Dafür werden hier verschiedene 7 mathematische Funktionen vorgestellt mit denen das durchgeführt werden kann. Es handelt sich dabei um die fünf Grundfunktionen, aus denen sich weitere Funktionen ableiten lassen, die an dieser Stelle aber nicht erwähnenswert sind. • Selektion Es werden Zeilen ausgegeben, die eine bestimmte Bedingung erfüllen. SQL-Beispiel: SELECT ∗ FROM [ T a b e l l e ] 2 WHERE [ Bedingung erfüllt ]; 1 • Projektion Es wird die Auswahl an Spalten ausgegeben. SQL-Beispiel: 1 SELECT [ S p a l t e ] FROM [ T a b e l l e ] • Vereinigung Zwei Relationen mit verknüpften Schlüssel werden zusammengefast ausgegeben. SQL-Beispiel: 1 2 3 SELECT ∗ FROM [ T a b e l l e 1 ] UNION SELECT ∗ FROM [ T a b e l l e 2 ] • Mengendifferenz Es werden alle Zeilen ausgegeben, deren Schlüssel in der ersten, aber nicht in der zweiten Tabelle vorkommen. SQL-Beispiel: 1 2 3 SELECT ∗ FROM [ T a b e l l e 1 ] EXCEPT SELECT ∗ FROM [ T a b e l l e 2 ] • kartesisches Produkt Es werden ohne Einschränkung alle möglilchen Paare aus Tabelle 1 und Tabelle 2 gebildet. SQL-Beispiel: 1 SELECT ∗ FROM [ T a b e l l e 1 ] , [ T a b e l l e 2 ] 8 3 Entwurf des Datenbankschemas Der Entwurf erfolgte nach der „top-down“-Vorgehensweise, in der ein Schema in mehreren aufeinanderfolgenden Entwurfsschritten entwickelt wird. Nach jedem Schritt wird der Prototyp mit den Anforderungen an das Schema verglichen und im nächsten Schritt erneut darauf angepasst. Im ersten Zyklus wurde in der Anforderungsanalyse geprüft, welche Entitäten für die Datenbank relevant sind. Das hat die notwendigen Entitäten „spec“ (Vertiefungsrichtung), „course“ (Studiengang), „module“ (Fach), „Date“ (Datum), „room“ (Raum), „examiner“ (Prüfer) und „Aufsichtsperson“ ergeben. Diesen Entitäten fügt man in einem zweiten Schritt beschreibende Attribute zu. „Prüfer“ und „Aufsichtsperson“ benötigen z.B. eine Peronalnummer zur internen Identifikation und einen Namen. Es war aufgrund der identischen Attributsmenge also möglich diese beiden Entitäten unter „Person“ zusammenzufassen. Auf diese Weise wurden alle Entitäten des Modells ermittelt und sie können nun zueinander in Beziehung gesetzt werden. • Beziehung „part of“: Ein Fach ist immmer genau einem Studiengang zugeordnet, ein Studiengang kann allerdings auf mehrere Fächer verweisen. Im ER-Diagramm wird diese Beziehung als 1:N-Beziehung bezeichnet. Außerdem kann ein Fach nur dann geben, wenn es in einem Studiengang angeboten wird. „Fach“ ist eine sogenannte existenzabhängige Entität. • Beziehung „has a“: Jedes Fach gehört zu einer Vertiefungsrichtung, es können mehere Fächer zu einer Vertiefungsrichtung gehören. • Beziehung „examines“: Es gibt für ein Fach genau eine Person (den Prüfer), allerdings kann es noch ein oder mehrere Zweitprüfer geben, daher wird diese Beziehung als 1:N-Beziehung dargestellt. • Beziehung „when“: 1:N-Beziehung. An einem Datum können mehrere Klausuren geschrieben werden, aber eine Klausur nur an einem Datum (pro Prüfungsperiode). • Beziehung „where“: Hier wird darauf Rücksicht genommen, dass durchaus verschiedene Klausuren in einem Raum statt finden können, als auch die Teilnehmer einer Klausur auf mehrere Räume verteilt werden können. Hier handelt es sich um eine N:M-Beziehung. • Beziehung „supervise“: Jeweils eine Person reicht aus, um einen Raum zu betreuen. Mehr als eine 1:1-Beziehung ist hier also nicht notwendig. 9 In Abb. 1 ist das aus diesen Entitäten und Beziehungen entwickelte Entity-RelationshipDiagram zu sehen. 10 4 Umsetzung 4.1 Verwendete Software • GNU/Linux Debian Squeeze 6.0.2 (Lizenz: div. DFSG konforme Lizenzen6 ) • PostgreSQL 8.4.9 (Lizenz: PostgreSQL License 7 ) • Git 1.7.5.4 (Lizenz: GPL 3.0 8 ) • Python 2.7.2+ (Lizenz: PSF License 9 ) • PsycoPG 2.4.2 (Lizenz: LGPL License 10 ) • PGAdmin3 1.12.3 (Lizenz: PostgreSQL License) 4.2 Datenbank Auf einer mit dem Betriebssystem Debian betriebenen virtuellen Maschine wurde mit PostgreSQL eine Datenbank erstellt. Mittels PGAdmin3 wurde das vorher beschriebene Datenbankschema implementiert. In Abb. 2 ist das endgültig implementierte Schema zu sehen. Alle Entitäten und Relationen, die bereits in der Konzeption angedacht waren sind auch hier wieder als solche zu erkennen. Die Tabellen „course“, „date“, „room“, „person“ und „spec“ sollen hierals primäre Tabellen bezeichnet werden, da sie ohne einen Primärschlüssel auskommen. Dies sind die Entsprechungen der Entitäten aus dem vorherigen Kapitel. Die Tabelle „modul“ hat zwar auch eine Entität als Entsprechung, ist aber noch von den Tabellen „course“ und „spec“ abhängig. Hier wurde auf eine Normalisierung vorerst verzichtet, um die Datenmigration von der alten Datenbank nicht unnötig zu erschweren (vgl. Kap.4.3). Die Relationen „part of“ und „has a“ haben hier keine entsprechenden Tabellen. Eine ebensolche Vereinfachung wurde auch bei der Relationen „supervise“ und „when"gemacht, die als Tabelle neben den beiden Fremdschlüsseln, die „room“ mit „person“ verknüpfen sollen, noch einen Primärschlüssel besitzen. „supervised_exam“ als Entsprechung zu „supervise“ braucht diesen Primärschlüssel, 6 http://wiki.debian.org/DFSGLicenses (Stand: Dez. 2011) http://www.postgresql.org/about/licence/ (Stand: Dez. 2011) 8 http://www.gnu.org/licenses/gpl-3.0.html (Stand: Dez. 2011) 9 http://docs.python.org/license.html (Stand: Dez. 2011) 10 http://initd.org/psycopg/license/ (Stand: Dez. 2011) 7 11 um in der Tabelle „exam_room“ referenziert werden zu können. Die Tabelle „exam_date“ (für die Relation „when“) erhielt zusätzlich das Attribut „attendees“, um die Prüfungsteilnehmer an einem bestimmten Klausurtermin aufnehmen zu können. Die Tabelle „exam“ ist gänzlich neu hinzugekommen, da neue Attribute, „po_version“ (Prüfungsordnung) und „mode“ (Prüfungsart) hinzugekommen sind, die logisch nicht noch dem „modul“ zugeschlagen werden konnten. 4.3 Datenmigration Die Migration der Daten aus der alten MySQL Datenbank wurde erreicht, indem zunächst mithilfe des MySQL Frontends PHPMyAdmin alle Inhalte mitsamt des Schemas in eine zu PostgreSQL syntaktisch kompatible Form übersetzt und dann als SQL-Dumpfile exportiert wurden. Diese Daten wurden dann in die PostgreSQL Datenbank importiert, so dass zunächst sowohl das alte, als auch das neue Datenbankschema für kurze Zeit parallel in einer Datenbank liefen. Nun konnten die Daten mit SQL-Abfragen aus dem alten Schema in das neue Schema übertragen werden. Wenn eine Datenbank nicht das Kriterium der ersten Normalform einhält, wird dieses Unterfangen jedoch enorm erschwert. Der alte Datenbestand enthielt sowohl Elemente, in denen mehrere Attribute den eindeutigen Schlüssel bildeteten (Verletzung der zweiten Normalform) als auch mengenwertige Attributdaten (Verletzung der ersten Normalform). Dies führte dazu, dass die Abfragen enorm komplex wurden. 4.4 Python Schnittstellen Die Schnittstellen, die die Daten aus der Datenbank in Python zur Verfügung stellen sind in zwei Klassen aufgetrennt. Der „DBHandler“(Listing 2) sorgt für eine Anbindung zur Datenbank und ist für den Lesezugriff auf die Datenbank verantwortlich. Die GUI-Anwendung erhält Daten über die Methode „getList“ (Z.43f), der der Name der jeweils gewünschten Tabelle übergeben werden muss. Die Ausgabe erfolgt im Datentyp Dictionary. In jedem Element befindet sich dann eine Zeile der Tabelle in der Form „[Spaltenname1]:[Feldinhalt1],[Spaltenname2]:[Feldinhalt2],. . . “, die dann weiterverarbeitet werden können. Wird neben dem Lesezugriff auch Schreibzugriff benötigt, so bietet die Klasse DBInputHandler (Listing 3 die Möglichkeit der Authentifizierung als Administrator (Z.27f). Für jede Tabelle gibt es hier außerdem eine Methode, die als jeweilige Übergabeparameter die Eingabedaten für jeden Attributwert der Tabelle benötigen. 12 Intern stellt die Bibliothek PsycoDB die Methoden zur Verfügung, um die Eingabedaten in SQL Abfragen zu verwandeln. Die Methode psycopg2.connect gibt eine Instanz der Klasse Connection zurück. Diese Klasse erzeugt nun ihrerseits einen Object der Klasse Cursor, der sich nun um das Handling der Datenbankabfragen kümmert. Mittels cursor.execute([SQLAbfrage]) ist es nun möglich direkt SQL-Abfragen per Python an die Datenbank zu stellen und die Methode cursor.fetchall() gibt den Rückgabewert der letzten Abfrage in Listenform aus. Umgekehrt können dem curser ebenfalls per Methode exute() Daten in die Datenbank eingespeist werden. Statt einer normalen Abfrage übergibt man der Methode nun einfach deinen update- oder insert-Befehl. Die Methoden fetch() und fetchall() geben nun entweder None oder einen Fehlercode bei einem Fehlschlag zurück. 13 5 Fazit Es stellte sich heraus, dass das Arbeiten mit Datenbanken vor allem darin eine Herausforderung ist, die wirkliche Welt zu abstrahieren und möglichst gut in einem Beziehungsschema abbzubilden. Wie bei der Diskrepanz zwischen Theorie und Praxis üblich, gelingt das mal mehr und mal weniger erfolgreich. Oft muss man Kompromisse eingehen, die mathematisch keine optimalen Lösungen darstellen, um zu gewährleisten, dass die Datenbank auch für diejenigen noch schnell erfassbar bleibt, wenn sie neu in das Projekt hinzukommen (neue Programmierer) oder mit der Thematik wenig vertraut sind (Kunden, Nutzer). Die standardisierten Vorgehensweisen, die in der Literatur beschrieben sind, helfen dabei ungemein einen roten Faden zu behalten und man sollte unbedingt ein solches Vorgehensmodell wählen, wenn man eine Datenbank entwickeln möchte. Hier, wie auch in der 6 Ausblick Wie wohl jedes Softwareprojekt ist auch dieses noch weit von Perfektion entfernt. Eine konkrete Funktionalität, die möglich, aber noch nicht umgesetzt wurde, ist ein automatisierter Datenimport aus dem QiS-System der Fachhochschule, das die meisten Daten über Klausuren bereits vorhält. Bisher müssen diese Daten noch von Hand in eine entsprechende GUI-Maske eingegeben werden, was unnötig Zeit und Mühe kostet. Es ist zu ergründen in wie weit diese Anbindung auch in beide Richtungen funktionieren könnte, dann könnten Dozenten ihre Modul und Klausurdaten frei zwischen beiden Systemen austauschen. Sogar eine Anmeldung am Qis-System über dieses System wäre denkbar, auch wenn dabei sicherheits- und dsatenschutztechnische Aspekte geklärt werden müssen. Eine weitere Möglichkeit ist die Erweiterung der Datenbasis, in dem weitere für Studenten, Dozenten und Prüfer relevante Daten aufgenommen werden, wie z.B. Hinweise zu Klausurvorbereitungen. Der Ausbau zu einer Raumverwaltung über die Klausurzuteilung hinaus ist ebenfalls denkbar. 14 7 Anhang 7.1 Abbildungen Abbildung 1: Entity-Relationship-Diagram des Datenbankkonzeptes 15 Abbildung 2: Das Schema der implementierten Datenbank 7.2 Quelltextbeispiele Listing 1: Der Quelltext zur Erstellung des SQL-Datenschemas. 1 −− Table : course 2 3 −− DROP TABLE course ; 4 5 6 7 8 9 10 11 12 CREATE TABLE course ( i d _ c o u r s e character varying ( 3 2 ) NOT NULL, " name " character varying ( 1 2 8 ) NOT NULL, degree character varying ( 3 2 ) NOT NULL, CONSTRAINT course_pkey PRIMARY KEY ( i d _ c o u r s e ) ) WITH ( 16 13 14 15 OIDS=FALSE ); ALTER TABLE course OWNER TO p o s t g r e s ; 16 17 18 −− Table : date 19 20 −− DROP TABLE date ; 21 22 23 24 25 26 27 28 29 30 31 32 33 34 CREATE TABLE date ( i d _ d a t e character varying ( 3 2 ) NOT NULL, pp character varying ( 3 2 ) , due_date timestamp w i t h o u t time zone , d e a d l i n e timestamp w i t h o u t time zone , CONSTRAINT date_pkey PRIMARY KEY ( i d _ d a t e ) , CONSTRAINT date_check CHECK ( d e a d l i n e < due_date ) ) WITH ( OIDS=FALSE ); ALTER TABLE date OWNER TO p o s t g r e s ; 35 36 37 −− Table : exam 38 39 −− DROP TABLE exam ; 40 41 42 43 44 45 46 47 48 49 CREATE TABLE exam ( id_exam character varying ( 3 2 ) NOT NULL, examiner1 character varying ( 3 2 ) NOT NULL, examiner2 character varying ( 3 2 ) , modul character varying ( 3 2 ) NOT NULL, " mode " character varying ( 3 2 ) NOT NULL, p o _ v e r s i on character varying ( 3 2 ) NOT NULL, CONSTRAINT exam_pkey PRIMARY KEY ( id_exam ) , 17 50 51 52 53 54 55 56 57 58 59 60 61 62 63 CONSTRAINT exam_examiner1_fkey FOREIGN KEY ( examiner1 ) REFERENCES person ( i d _ pe r s o n ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION , CONSTRAINT exam_examiner2_fkey FOREIGN KEY ( examiner2 ) REFERENCES person ( i d _ pe r s o n ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION , CONSTRAINT exam_modul_fkey FOREIGN KEY ( modul ) REFERENCES modul ( id_modul ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) WITH ( OIDS=FALSE ); ALTER TABLE exam OWNER TO p o s t g r e s ; 64 65 66 −− Table : exam_date 67 68 −− DROP TABLE exam_date ; 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 CREATE TABLE exam_date ( id_exam_date character varying ( 3 2 ) NOT NULL, exam character varying ( 3 2 ) NOT NULL, a t t e n d e es i n t e g e r NOT NULL, date character varying ( 3 2 ) NOT NULL, CONSTRAINT exam_date_pkey PRIMARY KEY ( id_exam_date ) , CONSTRAINT exam_date_date_fkey FOREIGN KEY ( date ) REFERENCES date ( i d _ d a t e ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION , CONSTRAINT exam_date_exam_fkey FOREIGN KEY ( exam ) REFERENCES exam ( id_exam ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) WITH ( OIDS=FALSE ); 18 87 ALTER TABLE exam_date OWNER TO p o s t g r e s ; 88 89 90 −− Table : exam_room 91 92 −− DROP TABLE exam_room ; 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 CREATE TABLE exam_room ( exam_date character varying ( 3 2 ) , supervised_room character varying ( 3 2 ) , CONSTRAINT exam_room_exam_date_fkey FOREIGN KEY ( exam_date ) REFERENCES exam_date ( id_exam_date ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION , CONSTRAINT exam_room_supervised_room_fkey FOREIGN KEY ( supervised_room ) REFERENCES supervised_room ( id_supervised_room ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) WITH ( OIDS=FALSE ); ALTER TABLE exam_room OWNER TO p o s t g r e s ; 109 110 111 −− Table : modul 112 113 −− DROP TABLE modul ; 114 115 116 117 118 119 120 CREATE TABLE modul ( id_modul character varying ( 3 2 ) NOT NULL, " name " character varying ( 6 4 ) NOT NULL, course character varying ( 3 2 ) NOT NULL, d u r a t i o n double p r e c i s i o n , 19 121 122 123 124 125 126 127 128 129 130 131 132 133 134 " comment " character varying ( 3 2 ) , spec character varying ( 3 2 ) , CONSTRAINT modul_pkey PRIMARY KEY ( id_modul ) , CONSTRAINT modul_course_fkey FOREIGN KEY ( course ) REFERENCES course ( i d _ c o u r s e ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION , CONSTRAINT modul_spec_fkey FOREIGN KEY ( spec ) REFERENCES spec ( id_spec ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) WITH ( OIDS=FALSE ); ALTER TABLE modul OWNER TO p o s t g r e s ; 135 136 137 −− Table : other_examiners 138 139 −− DROP TABLE other_examiners ; 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 CREATE TABLE other_examiners ( modul character varying ( 3 2 ) NOT NULL, person character varying ( 3 2 ) NOT NULL, CONSTRAINT other_examiners_modul_fkey FOREIGN KEY ( modul ) REFERENCES modul ( id_modul ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION , CONSTRAINT other_examiners_person_fkey FOREIGN KEY ( person ) REFERENCES person ( i d _ pe r s o n ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) WITH ( OIDS=FALSE ); ALTER TABLE other_examiners OWNER TO p o s t g r e s ; 156 20 157 158 −− Table : person 159 160 −− DROP TABLE person ; 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 CREATE TABLE person ( i d _ p e rs o n character varying ( 3 2 ) NOT NULL, " name " character varying ( 6 4 ) NOT NULL, t i t e l character varying ( 3 2 ) , w e b s i t e character varying ( 2 5 6 ) , " comment " character varying ( 2 5 6 ) , i s _ e x a m i n er boolean DEFAULT f a l s e , CONSTRAINT person_pkey PRIMARY KEY ( i d _ p e r s o n ) ) WITH ( OIDS=FALSE ); ALTER TABLE person OWNER TO p o s t g r e s ; 176 177 178 −− Table : room 179 180 −− DROP TABLE room ; 181 182 183 184 185 186 187 188 189 190 191 192 193 CREATE TABLE room ( id_room character varying ( 3 2 ) NOT NULL, c a p a c i t y integer , " comment " character varying ( 2 5 6 ) , " name " character varying ( 3 2 ) , CONSTRAINT room_pkey PRIMARY KEY ( id_room ) ) WITH ( OIDS=FALSE ); ALTER TABLE room OWNER TO p o s t g r e s ; 21 194 195 196 −− Table : spec 197 198 −− DROP TABLE spec ; 199 200 201 202 203 204 205 206 207 208 209 CREATE TABLE spec ( id_spec character varying ( 3 2 ) NOT NULL, " name " character varying ( 3 2 ) , CONSTRAINT spec_pkey PRIMARY KEY ( id_spec ) ) WITH ( OIDS=FALSE ); ALTER TABLE spec OWNER TO p o s t g r e s ; 210 211 212 −− Table : supervised_room 213 214 −− DROP TABLE supervised_room ; 215 216 217 218 219 220 221 222 223 224 225 226 227 228 CREATE TABLE supervised_room ( id_supervised_room character varying ( 3 2 ) NOT NULL, room character varying ( 3 2 ) , person character varying ( 3 2 ) , CONSTRAINT supervised_room_pkey PRIMARY KEY ( id_supervised_room ) , CONSTRAINT supervised_room_person_fkey FOREIGN KEY ( person ) REFERENCES person ( i d _ pe r s o n ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION , CONSTRAINT supervised_room_room_fkey FOREIGN KEY ( room ) REFERENCES room ( id_room ) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) 22 229 230 231 232 WITH ( OIDS=FALSE ); ALTER TABLE supervised_room OWNER TO p o s t g r e s ; Listing 2: DBHandler.py 1 2 3 import md5 import psycopg2 import DBInputHandler 4 5 6 7 8 9 class DBHandler : dbhost = " 14 9. 20 1.6 3. 90 " # h o s t o f database dbname = " pp1_2012 " #name o f database dbuser = " p o s t g r e s " #name o f database user dbpasswd = " h u n t e r 2 " #password o f database user 10 11 12 conn = None ; c u r = None ; operations # c o n n e c t i o n t o database # c u r s o r f o r database 13 14 15 16 def _ _ i n i t _ _ ( s e l f ) : openDB ( dbname ) 17 18 19 def __del__ ( s e l f ) : closeDB ( ) 20 21 22 23 24 25 def getColumnNames ( tablename ) : # r e t u r n s a l i s t o f names o f t h e columnenames o f t h e t a b l e ’ tablename ’ c u r . execute ( "SELECT attname FROM p g _ a t t r i b u t e , pg_class WHERE pg_class . o i d = a t t r e l i d AND attnum >0 AND relname=%s " , ( tablename , ) ) columnnames = c u r . f e t c h a l l ( ) r e t u r n columnnames 26 27 def checkLogin ( name , password ) : 23 28 29 30 31 32 #checks l o g i n f o r admin i n t e r f a c e ( n o t r e l a t e d t o database ! ) i f name == " admin " and password . d i g e s t ( ) == md5 . new ( " admin " ) . d i g e s t ( ) : r e t u r n None else : r e t u r n " Irgendwas i s t s c h i e f g e l a u f e n " 33 34 35 36 37 def openDB ( dbname ) : # connects t o t h e p o s t g r e s q l database w i t h parameters above conn = psycopg2 . connect ( h o s t =dbhost , database= dbname , user=dbuser , password=dbpasswd ) c u r = conn . c u r s o r ( ) 38 39 40 41 def closeDB ( ) : conn . c l o s e ( ) cur . close ( ) 42 43 44 45 46 def g e t L i s t ( tablename ) : c u r . execute ( " s e l e c t ∗ from %s " , ( tablename , ) ) rows = c u r . f e t c h a l l ( ) columnnames = getColumnNames ( tablename ) 47 48 r e t u r n z i p ( columnnames , rows ) Listing 3: DBInputHandler.py 1 2 import md5 import psycopg2 3 4 5 6 def insertRoom ( id_room , name , c a p a c i t y , comment ) : c u r . execute ( " i n s e r t i n t o room ( id_room , name , c a p a c i t y , comment ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ ) " , ( id_room , name , c a p a c i t y , comment ) ) 7 8 def i n s e r t P e r s o n ( id_person , name , t i t e l , website , comment ) ; 24 9 c u r . execute ( " i n s e r t i n t o person ( id_person , name , t i t e l , website , comment ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ ) " , ( id_person , name , t i t e l , website , comment ) ) 10 11 12 def insertExamDate ( id_exam_date , i d _ s u b j e c t , i d _ d a t e , a t t e n d e es ) c u r . execute ( " i n s e r t i n t o exam_date ( id_exam_date , i d _ s u b j e c t , i d _ d a t e , a t te n d e es ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ ) " , ( id_exam_date , i d _ s u b j e c t , i d _ d a t e , a t t e n d e e s t )) 13 14 15 def i n s e r t S u b j e c t ( i d _ s u b j e c t , id_course , id_person , name , mode , d u r a t i o n , comment ) c u r . execute ( " i n s e r t i n t o s u b j e c t ( i d _ s u b j e c t , id_course , id_person , name , mode , d u r a t i o n , comment ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ ) " , ( i d _ s u b j e c t , id_course , id_person , name , mode , d u r a t i o n , comment ) 16 17 18 def i n s e r t C o u r s e ( id_course , degree , name , comment ) c u r . execute ( " i n s e r t i n t o course ( id_course , degree , name , comment ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ ) " , ( id_course , degree , name , comment ) ) 19 20 21 def i n s e r t D a t e ( i d _ d a t e , pp , due_date , d e a d l i n e ) c u r . execute ( " i n s e r t i n t o date ( i d _ d a t e , pp , due_date , d e a d l i n e ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ ) " , ( i d _ d a t e , pp , due_date , d e a d l i n e ) ) 22 23 24 def i n s e r t E x a m i n e r ( id_second_examiner , i d _ s u b j e c t , i d _ p e rs o n ) c u r . execute ( " i n s e r t i n t o second_examiner ( id_second_examiner , i d _ s u b j e c t , i d _ p e rs o n ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s ’ ) " , ( id_second_examiner , i d _ s u b j e c t , i d _ p er s o n ) ) 25 26 27 def combine ( t a r g e t , i d _ o b j e c t 1 , i d _ o b j e c t 2 ) #target − Zieltabelle 25 28 pass #TODO c u r . execute ( " " , ( ) ) 29 30 31 def d e l e t e ( tablename , i d _ o b j e c t ) c u r . execute ( " d e l e t e from %s where %s = %s " , ( tablename , " i d _ " +tablename , i d _ o b j e c t ) 32 33 34 35 36 37 38 def checkLogin ( name , password ) : #checks l o g i n f o r admin i n t e r f a c e ( n o t r e l a t e d t o database ! ) i f name == " admin " and password . d i g e s t ( ) == md5 . new ( " admin " ) . d i g e s t ( ) : r e t u r n None else : r e t u r n " Irgendwas i s t s c h i e f g e l a u f e n " 39 40 41 42 43 def openDB ( dbname ) : # connects t o t h e p o s t g r e s q l database w i t h parameters above conn = psycopg2 . connect ( h o s t =dbhost , database=dbname , user=dbuser , password=dbpasswd ) c u r = conn . c u r s o r ( ) 26 7.3 Literaturverzeichnis http://wiki.debian.org/ (Stand: Dez. 2011) http://www.postgresql.org/ (Stand: Dez. 2011) http://de.wikipedia.org/wiki/Normalisierung_(Datenbank) (Stand: Dez. 2011) http://www..python.org (Stand: Dez. 2011) http://initd.org/psycopg/ (Stand: Dez. 2011) http://www.tinohempel.de/info/info/datenbank/ Peter Kaiser, Johannes Ernesti: Python - Das umfassende Handbuch,Galileo Computing,Bonn,2008 Alfons Kemper, Andre Eickler: Datenbanksysteme - Eine Einführung, Oldenbourg Verlag,München,2009 Jürgen Thomas, Einführung in SQL, Wikibooks, 2011 27