# HG changeset patch # User Sandro Knauß # Date 1327695701 -3600 # Node ID 351a02310dd81631a2d5c8c9a88fe28eeceacae5 # Parent 48c70425bf6cb3a02b7584d56bf3ae8963c57168 model restructuring diff -r 48c70425bf6c -r 351a02310dd8 iro/controller/viewinterface.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): diff -r 48c70425bf6c -r 351a02310dd8 iro/main.py --- 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() diff -r 48c70425bf6c -r 351a02310dd8 iro/model/__init__.py --- 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 + + + + diff -r 48c70425bf6c -r 351a02310dd8 iro/model/dbdefer.py --- /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 + + diff -r 48c70425bf6c -r 351a02310dd8 iro/model/decorators.py --- 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 diff -r 48c70425bf6c -r 351a02310dd8 iro/model/pool.py --- /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) + + diff -r 48c70425bf6c -r 351a02310dd8 iro/model/schema.py --- 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. provider, typ und route 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): diff -r 48c70425bf6c -r 351a02310dd8 iro/model/user.py --- 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) diff -r 48c70425bf6c -r 351a02310dd8 iro/model/utils.py --- 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 diff -r 48c70425bf6c -r 351a02310dd8 tests/xmlrpc.py --- 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, "") self.failUnlessEqual(self.__rpc2().status('abcdef123456789','abcde'), ["",'abcde',False]) @@ -73,12 +84,11 @@ self.failUnlessEqual(self.__rpc2().status('abcdef123456789', '', 0), "") self.failUnlessEqual(self.__rpc2().status('abcdef123456789', '', 1), ["", '', 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()