save point (see note)
Added initial database schema and ability to install it, added init-db command to pyramid scaffold.
This commit is contained in:
		
							parent
							
								
									925cd30b96
								
							
						
					
					
						commit
						8ceb98baf4
					
				
					 29 changed files with 1116 additions and 191 deletions
				
			
		| 
						 | 
				
			
			@ -29,7 +29,7 @@
 | 
			
		|||
 | 
			
		||||
from edbob._version import __version__
 | 
			
		||||
from edbob.core import *
 | 
			
		||||
from edbob.times import *
 | 
			
		||||
from edbob.time import *
 | 
			
		||||
from edbob.files import *
 | 
			
		||||
from edbob.modules import *
 | 
			
		||||
from edbob.configuration import *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -157,7 +157,7 @@ Try '%(name)s help <command>' for more help.""" % self
 | 
			
		|||
 | 
			
		||||
        # Initialize everything...
 | 
			
		||||
        if not args.no_init:
 | 
			
		||||
            edbob.init(*(args.config_paths or []))
 | 
			
		||||
            edbob.init(self.name, *(args.config_paths or []))
 | 
			
		||||
 | 
			
		||||
            # Command line logging flags should override config.
 | 
			
		||||
            if args.verbose:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,52 +32,77 @@ from sqlalchemy.orm import sessionmaker
 | 
			
		|||
import edbob
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ['engines', 'engine', 'Session', 'metadata',
 | 
			
		||||
           'get_setting', 'save_setting']
 | 
			
		||||
 | 
			
		||||
inited = False
 | 
			
		||||
engines = None
 | 
			
		||||
engine = None
 | 
			
		||||
Session = sessionmaker()
 | 
			
		||||
metadata = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def init():
 | 
			
		||||
def init(config):
 | 
			
		||||
    """
 | 
			
		||||
    Called whenever ``'edbob.db'`` is configured to be auto-initialized.
 | 
			
		||||
    Initializes the database connection(s); called by :func:`edbob.init()` if
 | 
			
		||||
    config includes something like::
 | 
			
		||||
 | 
			
		||||
    This function is responsible for establishing the primary database engine
 | 
			
		||||
    (a ``sqlalchemy.Engine`` instance, read from config), and extending the
 | 
			
		||||
    root ``edbob`` namespace with the ORM classes (``Person``, ``User``, etc.),
 | 
			
		||||
    as well as a few other things, e.g. ``engine``, ``Session`` and
 | 
			
		||||
    ``metadata``.
 | 
			
		||||
       .. highlight:: ini
 | 
			
		||||
 | 
			
		||||
    In addition to this, if a connection to the primary database can be
 | 
			
		||||
    obtained, it will be consulted to see which extensions are active within
 | 
			
		||||
    it.  If any are found, edbob's ORM will be extended in-place accordingly.
 | 
			
		||||
       [edbob]
 | 
			
		||||
       init = ['edbob.db']
 | 
			
		||||
 | 
			
		||||
       [edbob.db]
 | 
			
		||||
       sqlalchemy.urls = {
 | 
			
		||||
               'default':       'postgresql://user:pass@localhost/edbob,
 | 
			
		||||
               }
 | 
			
		||||
 | 
			
		||||
    This function reads connection info from ``config`` and builds a dictionary
 | 
			
		||||
    or :class:`sqlalchemy.Engine` instances accordingly.  It also extends the
 | 
			
		||||
    root ``edbob`` namespace with the ORM classes (:class:`edbob.Person`,
 | 
			
		||||
    :class:`edbob.User`, etc.), as well as a few other things
 | 
			
		||||
    (e.g. :attr:`edbob.engine`, :attr:`edbob.Session`, :attr:`edbob.metadata`).
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    config = edbob.config.get_dict('edbob.db')
 | 
			
		||||
    engine = engine_from_config(config)
 | 
			
		||||
    edbob.graft(edbob, locals(), 'engine')
 | 
			
		||||
 | 
			
		||||
    Session.configure(bind=engine)
 | 
			
		||||
    edbob.graft(edbob, globals(), 'Session')
 | 
			
		||||
    
 | 
			
		||||
    import edbob.db
 | 
			
		||||
    from edbob.db import classes
 | 
			
		||||
    from edbob.db import enum
 | 
			
		||||
    from edbob.db.model import get_metadata
 | 
			
		||||
    metadata = get_metadata(bind=engine)
 | 
			
		||||
    edbob.graft(edbob, locals(), 'metadata')
 | 
			
		||||
 | 
			
		||||
    from edbob.db.mappers import make_mappers
 | 
			
		||||
    make_mappers(metadata)
 | 
			
		||||
    from edbob.db.extensions import extend_framework
 | 
			
		||||
 | 
			
		||||
    from edbob.db.ext import extend_framework
 | 
			
		||||
    global inited, engines, engine, metadata
 | 
			
		||||
 | 
			
		||||
    keys = config.get('edbob.db', 'sqlalchemy.keys')
 | 
			
		||||
    if keys:
 | 
			
		||||
        keys = keys.split()
 | 
			
		||||
    else:
 | 
			
		||||
        keys = ['default']
 | 
			
		||||
 | 
			
		||||
    engines = {}
 | 
			
		||||
    cfg = config.get_dict('edbob.db')
 | 
			
		||||
    for key in keys:
 | 
			
		||||
        try:
 | 
			
		||||
            engines[key] = engine_from_config(cfg, 'sqlalchemy.%s.' % key)
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            if key == 'default':
 | 
			
		||||
                try:
 | 
			
		||||
                    engines[key] = engine_from_config(cfg)
 | 
			
		||||
                except KeyError:
 | 
			
		||||
                    pass
 | 
			
		||||
 | 
			
		||||
    engine = engines.get('default')
 | 
			
		||||
    if engine:
 | 
			
		||||
        Session.configure(bind=engine)
 | 
			
		||||
    
 | 
			
		||||
    metadata = get_metadata(bind=engine)
 | 
			
		||||
    make_mappers(metadata)
 | 
			
		||||
    extend_framework()
 | 
			
		||||
 | 
			
		||||
    # Note that we extend the framework before we graft the 'classes' module
 | 
			
		||||
    # contents, since extensions may graft things to that module.
 | 
			
		||||
    import edbob.db.classes as classes
 | 
			
		||||
    edbob.graft(edbob, edbob.db)
 | 
			
		||||
    edbob.graft(edbob, classes)
 | 
			
		||||
 | 
			
		||||
    # Same goes for the enum module.
 | 
			
		||||
    import edbob.db.enum as enum
 | 
			
		||||
    edbob.graft(edbob, enum)
 | 
			
		||||
 | 
			
		||||
    # Add settings functions.
 | 
			
		||||
    edbob.graft(edbob, globals(), ('get_setting', 'save_setting'))
 | 
			
		||||
    inited = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_setting(name, session=None):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,15 +26,48 @@
 | 
			
		|||
``edbob.db.auth`` -- Authentication & Authorization
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import bcrypt
 | 
			
		||||
 | 
			
		||||
from sqlalchemy.orm import object_session
 | 
			
		||||
 | 
			
		||||
from edbob.db.classes import Role, User
 | 
			
		||||
import edbob
 | 
			
		||||
from edbob.db import needs_session
 | 
			
		||||
from edbob.db.classes import Permission, Role, User
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_administrator(session):
 | 
			
		||||
class BcryptAuthenticator(edbob.Object):
 | 
			
		||||
    """
 | 
			
		||||
    Returns a :class:`edbob.Role` instance representing the "Administrator"
 | 
			
		||||
    role, attached to the given ``session``.
 | 
			
		||||
    Authentication with py-bcrypt (Blowfish).
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def populate_user(self, user, password):
 | 
			
		||||
        user.salt = bcrypt.gensalt()
 | 
			
		||||
        user.password = bcrypt.hashpw(password, user.salt)
 | 
			
		||||
 | 
			
		||||
    def authenticate_user(self, user, password):
 | 
			
		||||
        return bcrypt.hashpw(password, user.salt) == user.password
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@needs_session
 | 
			
		||||
def authenticate_user(session, username, password):
 | 
			
		||||
    """
 | 
			
		||||
    Attempts to authenticate with ``username`` and ``password``.  If successful,
 | 
			
		||||
    returns the :class:`edbob.User` instance; otherwise returns ``None``.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    user = session.query(User).filter_by(username=username).first()
 | 
			
		||||
    if not user:
 | 
			
		||||
        return None
 | 
			
		||||
    auth = BcryptAuthenticator()
 | 
			
		||||
    if not auth.authenticate_user(user, password):
 | 
			
		||||
        return None
 | 
			
		||||
    return user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def administrator_role(session):
 | 
			
		||||
    """
 | 
			
		||||
    Returns the "Administrator" :class:`edbob.Role` instance, attached to the
 | 
			
		||||
    given ``session``.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    uuid = 'd937fa8a965611dfa0dd001143047286'
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +92,7 @@ def has_permission(obj, perm):
 | 
			
		|||
    elif isinstance(obj, Role):
 | 
			
		||||
        roles = [obj]
 | 
			
		||||
    else:
 | 
			
		||||
        raise TypeError, "You must pass either a User or Role for 'obj'; got: %s" % repr(obj)
 | 
			
		||||
        raise TypeError("You must pass either a User or Role for 'obj'; got: %s" % repr(obj))
 | 
			
		||||
    session = object_session(obj)
 | 
			
		||||
    assert session
 | 
			
		||||
    admin = get_administrator(session)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										36
									
								
								edbob/db/enum.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								edbob/db/enum.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8  -*-
 | 
			
		||||
################################################################################
 | 
			
		||||
#
 | 
			
		||||
#  edbob -- Pythonic Software Framework
 | 
			
		||||
#  Copyright © 2010-2012 Lance Edgar
 | 
			
		||||
#
 | 
			
		||||
#  This file is part of edbob.
 | 
			
		||||
#
 | 
			
		||||
#  edbob is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
#  terms of the GNU Affero General Public License as published by the Free
 | 
			
		||||
#  Software Foundation, either version 3 of the License, or (at your option)
 | 
			
		||||
#  any later version.
 | 
			
		||||
#
 | 
			
		||||
#  edbob 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 Affero General Public License for
 | 
			
		||||
#  more details.
 | 
			
		||||
#
 | 
			
		||||
#  You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
#  along with edbob.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
``edbob.db.enum`` -- Enumerations
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
USER_ACTIVE                     = 1
 | 
			
		||||
USER_INACTIVE                   = 2
 | 
			
		||||
 | 
			
		||||
USER_STATUS = {
 | 
			
		||||
    USER_ACTIVE                 : "active",
 | 
			
		||||
    USER_INACTIVE               : "inactive",
 | 
			
		||||
    }
 | 
			
		||||
							
								
								
									
										65
									
								
								edbob/db/exceptions.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								edbob/db/exceptions.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,65 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8  -*-
 | 
			
		||||
################################################################################
 | 
			
		||||
#
 | 
			
		||||
#  edbob -- Pythonic Software Framework
 | 
			
		||||
#  Copyright © 2010-2012 Lance Edgar
 | 
			
		||||
#
 | 
			
		||||
#  This file is part of edbob.
 | 
			
		||||
#
 | 
			
		||||
#  edbob is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
#  terms of the GNU Affero General Public License as published by the Free
 | 
			
		||||
#  Software Foundation, either version 3 of the License, or (at your option)
 | 
			
		||||
#  any later version.
 | 
			
		||||
#
 | 
			
		||||
#  edbob 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 Affero General Public License for
 | 
			
		||||
#  more details.
 | 
			
		||||
#
 | 
			
		||||
#  You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
#  along with edbob.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
``edbob.db.exceptions`` -- Database Exceptions
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CoreSchemaAlreadyInstalled(Exception):
 | 
			
		||||
    """
 | 
			
		||||
    Raised when a request is made to install the core schema to a database, but
 | 
			
		||||
    it is already installed there.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, installed_version):
 | 
			
		||||
        self.installed_version = installed_version
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "Core schema already installed (version %s)" % self.installed_version
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CoreSchemaNotInstalled(Exception):
 | 
			
		||||
    """
 | 
			
		||||
    Raised when a request is made which requires the core schema to be present
 | 
			
		||||
    in a database, yet such is not the case.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, engine):
 | 
			
		||||
        self.engine = engine
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "Core schema not installed: %s" % str(self.engine)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ExtensionNotFound(Exception):
 | 
			
		||||
    """
 | 
			
		||||
    Raised when an extension is requested which cannot be located.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, name):
 | 
			
		||||
        self.name = name
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "Extension not found: %s" % self.name
 | 
			
		||||
							
								
								
									
										426
									
								
								edbob/db/extensions.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										426
									
								
								edbob/db/extensions.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,426 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8  -*-
 | 
			
		||||
################################################################################
 | 
			
		||||
#
 | 
			
		||||
#  edbob -- Pythonic Software Framework
 | 
			
		||||
#  Copyright © 2010-2012 Lance Edgar
 | 
			
		||||
#
 | 
			
		||||
#  This file is part of edbob.
 | 
			
		||||
#
 | 
			
		||||
#  edbob is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
#  terms of the GNU Affero General Public License as published by the Free
 | 
			
		||||
#  Software Foundation, either version 3 of the License, or (at your option)
 | 
			
		||||
#  any later version.
 | 
			
		||||
#
 | 
			
		||||
#  edbob 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 Affero General Public License for
 | 
			
		||||
#  more details.
 | 
			
		||||
#
 | 
			
		||||
#  You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
#  along with edbob.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
``edbob.db.extensions`` -- Database Extensions
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
# from pkg_resources import iter_entry_points
 | 
			
		||||
 | 
			
		||||
import sqlalchemy.exc
 | 
			
		||||
# from sqlalchemy.orm import clear_mappers
 | 
			
		||||
 | 
			
		||||
# import migrate.versioning.api
 | 
			
		||||
# from migrate.versioning.schema import ControlledSchema
 | 
			
		||||
 | 
			
		||||
# import rattail
 | 
			
		||||
# from rattail.db import exc as exceptions
 | 
			
		||||
# from rattail.db import Session
 | 
			
		||||
# from rattail.db.classes import ActiveExtension
 | 
			
		||||
# from rattail.db.mappers import make_mappers
 | 
			
		||||
# from rattail.db.model import get_metadata
 | 
			
		||||
# from rattail.db.util import get_repository_path, get_repository_version
 | 
			
		||||
 | 
			
		||||
import edbob
 | 
			
		||||
import edbob.db
 | 
			
		||||
from edbob.db import exceptions
 | 
			
		||||
from edbob.db import Session
 | 
			
		||||
from edbob.db.classes import ActiveExtension
 | 
			
		||||
from edbob.db.util import get_database_version
 | 
			
		||||
from edbob.util import requires_impl
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
available_extensions = edbob.entry_point_map('edbob.db.extensions')
 | 
			
		||||
active_extensions = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Extension(edbob.Object):
 | 
			
		||||
    """
 | 
			
		||||
    Base class for schema/ORM extensions.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    # Set this to a list of strings (extension names) as needed within your
 | 
			
		||||
    # derived class.
 | 
			
		||||
    required_extensions = []
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    @requires_impl(is_property=True)
 | 
			
		||||
    def name(self):
 | 
			
		||||
        """
 | 
			
		||||
        The name of the extension.
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    @requires_impl(is_property=True)
 | 
			
		||||
    def schema(self):
 | 
			
		||||
        """
 | 
			
		||||
        Should return a reference to the extension's ``schema`` module, which
 | 
			
		||||
        is assumed to be a SQLAlchemy-Migrate repository.
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def add_class(self, cls):
 | 
			
		||||
        """
 | 
			
		||||
        Convenience method for use in :meth:`extend_classes()`.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        from  edbob.db import classes
 | 
			
		||||
 | 
			
		||||
        name = cls.__name__
 | 
			
		||||
        edbob.graft(classes, {name:cls}, name)
 | 
			
		||||
 | 
			
		||||
    def extend_classes(self):
 | 
			
		||||
        """
 | 
			
		||||
        Any extra classes provided by the extension should be added to the ORM
 | 
			
		||||
        whenever this method is called.
 | 
			
		||||
 | 
			
		||||
        Note that the :meth:`add_class()` convenience method is designed to be
 | 
			
		||||
        used when adding classes.
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def extend_mappers(self, metadata):
 | 
			
		||||
        """
 | 
			
		||||
        All SQLAlchemy mapping to be done by the extension should be done
 | 
			
		||||
        within this method.
 | 
			
		||||
 | 
			
		||||
        Any extra classes the extension provides will typically be mapped here.
 | 
			
		||||
        Any manipulation the extension needs to perform on the ``edbob`` core
 | 
			
		||||
        ORM should be done here as well.
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def get_metadata(self):
 | 
			
		||||
        """
 | 
			
		||||
        Should return a :class:`sqlalchemy.MetaData` instance containing the
 | 
			
		||||
        schema definition for the extension, or ``None``.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def remove_class(self, name):
 | 
			
		||||
        """
 | 
			
		||||
        Convenience method for use in :meth:`restore_classes()`.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        from edbob.db import classes
 | 
			
		||||
 | 
			
		||||
        if name in classes.__all__:
 | 
			
		||||
            classes.__all__.remove(name)
 | 
			
		||||
        if hasattr(classes, name):
 | 
			
		||||
            del classes.__dict__[name]
 | 
			
		||||
 | 
			
		||||
    def restore_classes(self):
 | 
			
		||||
        """
 | 
			
		||||
        This method should remove any extra classes which were added within
 | 
			
		||||
        :meth:`extend_classes()`.  Note that there is a :meth:`remove_class()`
 | 
			
		||||
        method for convenience in doing so.
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def activate_extension(extension, engine=None):
 | 
			
		||||
#     """
 | 
			
		||||
#     Activates the :class:`RattailExtension` instance represented by
 | 
			
		||||
#     ``extension`` (which can be the actual instance, or the extension's name)
 | 
			
		||||
#     by installing its schema and registering it within the database, and
 | 
			
		||||
#     immediately applies it to the current ORM API.
 | 
			
		||||
 | 
			
		||||
#     If ``engine`` is not provided, then ``rattail.engine`` is assumed.
 | 
			
		||||
#     """
 | 
			
		||||
 | 
			
		||||
#     if engine is None:
 | 
			
		||||
#         engine = rattail.engine
 | 
			
		||||
 | 
			
		||||
#     if not isinstance(extension, RattailExtension):
 | 
			
		||||
#         extension = get_extension(extension)
 | 
			
		||||
 | 
			
		||||
#     log.info("Activating extension: %s" % extension.name)
 | 
			
		||||
#     install_extension_schema(extension, engine)
 | 
			
		||||
 | 
			
		||||
#     session = Session(bind=engine)
 | 
			
		||||
#     if not session.query(ActiveExtension).get(extension.name):
 | 
			
		||||
#         session.add(ActiveExtension(name=extension.name))
 | 
			
		||||
#         session.commit()
 | 
			
		||||
#     session.close()
 | 
			
		||||
 | 
			
		||||
#     merge_extension_metadata(extension)
 | 
			
		||||
#     extension.extend_classes()
 | 
			
		||||
#     extension.extend_mappers(rattail.metadata)
 | 
			
		||||
#     _active_extensions[extension.name] = extension
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def deactivate_extension(extension, engine=None):
 | 
			
		||||
#     """
 | 
			
		||||
#     Uninstalls an extension's schema from the primary database, and immediately
 | 
			
		||||
#     requests it to restore the ORM API.
 | 
			
		||||
 | 
			
		||||
#     If ``engine`` is not provided, then ``rattail.engine`` is assumed.
 | 
			
		||||
#     """
 | 
			
		||||
 | 
			
		||||
#     if engine is None:
 | 
			
		||||
#         engine = rattail.engine
 | 
			
		||||
        
 | 
			
		||||
#     if not isinstance(extension, RattailExtension):
 | 
			
		||||
#         extension = get_extension(extension)
 | 
			
		||||
 | 
			
		||||
#     log.info("Deactivating extension: %s" % extension.name)
 | 
			
		||||
#     if extension.name in _active_extensions:
 | 
			
		||||
#         del _active_extensions[extension.name]
 | 
			
		||||
 | 
			
		||||
#     session = Session()
 | 
			
		||||
#     ext = session.query(ActiveExtension).get(extension.name)
 | 
			
		||||
#     if ext:
 | 
			
		||||
#         session.delete(ext)
 | 
			
		||||
#         session.commit()
 | 
			
		||||
#     session.close()
 | 
			
		||||
 | 
			
		||||
#     uninstall_extension_schema(extension, engine)
 | 
			
		||||
#     unmerge_extension_metadata(extension)
 | 
			
		||||
#     extension.restore_classes()
 | 
			
		||||
 | 
			
		||||
#     clear_mappers()
 | 
			
		||||
#     make_mappers(rattail.metadata)
 | 
			
		||||
#     for name in sorted(_active_extensions, extension_sorter(_active_extensions)):
 | 
			
		||||
#         _active_extensions[name].extend_mappers(rattail.metadata)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def extend_framework():
 | 
			
		||||
    """
 | 
			
		||||
    Attempts to connect to the primary database and, if successful, inspects it
 | 
			
		||||
    to determine which extensions are active within it.  Any such extensions
 | 
			
		||||
    found will be used to extend the ORM/API in-place.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    engine = edbob.db.engine
 | 
			
		||||
 | 
			
		||||
    # Check primary database connection.
 | 
			
		||||
    try:
 | 
			
		||||
        engine.connect()
 | 
			
		||||
    except sqlalchemy.exc.OperationalError:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # Check database version to see if core schema is installed.
 | 
			
		||||
    try:
 | 
			
		||||
        db_version = get_database_version(engine)
 | 
			
		||||
    except exceptions.CoreSchemaNotInstalled:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # Since extensions may depend on one another, we must first retrieve the
 | 
			
		||||
    # list of active extensions' names from the database and *then* sort them
 | 
			
		||||
    # according to their stated dependencies.  (This information is only known
 | 
			
		||||
    # after instantiating the extensions.)
 | 
			
		||||
 | 
			
		||||
    session = Session()
 | 
			
		||||
    try:
 | 
			
		||||
        active_extensions = session.query(ActiveExtension).all()
 | 
			
		||||
    except sqlalchemy.exc.ProgrammingError:
 | 
			
		||||
        session.close()
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    extensions = {}
 | 
			
		||||
    for ext in active_extensions:
 | 
			
		||||
        extensions[ext.name] = get_extension(ext.name)
 | 
			
		||||
    session.close()
 | 
			
		||||
 | 
			
		||||
    for name in sorted(extensions, extension_sorter(extensions)):
 | 
			
		||||
        ext = extensions[name]
 | 
			
		||||
        log.info("Applying active extension: %s" % name)
 | 
			
		||||
        merge_extension_metadata(ext)
 | 
			
		||||
        ext.extend_classes()
 | 
			
		||||
        ext.extend_mappers(rattail.metadata)
 | 
			
		||||
        active_extensions[name] = ext
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def extension_active(extension):
 | 
			
		||||
#     """
 | 
			
		||||
#     Returns boolean indicating whether or not the given ``extension`` is active
 | 
			
		||||
#     within the current database.
 | 
			
		||||
#     """
 | 
			
		||||
 | 
			
		||||
#     if not isinstance(extension, RattailExtension):
 | 
			
		||||
#         extension = get_extension(extension)
 | 
			
		||||
#     return extension.name in _active_extensions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def extension_sorter(extensions):
 | 
			
		||||
    """
 | 
			
		||||
    Returns a function to be used for sorting extensions according to their
 | 
			
		||||
    inter-dependencies.  ``extensions`` should be a dictionary containing the
 | 
			
		||||
    extensions which are to be sorted.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def sorter(name_x, name_y):
 | 
			
		||||
        ext_x = extensions[name_x]
 | 
			
		||||
        ext_y = extensions[name_y]
 | 
			
		||||
 | 
			
		||||
        if name_y in ext_x.required_extensions:
 | 
			
		||||
            return 1
 | 
			
		||||
        if name_x in ext_y.required_extensions:
 | 
			
		||||
            return -1
 | 
			
		||||
 | 
			
		||||
        if ext_x.required_extensions and not ext_y.required_extensions:
 | 
			
		||||
            return 1
 | 
			
		||||
        if ext_y.required_extensions and not ext_x.required_extensions:
 | 
			
		||||
            return -1
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
    return sorter        
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_extension(name):
 | 
			
		||||
    """
 | 
			
		||||
    Returns a :class:`Extension` instance, according to ``name``.  An error is
 | 
			
		||||
    raised if the extension cannot be found.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    if name in available_extensions:
 | 
			
		||||
        return available_extensions[name]()
 | 
			
		||||
    raise exceptions.ExtensionNotFound(name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def install_extension_schema(extension, engine=None):
 | 
			
		||||
#     """
 | 
			
		||||
#     Installs an extension's schema to the database and adds version control for
 | 
			
		||||
#     it.
 | 
			
		||||
#     """
 | 
			
		||||
 | 
			
		||||
#     if engine is None:
 | 
			
		||||
#         engine = rattail.engine
 | 
			
		||||
 | 
			
		||||
#     # Extensionls aren't required to provide metadata...
 | 
			
		||||
#     ext_meta = extension.get_metadata()
 | 
			
		||||
#     if not ext_meta:
 | 
			
		||||
#         return
 | 
			
		||||
 | 
			
		||||
#     # ...but if they do they must also provide a SQLAlchemy-Migrate repository!
 | 
			
		||||
#     assert extension.schema, "Extension does not implement 'schema': %s" % extension.name
 | 
			
		||||
        
 | 
			
		||||
#     meta = rattail.metadata
 | 
			
		||||
#     for table in meta.sorted_tables:
 | 
			
		||||
#         table.tometadata(ext_meta)
 | 
			
		||||
#     for table in ext_meta.sorted_tables:
 | 
			
		||||
#         if table.name not in meta.tables:
 | 
			
		||||
#             table.create(bind=engine, checkfirst=True)
 | 
			
		||||
 | 
			
		||||
#     migrate.versioning.api.version_control(
 | 
			
		||||
#         str(engine.url), get_repository_path(extension), get_repository_version(extension))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def merge_extension_metadata(ext):
 | 
			
		||||
    """
 | 
			
		||||
    Merges an extension's metadata with the global ``edbob.db.metadata``
 | 
			
		||||
    instance.
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
       ``edbob`` uses this internally; you should not need to.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    ext_meta = ext.get_metadata()
 | 
			
		||||
    if not ext_meta:
 | 
			
		||||
        return
 | 
			
		||||
    meta = edbob.db.metadata
 | 
			
		||||
    for table in meta.sorted_tables:
 | 
			
		||||
        table.tometadata(ext_meta)
 | 
			
		||||
    for table in ext_meta.sorted_tables:
 | 
			
		||||
        if table.name not in meta.tables:
 | 
			
		||||
            table.tometadata(meta)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def uninstall_extension_schema(extension, engine=None):
 | 
			
		||||
#     """
 | 
			
		||||
#     Uninstalls an extension's tables from the database represented by
 | 
			
		||||
#     ``engine`` (or ``rattail.engine`` if none is provided), and removes
 | 
			
		||||
#     SQLAlchemy-Migrate version control for the extension.
 | 
			
		||||
#     """
 | 
			
		||||
 | 
			
		||||
#     if engine is None:
 | 
			
		||||
#         engine = rattail.engine
 | 
			
		||||
    
 | 
			
		||||
#     ext_meta = extension.get_metadata()
 | 
			
		||||
#     if not ext_meta:
 | 
			
		||||
#         return
 | 
			
		||||
    
 | 
			
		||||
#     schema = ControlledSchema(engine, get_repository_path(extension))
 | 
			
		||||
#     engine.execute(schema.table.delete().where(
 | 
			
		||||
#             schema.table.c.repository_id == schema.repository.id))
 | 
			
		||||
 | 
			
		||||
#     meta = get_metadata()
 | 
			
		||||
#     for table in meta.sorted_tables:
 | 
			
		||||
#         table.tometadata(ext_meta)
 | 
			
		||||
#     for table in reversed(ext_meta.sorted_tables):
 | 
			
		||||
#         if table.name not in meta.tables:
 | 
			
		||||
#             table.drop(bind=engine)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def unmerge_extension_metadata(extension):
 | 
			
		||||
#     """
 | 
			
		||||
#     Removes an extension's metadata from the global ``rattail.metadata``
 | 
			
		||||
#     instance.
 | 
			
		||||
#     """
 | 
			
		||||
 | 
			
		||||
#     ext_meta = extension.get_metadata()
 | 
			
		||||
#     if not ext_meta:
 | 
			
		||||
#         return
 | 
			
		||||
 | 
			
		||||
#     meta = rattail.metadata
 | 
			
		||||
#     ext_tables = ext_meta.tables.keys()
 | 
			
		||||
#     for table in reversed(meta.sorted_tables):
 | 
			
		||||
#         if table.name in ext_tables:
 | 
			
		||||
#             meta.remove(table)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# # def merge_extension_permissions(extension):
 | 
			
		||||
# #     '''
 | 
			
		||||
# #     Helper function to merge an extension's permission definitions with those of
 | 
			
		||||
# #     the framework.  (This should only be called by the framework itself.)
 | 
			
		||||
# #     '''
 | 
			
		||||
# #     from rattail.v1.perms import permissions
 | 
			
		||||
# #     log.debug('Merging permissions from extension: %s' % extension.name)
 | 
			
		||||
# #     for group_name in extension.permissions:
 | 
			
		||||
# #         if group_name not in permissions:
 | 
			
		||||
# #             permissions[group_name] = extension.permissions[group_name]
 | 
			
		||||
# #         elif extension.permissions[group_name][0] != permissions[group_name][0]:
 | 
			
		||||
# #             log.warning("Extension '%s' tries to override UUID of permission group '%s' (but is denied)" % (
 | 
			
		||||
# #                     extension.name, group_name))
 | 
			
		||||
# #         else:
 | 
			
		||||
# #             # Extensions may override permission group display names.
 | 
			
		||||
# #             if extension.permissions[group_name][1]:
 | 
			
		||||
# #                 permissions[group_name][1] = extension.permissions[group_name][1]
 | 
			
		||||
# #             perms = permissions[group_name][2]
 | 
			
		||||
# #             ext_perms = extension.permissions[group_name][2]
 | 
			
		||||
# #             for perm_name in ext_perms:
 | 
			
		||||
# #                 if perm_name not in perms:
 | 
			
		||||
# #                     perms[perm_name] = ext_perms[perm_name]
 | 
			
		||||
# #                 elif ext_perms[perm_name][0] != perms[perm_name][0]:
 | 
			
		||||
# #                     log.warning("Extension '%s' tries to override UUID of permission '%s' (but is denied)" % (
 | 
			
		||||
# #                             extension.name, '.'.join((group_name, perm_name))))
 | 
			
		||||
# #                 else:
 | 
			
		||||
# #                     # Extensions may override permission display names.
 | 
			
		||||
# #                     if ext_perms[perm_name][1]:
 | 
			
		||||
# #                         perms[perm_name][1] = ext_perms[perm_name][1]
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +28,7 @@
 | 
			
		|||
 | 
			
		||||
from sqlalchemy.orm import mapper, relationship
 | 
			
		||||
 | 
			
		||||
import edbob.db.classes as c
 | 
			
		||||
from edbob.db import classes as c
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def make_mappers(metadata):
 | 
			
		||||
| 
						 | 
				
			
			@ -60,17 +60,6 @@ def make_mappers(metadata):
 | 
			
		|||
        c.Person, t['people'],
 | 
			
		||||
        properties=dict(
 | 
			
		||||
 | 
			
		||||
            customers=relationship(
 | 
			
		||||
                c.Customer,
 | 
			
		||||
                backref='person',
 | 
			
		||||
                ),
 | 
			
		||||
 | 
			
		||||
            employee=relationship(
 | 
			
		||||
                c.Employee,
 | 
			
		||||
                back_populates='person',
 | 
			
		||||
                uselist=False,
 | 
			
		||||
                ),
 | 
			
		||||
 | 
			
		||||
            user=relationship(
 | 
			
		||||
                c.User,
 | 
			
		||||
                back_populates='person',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@
 | 
			
		|||
 | 
			
		||||
from sqlalchemy import *
 | 
			
		||||
 | 
			
		||||
from edbob import get_uuid
 | 
			
		||||
from edbob.sqlalchemy import table_with_uuid
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_metadata(*args, **kwargs):
 | 
			
		||||
| 
						 | 
				
			
			@ -66,9 +66,8 @@ def get_metadata(*args, **kwargs):
 | 
			
		|||
            return None
 | 
			
		||||
        return '%(first_name)s %(last_name)s' % locals()
 | 
			
		||||
 | 
			
		||||
    people = Table(
 | 
			
		||||
    people = table_with_uuid(
 | 
			
		||||
        'people', metadata,
 | 
			
		||||
        Column('uuid', String(32), primary_key=True, default=get_uuid),
 | 
			
		||||
        Column('first_name', String(50)),
 | 
			
		||||
        Column('last_name', String(50)),
 | 
			
		||||
        Column('display_name', String(100), default=get_person_display_name),
 | 
			
		||||
| 
						 | 
				
			
			@ -80,9 +79,8 @@ def get_metadata(*args, **kwargs):
 | 
			
		|||
        Column('permission', String(50), primary_key=True),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    roles = Table(
 | 
			
		||||
    roles = table_with_uuid(
 | 
			
		||||
        'roles', metadata,
 | 
			
		||||
        Column('uuid', String(32), primary_key=True, default=get_uuid),
 | 
			
		||||
        Column('name', String(25), nullable=False, unique=True),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -92,16 +90,14 @@ def get_metadata(*args, **kwargs):
 | 
			
		|||
        Column('value', Text),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    users = Table(
 | 
			
		||||
    users = table_with_uuid(
 | 
			
		||||
        'users', metadata,
 | 
			
		||||
        Column('uuid', String(32), primary_key=True, default=get_uuid),
 | 
			
		||||
        Column('username', String(25), nullable=False, unique=True),
 | 
			
		||||
        Column('person_uuid', String(32), ForeignKey('people.uuid')),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    users_roles = Table(
 | 
			
		||||
    users_roles = table_with_uuid(
 | 
			
		||||
        'users_roles', metadata,
 | 
			
		||||
        Column('uuid', String(32), primary_key=True, default=get_uuid),
 | 
			
		||||
        Column('user_uuid', String(32), ForeignKey('users.uuid')),
 | 
			
		||||
        Column('role_uuid', String(32), ForeignKey('roles.uuid')),
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,84 +0,0 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8  -*-
 | 
			
		||||
################################################################################
 | 
			
		||||
#
 | 
			
		||||
#  edbob -- Pythonic Software Framework
 | 
			
		||||
#  Copyright © 2010-2012 Lance Edgar
 | 
			
		||||
#
 | 
			
		||||
#  This file is part of edbob.
 | 
			
		||||
#
 | 
			
		||||
#  edbob is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
#  terms of the GNU Affero General Public License as published by the Free
 | 
			
		||||
#  Software Foundation, either version 3 of the License, or (at your option)
 | 
			
		||||
#  any later version.
 | 
			
		||||
#
 | 
			
		||||
#  edbob 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 Affero General Public License for
 | 
			
		||||
#  more details.
 | 
			
		||||
#
 | 
			
		||||
#  You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
#  along with edbob.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
``edbob.db.perms`` -- Roles & Permissions
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sqlalchemy.orm import object_session
 | 
			
		||||
 | 
			
		||||
from edbob.db.classes import Role, User, Permission
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_administrator(session):
 | 
			
		||||
    """
 | 
			
		||||
    Returns the "Administrator" :class:`rattail.db.classes.Role` instance,
 | 
			
		||||
    attached to the given ``session``.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    uuid = 'd937fa8a965611dfa0dd001143047286'
 | 
			
		||||
    admin = session.query(Role).get(uuid)
 | 
			
		||||
    if admin:
 | 
			
		||||
        return admin
 | 
			
		||||
    admin = Role(uuid=uuid, name='Administrator')
 | 
			
		||||
    session.add(admin)
 | 
			
		||||
    return admin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def has_permission(object_, permission, session=None):
 | 
			
		||||
#     '''
 | 
			
		||||
#     Checks the given ``object_`` (which may be either a :class:`rattail.v1.User` or
 | 
			
		||||
#     a :class:`rattail.v1.Role`) and returns a boolean indicating whether or not the
 | 
			
		||||
#     object is allowed the given permission.  ``permission`` may be either a
 | 
			
		||||
#     :class:`rattail.v1.Permission` instance, or the fully-qualified name of one.
 | 
			
		||||
 | 
			
		||||
#     If ``object_`` is ``None``, the permission check is made against the special
 | 
			
		||||
#     "(Anybody)" role.
 | 
			
		||||
#     '''
 | 
			
		||||
 | 
			
		||||
def has_permission(obj, perm):
 | 
			
		||||
    """
 | 
			
		||||
    Checks the given ``obj`` (which may be either a
 | 
			
		||||
    :class:`rattail.db.classes.User`` or :class:`rattail.db.classes.Role`
 | 
			
		||||
    instance), and returns a boolean indicating whether or not the object is
 | 
			
		||||
    allowed the given permission.  ``perm`` should be a fully-qualified
 | 
			
		||||
    permission name, e.g. ``'employees.admin'``.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    if isinstance(obj, User):
 | 
			
		||||
        roles = obj.roles
 | 
			
		||||
    elif isinstance(obj, Role):
 | 
			
		||||
        roles = [obj]
 | 
			
		||||
    else:
 | 
			
		||||
        raise TypeError, "You must pass either a User or Role for 'obj'; got: %s" % repr(obj)
 | 
			
		||||
    session = object_session(obj)
 | 
			
		||||
    assert session
 | 
			
		||||
    admin = get_administrator(session)
 | 
			
		||||
    for role in roles:
 | 
			
		||||
        if role is admin:
 | 
			
		||||
            return True
 | 
			
		||||
        for permission in role.permissions:
 | 
			
		||||
            if permission == perm:
 | 
			
		||||
                return True
 | 
			
		||||
    return False
 | 
			
		||||
							
								
								
									
										4
									
								
								edbob/db/schema/README
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								edbob/db/schema/README
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
This is a database migration repository.
 | 
			
		||||
 | 
			
		||||
More information at
 | 
			
		||||
http://code.google.com/p/sqlalchemy-migrate/
 | 
			
		||||
							
								
								
									
										0
									
								
								edbob/db/schema/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								edbob/db/schema/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										5
									
								
								edbob/db/schema/manage.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								edbob/db/schema/manage.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
from migrate.versioning.shell import main
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    main(debug='False')
 | 
			
		||||
							
								
								
									
										25
									
								
								edbob/db/schema/migrate.cfg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								edbob/db/schema/migrate.cfg
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
[db_settings]
 | 
			
		||||
# Used to identify which repository this database is versioned under.
 | 
			
		||||
# You can use the name of your project.
 | 
			
		||||
repository_id=edbob
 | 
			
		||||
 | 
			
		||||
# The name of the database table used to track the schema version.
 | 
			
		||||
# This name shouldn't already be used by your project.
 | 
			
		||||
# If this is changed once a database is under version control, you'll need to 
 | 
			
		||||
# change the table name in each database too. 
 | 
			
		||||
version_table=migrate_version
 | 
			
		||||
 | 
			
		||||
# When committing a change script, Migrate will attempt to generate the 
 | 
			
		||||
# sql for all supported databases; normally, if one of them fails - probably
 | 
			
		||||
# because you don't have that database installed - it is ignored and the 
 | 
			
		||||
# commit continues, perhaps ending successfully. 
 | 
			
		||||
# Databases in this list MUST compile successfully during a commit, or the 
 | 
			
		||||
# entire commit will fail. List the databases your application will actually 
 | 
			
		||||
# be using to ensure your updates to that database work properly.
 | 
			
		||||
# This must be a list; example: ['postgres','sqlite']
 | 
			
		||||
required_dbs=[]
 | 
			
		||||
 | 
			
		||||
# When creating new change scripts, Migrate will stamp the new script with
 | 
			
		||||
# a version number. By default this is latest_version + 1. You can set this
 | 
			
		||||
# to 'true' to tell Migrate to use the UTC timestamp instead.
 | 
			
		||||
use_timestamp_numbering=False
 | 
			
		||||
							
								
								
									
										0
									
								
								edbob/db/schema/versions/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								edbob/db/schema/versions/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										163
									
								
								edbob/db/util.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								edbob/db/util.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,163 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8  -*-
 | 
			
		||||
################################################################################
 | 
			
		||||
#
 | 
			
		||||
#  edbob -- Pythonic Software Framework
 | 
			
		||||
#  Copyright © 2010-2012 Lance Edgar
 | 
			
		||||
#
 | 
			
		||||
#  This file is part of edbob.
 | 
			
		||||
#
 | 
			
		||||
#  edbob is free software: you can redistribute it and/or modify it under the
 | 
			
		||||
#  terms of the GNU Affero General Public License as published by the Free
 | 
			
		||||
#  Software Foundation, either version 3 of the License, or (at your option)
 | 
			
		||||
#  any later version.
 | 
			
		||||
#
 | 
			
		||||
#  edbob 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 Affero General Public License for
 | 
			
		||||
#  more details.
 | 
			
		||||
#
 | 
			
		||||
#  You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
#  along with edbob.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
``edbob.db.util`` -- Database Utilities
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import os.path
 | 
			
		||||
 | 
			
		||||
import sqlalchemy.exc
 | 
			
		||||
import migrate.versioning.api
 | 
			
		||||
import migrate.exceptions
 | 
			
		||||
 | 
			
		||||
# import rattail
 | 
			
		||||
# from rattail.db import exc as exceptions
 | 
			
		||||
# from rattail.db import Session
 | 
			
		||||
# from rattail.db.classes import Role
 | 
			
		||||
# from rattail.db.model import get_metadata
 | 
			
		||||
# from rattail.db.perms import get_administrator
 | 
			
		||||
 | 
			
		||||
import edbob.db
 | 
			
		||||
from edbob.db import exceptions
 | 
			
		||||
from edbob.db.model import get_metadata
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def core_schema_installed(engine=None):
 | 
			
		||||
#     """
 | 
			
		||||
#     Returns boolean indicating whether or not the core schema has been
 | 
			
		||||
#     installed to the database represented by ``engine``.  If ``engine`` is not
 | 
			
		||||
#     provided, then ``rattail.engine`` will be assumed.
 | 
			
		||||
#     """
 | 
			
		||||
    
 | 
			
		||||
#     if engine is None:
 | 
			
		||||
#         engine = rattail.engine
 | 
			
		||||
 | 
			
		||||
#     try:
 | 
			
		||||
#         get_database_version(engine)
 | 
			
		||||
#     except exceptions.CoreSchemaNotInstalled:
 | 
			
		||||
#         return False
 | 
			
		||||
#     return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_database_version(engine=None, extension=None):
 | 
			
		||||
    """
 | 
			
		||||
    Returns a SQLAlchemy-Migrate version number found in the database
 | 
			
		||||
    represented by ``engine``.
 | 
			
		||||
 | 
			
		||||
    If no engine is provided, :attr:`edbob.db.engine` is assumed.
 | 
			
		||||
 | 
			
		||||
    If ``extension`` is provided, the version for its schema is returned;
 | 
			
		||||
    otherwise the core schema is assumed.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    if engine is None:
 | 
			
		||||
        engine = edbob.db.engine
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        version = migrate.versioning.api.db_version(
 | 
			
		||||
            str(engine.url), get_repository_path(extension))
 | 
			
		||||
 | 
			
		||||
    except (sqlalchemy.exc.NoSuchTableError,
 | 
			
		||||
            migrate.exceptions.DatabaseNotControlledError):
 | 
			
		||||
        raise exceptions.CoreSchemaNotInstalled(engine)
 | 
			
		||||
 | 
			
		||||
    return version
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_repository_path(extension=None):
 | 
			
		||||
    """
 | 
			
		||||
    Returns the absolute filesystem path to the SQLAlchemy-Migrate repository
 | 
			
		||||
    for ``extension``.
 | 
			
		||||
 | 
			
		||||
    If no extension is provided, ``edbob``'s core repository is assumed.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    if not extension:
 | 
			
		||||
        from edbob.db import schema
 | 
			
		||||
        return os.path.dirname(schema.__file__)
 | 
			
		||||
 | 
			
		||||
    return os.path.dirname(extension.schema.__file__)
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
def get_repository_version(extension=None):
 | 
			
		||||
    """
 | 
			
		||||
    Returns the version of the SQLAlchemy-Migrate repository for ``extension``.
 | 
			
		||||
 | 
			
		||||
    If no extension is provided, ``edbob``'s core repository is assumed.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    return migrate.versioning.api.version(get_repository_path(extension))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def install_core_schema(engine=None):
 | 
			
		||||
    """
 | 
			
		||||
    Installs the core schema to the database represented by ``engine``.
 | 
			
		||||
 | 
			
		||||
    If no engine is provided, :attr:`edbob.db.engine` is assumed.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    if not engine:
 | 
			
		||||
        engine = edbob.db.engine
 | 
			
		||||
 | 
			
		||||
    # Try to connect in order to force an error, if applicable.
 | 
			
		||||
    conn = engine.connect()
 | 
			
		||||
    conn.close()
 | 
			
		||||
 | 
			
		||||
    # Check DB version to see if core schema is already installed.
 | 
			
		||||
    try:
 | 
			
		||||
        db_version = get_database_version(engine)
 | 
			
		||||
    except exceptions.CoreSchemaNotInstalled:
 | 
			
		||||
        pass
 | 
			
		||||
    else:
 | 
			
		||||
        raise exceptions.CoreSchemaAlreadyInstalled(db_version)
 | 
			
		||||
 | 
			
		||||
    # Create tables for core schema.
 | 
			
		||||
    metadata = get_metadata()
 | 
			
		||||
    metadata.create_all(bind=engine)
 | 
			
		||||
 | 
			
		||||
    # Add versioning for core schema.
 | 
			
		||||
    migrate.versioning.api.version_control(
 | 
			
		||||
        str(engine.url), get_repository_path(), get_repository_version())
 | 
			
		||||
 | 
			
		||||
    # WTF
 | 
			
		||||
    # session = Session(bind=engine)
 | 
			
		||||
    # get_administrator(session)
 | 
			
		||||
    # session.commit()
 | 
			
		||||
    # session.close()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def upgrade_schema(extension=None, engine=None):
 | 
			
		||||
#     """
 | 
			
		||||
#     Upgrades a schema within the database represented by ``engine`` (or
 | 
			
		||||
#     ``rattail.engine`` if none is provided).  If ``extension`` is provided,
 | 
			
		||||
#     then its schema will be upgraded; otherwise the core is assumed.
 | 
			
		||||
#     """
 | 
			
		||||
 | 
			
		||||
#     if engine is None:
 | 
			
		||||
#         engine = rattail.engine
 | 
			
		||||
#     repo_version = get_repository_version(extension)
 | 
			
		||||
#     db_version = get_database_version(engine, extension)
 | 
			
		||||
#     if db_version < repo_version:
 | 
			
		||||
#         migrate.versioning.api.upgrade(str(engine.url), get_repository_path(extension), repo_version)
 | 
			
		||||
| 
						 | 
				
			
			@ -27,13 +27,14 @@
 | 
			
		|||
"""
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
# import locale
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from edbob.configuration import AppConfigParser
 | 
			
		||||
from edbob.configuration import default_system_paths, default_user_paths
 | 
			
		||||
from edbob.core import graft
 | 
			
		||||
from edbob.times import set_timezone
 | 
			
		||||
import edbob
 | 
			
		||||
from edbob.configuration import (
 | 
			
		||||
    AppConfigParser,
 | 
			
		||||
    default_system_paths,
 | 
			
		||||
    default_user_paths,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ['init']
 | 
			
		||||
| 
						 | 
				
			
			@ -83,19 +84,15 @@ def init(appname='edbob', *args, **kwargs):
 | 
			
		|||
        config.read(paths, recurse=not shell)
 | 
			
		||||
    config.configure_logging()
 | 
			
		||||
 | 
			
		||||
    # loc = config.get('edbob', 'locale')
 | 
			
		||||
    # if loc:
 | 
			
		||||
    #     locale.setlocale(locale.LC_ALL, loc)
 | 
			
		||||
    #     log.info("Set locale to '%s'" % loc)
 | 
			
		||||
    default_modules = 'edbob.time'
 | 
			
		||||
    modules = config.get('edbob', 'init', default=default_modules)
 | 
			
		||||
    if modules:
 | 
			
		||||
        for name in modules.split(','):
 | 
			
		||||
            name = name.strip()
 | 
			
		||||
            module = __import__(name, globals(), locals(), fromlist=['init'])
 | 
			
		||||
            getattr(module, 'init')(config)
 | 
			
		||||
            # config.inited.append(name)
 | 
			
		||||
 | 
			
		||||
    tz = config.get('edbob', 'timezone')
 | 
			
		||||
    if tz:
 | 
			
		||||
        set_timezone(tz)
 | 
			
		||||
        log.info("Set timezone to '%s'" % tz)
 | 
			
		||||
    else:
 | 
			
		||||
        log.warning("No timezone configured; falling back to US/Central")
 | 
			
		||||
        set_timezone('US/Central')
 | 
			
		||||
 | 
			
		||||
    import edbob
 | 
			
		||||
    graft(edbob, locals(), 'config')
 | 
			
		||||
    # config.inited.append('edbob')
 | 
			
		||||
    edbob.graft(edbob, locals(), 'config')
 | 
			
		||||
    edbob.inited = True
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,10 +5,15 @@
 | 
			
		|||
"""
 | 
			
		||||
 | 
			
		||||
import os.path
 | 
			
		||||
import edbob
 | 
			
		||||
 | 
			
		||||
import pyramid_beaker
 | 
			
		||||
from pyramid.config import Configurator
 | 
			
		||||
 | 
			
		||||
import edbob
 | 
			
		||||
 | 
			
		||||
from {{package}}._version import __version__
 | 
			
		||||
from {{package}}.db import DBSession
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main(global_config, **settings):
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +44,7 @@ def main(global_config, **settings):
 | 
			
		|||
    config.set_session_factory(session_factory)
 | 
			
		||||
    pyramid_beaker.set_cache_regions_from_settings(settings)
 | 
			
		||||
 | 
			
		||||
    # Initialize edbob
 | 
			
		||||
    # Configure edbob
 | 
			
		||||
    edbob.basic_logging()
 | 
			
		||||
    edbob.init('{{package}}', os.path.abspath(settings['edbob.config']))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										82
									
								
								edbob/pyramid/scaffolds/edbob/+package+/commands.py_tmpl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								edbob/pyramid/scaffolds/edbob/+package+/commands.py_tmpl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
``{{package}}.commands`` -- Console Commands
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
import edbob
 | 
			
		||||
from edbob import commands
 | 
			
		||||
 | 
			
		||||
from {{package}} import __version__
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Command(commands.Command):
 | 
			
		||||
    """
 | 
			
		||||
    The primary command for {{project}}.
 | 
			
		||||
    """
 | 
			
		||||
    
 | 
			
		||||
    name = '{{package}}'
 | 
			
		||||
    version = __version__
 | 
			
		||||
    description = "{{project}}"
 | 
			
		||||
    long_description = ''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InitDatabaseCommand(commands.Subcommand):
 | 
			
		||||
    """
 | 
			
		||||
    Initializes the database.  This is meant to be leveraged as part of setting
 | 
			
		||||
    up the application.  The database used by this command will be determined
 | 
			
		||||
    by config, for example::
 | 
			
		||||
 | 
			
		||||
       .. highlight:: ini
 | 
			
		||||
 | 
			
		||||
       [edbob.db]
 | 
			
		||||
       sqlalchemy.url = postgresql://user:pass@localhost/{{package}}
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'init-db'
 | 
			
		||||
    description = "Initialize the database"
 | 
			
		||||
 | 
			
		||||
    def run(self, args):
 | 
			
		||||
        from edbob.db import engine, Session
 | 
			
		||||
        from edbob.db.util import install_core_schema
 | 
			
		||||
        from edbob.db.exceptions import CoreSchemaAlreadyInstalled
 | 
			
		||||
 | 
			
		||||
        # Install core schema to database.
 | 
			
		||||
        try:
 | 
			
		||||
            install_core_schema(engine)
 | 
			
		||||
        except CoreSchemaAlreadyInstalled, err:
 | 
			
		||||
            print err
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        from edbob.db.classes import Role, User
 | 
			
		||||
        from edbob.db.auth import administrator_role
 | 
			
		||||
 | 
			
		||||
        session = Session()
 | 
			
		||||
 | 
			
		||||
        # Create 'admin' user with full rights.
 | 
			
		||||
        admin = User(username='admin', password='admin')
 | 
			
		||||
        admin.roles.append(administrator_role(session))
 | 
			
		||||
        session.add(admin)
 | 
			
		||||
 | 
			
		||||
        # Do any other bootstrapping you like here...
 | 
			
		||||
        
 | 
			
		||||
        session.commit()
 | 
			
		||||
        session.close()
 | 
			
		||||
        
 | 
			
		||||
        print "Initialized database %s" % engine.url
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main(*args):
 | 
			
		||||
    """
 | 
			
		||||
    The primary entry point for the command system.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    if args:
 | 
			
		||||
        args = list(args)
 | 
			
		||||
    else:
 | 
			
		||||
        args = sys.argv[1:]
 | 
			
		||||
 | 
			
		||||
    cmd = Command()
 | 
			
		||||
    cmd.run(*args)
 | 
			
		||||
							
								
								
									
										11
									
								
								edbob/pyramid/scaffolds/edbob/+package+/db.py_tmpl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								edbob/pyramid/scaffolds/edbob/+package+/db.py_tmpl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
#!/usr/bin/env python
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
``{{package}}.db`` -- Database Stuff
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sqlalchemy.orm import scoped_session, sessionmaker
 | 
			
		||||
from zope.sqlalchemy import ZopeTransactionExtension
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
 | 
			
		||||
| 
						 | 
				
			
			@ -5,9 +5,13 @@
 | 
			
		|||
Welcome to the {{project}} project.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Installation
 | 
			
		||||
------------
 | 
			
		||||
Getting Started
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
Install the project with::
 | 
			
		||||
- cd <directory containing this file>
 | 
			
		||||
 | 
			
		||||
   $ pip install {{package}}
 | 
			
		||||
- $venv/bin/python setup.py develop
 | 
			
		||||
 | 
			
		||||
- $venv/bin/populate_{{package}} development.ini
 | 
			
		||||
 | 
			
		||||
- $venv/bin/pserve --reload development.ini
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,9 @@ port = 6543
 | 
			
		|||
[edbob]
 | 
			
		||||
include_config = ['%(here)s/production.ini']
 | 
			
		||||
 | 
			
		||||
[edbob.db]
 | 
			
		||||
sqlalchemy.url = sqlite:///{{package}}.sqlite
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
####################
 | 
			
		||||
# logging
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,9 +41,12 @@ port = 6543
 | 
			
		|||
####################
 | 
			
		||||
 | 
			
		||||
[edbob]
 | 
			
		||||
timezone = US/Central
 | 
			
		||||
init = edbob.time, edbob.db
 | 
			
		||||
# shell.python = ipython
 | 
			
		||||
 | 
			
		||||
[edbob.db]
 | 
			
		||||
sqlalchemy.url = postgresql://user:pass@localhost/{{package}}
 | 
			
		||||
 | 
			
		||||
[edbob.mail]
 | 
			
		||||
smtp.server = localhost
 | 
			
		||||
# smtp.username = user
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +59,9 @@ recipients.default = [
 | 
			
		|||
	]	
 | 
			
		||||
subject.default = Message from {{project}}
 | 
			
		||||
 | 
			
		||||
[edbob.time]
 | 
			
		||||
timezone = US/Central
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
####################
 | 
			
		||||
# logging
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,12 +39,19 @@ requires = [
 | 
			
		|||
    #
 | 
			
		||||
    # package                   # low                   high
 | 
			
		||||
 | 
			
		||||
    'decorator',                # 3.3.2
 | 
			
		||||
    'edbob',                    # 0.1a1
 | 
			
		||||
    'Mako',                     # 0.6.2
 | 
			
		||||
    'pyramid',                  # 1.3b2
 | 
			
		||||
    'pyramid_beaker',           # 0.6.1
 | 
			
		||||
    'pyramid_debugtoolbar',     # 1.0
 | 
			
		||||
    'pyramid_tm',               # 0.3
 | 
			
		||||
    'SQLAlchemy',               # 0.7.6
 | 
			
		||||
    'Tempita',                  # 0.5.1
 | 
			
		||||
    'transaction',              # 1.2.0
 | 
			
		||||
    'waitress',                 # 0.8.1
 | 
			
		||||
    'WebHelpers',               # 1.3
 | 
			
		||||
    'zope.sqlalchemy',          # 0.7
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -77,6 +84,12 @@ setup(
 | 
			
		|||
    zip_safe = False,
 | 
			
		||||
    entry_points = """
 | 
			
		||||
 | 
			
		||||
[console_scripts]
 | 
			
		||||
{{package}} = {{package}}.commands:main
 | 
			
		||||
 | 
			
		||||
[{{package}}.commands]
 | 
			
		||||
init-db = {{package}}.commands:InitDatabaseCommand
 | 
			
		||||
 | 
			
		||||
[paste.app_factory]
 | 
			
		||||
main = {{package}}:main
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,21 +26,56 @@
 | 
			
		|||
``edbob.pyramid.subscribers`` -- Subscribers
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from pyramid.security import authenticated_userid
 | 
			
		||||
# from sqlahelper import get_session
 | 
			
		||||
 | 
			
		||||
import edbob
 | 
			
		||||
from edbob.db.auth import has_permission
 | 
			
		||||
from edbob.pyramid import helpers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_renderer_globals(event):
 | 
			
		||||
def before_render(event):
 | 
			
		||||
    """
 | 
			
		||||
    Adds goodies to the global template renderer context.
 | 
			
		||||
    Adds goodies to the global template renderer context:
 | 
			
		||||
 | 
			
		||||
       * ``h``
 | 
			
		||||
       * ``url``
 | 
			
		||||
       * ``edbob``
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    renderer_globals = event
 | 
			
		||||
    renderer_globals['h'] = helpers
 | 
			
		||||
    renderer_globals['edbob'] = edbob
 | 
			
		||||
    renderer_globals['url'] = event['request'].route_url
 | 
			
		||||
    renderer_globals['edbob'] = edbob
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def context_found(event):
 | 
			
		||||
    """
 | 
			
		||||
    This hook attaches the :class:`edbob.User` instance for the currently
 | 
			
		||||
    logged-in user to the request (if there is one) as ``request.user``.
 | 
			
		||||
 | 
			
		||||
    Also adds a ``has_perm()`` function to the request, which is a shortcut for
 | 
			
		||||
    :func:`edbob.db.auth.has_permission()`.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def has_perm_func(request):
 | 
			
		||||
        def has_perm(perm):
 | 
			
		||||
            if not request.current_user:
 | 
			
		||||
                return False
 | 
			
		||||
            return has_permission(request.current_user, perm)
 | 
			
		||||
        return has_perm
 | 
			
		||||
 | 
			
		||||
    request = event.request
 | 
			
		||||
    request.user = None
 | 
			
		||||
    request.has_perm = has_perm_func(request)
 | 
			
		||||
 | 
			
		||||
    uuid = authenticated_userid(request)
 | 
			
		||||
    if uuid:
 | 
			
		||||
        request.user = get_session().query(rattail.User).get(uuid)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def includeme(config):
 | 
			
		||||
    config.add_subscriber('edbob.pyramid.subscribers:add_renderer_globals',
 | 
			
		||||
    config.add_subscriber('edbob.pyramid.subscribers:before_render',
 | 
			
		||||
                          'pyramid.events.BeforeRender')
 | 
			
		||||
    config.add_subscriber('edbob.pyramid.subscribers.context_found',
 | 
			
		||||
                          'pyramid.events.ContextFound')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,9 @@ import os.path
 | 
			
		|||
 | 
			
		||||
from pyramid.response import Response
 | 
			
		||||
from pyramid.view import view_config
 | 
			
		||||
from pyramid.httpexceptions import HTTPFound
 | 
			
		||||
 | 
			
		||||
from edbob.db.auth import authenticate_user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_here = os.path.join(os.path.dirname(__file__), os.pardir)
 | 
			
		||||
| 
						 | 
				
			
			@ -51,6 +54,29 @@ def home(context, request):
 | 
			
		|||
 | 
			
		||||
@view_config(route_name='login', renderer='login.mako')
 | 
			
		||||
def login(context, request):
 | 
			
		||||
    """
 | 
			
		||||
    The login view, responsible for displaying and handling the login form.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    if request.params.get('referer'):
 | 
			
		||||
        referer = request.params['referer']
 | 
			
		||||
    elif request.session.get('referer'):
 | 
			
		||||
        referer = request.session.pop('referer')
 | 
			
		||||
    else:
 | 
			
		||||
        referer = request.referer or request.route_url('home')
 | 
			
		||||
    # if request.current_user:
 | 
			
		||||
    #     return HTTPFound(location=referer)
 | 
			
		||||
    # form = Form(self.request, schema=UserLogin)
 | 
			
		||||
    # if form.validate():
 | 
			
		||||
    #     user = authenticate_user(self.Session(), form.data['username'], form.data['password'])
 | 
			
		||||
    #     if user:
 | 
			
		||||
    #         self.request.session.flash("%s logged in at %s" % (
 | 
			
		||||
    #                                    user.display_name,
 | 
			
		||||
    #                                    datetime.datetime.now().strftime("%I:%M %p")))
 | 
			
		||||
    #         headers = remember(self.request, user.uuid)
 | 
			
		||||
    #         return HTTPFound(location=referer, headers=headers)
 | 
			
		||||
    #     self.request.session.flash("Invalid username or password.")
 | 
			
		||||
    # return {'form':FormRenderer(form), 'referer':referer}
 | 
			
		||||
    return {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,15 @@
 | 
			
		|||
``edbob.sqlalchemy`` -- SQLAlchemy Stuff
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from __future__ import absolute_import
 | 
			
		||||
 | 
			
		||||
from sqlalchemy import Table, Column, String
 | 
			
		||||
 | 
			
		||||
from edbob.core import get_uuid
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ['getset_factory', 'table_with_uuid']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getset_factory(collection_class, proxy):
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			@ -37,3 +46,29 @@ def getset_factory(collection_class, proxy):
 | 
			
		|||
        return getattr(obj, proxy.value_attr)
 | 
			
		||||
    setter = lambda obj, val: setattr(obj, proxy.value_attr, val)
 | 
			
		||||
    return getter, setter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def table_with_uuid(name, metadata, *args, **kwargs):
 | 
			
		||||
    """
 | 
			
		||||
    Convenience function to abstract the addition of the ``uuid`` column to a
 | 
			
		||||
    new table.  Can be used to replace this::
 | 
			
		||||
 | 
			
		||||
       .. highlight:: python
 | 
			
		||||
 | 
			
		||||
       Table(
 | 
			
		||||
           'things', metadata,
 | 
			
		||||
           Column('uuid', String(32), primary_key=True, default=get_uuid),
 | 
			
		||||
           Column('name', String(50)),
 | 
			
		||||
           )
 | 
			
		||||
 | 
			
		||||
    ...with this::
 | 
			
		||||
 | 
			
		||||
        table_with_uuid(
 | 
			
		||||
            'things', metadata,
 | 
			
		||||
            Column('name', String(50)),
 | 
			
		||||
            )
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    return Table(name, metadata,
 | 
			
		||||
                 Column('uuid', String(32), primary_key=True, default=get_uuid),
 | 
			
		||||
                 *args, **kwargs)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,17 +23,35 @@
 | 
			
		|||
################################################################################
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
``edbob.times`` -- Date & Time Utilities
 | 
			
		||||
``edbob.time`` -- Date & Time Utilities
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import datetime
 | 
			
		||||
import pytz
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ['local_time', 'set_timezone', 'utc_time']
 | 
			
		||||
 | 
			
		||||
_timezone = None
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
timezone = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def init(config):
 | 
			
		||||
    """
 | 
			
		||||
    Initializes the time framework.  Currently this only sets the local
 | 
			
		||||
    timezone according to config.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    tz = config.get('edbob.time', 'timezone')
 | 
			
		||||
    if tz:
 | 
			
		||||
        set_timezone(tz)
 | 
			
		||||
        log.info("Set timezone to '%s'" % tz)
 | 
			
		||||
    else:
 | 
			
		||||
        log.warning("No timezone configured; falling back to US/Central")
 | 
			
		||||
        set_timezone('US/Central')
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
def local_time(timestamp=None):
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			@ -51,11 +69,11 @@ def local_time(timestamp=None):
 | 
			
		|||
    should none be specified.  ``timestamp`` will be returned unchanged.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    if _timezone:
 | 
			
		||||
    if timezone:
 | 
			
		||||
        if timestamp is None:
 | 
			
		||||
            timestamp = datetime.datetime.utcnow()
 | 
			
		||||
        timestamp = pytz.utc.localize(timestamp)
 | 
			
		||||
        return timestamp.astimezone(_timezone)
 | 
			
		||||
        return timestamp.astimezone(timezone)
 | 
			
		||||
 | 
			
		||||
    if timestamp is None:
 | 
			
		||||
        timestamp = datetime.datetime.now()
 | 
			
		||||
| 
						 | 
				
			
			@ -67,21 +85,25 @@ def set_timezone(tz):
 | 
			
		|||
    Sets edbob's notion of the "local" timezone.  ``tz`` should be an Olson
 | 
			
		||||
    name.
 | 
			
		||||
 | 
			
		||||
    .. highlight:: ini
 | 
			
		||||
 | 
			
		||||
    You usually don't need to call this yourself, since it's called by
 | 
			
		||||
    :func:`edbob.init()` whenever ``edbob.conf`` includes a timezone::
 | 
			
		||||
    :func:`edbob.init()` whenever the config file includes a timezone (but
 | 
			
		||||
    only as long as ``edbob.time`` is configured to be initialized)::
 | 
			
		||||
 | 
			
		||||
       .. highlight:: ini
 | 
			
		||||
 | 
			
		||||
       [edbob]
 | 
			
		||||
       init = ['edbob.time']
 | 
			
		||||
 | 
			
		||||
       [edbob.time]
 | 
			
		||||
       timezone = US/Central
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    global _timezone
 | 
			
		||||
    global timezone
 | 
			
		||||
 | 
			
		||||
    if tz is None:
 | 
			
		||||
        _timezone = None
 | 
			
		||||
        timezone = None
 | 
			
		||||
    else:
 | 
			
		||||
        _timezone = pytz.timezone(tz)
 | 
			
		||||
        timezone = pytz.timezone(tz)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def utc_time(timestamp=None):
 | 
			
		||||
							
								
								
									
										19
									
								
								setup.py
									
										
									
									
									
								
							
							
						
						
									
										19
									
								
								setup.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -99,15 +99,18 @@ setup(
 | 
			
		|||
 | 
			
		||||
    extras_require = {
 | 
			
		||||
        #
 | 
			
		||||
        # Same guidelines apply to the extra dependencies:
 | 
			
		||||
        # Same guidelines apply to the extra dependency versions.
 | 
			
		||||
 | 
			
		||||
        # 'db': [
 | 
			
		||||
        #     #
 | 
			
		||||
        #     # package                   # low                   high
 | 
			
		||||
        #     #
 | 
			
		||||
        #     'SQLAlchemy',               # 0.6.7
 | 
			
		||||
        #     'sqlalchemy-migrate',       # 0.6.1
 | 
			
		||||
        #     ],
 | 
			
		||||
        'db': [
 | 
			
		||||
            #
 | 
			
		||||
            # package                   # low                   high
 | 
			
		||||
            #
 | 
			
		||||
            'decorator',                # 3.3.2
 | 
			
		||||
            'py-bcrypt',                # 0.2
 | 
			
		||||
            'SQLAlchemy',               # 0.7.6
 | 
			
		||||
            'sqlalchemy-migrate',       # 0.7.2
 | 
			
		||||
            'Tempita',                  # 0.5.1
 | 
			
		||||
            ],
 | 
			
		||||
 | 
			
		||||
        'docs': [
 | 
			
		||||
            #
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue