adding database schema to documentation. devel
authorSandro Knauß <knauss@netzguerilla.net>
Sun, 18 Dec 2011 11:49:46 +0100
branchdevel
changeset 77 7dce6c0f06fb
parent 76 9f7da8dc8df8
child 78 7a4c7b65f20b
adding database schema to documentation.
createdoc.py
createerm.py
doc/current.html
doc/database.html
doc/images/db-schema.svg
doc/impressum.html
doc/index.html
doc/new.html
doc/tmpl/database.html
iro/schema.py
sqlalchemy_schemadisplay3.py
--- a/createdoc.py	Sun Dec 18 11:49:12 2011 +0100
+++ b/createdoc.py	Sun Dec 18 11:49:46 2011 +0100
@@ -10,6 +10,7 @@
 from iro.user import User as Current
 from iro.newuser import User as New
 
+from createerm import createSchemaPlot,tables
 
 
 
@@ -99,11 +100,20 @@
         self.args=[Arg(a,m) for a in args]
         _, self.rets=ret(m)
 
+class Table(Link):
+    def __init__(self,cls):
+        name=cls.__name__
+        self.tablename=cls.__tablename__
+        title=self.tablename[0].upper()+self.tablename[1:]
+        Link.__init__(self,name,title)
+        self.description = cls.__doc__.split("\n")[0].decode("utf8") 
+
 
 def main():
     sites=[Site("index.html","Iro"),
            Site("current.html","API Documentation"),
            Site("new.html","geplante API Documentation"),
+           Site("database.html","Datenbank Schema"),
            Site("impressum.html","Impressum"),
            ]
 
@@ -132,7 +142,9 @@
             Method("routes",new_methods),
             Method("defaultRoute",new_methods),
             ]
-
+    
+    t = [Table(f.class_) for f in tables]
+    createSchemaPlot('doc/images/db-schema.svg')
 
     for site in sites:
         print("generiere %s" % site.name)
@@ -140,7 +152,7 @@
         def a(s):
             if s == site:
                 return {"class":"menu active"}
-        stream = tmpl.generate(sites=sites,active=a,current=current,new=newm)
+        stream = tmpl.generate(sites=sites,active=a,current=current,new=newm,tables=t)
         with open('doc/'+site.name, "w") as g:
             g.write(stream.render('html', doctype='html'))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/createerm.py	Sun Dec 18 11:49:46 2011 +0100
@@ -0,0 +1,48 @@
+from iro import schema
+
+from sqlalchemy.orm import class_mapper
+tables = []
+for attr in schema.__tables__:
+    if attr[0] == '_': continue
+    try:
+        cls = getattr(schema, attr)
+        tables.append(class_mapper(cls))
+    except:
+        pass
+
+
+#schema plot
+def createSchemaPlot(fname):
+    from sqlalchemy_schemadisplay3 import create_schema_graph
+    graph = create_schema_graph(metadata=schema.Base.metadata,
+        show_datatypes=True, # The image too large if datatypes shown
+        show_indexes=True, # ditto for indexes
+        rankdir='LR', # From left to right (instead of top to bottom)
+        concentrate=True, # Don't try to join the relation lines together
+    )
+
+    graph.set_size('6.5,10')
+    #graph.set_ratio("fill")
+    graph.write_svg(fname)
+
+#umlplot
+def createUMLPlot(fname):
+    from sqlalchemy_schemadisplay3 import create_uml_graph
+    from sqlalchemy.orm import class_mapper
+    mappers = []
+    for attr in dir(schema.model):
+        if attr[0] == '_': continue
+        try:
+            cls = getattr(schema.model, attr)
+            mappers.append(class_mapper(cls))
+        except:
+            pass
+    #pass them to the function and set some formatting options
+    graph = create_uml_graph(mappers,
+        show_operations=False, # not necessary in this case
+        show_multiplicity_one=True, # some people like to see the ones
+        show_attributes=True,
+    )
+    graph.set_size('6,5')
+    graph.set_ratio("fill")
+    graph.write_png('test.png')
--- a/doc/current.html	Sun Dec 18 11:49:12 2011 +0100
+++ b/doc/current.html	Sun Dec 18 11:49:46 2011 +0100
@@ -19,7 +19,7 @@
 		<div id="head">
 			<h1 id="logo"><a href="index.html" class="logo" title="Netzguerilla"><span>Netzguerilla</span></a></h1>
 			<ul id="menu">
-				<li><a href="index.html" class="menu">Iro</a></li><li><a href="current.html" class="menu active">API Documentation</a></li><li><a href="new.html" class="menu">geplante API Documentation</a></li><li><a href="impressum.html" class="menu">Impressum</a></li>
+				<li><a href="index.html" class="menu">Iro</a></li><li><a href="current.html" class="menu active">API Documentation</a></li><li><a href="new.html" class="menu">geplante API Documentation</a></li><li><a href="database.html" class="menu">Datenbank Schema</a></li><li><a href="impressum.html" class="menu">Impressum</a></li>
 			</ul>
 		</div>
 	</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/database.html	Sun Dec 18 11:49:46 2011 +0100
@@ -0,0 +1,75 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+	
+
+	<head>
+		<title>Iro ·  Datenbank</title>
+		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+		<meta charset="utf-8">
+		<meta name="description" content="">
+		<meta name="keywords" content="">
+		<link rel="shortcut icon" type="image/x-icon" href="images/favicon.png">
+		<link rel="stylesheet" href="css/reset.css" type="text/css" media="screen">
+		<link rel="stylesheet" href="css/960.css" type="text/css" media="screen">
+		<link rel="stylesheet" href="css/style.css" type="text/css" media="screen">
+		<!--[if IE]><link rel="stylesheet" href="css/style-ie.css" type="text/css"  media="screen" /><![endif] -->
+	</head>
+	<body>
+	<div id="head-container">
+		<div id="head">
+			<h1 id="logo"><a href="index.html" class="logo" title="Netzguerilla"><span>Netzguerilla</span></a></h1>
+			<ul id="menu">
+				<li><a href="index.html" class="menu">Iro</a></li><li><a href="current.html" class="menu">API Documentation</a></li><li><a href="new.html" class="menu">geplante API Documentation</a></li><li><a href="database.html" class="menu active">Datenbank Schema</a></li><li><a href="impressum.html" class="menu">Impressum</a></li>
+			</ul>
+		</div>
+	</div>
+	<div id="content-container">
+		<div id="content" class="container_12">
+			<div id="main" class="grid_9">
+				<h2>Datenbank Schema</h2>
+				<div class="item">
+			<p>
+			</p>
+			<ol>
+				<li value="1">1. <a href="#schema">Datenbankschema</a></li>
+				<li value="3">3.
+					<a href="#tables">Tabellen</a>
+					<ol>
+						<li value="2.1">2.1 <a href="#table-apiuser">Apiuser</a></li><li value="2.2">2.2 <a href="#table-job">Job</a></li><li value="2.3">2.3 <a href="#table-message">Message</a></li><li value="2.4">2.4 <a href="#table-offer">Offer</a></li><li value="2.5">2.5 <a href="#table-userright">Userright</a></li>
+					</ol>
+				</li>
+			</ol>
+		</div><div class="item" id="schema">
+			<h3>Schema</h3>
+			<img src="images/db-schema.svg">
+			<p>Dies ist eine Übersicht der benutzen Tabellen die Iro benötigt.</p>
+		</div><div class="item" id="tables">
+			<h3>Tabellen</h3>
+			<div class="item" id="table-apiuser">
+				<h4>Apiuser</h4>
+				<p>Die Benutzerdatenbank von Iro.</p>
+			</div><div class="item" id="table-job">
+				<h4>Job</h4>
+				<p>Ein kompletter Auftrag, der an Iro zum verschicken übergeben wird. Status zeigt den generellen Status des Auftrages an (<em>init</em>, <em>started</em>, <em>sending</em>, <em>sended</em> oder <em>error</em>). <em>info</em> wird verwendet um dem Benutzer eine Möglickeit zu geben verschiede Auftragsgruppen zu erstellen.</p>
+			</div><div class="item" id="table-message">
+				<h4>Message</h4>
+				<p>Wenn ein Vorgang von Iro Kosten erzeugt hat wird eine neue Zeile eingefügt. Solange nicht bezahlt wurde ist <em>isBilled=0</em>.</p>
+			</div><div class="item" id="table-offer">
+				<h4>Offer</h4>
+				<p>Alle Routen über die SMS, Faxe und Mails verschickt werden könnnen. <em>provider</em>, <em>typ</em> und <em>route</em> werden verwendet, um die entsprechenden Zugangsdaten laden zu können.</p>
+			</div><div class="item" id="table-userright">
+				<h4>Userright</h4>
+				<p>Über welche Routen darf ein Benutzer Daten verschicken und welches sind die Standardrouten (<em>isDefault=1</em>) für den Benuter.</p>
+			</div>
+		</div>
+			</div>
+			<div class="clear"></div>
+		</div>
+	</div>
+	<div id="foot-container">
+		<div id="foot">
+			<p>© 2010-2011 <a href="impressum.html">Netzguerilla.net</a>.</p>
+		</div>
+	</div>
+	</body>
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/images/db-schema.svg	Sun Dec 18 11:49:46 2011 +0100
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.26.3 (20100126.1600)
+ -->
+<!-- Title: G Pages: 1 -->
+<svg width="468pt" height="131pt"
+ viewBox="0.00 0.00 468.00 131.29" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph1" class="graph" transform="scale(0.89313 0.89313) rotate(0) translate(4 143)">
+<title>G</title>
+<polygon fill="white" stroke="white" points="-4,5 -4,-143 521,-143 521,5 -4,5"/>
+<!-- message -->
+<g id="node1" class="node"><title>message</title>
+<polygon fill="none" stroke="black" points="8,-25.5 8,-118.5 98,-118.5 98,-25.5 8,-25.5"/>
+<text text-anchor="start" x="36.5" y="-109.367" font-family="Bitstream-Vera Sans" font-size="7.00">message</text>
+<polygon fill="none" stroke="black" points="9,-103 9,-105 97,-105 97,-103 9,-103"/>
+<text text-anchor="start" x="11" y="-95.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; id : INTEGER</text>
+<text text-anchor="start" x="11" y="-84.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; recipient : VARCHAR</text>
+<text text-anchor="start" x="11" y="-73.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; isBilled : BOOLEAN</text>
+<text text-anchor="start" x="11" y="-62.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; date : DATETIME</text>
+<text text-anchor="start" x="11" y="-51.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; price : NUMERIC(8, 2)</text>
+<text text-anchor="start" x="11" y="-40.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; job : INTEGER</text>
+<text text-anchor="start" x="11" y="-29.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; offer : VARCHAR</text>
+</g>
+<!-- job -->
+<g id="node3" class="node"><title>job</title>
+<polygon fill="none" stroke="black" points="150.5,-75 150.5,-135 235.5,-135 235.5,-75 150.5,-75"/>
+<text text-anchor="start" x="187.5" y="-126.367" font-family="Bitstream-Vera Sans" font-size="7.00">job</text>
+<polygon fill="none" stroke="black" points="152,-120 152,-122 235,-122 235,-120 152,-120"/>
+<text text-anchor="start" x="154" y="-112.867" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; hash : VARCHAR</text>
+<text text-anchor="start" x="154" y="-101.867" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; info : VARCHAR</text>
+<text text-anchor="start" x="154" y="-90.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; status : VARCHAR(7)</text>
+<text text-anchor="start" x="154" y="-79.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; user : VARCHAR</text>
+</g>
+<!-- message&#45;&gt;job -->
+<g id="edge2" class="edge"><title>message&#45;&gt;job</title>
+<path fill="none" stroke="black" d="M106.327,-84.57C115.412,-86.7114 124.894,-88.9465 134.112,-91.1194"/>
+<ellipse fill="none" stroke="black" cx="138.236" cy="-92.0912" rx="4.00001" ry="4.00001"/>
+<text text-anchor="middle" x="132.338" y="-92.7431" font-family="Bitstream-Vera Sans" font-size="7.00">+ hash</text>
+<text text-anchor="middle" x="116.118" y="-80.2359" font-family="Bitstream-Vera Sans" font-size="7.00">+ job</text>
+</g>
+<!-- offer -->
+<g id="node5" class="node"><title>offer</title>
+<polygon fill="none" stroke="black" points="288,-4 288,-64 370,-64 370,-4 288,-4"/>
+<text text-anchor="start" x="320.5" y="-55.3667" font-family="Bitstream-Vera Sans" font-size="7.00">offer</text>
+<polygon fill="none" stroke="black" points="289,-49 289,-51 369,-51 369,-49 289,-49"/>
+<text text-anchor="start" x="291" y="-41.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; name : VARCHAR</text>
+<text text-anchor="start" x="291" y="-30.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; provider : VARCHAR</text>
+<text text-anchor="start" x="291" y="-19.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; route : VARCHAR</text>
+<text text-anchor="start" x="291" y="-8.86667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; typ : VARCHAR</text>
+</g>
+<!-- message&#45;&gt;offer -->
+<g id="edge4" class="edge"><title>message&#45;&gt;offer</title>
+<path fill="none" stroke="black" d="M106.216,-64.6732C153.699,-58.1357 223.047,-48.5878 271.726,-41.8855"/>
+<ellipse fill="none" stroke="black" cx="275.694" cy="-41.3393" rx="4.00001" ry="4.00001"/>
+<text text-anchor="middle" x="271.255" y="-43.9165" font-family="Bitstream-Vera Sans" font-size="7.00">+ name</text>
+<text text-anchor="middle" x="114.618" y="-56.9504" font-family="Bitstream-Vera Sans" font-size="7.00">+ offer</text>
+</g>
+<!-- userright -->
+<g id="node2" class="node"><title>userright</title>
+<polygon fill="none" stroke="black" points="422.5,-46.5 422.5,-95.5 507.5,-95.5 507.5,-46.5 422.5,-46.5"/>
+<text text-anchor="start" x="448.5" y="-86.3667" font-family="Bitstream-Vera Sans" font-size="7.00">userright</text>
+<polygon fill="none" stroke="black" points="424,-80 424,-82 507,-82 507,-80 424,-80"/>
+<text text-anchor="start" x="426" y="-72.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; user : VARCHAR</text>
+<text text-anchor="start" x="426" y="-61.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; offer : VARCHAR</text>
+<text text-anchor="start" x="426" y="-50.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; isDefault : BOOLEAN</text>
+</g>
+<!-- apiuser -->
+<g id="node4" class="node"><title>apiuser</title>
+<polygon fill="none" stroke="black" points="291.5,-90 291.5,-128 366.5,-128 366.5,-90 291.5,-90"/>
+<text text-anchor="start" x="315.5" y="-119.367" font-family="Bitstream-Vera Sans" font-size="7.00">apiuser</text>
+<polygon fill="none" stroke="black" points="293,-113 293,-115 366,-115 366,-113 293,-113"/>
+<text text-anchor="start" x="295" y="-105.867" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; name : VARCHAR</text>
+<text text-anchor="start" x="295" y="-94.8667" font-family="Bitstream-Vera Sans" font-size="7.00">&#45; apikey : VARCHAR</text>
+</g>
+<!-- job&#45;&gt;apiuser -->
+<g id="edge10" class="edge"><title>job&#45;&gt;apiuser</title>
+<path fill="none" stroke="black" d="M243.641,-106.489C253.891,-106.791 264.704,-107.109 275.063,-107.414"/>
+<ellipse fill="none" stroke="black" cx="279.255" cy="-107.537" rx="4" ry="4"/>
+<text text-anchor="middle" x="274.069" y="-109.312" font-family="Bitstream-Vera Sans" font-size="7.00">+ name</text>
+<text text-anchor="middle" x="252.825" y="-100.232" font-family="Bitstream-Vera Sans" font-size="7.00">+ user</text>
+</g>
+<!-- apiuser&#45;&gt;userright -->
+<g id="edge6" class="edge"><title>apiuser&#45;&gt;userright</title>
+<path fill="none" stroke="black" d="M374.664,-96.2408C387.32,-92.7048 401.136,-88.8443 414.071,-85.23"/>
+<text text-anchor="middle" x="406.48" y="-89.4392" font-family="Bitstream-Vera Sans" font-size="7.00">+ name</text>
+<text text-anchor="middle" x="382.256" y="-87.4316" font-family="Bitstream-Vera Sans" font-size="7.00">+ user</text>
+</g>
+<!-- offer&#45;&gt;userright -->
+<g id="edge8" class="edge"><title>offer&#45;&gt;userright</title>
+<path fill="none" stroke="black" d="M378.099,-47.3578C389.816,-50.5456 402.347,-53.9546 414.141,-57.1634"/>
+<text text-anchor="middle" x="404.286" y="-56.5621" font-family="Bitstream-Vera Sans" font-size="7.00">+ name</text>
+<text text-anchor="middle" x="387.954" y="-43.3591" font-family="Bitstream-Vera Sans" font-size="7.00">+ offer</text>
+</g>
+</g>
+</svg>
--- a/doc/impressum.html	Sun Dec 18 11:49:12 2011 +0100
+++ b/doc/impressum.html	Sun Dec 18 11:49:46 2011 +0100
@@ -19,7 +19,7 @@
 		<div id="head">
 			<h1 id="logo"><a href="index.html" class="logo" title="Netzguerilla"><span>Netzguerilla</span></a></h1>
 			<ul id="menu">
-				<li><a href="index.html" class="menu">Iro</a></li><li><a href="current.html" class="menu">API Documentation</a></li><li><a href="new.html" class="menu">geplante API Documentation</a></li><li><a href="impressum.html" class="menu active">Impressum</a></li>
+				<li><a href="index.html" class="menu">Iro</a></li><li><a href="current.html" class="menu">API Documentation</a></li><li><a href="new.html" class="menu">geplante API Documentation</a></li><li><a href="database.html" class="menu">Datenbank Schema</a></li><li><a href="impressum.html" class="menu active">Impressum</a></li>
 			</ul>
 		</div>
 	</div>
--- a/doc/index.html	Sun Dec 18 11:49:12 2011 +0100
+++ b/doc/index.html	Sun Dec 18 11:49:46 2011 +0100
@@ -19,7 +19,7 @@
 		<div id="head">
 			<h1 id="logo"><a href="index.html" class="logo" title="Netzguerilla"><span>Netzguerilla</span></a></h1>
 			<ul id="menu">
-				<li><a href="index.html" class="menu active">Iro</a></li><li><a href="current.html" class="menu">API Documentation</a></li><li><a href="new.html" class="menu">geplante API Documentation</a></li><li><a href="impressum.html" class="menu">Impressum</a></li>
+				<li><a href="index.html" class="menu active">Iro</a></li><li><a href="current.html" class="menu">API Documentation</a></li><li><a href="new.html" class="menu">geplante API Documentation</a></li><li><a href="database.html" class="menu">Datenbank Schema</a></li><li><a href="impressum.html" class="menu">Impressum</a></li>
 			</ul>
 		</div>
 	</div>
--- a/doc/new.html	Sun Dec 18 11:49:12 2011 +0100
+++ b/doc/new.html	Sun Dec 18 11:49:46 2011 +0100
@@ -19,7 +19,7 @@
 		<div id="head">
 			<h1 id="logo"><a href="index.html" class="logo" title="Netzguerilla"><span>Netzguerilla</span></a></h1>
 			<ul id="menu">
-				<li><a href="index.html" class="menu">Iro</a></li><li><a href="current.html" class="menu">API Documentation</a></li><li><a href="new.html" class="menu active">geplante API Documentation</a></li><li><a href="impressum.html" class="menu">Impressum</a></li>
+				<li><a href="index.html" class="menu">Iro</a></li><li><a href="current.html" class="menu">API Documentation</a></li><li><a href="new.html" class="menu active">geplante API Documentation</a></li><li><a href="database.html" class="menu">Datenbank Schema</a></li><li><a href="impressum.html" class="menu">Impressum</a></li>
 			</ul>
 		</div>
 	</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/tmpl/database.html	Sun Dec 18 11:49:46 2011 +0100
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:xi="http://www.w3.org/2001/XInclude"
+	xmlns:py="http://genshi.edgewall.org/">
+	<xi:include href="layout.tmpl" />
+	<head>
+		<title>Datenbank</title>
+	</head>
+	<body>
+		<title>Datenbank Schema</title>
+		<div class="item">
+			<p>
+
+			</p>
+			<ol>
+				<li value="1">1. <a href="#schema">Datenbankschema</a></li>
+				<li value="3">3. 
+					<a href="#tables">Tabellen</a>
+					<ol>
+						<li py:for="(key,table) in enumerate(tables)" value="2.${key+1}">2.${key+1} <a href="#table-${table.tablename}">${table.title}</a></li>
+					</ol>				
+				</li>
+			</ol>
+		</div>
+
+
+		<div class="item" id="schema">
+			<h3>Schema</h3>
+			<img src="images/db-schema.svg" />
+			<p>Dies ist eine Übersicht der benutzen Tabellen die Iro benötigt.</p>
+		</div>
+		<div class="item" id="tables">
+			<h3>Tabellen</h3>
+			<div py:for="table in tables" class="item" id="table-${table.tablename}">
+				<h4>${table.title}</h4>
+				<p py:content="Markup(table.description)">
+					Dieser Tabelle fehlt noch die Beschreibung.
+				</p>
+			</div>
+		</div>
+	</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iro/schema.py	Sun Dec 18 11:49:46 2011 +0100
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*- 
+
+from sqlalchemy import Column, Integer, String, Sequence, Boolean, DateTime, Numeric, Enum
+from sqlalchemy import create_engine
+from sqlalchemy.ext.declarative import declarative_base
+
+#relationship
+from sqlalchemy import ForeignKey
+from sqlalchemy.orm import relationship, backref
+
+engine = create_engine('sqlite:///:memory:', echo=True)
+Base = declarative_base()
+
+__tables__=["User", "Job", "Message", "Offer", "Userright"]
+
+class Userright(Base):
+    """Über welche Routen darf ein Benutzer Daten verschicken und welches sind die Standardrouten (<em>isDefault=1</em>) für den Benuter."""
+    __tablename__ = 'userright'
+    user_name = Column('user', String, ForeignKey('apiuser.name'), primary_key=True)
+    offer_name = Column('offer', String, ForeignKey('offer.name'), primary_key=True)
+    isDefault = Column(Boolean)
+    offer = relationship("Offer")
+
+class Offer(Base):
+    """Alle Routen über die SMS, Faxe und Mails verschickt werden könnnen. <em>provider</em>, <em>typ</em> und <em>route</em> werden verwendet, um die entsprechenden Zugangsdaten laden zu können."""
+    __tablename__ = "offer"
+    name = Column(String, primary_key=True)
+    provider = Column(String)
+    route = Column(String)
+    typ = Column(String)
+    
+
+class Message(Base):
+    """Wenn ein Vorgang von Iro Kosten erzeugt hat wird eine neue Zeile eingefügt. Solange nicht bezahlt wurde ist <em>isBilled=0</em>."""
+    __tablename__ = "message"
+    id = Column(Integer, Sequence('message_id_seq'), primary_key=True)
+    recipient = Column(String)
+    isBilled = Column(Boolean)
+    date = Column(DateTime)
+    price = Column(Numeric(8,2))
+    job_hash = Column("job",Integer, ForeignKey('job.hash'))
+    job = relationship("Job", backref=backref('messages'))
+    offer_id = Column("offer",String, ForeignKey('offer.name'))
+    offer = relationship("Offer", backref=backref('messages'))
+
+
+class Job(Base):
+    """Ein kompletter Auftrag, der an Iro zum verschicken übergeben wird. Status zeigt den generellen Status des Auftrages an (<em>init</em>, <em>started</em>, <em>sending</em>, <em>sended</em> oder <em>error</em>). <em>info</em> wird verwendet um dem Benutzer eine Möglickeit zu geben verschiede Auftragsgruppen zu erstellen."""
+    __tablename__ = "job"
+    hash = Column(String, primary_key=True)
+    info = Column(String)
+    status = Column(Enum("init","started","sending","sended","error"))
+    user_id = Column("user", String, ForeignKey('apiuser.name'))
+    user = relationship("User", backref=backref('jobs'))
+
+class User(Base):
+    """Die Benutzerdatenbank von Iro."""
+    __tablename__ = "apiuser"
+    name = Column(String, primary_key=True)
+    apikey = Column(String,unique=True)
+    rights = relationship('Userright')
+    def __init__(self, name, apikey):
+        self.name=name
+        self.apikey=apikey
+
+    def __repr__(self):
+        return "<User('%s','%s')>"%(self.name,self.apikey)
+
+
+#Base.metadata.create_all(engine)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sqlalchemy_schemadisplay3.py	Sun Dec 18 11:49:46 2011 +0100
@@ -0,0 +1,173 @@
+# updated SQLA schema display to work with pydot 1.0.2
+# download from: http://www.sqlalchemy.org/trac/wiki/UsageRecipes/SchemaDisplay
+
+
+from sqlalchemy.orm.properties import PropertyLoader
+import pydot
+import types
+
+__all__ = ['create_uml_graph', 'create_schema_graph', 'show_uml_graph', 'show_schema_graph']
+
+def _mk_label(mapper, show_operations, show_attributes, show_datatypes, bordersize):
+    html = '<<TABLE CELLSPACING="0" CELLPADDING="1" BORDER="0" CELLBORDER="%d" BALIGN="LEFT"><TR><TD><FONT POINT-SIZE="10">%s</FONT></TD></TR>' % (bordersize, mapper.class_.__name__)
+    def format_col(col):
+        colstr = '+%s' % (col.name)
+        if show_datatypes:
+            colstr += ' : %s' % (col.type.__class__.__name__)
+        return colstr
+            
+    if show_attributes:
+        html += '<TR><TD ALIGN="LEFT">%s</TD></TR>' % '<BR ALIGN="LEFT"/>'.join(format_col(col) for col in sorted(mapper.columns, key=lambda col:not col.primary_key))
+    else:
+        [format_col(col) for col in sorted(mapper.columns, key=lambda col:not col.primary_key)]
+    if show_operations:
+        html += '<TR><TD ALIGN="LEFT">%s</TD></TR>' % '<BR ALIGN="LEFT"/>'.join(
+            '%s(%s)' % (name,", ".join(default is _mk_label and ("%s") % arg or ("%s=%s" % (arg,repr(default))) for default,arg in 
+                zip((func.func_defaults and len(func.func_code.co_varnames)-1-(len(func.func_defaults) or 0) or func.func_code.co_argcount-1)*[_mk_label]+list(func.func_defaults or []), func.func_code.co_varnames[1:])
+            ))
+            for name,func in mapper.class_.__dict__.items() if isinstance(func, types.FunctionType) and func.__module__ == mapper.class_.__module__
+        )
+    html+= '</TABLE>>'
+    return html
+
+
+def create_uml_graph(mappers, show_operations=True, show_attributes=True, show_multiplicity_one=False, show_datatypes=True, linewidth=1.0, font="Bitstream-Vera Sans"):
+    graph = pydot.Dot(prog='neato',mode="major",overlap="0", sep="0.01",dim="3", pack="True", ratio=".75")
+    relations = set()
+    for mapper in mappers:
+        graph.add_node(pydot.Node(mapper.class_.__name__,
+            shape="plaintext", label=_mk_label(mapper, show_operations, show_attributes, show_datatypes, linewidth),
+            fontname=font, fontsize="8.0",
+        ))
+        if mapper.inherits:
+            graph.add_edge(pydot.Edge(mapper.inherits.class_.__name__,mapper.class_.__name__,
+                arrowhead='none',arrowtail='empty', style="setlinewidth(%s)" % linewidth, arrowsize=str(linewidth)))
+        for loader in mapper.iterate_properties:
+            if isinstance(loader, PropertyLoader) and loader.mapper in mappers:
+                if hasattr(loader, 'reverse_property'):
+                    relations.add(frozenset([loader, loader.reverse_property]))
+                else:
+                    relations.add(frozenset([loader]))
+
+    for relation in relations:
+        #if len(loaders) > 2:
+        #    raise Exception("Warning: too many loaders for join %s" % join)
+        args = {}
+        def multiplicity_indicator(prop):
+            if prop.uselist:
+                return ' *'
+            if any(col.nullable for col in prop.local_side):
+                return ' 0..1'
+            if show_multiplicity_one:
+                return ' 1'
+            return ''
+        
+        if len(relation) == 2:
+            src, dest = relation
+            from_name = src.parent.class_.__name__
+            to_name = dest.parent.class_.__name__
+            
+            def calc_label(src,dest):
+                return '+' + src.key + multiplicity_indicator(src)
+            args['headlabel'] = calc_label(src,dest)
+            
+            args['taillabel'] = calc_label(dest,src)
+            args['arrowtail'] = 'none'
+            args['arrowhead'] = 'none'
+            args['constraint'] = False
+        else:
+            prop, = relation
+            from_name = prop.parent.class_.__name__
+            to_name = prop.mapper.class_.__name__
+            args['headlabel'] = '+%s%s' % (prop.key, multiplicity_indicator(prop))
+            args['arrowtail'] = 'none'
+            args['arrowhead'] = 'vee'
+        
+        graph.add_edge(pydot.Edge(from_name,to_name,
+            fontname=font, fontsize="7.0", style="setlinewidth(%s)"%linewidth, arrowsize=str(linewidth),
+            **args)
+        )
+
+    return graph
+
+#from sqlalchemy import Table, text
+
+def _render_table_html(table, metadata, show_indexes, show_datatypes):
+    def format_col_type(col):
+        try:
+            return col.type.get_col_spec()
+        except NotImplementedError:
+            return str(col.type)
+        except AttributeError:
+            return str(col.type)
+    def format_col_str(col):
+         if show_datatypes:
+             return "- %s : %s" % (col.name, format_col_type(col))
+         else:
+             return "- %s" % col.name
+    html = '<<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0"><TR><TD ALIGN="CENTER">%s</TD></TR><TR><TD BORDER="1" CELLPADDING="0"></TD></TR>' % table.name 
+
+    html += ''.join('<TR><TD ALIGN="LEFT" PORT="%s">%s</TD></TR>' % (col.name, format_col_str(col)) for col in table.columns)
+    html += '</TABLE>>'
+    return html
+
+def create_schema_graph(tables=None, metadata=None, show_indexes=True, show_datatypes=True, font="Bitstream-Vera Sans",
+    concentrate=True, relation_options={}, rankdir='TB'):
+    relation_kwargs = {
+        'fontsize':"7.0"
+    }
+    relation_kwargs.update(relation_options)
+    
+    if not metadata and len(tables):
+        metadata = tables[0].metadata
+    elif not tables and metadata:
+        if not len(metadata.tables):
+            metadata.reflect()
+        tables = metadata.tables.values()
+    else:
+        raise Exception("You need to specify at least tables or metadata")
+    
+    graph = pydot.Dot(prog="dot",mode="ipsep",overlap="ipsep",sep="0.01",concentrate=str(concentrate), rankdir=rankdir)
+    for table in tables:
+        graph.add_node(pydot.Node(str(table.name),
+            shape="plaintext",
+            label=_render_table_html(table, metadata, show_indexes, show_datatypes),
+            fontname=font, fontsize="7.0"
+        ))
+    
+    for table in tables:
+        for fk in table.foreign_keys:
+            edge = [table.name, fk.column.table.name]
+            is_inheritance = fk.parent.primary_key and fk.column.primary_key
+            if is_inheritance:
+                edge = edge[::-1]
+            graph_edge = pydot.Edge(
+                headlabel="+ %s"%fk.column.name, taillabel='+ %s'%fk.parent.name,
+                arrowhead=is_inheritance and 'none' or 'odot' ,
+                arrowtail=(fk.parent.primary_key or fk.parent.unique) and 'empty' or 'crow' ,
+                fontname=font, 
+                #samehead=fk.column.name, sametail=fk.parent.name,
+                *edge, **relation_kwargs
+            )
+            graph.add_edge(graph_edge)
+
+# not sure what this part is for, doesn't work with pydot 1.0.2
+#            graph_edge.parent_graph = graph.parent_graph
+#            if table.name not in [e.get_source() for e in graph.get_edge_list()]:
+#                graph.edge_src_list.append(table.name)
+#            if fk.column.table.name not in graph.edge_dst_list:
+#                graph.edge_dst_list.append(fk.column.table.name)
+#            graph.sorted_graph_elements.append(graph_edge)
+    return graph
+
+def show_uml_graph(*args, **kwargs):
+    from cStringIO import StringIO
+    from PIL import Image
+    iostream = StringIO(create_uml_graph(*args, **kwargs).create_png())
+    Image.open(iostream).show(command=kwargs.get('command','gwenview'))
+
+def show_schema_graph(*args, **kwargs):
+    from cStringIO import StringIO
+    from PIL import Image
+    iostream = StringIO(create_schema_graph(*args, **kwargs).create_png())
+    Image.open(iostream).show(command=kwargs.get('command','gwenview'))