Still very initial scaffolding

This commit is contained in:
tanshu 2015-06-09 17:04:58 +05:30
parent 9000ecdce5
commit 0c6601576f
7 changed files with 401 additions and 8 deletions

View File

@ -11,12 +11,8 @@ pyramid.debug_authorization = false
pyramid.debug_notfound = false pyramid.debug_notfound = false
pyramid.debug_routematch = false pyramid.debug_routematch = false
pyramid.default_locale_name = en pyramid.default_locale_name = en
pyramid.includes =
pyramid_debugtoolbar
# By default, the toolbar only appears for clients from IP addresses sqlalchemy.url = postgresql://postgres:123456@localhost:5432/pics
# '127.0.0.1' and '::1'.
# debugtoolbar.hosts = 127.0.0.1 ::1
### ###
# wsgi server configuration # wsgi server configuration

View File

@ -10,9 +10,10 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [ requires = [
'pyramid', 'pyramid',
'pyramid_chameleon',
'pyramid_debugtoolbar',
'waitress', 'waitress',
'transaction',
'zope.sqlalchemy',
'SQLAlchemy',
] ]
setup(name='Soter', setup(name='Soter',

View File

@ -1,11 +1,15 @@
from pyramid.config import Configurator from pyramid.config import Configurator
from sqlalchemy import engine_from_config
from soter.models import initialize_sql
def main(global_config, **settings): def main(global_config, **settings):
""" This function returns a Pyramid WSGI application. """ This function returns a Pyramid WSGI application.
""" """
engine = engine_from_config(settings, 'sqlalchemy.')
initialize_sql(engine)
config = Configurator(settings=settings) config = Configurator(settings=settings)
config.include('pyramid_chameleon')
config.add_static_view('static', 'static', cache_max_age=3600) config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/') config.add_route('home', '/')

52
soter/models/__init__.py Normal file
View File

@ -0,0 +1,52 @@
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), expire_on_commit=False))
Base = declarative_base()
def initialize_sql(engine):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
if not schema_exists(engine):
fixtures(engine)
def schema_exists(engine):
from soter.models.master import DbSetting
with engine.connect() as connection:
return engine.dialect.has_table(connection, DbSetting.__tablename__)
def fixtures(engine):
import transaction
import uuid
from soter.models.master import Album, DbSetting, Picture, Tag, picture_tag
from soter.models.auth import Permission, Role, User, role_permission, role_user
Base.metadata.create_all(engine)
user = User('Admin', 'Administrator', 'soter@tanshu.com', '123456', False,
uuid.UUID('8de98592-76d9-c74d-bb3f-d6184d388b5a'))
DBSession.add(user)
roles = [Role('Owner', uuid.UUID('52e08c0c-048a-784f-be10-6e129ad4b5d4'))]
for role in roles:
DBSession.add(role)
user.roles.append(role)
permissions = [Permission('Attendance', uuid.UUID('09d05434-a09a-fa45-963b-769a2e3fc667')),
Permission('Trial Balance', uuid.UUID('3b099fec-ddc5-4243-b30e-afb78d9ca14a')),
Permission('Cash Flow', uuid.UUID('c4d3ae29-420b-ea4c-ae90-00a356263fd9')),
Permission('Cost Centers', uuid.UUID('6fcc1a20-6aec-e840-b334-1632b34aeab8'))]
for permission in permissions:
DBSession.add(permission)
roles[0].permissions.append(permission)
transaction.commit()

149
soter/models/auth.py Normal file
View File

@ -0,0 +1,149 @@
from hashlib import sha256
import uuid
from sqlalchemy import Column, Unicode, Boolean, Table, ForeignKey, UniqueConstraint
from sqlalchemy.orm import relationship, synonym
from soter.models import Base, DBSession
from soter.models.guidtype import GUID
def encrypt(val):
return sha256(val.encode('utf-8') + "Salt".encode('utf-8')).hexdigest()
role_user = Table(
'role_users', Base.metadata,
Column('id', GUID(), primary_key=True, default=uuid.uuid4),
Column('user_id', GUID(), ForeignKey('users.id')),
Column('role_id', GUID(), ForeignKey('roles.id')),
UniqueConstraint('user_id', 'role_id')
)
role_permission = Table(
'role_permissions', Base.metadata,
Column('id', GUID(), primary_key=True, default=uuid.uuid4),
Column('permission_id', GUID(), ForeignKey('permissions.id')),
Column('role_id', GUID(), ForeignKey('roles.id')),
UniqueConstraint('permission_id', 'role_id')
)
class User(Base):
__tablename__ = 'users'
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
name = Column('name', Unicode(255), unique=True)
display_name = Column('display_name', Unicode(255))
email = Column('email', Unicode(255), unique=True)
_password = Column('password', Unicode(64))
locked_out = Column('locked_out', Boolean)
roles = relationship("Role", secondary=role_user)
def _get_password(self):
return self._password
def _set_password(self, password):
self._password = encrypt(password)
password = property(_get_password, _set_password)
password = synonym('_password', descriptor=password)
@property
def __name__(self):
return self.name
def __init__(self, name=None, display_name=None, email=None, password=None, locked_out=None, id=None):
self.name = name
self.display_name = display_name
self.email = email
self.password = password
self.locked_out = locked_out
self.id = id
@classmethod
def by_name(cls, name):
if not name:
return None
return DBSession.query(cls).filter(cls.name.ilike(name)).first()
@classmethod
def by_email(cls, email):
if not email:
return None
return DBSession.query(cls).filter(cls.email.ilike(email)).first()
@classmethod
def by_id(cls, id):
if not isinstance(id, uuid.UUID):
id = uuid.UUID(id)
return DBSession.query(cls).filter(cls.id == id).one()
@classmethod
def auth(cls, name, password):
if password is None:
return False, None
user = cls.by_name(name)
if not user:
return False, None
if user.password != encrypt(password) or user.locked_out:
return False, None
else:
return True, user
@classmethod
def list(cls):
return DBSession.query(cls).order_by(cls.name).all()
@classmethod
def query(cls):
return DBSession.query(cls)
@classmethod
def filtered_list(cls, name):
query = DBSession.query(cls)
for item in name.split():
query = query.filter(cls.name.ilike('%' + item + '%'))
return query.order_by(cls.name)
class Role(Base):
__tablename__ = 'roles'
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
name = Column('name', Unicode(255), unique=True)
def __init__(self, name=None, id=None):
self.name = name
self.id = id
@classmethod
def by_id(cls, id):
return DBSession.query(cls).filter(cls.id == id).one()
@classmethod
def list(cls):
return DBSession.query(cls).order_by(cls.name).all()
class Permission(Base):
__tablename__ = 'permissions'
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
name = Column('name', Unicode(255), unique=True)
roles = relationship("Role", secondary=role_permission, backref="permissions")
def __init__(self, name=None, id=None):
self.name = name
self.id = id
@classmethod
def list(cls):
return DBSession.query(cls).order_by(cls.name).all()
@classmethod
def by_id(cls, id):
return DBSession.query(cls).filter(cls.id == id).one()

56
soter/models/guidtype.py Normal file
View File

@ -0,0 +1,56 @@
from sqlalchemy.types import TypeDecorator, CHAR, Binary
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.dialects.sqlite import BLOB
import uuid
class GUID(TypeDecorator):
"""Platform-independent GUID type.
Uses Postgresql's UUID type, otherwise uses
CHAR(32), storing as stringified hex values.
"""
impl = Binary
# if dialect.value == 'postgresql':
# impl = CHAR
# elif dialect.value == 'mysql':
# impl = MSBinary
# elif dialect.valie == 'sqlite':
# impl = Binary
# else:
# impl = Binary
def load_dialect_impl(self, dialect):
if dialect.name == 'postgresql':
return dialect.type_descriptor(UUID())
elif dialect.name == 'sqlite':
return dialect.type_descriptor(BLOB())
else:
return dialect.type_descriptor(CHAR(32))
def process_bind_param(self, value, dialect):
if value is None:
return None
elif dialect.name == 'postgresql':
return str(value)
elif not isinstance(value,uuid.UUID):
raise ValueError('value %s is not a valid uuid.UUID' % value)
else:
return value.bytes
# if not isinstance(value, uuid.UUID):
# return "%.32x" % uuid.UUID(value)
# else:
# # hexstring
# return "%.32x" % value
def process_result_value(self,value,dialect=None):
if value is None:
return None
elif isinstance(value, bytes):
return uuid.UUID(bytes=value)
else:
return uuid.UUID(value)
def is_mutable(self):
return False

135
soter/models/master.py Normal file
View File

@ -0,0 +1,135 @@
import uuid
from sqlalchemy import Column, PickleType, Unicode, ForeignKey, Boolean, Table, UniqueConstraint
from sqlalchemy.orm import relationship
from soter.models import Base, DBSession
from soter.models.guidtype import GUID
class DbSetting(Base):
__tablename__ = 'settings'
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
name = Column('name', Unicode(255), unique=True, nullable=False)
data = Column('data', PickleType)
def __init__(self, id=None, name=None, data=None):
self.id = id
self.name = name
self.data = data
@classmethod
def by_id(cls, id):
if not isinstance(id, uuid.UUID):
id = uuid.UUID(id)
return DBSession.query(cls).filter(cls.id == id).first()
@classmethod
def by_name(cls, name):
return DBSession.query(cls).filter(cls.name == name).first()
@classmethod
def list(cls):
return DBSession.query(cls).order_by(cls.name).all()
picture_tag = Table(
'picture_tags', Base.metadata,
Column('id', GUID(), primary_key=True, default=uuid.uuid4),
Column('picture_id', GUID(), ForeignKey('pictures.id'), nullable=False),
Column('tag_id', GUID(), ForeignKey('tags.id'), nullable=False),
UniqueConstraint('picture_id', 'tag_id')
)
class Album(Base):
__tablename__ = 'albums'
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
name = Column('name', Unicode(255), unique=True)
user_id = Column('user_id', GUID(), ForeignKey('users.id'), nullable=False)
is_fixture = Column('is_fixture', Boolean, nullable=False)
is_public = Column('is_public', Boolean, nullable=False)
user = relationship('User', primaryjoin="User.id==Album.user_id", cascade=None)
pictures = relationship('Picture', backref='album')
def __init__(self, name, user_id, is_public, id=None, is_fixture=False):
self.name = name
self.user_id = user_id
self.is_public = is_public
self.id = id
self.is_fixture = is_fixture
@classmethod
def list(cls):
return DBSession.query(cls).order_by(cls.name)
@classmethod
def by_id(cls, id):
return DBSession.query(cls).filter(cls.id == id).first()
@classmethod
def menu_item(cls):
return uuid.UUID('dad46805-f577-4e5b-8073-9b788e0173fc')
@classmethod
def semi(cls):
return uuid.UUID('e6bf81b9-1e9b-499f-81d5-ab5662e9d9b1')
class Picture(Base):
__tablename__ = 'pictures'
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
name = Column('name', Unicode(255), unique=True)
user_id = Column('user_id', GUID(), ForeignKey('users.id'), nullable=False)
album_id = Column('album_id', GUID(), ForeignKey('albums.id'), nullable=False)
is_public = Column('is_public', Boolean, nullable=False)
user = relationship('User', primaryjoin="User.id==Picture.user_id", cascade=None)
def __init__(self, name, user_id, is_public, id=None, is_fixture=False):
self.name = name
self.user_id = user_id
self.is_public = is_public
self.id = id
self.is_fixture = is_fixture
@classmethod
def list(cls):
return DBSession.query(cls).order_by(cls.name)
@classmethod
def by_id(cls, id):
return DBSession.query(cls).filter(cls.id == id).first()
@classmethod
def menu_item(cls):
return uuid.UUID('dad46805-f577-4e5b-8073-9b788e0173fc')
@classmethod
def semi(cls):
return uuid.UUID('e6bf81b9-1e9b-499f-81d5-ab5662e9d9b1')
class Tag(Base):
__tablename__ = 'tags'
id = Column('id', GUID(), primary_key=True, default=uuid.uuid4)
name = Column('name', Unicode(100), unique=True, nullable=False)
def __init__(self, name=None):
self.name = name
@classmethod
def by_id(cls, id):
return DBSession.query(cls).filter(cls.id == id).first()
@classmethod
def by_name(cls, name):
return DBSession.query(cls).filter(cls.name == name).first()
@classmethod
def list(cls):
return DBSession.query(cls).all()