--- a/createdoc.py Wed Dec 21 21:48:27 2011 +0100
+++ b/createdoc.py Wed Dec 21 21:51:47 2011 +0100
@@ -8,7 +8,9 @@
import re
import inspect
from iro.user import User as Current
-from iro.newuser import User as New
+from iro.newinterface import Interface as New
+
+from createerm import createSchemaPlot,tables
@@ -59,7 +61,7 @@
def keywords(f):
- doc=f.__doc__
+ doc=f.__doc__.decode('utf8')
kwds=re.search("Keywords:\n(?P<keywords>(?P<whitespace>\s*)(.+\n)*)\n",doc)
k=kwds.group("keywords")
#get rid of beginning whitespaces
@@ -67,7 +69,7 @@
return section(k)
def ret(f):
- doc=f.__doc__
+ doc=f.__doc__.decode('utf8')
kwds=re.search("Return:\n(?P<ret>(?P<whitespace>\s*)(.+\n)*)\n",doc)
k=kwds.group("ret")
#get rid of beginning whitespaces
@@ -94,15 +96,24 @@
(args, varargs, keywords, defaults)=inspect.getargspec(m)
args= [b for b in args if b is not "self"]
self.func_line=inspect.formatargspec(args, varargs, keywords, defaults)
- self.description = m.__doc__.split("\n")[0]
+ self.description = m.__doc__.split("\n")[0].decode("utf8")
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"),
]
@@ -131,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)
@@ -139,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 Wed Dec 21 21:51:47 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 Wed Dec 21 21:48:27 2011 +0100
+++ b/doc/current.html Wed Dec 21 21:51:47 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 Wed Dec 21 21:51:47 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 Wed Dec 21 21:51:47 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">- id : INTEGER</text>
+<text text-anchor="start" x="11" y="-84.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- recipient : VARCHAR</text>
+<text text-anchor="start" x="11" y="-73.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- isBilled : BOOLEAN</text>
+<text text-anchor="start" x="11" y="-62.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- date : DATETIME</text>
+<text text-anchor="start" x="11" y="-51.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- price : NUMERIC(8, 2)</text>
+<text text-anchor="start" x="11" y="-40.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- job : INTEGER</text>
+<text text-anchor="start" x="11" y="-29.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- 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">- hash : VARCHAR</text>
+<text text-anchor="start" x="154" y="-101.867" font-family="Bitstream-Vera Sans" font-size="7.00">- info : VARCHAR</text>
+<text text-anchor="start" x="154" y="-90.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- status : VARCHAR(7)</text>
+<text text-anchor="start" x="154" y="-79.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- user : VARCHAR</text>
+</g>
+<!-- message->job -->
+<g id="edge2" class="edge"><title>message->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">- name : VARCHAR</text>
+<text text-anchor="start" x="291" y="-30.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- provider : VARCHAR</text>
+<text text-anchor="start" x="291" y="-19.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- route : VARCHAR</text>
+<text text-anchor="start" x="291" y="-8.86667" font-family="Bitstream-Vera Sans" font-size="7.00">- typ : VARCHAR</text>
+</g>
+<!-- message->offer -->
+<g id="edge4" class="edge"><title>message->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">- user : VARCHAR</text>
+<text text-anchor="start" x="426" y="-61.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- offer : VARCHAR</text>
+<text text-anchor="start" x="426" y="-50.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- 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">- name : VARCHAR</text>
+<text text-anchor="start" x="295" y="-94.8667" font-family="Bitstream-Vera Sans" font-size="7.00">- apikey : VARCHAR</text>
+</g>
+<!-- job->apiuser -->
+<g id="edge10" class="edge"><title>job->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->userright -->
+<g id="edge6" class="edge"><title>apiuser->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->userright -->
+<g id="edge8" class="edge"><title>offer->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 Wed Dec 21 21:48:27 2011 +0100
+++ b/doc/impressum.html Wed Dec 21 21:51:47 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 Wed Dec 21 21:48:27 2011 +0100
+++ b/doc/index.html Wed Dec 21 21:51:47 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 Wed Dec 21 21:48:27 2011 +0100
+++ b/doc/new.html Wed Dec 21 21:51:47 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 Wed Dec 21 21:51:47 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>
--- a/iro/__init__.py Wed Dec 21 21:48:27 2011 +0100
+++ b/iro/__init__.py Wed Dec 21 21:51:47 2011 +0100
@@ -1,1 +1,1 @@
-
+__version__='2.0rc0'
--- a/iro/dump_test_log.py Wed Dec 21 21:48:27 2011 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-import time, os, signal
-LOG_FILE = 'test.log'
-
-log_file = open(LOG_FILE, 'a')
-
-def log(msg):
- log_file.write(msg + '\n')
- log_file.flush()
-
-def SigUSR1Handler(signum, frame):
- print "Reacting on USR1 signal (signal 10)"
- global log_file
- log_file.close()
- log_file = open(LOG_FILE, 'a')
- return
-
-def init():
- if os.path.isfile('/var/usr/dump_test_log.pid'):
- print 'Have to stop server first'
- os.exit(1)
- else:
- print 'Starting server...'
- #write process id file
- f = open('/var/run/dump_test_log.pid', 'w')
- f.write(str(os.getpid()))
- f.flush()
- f.close()
- print 'Process start with pid ', os.getpid()
-
- signal.signal(signal.SIGUSR1, SigUSR1Handler)
-
-def main():
- init()
- count = 1
- while True:
- log('log line #%d, pid: %d' % (count, os.getpid()))
- count = count + 1
- time.sleep(1)
-
-if __name__ == '__main__':
- main()
-
--- a/iro/merlin Wed Dec 21 21:48:27 2011 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# merlin commando
-#
-# eine überprüfung auf korrekten aufruf findet nicht statt
-#
-# beispiel:
-#
-# ./merlin ./arthur
-#
-# startet programm arthur und wenn er stirbt, wird er sofort
-# wiederbelebt.
-# harmlose magie halt... :-)
-#
-LOG=/home/sandy/var/log/merlin_Iro.log
-while : ; do
- echo -n "$(date +'%F %T %Z') " >> $LOG
- $1 status >> $LOG
- if [ $? -eq 1 ]; then
- echo $(date +'%F %T %Z') $1 neustarten >> $LOG
- $1 start >> $LOG
- fi
- sleep 60
-done
-
--- a/iro/merlin_daemon Wed Dec 21 21:48:27 2011 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-#! /bin/sh
-NAME="merlin"
-DEAMON=/home/sandy/svn/iro/$NAME
-DEAMON_OPTS="/home/sandy/svn/iro/MyIro_daemon"
-PID=/home/sandy/var/run/$NAME.pid
-
-test -x $DEAMON || exit 0
-
-. /lib/lsb/init-functions
-
-case "$1" in
- start)
- log_daemon_msg "Starting $NAME server" $NAME
- if start-stop-daemon --start --quiet --oknodo --pidfile $PID --make-pidfile --background --startas $DEAMON -- $DEAMON_OPTS; then
- log_end_msg 0
- else
- log_end_msg 1
- fi
- ;;
- stop)
- log_daemon_msg "Stopping $NAME server" $NAME
- if start-stop-daemon --stop --quiet --oknodo --pidfile $PID; then
- log_end_msg 0
- else
- log_end_msg 1
- fi
- ;;
-
- restart)
- $0 stop
- sleep 1
- $0 start
- ;;
-
- status)
- status_of_proc -p $PID $DEAMON $NAME && exit 0 || exit $?
- ;;
-
- *)
- log_action_msg "Usage: $0 {start|stop|restart|status}"
- exit 1
-esac
-
-exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/iro/newinterface.py Wed Dec 21 21:51:47 2011 +0100
@@ -0,0 +1,269 @@
+# -*- coding: utf-8 -*-
+#Copyright (C) 2009 Sandro Knauß <bugs@sandroknauss.de>
+
+#This program is free software; you can redistribute it and/or modify it under the terms
+#of the GNU General Public License as published by the Free Software Foundation;
+#either version 3 of the License, or any later version.
+#This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+#without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#See the GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, see <http://www.gnu.org/licenses/>.
+from twisted.web import soap, xmlrpc, resource, server
+import logging
+logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)s(%(processName)s)-%(levelname)s: %(message)s')
+
+
+class User(object):
+ def __init__(self,name,userhash):
+ self.name=name
+ self.userhash=userhash
+
+ def __repr__(self):
+ return"User<'%s','%s'>"%(self.name,self.userhash)
+
+users={"1":User("spam","1"),
+ "2":User("foo","2")
+}
+
+def getuser(userhash):
+ try:
+ return users[userhash]
+ except KeyError:
+ raise UserNotFound()
+
+def with_user(f):
+ def new_f(*args,**kargs):
+ args=list(args)
+ logging.debug("Entering %s"%f.__name__)
+ try:
+ kargs["user"]=getuser(kargs["apikey"])
+ del kargs["apikey"]
+ except KeyError:
+ kargs["user"]=getuser(args[1])
+ del args[1]
+ ret=f(*args,**kargs)
+ logging.debug("Exited %s"%f.__name__)
+ return ret
+ new_f.__name__ = f.__name__
+ return new_f
+
+
+class InterfaceException(Exception):
+ def __init__(self, code=999, msg="Unbekannter Fehler."):
+ self.code=code
+ self.msg=msg
+
+ def dict(self):
+ return {"code":self.code,
+ "msg":self.msg,
+ }
+ def __str__(self):
+ return "%i:%s"%(self.code,self.msg)
+
+class UserNotFound(InterfaceException):
+ def __init__(self):
+ InterfaceException.__init__(self, 901, "Der API-Key ist ungültig.")
+
+class ExternalException(InterfaceException):
+ def __init__(self):
+ InterfaceException.__init__(self, 950, "Fehler in externer API.")
+
+
+class Interface(object):
+ '''class for a xmlrpc user
+ '''
+
+ @with_user
+ def status(self, user, id=None, detailed=False):
+ '''Gibt den aktuellen Status eines Auftrages oder Mehreren zurück.
+
+ Keywords:
+ apikey[string]: Der API Key
+ id[hash]: Eine Auftragsnummer
+ detailed[boolean]: Details ausgeben
+
+ Return:
+ jobs[list]: Eine Liste der Aufträge.
+ job.name[string]: Angebener Name
+ job.status[string]: Status des Auftrages
+
+
+ '''
+ #return user.status(id,detailed)
+ return ""
+
+ @with_user
+ def stop(self, user, id):
+ '''Stoppt den angegeben Auftrag.
+
+ Keywords:
+ apikey[string]: Der API Key
+ id[hash]: Eine Auftragsnummer
+
+ Return:
+
+ '''
+ return ""
+
+ @with_user
+ def sms(self, user, message, recipients, route="default"):
+ '''Versendet eine SMS.
+
+ Keywords:
+ apikey[string]: Der API Key
+ message[string]: Nachricht
+ recipients[list]: eine Liste von Emfänger-Nummern (gemäß ITU-T E.123)
+ route[string|list]: Route über den geschickt werden soll,
+ oder eine Liste von Routen, um Fallbacks anzugeben
+
+ Return:
+ id[hash]: Die ID des Auftrages
+
+ '''
+ return ""
+
+ @with_user
+ def fax(self, user, subject, fax, recipients, route="default"):
+ '''Versendet ein FAX.
+
+ Keywords:
+ apikey[string]: Der API Key
+ subject[string]: Der Betreff
+ fax[string]: Das PDF base64 kodiert
+ recipients[list]: Eine Liste von Emfänger-Nummern (gemäß ITU-T E.123)
+ route[string|list]: Route über den geschickt werden soll,
+ oder eine Liste von Routen, um Fallbacks anzugeben
+
+ Return:
+ id[hash]: Die ID des Auftrages
+
+ '''
+ return ""
+
+ @with_user
+ def mail(self, user, subject, body, recipients, frm, route="default"):
+ '''Versendet eine Email.
+
+ Keywords:
+ apikey[string]: Der API Key
+ subject[string]: Der Betreff
+ body[string]: Der Email Body
+ recipients[list]: Eine Liste von Emailadressen
+ frm[string]: Die Absender Emailadresse
+ route[string|list]: Route über den geschickt werden soll,
+ oder eine Liste von Routen, um Fallbacks anzugeben
+
+ Return:
+ id[hash]: Die ID des Auftrages
+
+ '''
+ return ""
+
+ @with_user
+ def routes(self, user, typ):
+ '''Gibt eine Liste aller verfügbaren Provider zurück.
+
+ Keywords:
+ apikey[string]: Der API Key
+ typ[string]: Der Typ zu dem die Providerloste ausgeben werden soll
+ Einer der Liste ["sms","fax","mail"]
+
+ Return:
+ providerlist[list]: Eine Liste aller möglichen Provider
+
+ '''
+ return ""
+
+ @with_user
+ def defaultRoute(self, user, typ):
+ '''Gibt den Standardprovider zurück.
+
+ Keywords:
+ apikey[string]: Der API Key
+ typ[string]: Der Typ zu dem die Providerloste ausgeben werden soll
+ Einer der Liste ["sms","fax","mail"]
+
+ Return:
+ provider[string]: Der Standardprovider für den angeben Typ
+
+
+ '''
+ return ""
+
+ @with_user
+ def statistic(self, user):
+ '''Gibt eine Statik zurück über die versendendeten Nachrichten und des Preises.
+
+ Keywords:
+ apikey[string]: Der API Key
+
+ Return:
+ statistic[list]: Eine Liste nach Nachrichtentypen
+ '''
+ return ""
+
+ def listMethods(self):
+ """Since we override lookupProcedure, its suggested to override
+ listProcedures too.
+ """
+ return self.listProcedures()
+
+
+ def listProcedures(self):
+ """Since we override lookupProcedure, its suggested to override
+ listProcedures too.
+ """
+ return ['listMethods','status','stop','sms','fax','mail','routes','defaultRoute','statistic']
+
+
+class XMLRPCInterface(Interface,xmlrpc.XMLRPC):
+ def __init__(self):
+ xmlrpc.XMLRPC.__init__(self)
+ Interface.__init__(self)
+
+ def lookupProcedure(self, procedurePath):
+ logging.debug("lookupProcedure('%s')"%procedurePath)
+ if procedurePath not in self.listProcedures():
+ raise xmlrpc.NoSuchFunction(self.NOT_FOUND,
+ "procedure %s not found" % procedurePath)
+ try:
+ return getattr(self,procedurePath)
+ except KeyError:
+ raise xmlrpc.NoSuchFunction(self.NOT_FOUND,
+ "procedure %s not found" % procedurePath)
+
+class SOAPInterface(Interface,soap.SOAPPublisher):
+ def __init__(self):
+ soap.SOAPPublisher.__init__(self)
+ Interface.__init__(self)
+
+ def lookupFunction(self, functionName):
+ """Lookup published SOAP function.
+
+ Override in subclasses. Default behaviour - publish methods
+ starting with soap_, if they have true attribute useKeywords
+ they are expected to accept keywords.
+
+ @return: tuple (callable, useKeywords), or (None, None) if not found.
+ """
+ if functionName in self.listProcedures():
+ function = getattr(self, functionName, None)
+ if function:
+ return function, getattr(function, "useKeywords", False)
+ return None
+ else:
+ return None
+
+
+def main():
+ from twisted.internet import reactor
+ root = resource.Resource()
+ root.putChild('RPC2', XMLRPCInterface())
+ root.putChild('SOAP', SOAPInterface())
+ reactor.listenTCP(7080, server.Site(root))
+ reactor.run()
+
+if __name__ == '__main__':
+ main()
--- a/iro/newuser.py Wed Dec 21 21:48:27 2011 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-# -*- coding: utf-8 -*-
-#Copyright (C) 2009 Sandro Knauß <bugs@sandroknauss.de>
-
-#This program is free software; you can redistribute it and/or modify it under the terms
-#of the GNU General Public License as published by the Free Software Foundation;
-#either version 3 of the License, or any later version.
-#This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-#without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-#See the GNU General Public License for more details.
-
-#You should have received a copy of the GNU General Public License
-#along with this program; if not, see <http://www.gnu.org/licenses/>.
-
-class User:
- '''
- class for a xmlrpc user
- '''
-
- def status(self, apikey, id=None, detailed=False):
- u'''Gibt den aktuellen Status eines Auftrages zurück.
-
- Keywords:
- apikey[string]: Der API Key
- id[hash]: Eine Auftragsnummer
- detailed[boolean]: Details ausgeben
-
- Return:
- jobs[list]: Eine Liste der Aufträge.
- job.name[string]: Angebener Name
- job.status[string]: Status des Auftrages
-
-
- '''
- pass
-
- def stop(self, apikey,id):
- u'''Stoppt den angegeben Auftrag.
-
- Keywords:
- apikey[string]: Der API Key
- id[hash]: Eine Auftragsnummer
-
- Return:
-
- '''
- pass
-
- def sms(self, apikey, message, recipients, route="default"):
- u'''Versendet eine SMS.
-
- Keywords:
- apikey[string]: Der API Key
- message[string]: Nachricht
- recipients[list]: eine Liste von Emfänger-Nummern (gemäß ITU-T E.123)
- route[string|list]: Route über den geschickt werden soll,
- oder eine Liste von Routen, um Fallbacks anzugeben
-
- Return:
- id[hash]: Die ID des Auftrages
-
- '''
- pass
-
-
- def fax(self, apikey, subject, fax, recipients, route="default"):
- u'''Versendet ein FAX.
-
- Keywords:
- apikey[string]: Der API Key
- subject[string]: Der Betreff
- fax[string]: Das PDF base64 kodiert
- recipients[list]: Eine Liste von Emfänger-Nummern (gemäß ITU-T E.123)
- route[string|list]: Route über den geschickt werden soll,
- oder eine Liste von Routen, um Fallbacks anzugeben
-
- Return:
- id[hash]: Die ID des Auftrages
-
- '''
- pass
-
- def mail(self, apikey, subject, body, recipients, frm, route="default"):
- u'''Versendet eine Email.
-
- Keywords:
- apikey[string]: Der API Key
- subject[string]: Der Betreff
- body[string]: Der Email Body
- recipients[list]: Eine Liste von Emailadressen
- frm[string]: Die Absender Emailadresse
- route[string|list]: Route über den geschickt werden soll,
- oder eine Liste von Routen, um Fallbacks anzugeben
-
- Return:
- id[hash]: Die ID des Auftrages
-
- '''
- pass
-
- def routes(self, apikey, typ):
- u'''Gibt eine Liste aller verfügbaren Provider zurück.
-
- Keywords:
- apikey[string]: Der API Key
- typ[string]: Der Typ zu dem die Providerloste ausgeben werden soll
- Einer der Liste ["sms","fax","mail"]
-
- Return:
- providerlist[list]: Eine Liste aller möglichen Provider
-
- '''
- pass
-
- def defaultRoute(self, apikey, typ):
- u'''Gibt den Standardprovider zurück.
-
- Keywords:
- apikey[string]: Der API Key
- typ[string]: Der Typ zu dem die Providerloste ausgeben werden soll
- Einer der Liste ["sms","fax","mail"]
-
- Return:
- provider[string]: Der Standardprovider für den angeben Typ
-
-
- '''
- pass
-
- def statistic(self,apikey):
- u'''Gibt eine Statik zurück über die versendendeten Nachrichten und des Preises.
-
- Keywords:
- apikey[string]: Der API Key
-
- Return:
- statistic[list]: Eine Liste nach Nachrichtentypen
- '''
- pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/iro/schema.py Wed Dec 21 21:51:47 2011 +0100
@@ -0,0 +1,71 @@
+# -*- 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. <em>ng_kunde</em> ist der verknüpfte netzguerilla.net Benutzer, der die Rechnung zahlt."""
+ __tablename__ = "apiuser"
+ name = Column(String, primary_key=True)
+ ng_kunde = Column(Integer)
+ 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)
--- a/iro/tests/stopableServer.py Wed Dec 21 21:48:27 2011 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-import ConfigParser
-
-import threading
-
-from multiprocessing import Queue
-from multiprocessing.managers import BaseManager
-
-from iro import xmlrpc,anbieter
-from iro.user import User, Admin
-from iro.iro import MySMTP,MySmstrade,MyUserDB
-from iro.job import SMSJob, FAXJob, MailJob
-from iro.joblist import Joblist
-from iro.providerlist import Providerlist
-
-class StoppableXMLRPCServer(xmlrpc.SecureUserDBXMLRPCServer, threading.Thread):
- running=False
- def __init__(self, *args, **kwargs):
- xmlrpc.SecureUserDBXMLRPCServer.__init__(self, *args, **kwargs)
- threading.Thread.__init__(self)
- self.timeout=.5
-
- def start(self):
- self.running=True
- threading.Thread.start(self)
-
-
- def run(self):
- # *serve_forever* muss in einem eigenen Thread laufen, damit man es
- # unterbrochen werden kann!
- while (self.running):
- try:
- self.handle_request()
- except :
- break
-
- def stop(self):
- if (self.running):
- self.running=False
- self.server_close()
- self.join()
-
- def __del__(self):
- self.stop()
-
- def __enter__(self):
- self.start()
- return self
-
- def __exit__(self,type,value,traceback):
- self.stop()
-
-
-class Internal:
- pass
-
-def init_server():
- userlist=[{"name":"test","password":"test", "class":User},
- {"name":"test2","password":"test2", "class": User},
- {"name":"admin","password":"admin", "class": Admin}]
-
-
-
- class MyManager(BaseManager):
- pass
-
- internal=Internal()
-
- MyManager.register('SMSJob', SMSJob)
- MyManager.register('FaxJob', FAXJob)
- MyManager.register('MailJob',MailJob)
- MyManager.register('Providerlist',Providerlist)
- manager = MyManager()
- manager.start()
-
- internal.manager=manager
-
- #anbieter erzeugen und konfigurieren
- sip=anbieter.sipgate()
- sip.read_basic_config("iro.conf")
-
- localhost=MySMTP()
- localhost.read_basic_config("iro.conf")
-
- smstrade=MySmstrade()
- smstrade.read_basic_config("iro.conf")
-
- #Benutzerdatenbank erstellen
- queue = Queue()
- internal.queue=queue
- provider=Providerlist()
- internal.provider=provider
- provider.add("sipgate", sip, ["sms", "fax", ])
- provider.add("smstrade", smstrade, ["sms", ])
- provider.add("geonet", None, ["sms", "fax", ])
- provider.add("fax.de", None, ["sms", "fax", ])
- provider.add("localhost", localhost, ["mail", ])
- provider.setDefault("sms","smstrade")
- provider.setDefault("fax","sipgate")
- provider.setDefault("mail","localhost")
- jobqueue=Joblist(manager, queue, provider)
- internal.jobqueue=jobqueue
- userdb=MyUserDB(userlist,jobqueue)
- internal.userdb=userdb
-
-
- #Server starten
- cp = ConfigParser.ConfigParser()
- cp.read(["iro.conf"])
- cert=cp.get('server', 'cert')
- key=cp.get('server', 'key')
- serv = StoppableXMLRPCServer(addr=("localhost", 8000),
- userdb=userdb,
- certificate=cert,privatekey=key,
- logRequests=False)
- serv.relam="xmlrpc"
- internal.serv=serv
- return internal
-
Binary file iro/tests/test.pdf has changed
--- a/iro/tests/testJob.py Wed Dec 21 21:48:27 2011 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import unittest
-
-import xmlrpclib
-from stopableServer import init_server
-from iro.anbieter.content import SMS,FAX,Mail
-
-class TestServer(unittest.TestCase):
-
- def setUp(self):
- self.i = init_server()
- self.serv=self.i.serv
- self.serv.start()
-
- def tearDown(self):
- self.serv.stop()
-
-
- def SendSMS(self,msg):
- servstr="https://test:test@localhost:8000"
- client=xmlrpclib.Server(servstr)
- id=client.startSMS(msg,["01234", ] )
- self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': unicode(msg)}} )
- ele=self.i.queue.get(.1)
- self.assertEqual(ele.getRecipients(),["01234", ] )
- self.assertNotEqual(ele.getMessage(),SMS('') )
- self.assertEqual(ele.getMessage(),SMS(msg) )
-
- def testSimpleSMS(self):
- self.SendSMS("test")
-
- def testSpecialCharacters(self):
- self.SendSMS(u"!\"§$%&/()=?\'")
- self.SendSMS(u"@ł€ł€¶ŧł¼¼½¬¬↓ŧ←ĸ↓→øđŋħ“”µ·…–|")
-
- def testSendFAX(self):
- servstr="https://test:test@localhost:8000"
- client=xmlrpclib.Server(servstr)
- msg="2134wergsdfg4w56q34134æſðđæðſđŋ³@¼ŧæðđŋł€¶ŧ€¶ŧ"
- id=client.startFAX("test",xmlrpclib.Binary(msg),["01234", ] )
- self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': 'test'}} )
- ele=self.i.queue.get(.1)
- self.assertEqual(ele.getRecipients(),["01234", ] )
- self.assertEqual(ele.getMessage(),FAX('test','',[msg]))
-
- def testDoubleFAX(self):
- servstr="https://test:test@localhost:8000"
- client=xmlrpclib.Server(servstr)
- msg="2134wergsdfg4w56q34134æſðđæðſđŋ³@¼ŧæðđŋł€¶ŧ€¶ŧ"
- pdf=open('tests/test.pdf').read()
- id=client.startFAX("test",[xmlrpclib.Binary(msg),xmlrpclib.Binary(pdf)],["01234", ] )
- self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': 'test'}} )
- ele=self.i.queue.get(.1)
- self.assertEqual(ele.getRecipients(),["01234", ] )
- self.assertEqual(ele.getMessage(),FAX('test','',[msg, pdf]))
-
- def testSendMail(self):
- servstr="https://test:test@localhost:8000"
- client=xmlrpclib.Server(servstr)
- msg=u"2134wergsdfg4w56q34134æſðđæðſđŋ³@¼ŧæðđŋł€¶ŧ€¶ŧ"
- id=client.startMail("test",msg,["test@test.de", ],'absender@test.de' )
- self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': 'test'}} )
- ele=self.i.queue.get(.1)
- self.assertEqual(ele.getRecipients(),["test@test.de", ] )
- self.assertEqual(ele.getMessage(),Mail('test',msg,'absender@test.de'))
- self.assertEqual(ele.getMessage().as_string(),"""Content-Type: text/plain; charset="utf-8"
-MIME-Version: 1.0
-Content-Transfer-Encoding: base64
-Subject: =?utf-8?q?test?=
-
-MjEzNHdlcmdzZGZnNHc1NnEzNDEzNMOmxb/DsMSRw6bDsMW/xJHFi8KzQMK8xafDpsOwxJHFi8WC
-4oKswrbFp+KCrMK2xac=
-""")
- sub=u"³¼½ſðđŋſ€¼½ÖÄÜß"
- id=client.startMail(sub,msg,["test@test.de", ],'absender@test.de' )
- self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': sub}})
- ele=self.i.queue.get(.1)
- self.assertEqual(ele.getMessage(),Mail(sub, msg, 'absender@test.de'))
-
-if __name__ == "__main__":
- unittest.main()
--- a/iro/tests/testWorker.py Wed Dec 21 21:48:27 2011 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import unittest
-import logging
-from time import sleep
-
-from multiprocessing import Queue
-from multiprocessing.managers import BaseManager, ListProxy
-
-from iro.worker import Worker
-from iro.job import Job, SMSJob
-from iro.anbieter.anbieter import anbieter
-from iro.anbieter.content import SMS
-from iro.providerlist import Providerlist
-
-from logging.handlers import BufferingHandler
-
-class MyHandler(BufferingHandler):
- def __init__(self,buffer=None):
- '''BufferingHandler takes a "capacity" argument
- so as to know when to flush. As we're overriding
- shouldFlush anyway, we can set a capacity of zero.
- You can call flush() manually to clear out the
- buffer.
- buffer: log messages to this buffer, needed f.ex for processes or threads'''
- BufferingHandler.__init__(self, 0)
- self.buffer=buffer
-
- def shouldFlush(self):
- return False
-
- def emit(self, record):
- if record.exc_info: #sonst geht das append schief, weil nicht picklebar
- record.exc_info=record.exc_info[:-1]
- self.buffer.append(record.__dict__)
-
-
-class BadJob(Job):
- def start(self,id=None):
- Job.start(self,id)
- raise Exception("Error")
-
-#einen Manager anlegen, der Job und eine Liste anbietet
-class MyManager(BaseManager):
- pass
-MyManager.register('Job', Job)
-MyManager.register('SMSJob', SMSJob)
-MyManager.register('BadJob', BadJob)
-MyManager.register('list', list, ListProxy)
-MyManager.register('Providerlist',Providerlist)
-
-class TestWorker(unittest.TestCase):
- def setUp(self):
- #erstelle eine Queue&Manager
- self.manager = MyManager()
- self.queue = Queue()
-
- #manager starten, damit wir Logging anpassen können
- self.manager.start()
- self.setUpLogging()
-
- self.providerlist=self.manager.Providerlist()
- self.providerlist.add("test", anbieter() , ["sms", ])
-
- #eigentlich Workerprocess starten
- self.worker= Worker(self.queue)
- self.worker.start()
-
- def tearDown(self):
- #Thread&Queue stoppen
- self.stop()
-
- #Logging änderungen rückgängig
- self.tearDownLogging()
- self.manager.shutdown()
-
- def stop(self):
- self.queue.close()
- self.queue.join_thread()
- self.worker.terminate()
-
-
- def setUpLogging(self):
- '''Logging so umbasteln, das wir alle logging Meldung in self.buf sind'''
- #wir brauchen eine threadsichere liste
- self.buffer=self.manager.list()
-
- #Handler erstellen, der in den Buffer schreibt
- self.handler = h = MyHandler(self.buffer)
- self.logger =l= logging.getLogger()
-
- #Level anpassen
- l.setLevel(logging.DEBUG)
- h.setLevel(logging.DEBUG)
- l.addHandler(h)
-
- def tearDownLogging(self):
- '''crazy logging hacks wieder entfernen'''
- self.logger.removeHandler(self.handler)
- self.logger.setLevel(logging.NOTSET)
- self.handler.close()
-
-
- def testJob(self):
- '''einen Job verarbeiten'''
- job=self.manager.Job(None,None,"test")
- self.assertEqual(job.getStatus(),("init",{}))
- self.queue.put(job)
- sleep(.1)
- self.stop()
- self.assertEqual(job.getStatus(),("started",{}))
- self.assertEqual([(l['levelno'],l['msg']) for l in self.buffer if l['name']=="iro.worker"],
- [(20,'Workerprocess läuft nun...'),
- (20,'ein neuer Job(1)'),
- (20,'Job(1) fertig ;)')])
-
- def testBadJob(self):
- '''einen Job verarbeiten, der fehlschlägt'''
- job=self.manager.BadJob(None,None,"test")
- self.assertEqual(job.getStatus(),("init",{}))
- self.queue.put(job)
- sleep(.1)
- self.stop()
- self.assertEqual(job.getStatus(),("error",{}))
- self.assertEqual([(l['levelno'],l['msg']) for l in self.buffer if l['name']=="iro.worker"],
- [(20,'Workerprocess läuft nun...'),
- (20,'ein neuer Job(1)'),
- (40,'Job(1) fehlgeschlagen :(')])
- error=Exception('Error')
- self.assertEqual(self.buffer[-1]['exc_info'][0],type(error))
- self.assertEqual(str(self.buffer[-1]['exc_info'][1]),str(error))
-
- def testSMSJob(self):
- job=self.manager.SMSJob(self.providerlist, "test", "name", SMS("message"),[012345])
- self.assertEqual(job.getStatus(),("init",{}))
- self.queue.put(job)
- sleep(.1)
- self.stop()
- self.assertEqual(job.getStatus(),("sended",{'failed': [], 'good': []}))
- self.assertEqual([(l['levelno'],l['msg']) for l in self.buffer if l['name']=="iro.worker"],
- [(20,'Workerprocess läuft nun...'),
- (20,'ein neuer Job(1)'),
- (20,'Job(1) fertig ;)')])
-
-
-if __name__ == "__main__":
- unittest.main()
--- a/iro/tests/testXMLRPCServer.py Wed Dec 21 21:48:27 2011 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import unittest
-
-import xmlrpclib
-from stopableServer import init_server
-
-class TestServer(unittest.TestCase):
-
- def setUp(self):
- self.i = init_server()
- self.serv=self.i.serv
-
- self.serv.start()
-
- def tearDown(self):
- self.serv.stop()
-
- def testLogin(self):
- self.assertEqual(xmlrpclib.Server("https://test:test@localhost:8000").status(), {})
- self.assertEqual(xmlrpclib.Server("https://test2:test2@localhost:8000").status(), {})
- self.assertRaises(xmlrpclib.ProtocolError, xmlrpclib.Server("https://test2:test@localhost:8000").status)
- self.assertRaises(xmlrpclib.ProtocolError,xmlrpclib.Server ("https://test:test2@localhost:8000").status)
-
- def testsendSMS(self):
- servstr="https://test:test@localhost:8000"
- client=xmlrpclib.Server(servstr)
- id=client.startSMS("test",["01234", ] )
- self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': 'test'}} )
-
- def testTwoUser(self):
- u1="https://test:test@localhost:8000"
- u2="https://test2:test2@localhost:8000"
- admin="https://admin:admin@localhost:8000"
- client1=xmlrpclib.Server(u1)
- client2=xmlrpclib.Server(u2)
- admin=xmlrpclib.Server(admin)
- id1=client1.startSMS("test",["01234"] )
- self.assertEqual(client2.status(),{} )
- self.assertEqual(admin.status(id1),{id1: {'status': ['init', {}], 'name': 'test'}} )
- id2=client2.startSMS("test2",["01234"] )
- self.assertNotEqual(id1, id2)
- self.assertEqual(client1.status(),{id1: {'status': ['init', {}], 'name': 'test'}})
- self.assertEqual(client2.status(),{id2: {'status': ['init', {}], 'name': 'test2'}})
- self.assertEqual(admin.status(),{id1: {'status': ['init', {}], 'name': 'test'},
- id2: {'status': ['init', {}], 'name': 'test2'}} )
-
- self.assertEqual(client2.status(id1), {})
- self.assertEqual(client1.status(id2), {})
-
- def testGetProvider(self):
- servstr="https://test:test@localhost:8000"
- client=xmlrpclib.Server(servstr)
- self.assertEqual(client.getProvider("sms"), ["fax.de","geonet", "sipgate", "smstrade"])
- self.assertEqual(client.getProvider("fax"), ["fax.de","geonet", "sipgate"])
- self.assertEqual(client.getProvider("mail"), ["localhost"])
-
- self.assertRaises(xmlrpclib.ProtocolError,client.getProvider, "temp")
-
- def testGetDefault(self):
- servstr="https://test:test@localhost:8000"
- client=xmlrpclib.Server(servstr)
- self.assertEqual(client.getDefaultProvider("sms"), "smstrade")
- self.assertEqual(client.getDefaultProvider("fax"),"sipgate")
- self.assertEqual(client.getDefaultProvider("mail"), "localhost")
-
- self.assertRaises(xmlrpclib.ProtocolError,client.getDefaultProvider, "temp")
-
-
-if __name__ == "__main__":
- unittest.main()
--- a/iro/tests/testloglock.py Wed Dec 21 21:48:27 2011 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-
-import unittest
-import os
-import tempfile
-import signal
-import threading
-import time
-
-class testLogLock(unittest.TestCase):
- def test_ThreadingAndLocks(self):
- #create a thread, have that thread grab a lock, and sleep.
- fd, file = tempfile.mkstemp('.nonlog')
- _lock = threading.RLock()
- os.close(fd)
- def locker():
- os.write(fd, 'Thread acquiring lock\n')
- _lock.acquire()
- os.write(fd, 'Thread acquired lock\n')
- time.sleep(0.4)
- os.write(fd, 'Thread releasing lock\n')
- _lock.release()
- os.write(fd, 'Thread released lock\n')
-
- #Then in the main thread, throw a signal to self
- def handleSignal(sigNum, frame):
- os.write(fd, 'Main Thread acquiring lock\n')
- lock = False
- endtime = time.time() + 1.0
- while not lock:
- lock = _lock.acquire(blocking=0)
- time.sleep(0.01)
- if time.time() > endtime:
- break
- if not lock:
- os.write(fd, 'Main Thread could not acquire lock\n')
- return
- os.write(fd, 'Main Thread acquired lock\n')
- os.write(fd, 'Main Thread releasing lock\n')
- _lock.release()
- os.write(fd, 'Main Thread released lock\n')
-
- sighndlr = signal.signal(signal.SIGUSR1, handleSignal)
- try:
- fd = os.open(file, os.O_SYNC | os.O_WRONLY | os.O_CREAT)
- thread = threading.Thread(target=locker)
- thread.start()
- time.sleep(0.1)
- os.kill(os.getpid(), signal.SIGUSR1)
- thread.join()
-
- #check the results
- os.close(fd)
- fileconts = open(file, 'r').read()
- self.assertTrue('Main Thread released lock' in fileconts)
- self.assertEqual(fileconts,
- '''Thread acquiring lock
-Thread acquired lock
-Main Thread acquiring lock
-Thread releasing lock
-Thread released lock
-Main Thread acquired lock
-Main Thread releasing lock
-Main Thread released lock
-''')
-
- #Now try after acquiring the lock from the main thread
- fd = os.open(file, os.O_SYNC | os.O_WRONLY | os.O_CREAT)
- _lock.acquire()
- thread = threading.Thread(target=locker)
- thread.start()
- time.sleep(0.1)
- os.kill(os.getpid(), signal.SIGUSR1)
- _lock.release()
- thread.join()
- os.close(fd)
- fileconts = open(file, 'r').read()
- self.assertEqual(fileconts,
- '''Thread acquiring lock
-Main Thread acquiring lock
-Main Thread acquired lock
-Main Thread releasing lock
-Main Thread released lock
-Thread acquired lock
-Thread releasing lock
-Thread released lock
-''')
-
- finally:
- signal.signal(signal.SIGUSR1, sighndlr)
- try:
- os.close(fd)
- except OSError:
- pass
- try:
- os.unlink(file)
- except OSError:
- pass
-
--- a/iro/user.py Wed Dec 21 21:48:27 2011 +0100
+++ b/iro/user.py Wed Dec 21 21:51:47 2011 +0100
@@ -28,8 +28,7 @@
class User:
- '''
- class for a xmlrpc user
+ '''class for a xmlrpc user
'''
def __init__(self, name, jobqueue):
self.jobqueue=jobqueue
@@ -38,7 +37,7 @@
self.features=["mail", "sms", "fax", ]
def status(self,id=None,detailed=False):
- u'''Gibt den aktuellen Status eines Auftrages zurück.
+ '''Gibt den aktuellen Status eines Auftrages zurück.
Keywords:
id[hash]: Eine Auftragsnummer
@@ -75,7 +74,7 @@
return {}
def stop(self,id):
- u'''Stoppt den angegeben Auftrag.
+ '''Stoppt den angegeben Auftrag.
Keywords:
id[hash]: Eine Auftragsnummer
@@ -92,7 +91,7 @@
def startSMS(self, message, recipients, provider="default"):
- u'''Versendet eine SMS.
+ '''Versendet eine SMS.
Keywords:
message[string]: Nachricht
@@ -111,7 +110,7 @@
def startFAX(self, subject, fax, recipients, provider="default"):
- u'''Versendet ein FAX.
+ '''Versendet ein FAX.
Keywords:
subject[string]: der Betreff
@@ -136,7 +135,7 @@
return id
def startMail(self, subject, body, recipients, frm, provider="default"):
- u'''Versendet eine Email.
+ '''Versendet eine Email.
Keywords:
subject[string]: der Betreff
@@ -157,7 +156,7 @@
return id
def getProvider(self, typ):
- u'''Gibt eine Liste aller verfügbaren Provider zurück.
+ '''Gibt eine Liste aller verfügbaren Provider zurück.
Keywords:
typ[string]: Der Typ zu dem die Providerloste ausgeben werden soll
@@ -173,7 +172,7 @@
return self.jobqueue.providerlist.getProviderlist(typ)
def getDefaultProvider(self, typ):
- u'''Gibt den Standardprovider zurück.
+ '''Gibt den Standardprovider zurück.
Keywords:
typ[string]: Der Typ zu dem die Providerloste ausgeben werden soll
--- a/setup.py Wed Dec 21 21:48:27 2011 +0100
+++ b/setup.py Wed Dec 21 21:51:47 2011 +0100
@@ -1,2 +1,16 @@
+# -*- coding: utf-8 -*-
+
from setuptools import setup
-setup(name="iro",version='0.1',install_requires=["xmlrpclib","ConfigParser","base64","multiprocessing"])
+from iro import __version__
+
+setup(name='iro',
+ version=__version__,
+ packages=['iro'],
+ setup_requires = ['nose>=0.11'],
+ install_requires=['twisted>=11.1.0',"xmlrpclib","ConfigParser","base64","multiprocessing"],
+ test_suite="nose.collector",
+ description='Non Blocking Interface for sending a bunsh of SMSes, FAXes and Mails',
+ author='Sandro Knauß',
+ author_email='knauss@netzguerilla.net',
+ url='https://netzguerilla.net/admin/hg/iro',
+)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sqlalchemy_schemadisplay3.py Wed Dec 21 21:51:47 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'))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/dump_test_log.py Wed Dec 21 21:51:47 2011 +0100
@@ -0,0 +1,42 @@
+import time, os, signal
+LOG_FILE = 'test.log'
+
+log_file = open(LOG_FILE, 'a')
+
+def log(msg):
+ log_file.write(msg + '\n')
+ log_file.flush()
+
+def SigUSR1Handler(signum, frame):
+ print "Reacting on USR1 signal (signal 10)"
+ global log_file
+ log_file.close()
+ log_file = open(LOG_FILE, 'a')
+ return
+
+def init():
+ if os.path.isfile('/var/usr/dump_test_log.pid'):
+ print 'Have to stop server first'
+ os.exit(1)
+ else:
+ print 'Starting server...'
+ #write process id file
+ f = open('/var/run/dump_test_log.pid', 'w')
+ f.write(str(os.getpid()))
+ f.flush()
+ f.close()
+ print 'Process start with pid ', os.getpid()
+
+ signal.signal(signal.SIGUSR1, SigUSR1Handler)
+
+def main():
+ init()
+ count = 1
+ while True:
+ log('log line #%d, pid: %d' % (count, os.getpid()))
+ count = count + 1
+ time.sleep(1)
+
+if __name__ == '__main__':
+ main()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/stopableServer.py Wed Dec 21 21:51:47 2011 +0100
@@ -0,0 +1,118 @@
+import ConfigParser
+
+import threading
+
+from multiprocessing import Queue
+from multiprocessing.managers import BaseManager
+
+from iro import xmlrpc,anbieter
+from iro.user import User, Admin
+from iro.iro import MySMTP,MySmstrade,MyUserDB
+from iro.job import SMSJob, FAXJob, MailJob
+from iro.joblist import Joblist
+from iro.providerlist import Providerlist
+
+class StoppableXMLRPCServer(xmlrpc.SecureUserDBXMLRPCServer, threading.Thread):
+ running=False
+ def __init__(self, *args, **kwargs):
+ xmlrpc.SecureUserDBXMLRPCServer.__init__(self, *args, **kwargs)
+ threading.Thread.__init__(self)
+ self.timeout=.5
+
+ def start(self):
+ self.running=True
+ threading.Thread.start(self)
+
+
+ def run(self):
+ # *serve_forever* muss in einem eigenen Thread laufen, damit man es
+ # unterbrochen werden kann!
+ while (self.running):
+ try:
+ self.handle_request()
+ except :
+ break
+
+ def stop(self):
+ if (self.running):
+ self.running=False
+ self.server_close()
+ self.join()
+
+ def __del__(self):
+ self.stop()
+
+ def __enter__(self):
+ self.start()
+ return self
+
+ def __exit__(self,type,value,traceback):
+ self.stop()
+
+
+class Internal:
+ pass
+
+def init_server():
+ userlist=[{"name":"test","password":"test", "class":User},
+ {"name":"test2","password":"test2", "class": User},
+ {"name":"admin","password":"admin", "class": Admin}]
+
+
+
+ class MyManager(BaseManager):
+ pass
+
+ internal=Internal()
+
+ MyManager.register('SMSJob', SMSJob)
+ MyManager.register('FaxJob', FAXJob)
+ MyManager.register('MailJob',MailJob)
+ MyManager.register('Providerlist',Providerlist)
+ manager = MyManager()
+ manager.start()
+
+ internal.manager=manager
+
+ #anbieter erzeugen und konfigurieren
+ sip=anbieter.sipgate()
+ sip.read_basic_config("iro.conf")
+
+ localhost=MySMTP()
+ localhost.read_basic_config("iro.conf")
+
+ smstrade=MySmstrade()
+ smstrade.read_basic_config("iro.conf")
+
+ #Benutzerdatenbank erstellen
+ queue = Queue()
+ internal.queue=queue
+ provider=Providerlist()
+ internal.provider=provider
+ provider.add("sipgate", sip, ["sms", "fax", ])
+ provider.add("smstrade", smstrade, ["sms", ])
+ provider.add("geonet", None, ["sms", "fax", ])
+ provider.add("fax.de", None, ["sms", "fax", ])
+ provider.add("localhost", localhost, ["mail", ])
+ provider.setDefault("sms","smstrade")
+ provider.setDefault("fax","sipgate")
+ provider.setDefault("mail","localhost")
+ jobqueue=Joblist(manager, queue, provider)
+ internal.jobqueue=jobqueue
+ userdb=MyUserDB(userlist,jobqueue)
+ internal.userdb=userdb
+
+
+ #Server starten
+ cp = ConfigParser.ConfigParser()
+ cp.read(["iro.conf"])
+ cert=cp.get('server', 'cert')
+ key=cp.get('server', 'key')
+ serv = StoppableXMLRPCServer(addr=("localhost", 8000),
+ userdb=userdb,
+ certificate=cert,privatekey=key,
+ logRequests=False)
+ serv.relam="xmlrpc"
+ internal.serv=serv
+ return internal
+
Binary file tests/test.pdf has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testJob.py Wed Dec 21 21:51:47 2011 +0100
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+
+import xmlrpclib
+from stopableServer import init_server
+from iro.anbieter.content import SMS,FAX,Mail
+
+class TestServer(unittest.TestCase):
+
+ def setUp(self):
+ self.i = init_server()
+ self.serv=self.i.serv
+ self.serv.start()
+
+ def tearDown(self):
+ self.serv.stop()
+
+
+ def SendSMS(self,msg):
+ servstr="https://test:test@localhost:8000"
+ client=xmlrpclib.Server(servstr)
+ id=client.startSMS(msg,["01234", ] )
+ self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': unicode(msg)}} )
+ ele=self.i.queue.get(.1)
+ self.assertEqual(ele.getRecipients(),["01234", ] )
+ self.assertNotEqual(ele.getMessage(),SMS('') )
+ self.assertEqual(ele.getMessage(),SMS(msg) )
+
+ def testSimpleSMS(self):
+ self.SendSMS("test")
+
+ def testSpecialCharacters(self):
+ self.SendSMS(u"!\"§$%&/()=?\'")
+ self.SendSMS(u"@ł€ł€¶ŧł¼¼½¬¬↓ŧ←ĸ↓→øđŋħ“”µ·…–|")
+
+ def testSendFAX(self):
+ servstr="https://test:test@localhost:8000"
+ client=xmlrpclib.Server(servstr)
+ msg="2134wergsdfg4w56q34134æſðđæðſđŋ³@¼ŧæðđŋł€¶ŧ€¶ŧ"
+ id=client.startFAX("test",xmlrpclib.Binary(msg),["01234", ] )
+ self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': 'test'}} )
+ ele=self.i.queue.get(.1)
+ self.assertEqual(ele.getRecipients(),["01234", ] )
+ self.assertEqual(ele.getMessage(),FAX('test','',[msg]))
+
+ def testDoubleFAX(self):
+ servstr="https://test:test@localhost:8000"
+ client=xmlrpclib.Server(servstr)
+ msg="2134wergsdfg4w56q34134æſðđæðſđŋ³@¼ŧæðđŋł€¶ŧ€¶ŧ"
+ pdf=open('tests/test.pdf').read()
+ id=client.startFAX("test",[xmlrpclib.Binary(msg),xmlrpclib.Binary(pdf)],["01234", ] )
+ self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': 'test'}} )
+ ele=self.i.queue.get(.1)
+ self.assertEqual(ele.getRecipients(),["01234", ] )
+ self.assertEqual(ele.getMessage(),FAX('test','',[msg, pdf]))
+
+ def testSendMail(self):
+ servstr="https://test:test@localhost:8000"
+ client=xmlrpclib.Server(servstr)
+ msg=u"2134wergsdfg4w56q34134æſðđæðſđŋ³@¼ŧæðđŋł€¶ŧ€¶ŧ"
+ id=client.startMail("test",msg,["test@test.de", ],'absender@test.de' )
+ self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': 'test'}} )
+ ele=self.i.queue.get(.1)
+ self.assertEqual(ele.getRecipients(),["test@test.de", ] )
+ self.assertEqual(ele.getMessage(),Mail('test',msg,'absender@test.de'))
+ self.assertEqual(ele.getMessage().as_string(),"""Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: base64
+Subject: =?utf-8?q?test?=
+
+MjEzNHdlcmdzZGZnNHc1NnEzNDEzNMOmxb/DsMSRw6bDsMW/xJHFi8KzQMK8xafDpsOwxJHFi8WC
+4oKswrbFp+KCrMK2xac=
+""")
+ sub=u"³¼½ſðđŋſ€¼½ÖÄÜß"
+ id=client.startMail(sub,msg,["test@test.de", ],'absender@test.de' )
+ self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': sub}})
+ ele=self.i.queue.get(.1)
+ self.assertEqual(ele.getMessage(),Mail(sub, msg, 'absender@test.de'))
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testWorker.py Wed Dec 21 21:51:47 2011 +0100
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+import logging
+from time import sleep
+
+from multiprocessing import Queue
+from multiprocessing.managers import BaseManager, ListProxy
+
+from iro.worker import Worker
+from iro.job import Job, SMSJob
+from iro.anbieter.anbieter import anbieter
+from iro.anbieter.content import SMS
+from iro.providerlist import Providerlist
+
+from logging.handlers import BufferingHandler
+
+class MyHandler(BufferingHandler):
+ def __init__(self,buffer=None):
+ '''BufferingHandler takes a "capacity" argument
+ so as to know when to flush. As we're overriding
+ shouldFlush anyway, we can set a capacity of zero.
+ You can call flush() manually to clear out the
+ buffer.
+ buffer: log messages to this buffer, needed f.ex for processes or threads'''
+ BufferingHandler.__init__(self, 0)
+ self.buffer=buffer
+
+ def shouldFlush(self):
+ return False
+
+ def emit(self, record):
+ if record.exc_info: #sonst geht das append schief, weil nicht picklebar
+ record.exc_info=record.exc_info[:-1]
+ self.buffer.append(record.__dict__)
+
+
+class BadJob(Job):
+ def start(self,id=None):
+ Job.start(self,id)
+ raise Exception("Error")
+
+#einen Manager anlegen, der Job und eine Liste anbietet
+class MyManager(BaseManager):
+ pass
+MyManager.register('Job', Job)
+MyManager.register('SMSJob', SMSJob)
+MyManager.register('BadJob', BadJob)
+MyManager.register('list', list, ListProxy)
+MyManager.register('Providerlist',Providerlist)
+
+class TestWorker(unittest.TestCase):
+ def setUp(self):
+ #erstelle eine Queue&Manager
+ self.manager = MyManager()
+ self.queue = Queue()
+
+ #manager starten, damit wir Logging anpassen können
+ self.manager.start()
+ self.setUpLogging()
+
+ self.providerlist=self.manager.Providerlist()
+ self.providerlist.add("test", anbieter() , ["sms", ])
+
+ #eigentlich Workerprocess starten
+ self.worker= Worker(self.queue)
+ self.worker.start()
+
+ def tearDown(self):
+ #Thread&Queue stoppen
+ self.stop()
+
+ #Logging änderungen rückgängig
+ self.tearDownLogging()
+ self.manager.shutdown()
+
+ def stop(self):
+ self.queue.close()
+ self.queue.join_thread()
+ self.worker.terminate()
+
+
+ def setUpLogging(self):
+ '''Logging so umbasteln, das wir alle logging Meldung in self.buf sind'''
+ #wir brauchen eine threadsichere liste
+ self.buffer=self.manager.list()
+
+ #Handler erstellen, der in den Buffer schreibt
+ self.handler = h = MyHandler(self.buffer)
+ self.logger =l= logging.getLogger()
+
+ #Level anpassen
+ l.setLevel(logging.DEBUG)
+ h.setLevel(logging.DEBUG)
+ l.addHandler(h)
+
+ def tearDownLogging(self):
+ '''crazy logging hacks wieder entfernen'''
+ self.logger.removeHandler(self.handler)
+ self.logger.setLevel(logging.NOTSET)
+ self.handler.close()
+
+
+ def testJob(self):
+ '''einen Job verarbeiten'''
+ job=self.manager.Job(None,None,"test")
+ self.assertEqual(job.getStatus(),("init",{}))
+ self.queue.put(job)
+ sleep(.1)
+ self.stop()
+ self.assertEqual(job.getStatus(),("started",{}))
+ self.assertEqual([(l['levelno'],l['msg']) for l in self.buffer if l['name']=="iro.worker"],
+ [(20,'Workerprocess läuft nun...'),
+ (20,'ein neuer Job(1)'),
+ (20,'Job(1) fertig ;)')])
+
+ def testBadJob(self):
+ '''einen Job verarbeiten, der fehlschlägt'''
+ job=self.manager.BadJob(None,None,"test")
+ self.assertEqual(job.getStatus(),("init",{}))
+ self.queue.put(job)
+ sleep(.1)
+ self.stop()
+ self.assertEqual(job.getStatus(),("error",{}))
+ self.assertEqual([(l['levelno'],l['msg']) for l in self.buffer if l['name']=="iro.worker"],
+ [(20,'Workerprocess läuft nun...'),
+ (20,'ein neuer Job(1)'),
+ (40,'Job(1) fehlgeschlagen :(')])
+ error=Exception('Error')
+ self.assertEqual(self.buffer[-1]['exc_info'][0],type(error))
+ self.assertEqual(str(self.buffer[-1]['exc_info'][1]),str(error))
+
+ def testSMSJob(self):
+ job=self.manager.SMSJob(self.providerlist, "test", "name", SMS("message"),[012345])
+ self.assertEqual(job.getStatus(),("init",{}))
+ self.queue.put(job)
+ sleep(.1)
+ self.stop()
+ self.assertEqual(job.getStatus(),("sended",{'failed': [], 'good': []}))
+ self.assertEqual([(l['levelno'],l['msg']) for l in self.buffer if l['name']=="iro.worker"],
+ [(20,'Workerprocess läuft nun...'),
+ (20,'ein neuer Job(1)'),
+ (20,'Job(1) fertig ;)')])
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testXMLRPCServer.py Wed Dec 21 21:51:47 2011 +0100
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+
+import xmlrpclib
+from stopableServer import init_server
+
+class TestServer(unittest.TestCase):
+
+ def setUp(self):
+ self.i = init_server()
+ self.serv=self.i.serv
+
+ self.serv.start()
+
+ def tearDown(self):
+ self.serv.stop()
+
+ def testLogin(self):
+ self.assertEqual(xmlrpclib.Server("https://test:test@localhost:8000").status(), {})
+ self.assertEqual(xmlrpclib.Server("https://test2:test2@localhost:8000").status(), {})
+ self.assertRaises(xmlrpclib.ProtocolError, xmlrpclib.Server("https://test2:test@localhost:8000").status)
+ self.assertRaises(xmlrpclib.ProtocolError,xmlrpclib.Server ("https://test:test2@localhost:8000").status)
+
+ def testsendSMS(self):
+ servstr="https://test:test@localhost:8000"
+ client=xmlrpclib.Server(servstr)
+ id=client.startSMS("test",["01234", ] )
+ self.assertEqual(client.status(id),{id: {'status': ['init',{}], 'name': 'test'}} )
+
+ def testTwoUser(self):
+ u1="https://test:test@localhost:8000"
+ u2="https://test2:test2@localhost:8000"
+ admin="https://admin:admin@localhost:8000"
+ client1=xmlrpclib.Server(u1)
+ client2=xmlrpclib.Server(u2)
+ admin=xmlrpclib.Server(admin)
+ id1=client1.startSMS("test",["01234"] )
+ self.assertEqual(client2.status(),{} )
+ self.assertEqual(admin.status(id1),{id1: {'status': ['init', {}], 'name': 'test'}} )
+ id2=client2.startSMS("test2",["01234"] )
+ self.assertNotEqual(id1, id2)
+ self.assertEqual(client1.status(),{id1: {'status': ['init', {}], 'name': 'test'}})
+ self.assertEqual(client2.status(),{id2: {'status': ['init', {}], 'name': 'test2'}})
+ self.assertEqual(admin.status(),{id1: {'status': ['init', {}], 'name': 'test'},
+ id2: {'status': ['init', {}], 'name': 'test2'}} )
+
+ self.assertEqual(client2.status(id1), {})
+ self.assertEqual(client1.status(id2), {})
+
+ def testGetProvider(self):
+ servstr="https://test:test@localhost:8000"
+ client=xmlrpclib.Server(servstr)
+ self.assertEqual(client.getProvider("sms"), ["fax.de","geonet", "sipgate", "smstrade"])
+ self.assertEqual(client.getProvider("fax"), ["fax.de","geonet", "sipgate"])
+ self.assertEqual(client.getProvider("mail"), ["localhost"])
+
+ self.assertRaises(xmlrpclib.ProtocolError,client.getProvider, "temp")
+
+ def testGetDefault(self):
+ servstr="https://test:test@localhost:8000"
+ client=xmlrpclib.Server(servstr)
+ self.assertEqual(client.getDefaultProvider("sms"), "smstrade")
+ self.assertEqual(client.getDefaultProvider("fax"),"sipgate")
+ self.assertEqual(client.getDefaultProvider("mail"), "localhost")
+
+ self.assertRaises(xmlrpclib.ProtocolError,client.getDefaultProvider, "temp")
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/testloglock.py Wed Dec 21 21:51:47 2011 +0100
@@ -0,0 +1,98 @@
+
+import unittest
+import os
+import tempfile
+import signal
+import threading
+import time
+
+class testLogLock(unittest.TestCase):
+ def test_ThreadingAndLocks(self):
+ #create a thread, have that thread grab a lock, and sleep.
+ fd, file = tempfile.mkstemp('.nonlog')
+ _lock = threading.RLock()
+ os.close(fd)
+ def locker():
+ os.write(fd, 'Thread acquiring lock\n')
+ _lock.acquire()
+ os.write(fd, 'Thread acquired lock\n')
+ time.sleep(0.4)
+ os.write(fd, 'Thread releasing lock\n')
+ _lock.release()
+ os.write(fd, 'Thread released lock\n')
+
+ #Then in the main thread, throw a signal to self
+ def handleSignal(sigNum, frame):
+ os.write(fd, 'Main Thread acquiring lock\n')
+ lock = False
+ endtime = time.time() + 1.0
+ while not lock:
+ lock = _lock.acquire(blocking=0)
+ time.sleep(0.01)
+ if time.time() > endtime:
+ break
+ if not lock:
+ os.write(fd, 'Main Thread could not acquire lock\n')
+ return
+ os.write(fd, 'Main Thread acquired lock\n')
+ os.write(fd, 'Main Thread releasing lock\n')
+ _lock.release()
+ os.write(fd, 'Main Thread released lock\n')
+
+ sighndlr = signal.signal(signal.SIGUSR1, handleSignal)
+ try:
+ fd = os.open(file, os.O_SYNC | os.O_WRONLY | os.O_CREAT)
+ thread = threading.Thread(target=locker)
+ thread.start()
+ time.sleep(0.1)
+ os.kill(os.getpid(), signal.SIGUSR1)
+ thread.join()
+
+ #check the results
+ os.close(fd)
+ fileconts = open(file, 'r').read()
+ self.assertTrue('Main Thread released lock' in fileconts)
+ self.assertEqual(fileconts,
+ '''Thread acquiring lock
+Thread acquired lock
+Main Thread acquiring lock
+Thread releasing lock
+Thread released lock
+Main Thread acquired lock
+Main Thread releasing lock
+Main Thread released lock
+''')
+
+ #Now try after acquiring the lock from the main thread
+ fd = os.open(file, os.O_SYNC | os.O_WRONLY | os.O_CREAT)
+ _lock.acquire()
+ thread = threading.Thread(target=locker)
+ thread.start()
+ time.sleep(0.1)
+ os.kill(os.getpid(), signal.SIGUSR1)
+ _lock.release()
+ thread.join()
+ os.close(fd)
+ fileconts = open(file, 'r').read()
+ self.assertEqual(fileconts,
+ '''Thread acquiring lock
+Main Thread acquiring lock
+Main Thread acquired lock
+Main Thread releasing lock
+Main Thread released lock
+Thread acquired lock
+Thread releasing lock
+Thread released lock
+''')
+
+ finally:
+ signal.signal(signal.SIGUSR1, sighndlr)
+ try:
+ os.close(fd)
+ except OSError:
+ pass
+ try:
+ os.unlink(file)
+ except OSError:
+ pass
+