model restructuring devel
authorSandro Knauß <knauss@netzguerilla.net>
Fri, 27 Jan 2012 21:21:41 +0100
branchdevel
changeset 117 351a02310dd8
parent 116 48c70425bf6c
child 118 e16c0250c974
model restructuring
iro/controller/viewinterface.py
iro/main.py
iro/model/__init__.py
iro/model/dbdefer.py
iro/model/decorators.py
iro/model/pool.py
iro/model/schema.py
iro/model/user.py
iro/model/utils.py
tests/xmlrpc.py
--- a/iro/controller/viewinterface.py	Fri Jan 27 21:14:18 2012 +0100
+++ b/iro/controller/viewinterface.py	Fri Jan 27 21:21:41 2012 +0100
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
-from ..model.decorators import vUser, vRoute
-from ..model.utils import vTyp
+from ..model.decorators import vUser, vRoute, dbdefer, vTyp
+from ..model.schema import User
 from ..validate import validate, vBool, vHash, vTel, vEmail
 
 class Interface(object):
@@ -108,7 +108,8 @@
        
     @validate(kwd="typ", func=vTyp)
     @vUser
-    def routes(self, user, typ):
+    @dbdefer
+    def routes(self, user, typ, session):
         '''Gibt eine Liste aller verfügbaren Provider zurück.
 
         Keywords:
@@ -120,11 +121,13 @@
         providerlist[list]: Eine Liste aller möglichen Provider
 
         '''
-        return ""
+        user = session.query(User).filter_by(name=user.name).first()
+        return [u.offer_name for u in user.rights]
         
     @validate(kwd="typ", func=vTyp)
     @vUser
-    def defaultRoute(self, user, typ):
+    @dbdefer
+    def defaultRoute(self, user, typ, session):
         '''Gibt den Standardprovider zurück.
  
         Keywords:
@@ -137,7 +140,8 @@
 
 
         '''
-        return ""
+        user = session.query(User).filter_by(name=user.name).first()
+        return [u.offer_name for u in user.rights if u.isDefault == True]
 
     @vUser
     def statistic(self, user):
--- a/iro/main.py	Fri Jan 27 21:14:18 2012 +0100
+++ b/iro/main.py	Fri Jan 27 21:21:41 2012 +0100
@@ -5,8 +5,7 @@
 
 import logging
 
-from .model.user import setEngine
-from .model.utils import POOL_SIZE as DB_POOL_SIZE, startPool
+from .model import setEngine,startPool, POOL_SIZE
 from .view import xmlrpc
 
 def runReactor(reactor, engine, root):
@@ -20,7 +19,7 @@
 
 if __name__ == '__main__':
     engine = create_engine('mysql://test:test@localhost/test',
-           poolclass = pool.SingletonThreadPool,  pool_size=DB_POOL_SIZE, )
+           poolclass = pool.SingletonThreadPool,  pool_size=POOL_SIZE, )
 
 
     root = resource.Resource()
--- a/iro/model/__init__.py	Fri Jan 27 21:14:18 2012 +0100
+++ b/iro/model/__init__.py	Fri Jan 27 21:21:41 2012 +0100
@@ -1,3 +1,10 @@
 import schema
 import user
 import utils
+
+from dbdefer import setEngine
+from pool import startPool,POOL_SIZE
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iro/model/dbdefer.py	Fri Jan 27 21:21:41 2012 +0100
@@ -0,0 +1,39 @@
+from decorator import FunctionMaker
+
+from .pool import runInDBPool
+from .utils import WithSession
+
+import inspect
+
+class DBDefer(object):
+    '''a twisted sqlalchemy connector this Decorator adds a session parameter, with a valid session connection'''
+    def __init__(self, engine, autocommit=False):
+        self.autocommit=autocommit
+        self.engine = engine
+
+    def __call__(self, func):
+        @runInDBPool
+        def wrapper(func,*args, **kwargs):
+            with WithSession(self.engine, self.autocommit) as session:
+                return func(*args, session=session, **kwargs)
+
+        caller=func
+        args =[i for i in inspect.getargspec(caller)[0] if i != "session" ]
+        sargs=", ".join(args)        
+        evaldict = caller.func_globals.copy()
+        evaldict['_call_'] = func
+        evaldict['decorator'] = wrapper
+        wrap = FunctionMaker.create(
+              '%s(%s)' % (caller.__name__, sargs), 
+              'return decorator(_call_, %s)' % sargs,
+              evaldict, undecorated=caller, __wrapped__=caller,
+              doc=caller.__doc__, module=caller.__module__, addsource=True)
+        return wrap
+
+dbdefer=DBDefer(None)
+
+def setEngine(engine,autocommit=False): 
+    dbdefer.engine = engine
+    dbdefer.autocommit = autocommit
+
+
--- a/iro/model/decorators.py	Fri Jan 27 21:14:18 2012 +0100
+++ b/iro/model/decorators.py	Fri Jan 27 21:21:41 2012 +0100
@@ -1,10 +1,25 @@
 from decorator import decorator
 
-from .utils import DBDefer
 from .user import vUser
+from .dbdefer import  dbdefer
+from .pool import runInDBPool
+from .schema import Offer
+
+from ..error import ValidateException
 
 def vRoute(typ):
     @decorator
     def wrapper(f, *args, **kwargs):
         return f(*args, **kwargs)
     return wrapper
+
+from .utils import WithSession
+#@dbdefer
+def vTyp(value,field):
+    with WithSession(dbdefer.engine) as session:
+        for typ in session.query(Offer.typ).distinct():
+            if value == typ[0]:
+                break
+        else:
+            raise ValidateException(field=field,msg='Typ is not valid.')
+    return value
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iro/model/pool.py	Fri Jan 27 21:21:41 2012 +0100
@@ -0,0 +1,25 @@
+from decorator import decorator
+from twisted.python.threadpool import ThreadPool
+from twisted.internet import threads
+
+POOL_SIZE=5     #how many threads should the db connector pool should have
+
+class Data:
+    def __init__(self):
+        self.pool =  ThreadPool(minthreads=1, maxthreads=POOL_SIZE, name='database')
+        self.reactor = None
+
+data = Data()
+#a valid dbDefer decorator
+
+def startPool(reactor): 
+    data.pool.start()
+    data.reactor = reactor
+    data.reactor.addSystemEventTrigger('before', 'shutdown', data.pool.stop)
+
+@decorator
+def runInDBPool(f,*args,**kwargs):
+    """Decorator to run DB queries in Twisted's thread pool"""
+    return threads.deferToThreadPool(data.reactor, data.pool, f, *args, **kwargs)
+
+
--- a/iro/model/schema.py	Fri Jan 27 21:14:18 2012 +0100
+++ b/iro/model/schema.py	Fri Jan 27 21:21:41 2012 +0100
@@ -19,6 +19,10 @@
     isDefault = Column(Boolean)
     offer = relationship("Offer")
 
+    def __init__(self, offer, default=False):
+        self.offer = offer
+        self.isDefault = default
+
 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"
@@ -26,6 +30,12 @@
     provider = Column(String(100))
     route = Column(String(100))
     typ = Column(String(100))
+
+    def __init__(self, name, provider, route, typ):
+        self.name = name
+        self.provider = provider
+        self.route = route
+        self.typ = typ
     
 
 class Message(Base):
--- a/iro/model/user.py	Fri Jan 27 21:14:18 2012 +0100
+++ b/iro/model/user.py	Fri Jan 27 21:21:41 2012 +0100
@@ -2,16 +2,11 @@
 from decorator import decorator
 
 from .schema import User
-from .utils import DBDefer
+from .dbdefer import dbdefer
+
 from ..validate import validate, vHash
 from ..error import UserNotFound, InterfaceException
 
-dbdefer=DBDefer(None)
-
-def setEngine(engine,autocommit=False): 
-    dbdefer.engine = engine
-    dbdefer.autocommit = autocommit
-
 
 @dbdefer
 @validate(kwd="apikey", func=vHash, minlength=15, maxlength=15)
--- a/iro/model/utils.py	Fri Jan 27 21:14:18 2012 +0100
+++ b/iro/model/utils.py	Fri Jan 27 21:21:41 2012 +0100
@@ -1,32 +1,5 @@
 from sqlalchemy.orm import sessionmaker
 
-from twisted.internet import threads
-from twisted.python.threadpool import ThreadPool
-
-from functools import wraps
-
-POOL_SIZE=5     #how many threads should the db connector pool should have
-
-class Data:
-    def __init__(self):
-        self.pool =  ThreadPool(minthreads=1, maxthreads=POOL_SIZE, name='database')
-        self.reactor = None
-
-d = Data()
-
-def startPool(reactor): 
-    d.pool.start()
-    d.reactor = reactor
-    d.reactor.addSystemEventTrigger('before', 'shutdown', d.pool.stop)
-
-def run_in_db_thread(f):
-    """Decorator to run DB queries in Twisted's thread pool"""
-    @wraps(f)
-    def wrapper(*args, **kwargs):
-        return threads.deferToThreadPool(d.reactor, d.pool, f, *args, **kwargs)
-    return wrapper
-
-
 class WithSession(object):
     '''a with statement for a database session connection'''
     def __init__(self, engine, autocommit=False):
@@ -45,19 +18,5 @@
             self.session.rollback()
         self.session.close()
 
-class DBDefer(object):
-    '''a twisted sqlalchemy connector this Decorator adds a session parameter, with a valid session connection'''
-    def __init__(self, engine, autocommit=False):
-        self.autocommit=autocommit
-        self.engine = engine
 
-    def __call__(self, func):
-        @run_in_db_thread
-        @wraps(func)
-        def wrapper(*args, **kwargs):
-            with WithSession(self.engine, self.autocommit) as session:
-                return func(*args, session=session, **kwargs)
-        return wrapper
 
-def vTyp(value,field):
-    return value
--- a/tests/xmlrpc.py	Fri Jan 27 21:14:18 2012 +0100
+++ b/tests/xmlrpc.py	Fri Jan 27 21:21:41 2012 +0100
@@ -6,9 +6,11 @@
 from tempfile import mkdtemp
 import shutil
 
-from iro.model.utils import WithSession, POOL_SIZE as DB_POOL_SIZE
+from iro.model.utils import WithSession
+from iro.model import POOL_SIZE as DB_POOL_SIZE
 
-from iro.model.schema import User, Base
+from iro.model.schema import User, Base, Offer, Userright
+import iro.model.schema as schema
 
 from ngdatabase.mysql import Server, createConfig, Database
 
@@ -46,6 +48,12 @@
         self.__debug().stop()
         time.sleep(.2)
         self.s.join()
+        self.__cleanDB()
+
+    def __cleanDB(self):
+        with WithSession(md.engine, autocommit=True) as session:
+            for table in schema.__tables__:
+                session.query(getattr(schema,table)).delete()
 
     def __debug(self):
         return xServer('http://localhost:7080/debug')
@@ -64,6 +72,9 @@
         self.failUnlessEqual(ret, ['listMethods', 'status', 'stop', 'sms', 'fax', 'mail', 'routes', 'defaultRoute', 'statistic'])
 
     def testStatus(self):
+        ''' test the status function'''
+        with WithSession(md.engine, autocommit=True) as session:
+            session.add(User(name='test',apikey='abcdef123456789'))
         ret = self.__rpc2().status('abcdef123456789')
         self.failUnlessEqual(ret, "<User('test','abcdef123456789')>")
         self.failUnlessEqual(self.__rpc2().status('abcdef123456789','abcde'), ["<User('test','abcdef123456789')>",'abcde',False])
@@ -73,12 +84,11 @@
         self.failUnlessEqual(self.__rpc2().status('abcdef123456789', '', 0), "<User('test','abcdef123456789')>")
         self.failUnlessEqual(self.__rpc2().status('abcdef123456789', '', 1), ["<User('test','abcdef123456789')>", '', True])
 
-
     def testNoSuchUser(self):
         '''a unknown user should raise a UserNotNound Exception
         bewcause xmlrpc only has a Fault exception this Exception has to be deliverd through a xmlrpclib.Fault Exception'''
         with self.assertRaises(Fault) as fault:
-            self.__rpc2().status('abcdef123456788')
+            self.__rpc2().status('abcdef123456789')
         exc = fault.exception
         unf=IroError.UserNotFound()
         self.failUnlessEqual(exc.faultCode, unf.code)
@@ -100,6 +110,28 @@
         self.failUnlessEqual(exc.faultCode, 700)
         self.failUnlessEqual(exc.faultString, "Validation of 'apikey' failed.")
 
+    def testRoutes(self):
+        '''test the route function'''
+        with WithSession(md.engine, autocommit=True) as session:
+            u=User(name='test',apikey='abcdef123456789')
+            o=Offer(name="sipgate_basic", provider="sipgate", route="basic", typ="sms")
+            u.rights.append(Userright(o)) 
+            session.add(u)
+        self.failUnlessEqual(self.__rpc2().routes('abcdef123456789','sms'),['sipgate_basic'])
+
+        with self.assertRaises(Fault) as fault:
+            self.__rpc2().routes('abcdef123456789','fax')
+        exc = fault.exception
+        self.failUnlessEqual(exc.faultCode, 700)
+        self.failUnlessEqual(exc.faultString, "Typ is not valid.")
+        
+        with WithSession(md.engine, autocommit=True) as session:
+            o=Offer(name="sipgate_plus", provider="sipgate", route="plus", typ="sms")
+            u = session.query(User).filter_by(name="test").first()
+            u.rights.append(Userright(o)) 
+            session.commit()
+        self.failUnlessEqual(self.__rpc2().routes('abcdef123456789','sms'),['sipgate_basic','sipgate_plus'])
+
 def startReactor(engine):
     """starts the Rector with a special debug Clild, so that the reactor can be stopped remotly. """
     from twisted.internet import reactor
@@ -136,8 +168,6 @@
         self.server.start()
         self.db.create()
         Base.metadata.create_all(self.engine)
-        with WithSession(self.engine, autocommit=True) as session:
-            session.add(User(name='test',apikey='abcdef123456789'))
     
     def tearDown(self):
         self.server.stop()