extended commit (see note)
- Slight overhaul of init() system; added ``edbob.init_modules()`` function. - Added ``read_service()`` method to ``AppConfigParser`` class, for use with Windows services. - Added generic ``Service`` class to ``edbob.win32`` module. (File monitor now inherits from it.) - Tweaked ``edbob.db`` initialization somewhat. (``Base.metadata`` no longer binds to ``edbob.db.engine``.) - Fixed guest role bug in ``edbob.db.auth.has_permission()`` function. - Added "automagical" enumeration support for database extensions. - Added ``EMAIL_PREFERENCE`` enum to ``contact`` database extension. - Tweaked ``edbob.pyramid.includeme()``. - Tweaked ``people`` Pyramid views.
This commit is contained in:
parent
526ff5dd6b
commit
c7e0bfef6a
18 changed files with 457 additions and 248 deletions
|
@ -34,6 +34,3 @@ from edbob.files import *
|
||||||
from edbob.modules import *
|
from edbob.modules import *
|
||||||
from edbob.configuration import *
|
from edbob.configuration import *
|
||||||
from edbob.initialization import *
|
from edbob.initialization import *
|
||||||
|
|
||||||
|
|
||||||
inited = False
|
|
||||||
|
|
|
@ -289,6 +289,25 @@ class AppConfigParser(ConfigParser.SafeConfigParser):
|
||||||
self.paths_loaded.append(path)
|
self.paths_loaded.append(path)
|
||||||
log.info("Read config file: %s" % path)
|
log.info("Read config file: %s" % path)
|
||||||
|
|
||||||
|
def read_service(self, service, paths):
|
||||||
|
"""
|
||||||
|
"Special" version of :meth:`read()` which will first inspect the
|
||||||
|
file(s) for a service-specific directive, the presence of which
|
||||||
|
indicates the *true* config file to be used for the service.
|
||||||
|
|
||||||
|
This method is pretty much a hack to get around certain limitations of
|
||||||
|
Windows service implementations; it is not used otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
config = ConfigParser.SafeConfigParser()
|
||||||
|
config.read(paths)
|
||||||
|
|
||||||
|
if (config.has_section('edbob.service_config')
|
||||||
|
and config.has_option('edbob.service_config', service)):
|
||||||
|
paths = eval(config.get('edbob.service_config', service))
|
||||||
|
|
||||||
|
self.read(paths, recurse=True)
|
||||||
|
|
||||||
def require(self, section, option, msg=None):
|
def require(self, section, option, msg=None):
|
||||||
"""
|
"""
|
||||||
Convenience method which will raise an exception if the given option
|
Convenience method which will raise an exception if the given option
|
||||||
|
|
|
@ -37,7 +37,6 @@ import edbob
|
||||||
|
|
||||||
__all__ = ['engines', 'engine', 'Session', 'get_setting', 'save_setting']
|
__all__ = ['engines', 'engine', 'Session', 'get_setting', 'save_setting']
|
||||||
|
|
||||||
inited = False
|
|
||||||
engines = None
|
engines = None
|
||||||
engine = None
|
engine = None
|
||||||
Session = sessionmaker()
|
Session = sessionmaker()
|
||||||
|
@ -71,36 +70,36 @@ def init(config):
|
||||||
from edbob.db import enum
|
from edbob.db import enum
|
||||||
from edbob.db.extensions import extend_framework
|
from edbob.db.extensions import extend_framework
|
||||||
|
|
||||||
global inited, engines, engine
|
global engines, engine
|
||||||
|
|
||||||
keys = config.get('edbob.db', 'sqlalchemy.keys')
|
keys = config.get('edbob.db', 'keys')
|
||||||
if keys:
|
if keys:
|
||||||
keys = keys.split()
|
keys = keys.split(',')
|
||||||
else:
|
else:
|
||||||
keys = ['default']
|
keys = ['default']
|
||||||
|
|
||||||
engines = {}
|
engines = {}
|
||||||
cfg = config.get_dict('edbob.db')
|
cfg = config.get_dict('edbob.db')
|
||||||
for key in keys:
|
for key in keys:
|
||||||
|
key = key.strip()
|
||||||
try:
|
try:
|
||||||
engines[key] = engine_from_config(cfg, 'sqlalchemy.%s.' % key)
|
engines[key] = engine_from_config(cfg, '%s.' % key)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if key == 'default':
|
if key == 'default':
|
||||||
try:
|
try:
|
||||||
engines[key] = engine_from_config(cfg)
|
engines[key] = engine_from_config(cfg, 'sqlalchemy.')
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
engine = engines.get('default')
|
engine = engines.get('default')
|
||||||
if engine:
|
if engine:
|
||||||
Base.metadata.bind = engine
|
Session.configure(bind=engine)
|
||||||
|
|
||||||
extend_framework()
|
extend_framework()
|
||||||
|
|
||||||
edbob.graft(edbob, edbob.db)
|
edbob.graft(edbob, edbob.db)
|
||||||
edbob.graft(edbob, model)
|
edbob.graft(edbob, model)
|
||||||
edbob.graft(edbob, enum)
|
edbob.graft(edbob, enum)
|
||||||
inited = True
|
|
||||||
|
|
||||||
|
|
||||||
def get_setting(name, session=None):
|
def get_setting(name, session=None):
|
||||||
|
|
|
@ -85,12 +85,12 @@ def guest_role(session):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
uuid = 'f8a27c98965a11dfaff7001143047286'
|
uuid = 'f8a27c98965a11dfaff7001143047286'
|
||||||
admin = session.query(edbob.Role).get(uuid)
|
guest = session.query(edbob.Role).get(uuid)
|
||||||
if admin:
|
if guest:
|
||||||
return admin
|
return guest
|
||||||
admin = edbob.Role(uuid=uuid, name='Guest')
|
guest = edbob.Role(uuid=uuid, name='Guest')
|
||||||
session.add(admin)
|
session.add(guest)
|
||||||
return admin
|
return guest
|
||||||
|
|
||||||
|
|
||||||
def grant_permission(role, permission, session=None):
|
def grant_permission(role, permission, session=None):
|
||||||
|
@ -114,7 +114,7 @@ def has_permission(obj, perm, session=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(obj, edbob.User):
|
if isinstance(obj, edbob.User):
|
||||||
roles = obj.roles
|
roles = list(obj.roles)
|
||||||
elif isinstance(obj, edbob.Role):
|
elif isinstance(obj, edbob.Role):
|
||||||
roles = [obj]
|
roles = [obj]
|
||||||
elif obj is None:
|
elif obj is None:
|
||||||
|
|
|
@ -76,6 +76,11 @@ class Extension(edbob.Object):
|
||||||
# :meth:`Extension.get_models_module()` for more info).
|
# :meth:`Extension.get_models_module()` for more info).
|
||||||
model_module = ''
|
model_module = ''
|
||||||
|
|
||||||
|
# You can set this to any dotted module path you like. If unset a default
|
||||||
|
# will be assumed, of the form ``<path.to.extension>.enum`` (see
|
||||||
|
# :meth:`Extension.get_enum_module()` for more info).
|
||||||
|
enum_module = ''
|
||||||
|
|
||||||
# @property
|
# @property
|
||||||
# @requires_impl(is_property=True)
|
# @requires_impl(is_property=True)
|
||||||
# def name(self):
|
# def name(self):
|
||||||
|
@ -119,6 +124,9 @@ class Extension(edbob.Object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
edbob.graft(edbob, self.get_model_module())
|
edbob.graft(edbob, self.get_model_module())
|
||||||
|
enum = self.get_enum_module()
|
||||||
|
if enum:
|
||||||
|
edbob.graft(edbob, enum)
|
||||||
|
|
||||||
# def extend_mappers(self, metadata):
|
# def extend_mappers(self, metadata):
|
||||||
# """
|
# """
|
||||||
|
@ -144,6 +152,26 @@ class Extension(edbob.Object):
|
||||||
self.populate_metadata(meta, recurse)
|
self.populate_metadata(meta, recurse)
|
||||||
return meta
|
return meta
|
||||||
|
|
||||||
|
def get_enum_module(self):
|
||||||
|
"""
|
||||||
|
Imports and returns a reference to the Python module providing
|
||||||
|
enumeration data for the extension (if one exists).
|
||||||
|
|
||||||
|
:attr:`Extension.enum_module` is first consulted to determine the
|
||||||
|
dotted module path. If nothing is found there, a default path is
|
||||||
|
constructed by appending ``'.enum'`` to the extension module's own
|
||||||
|
dotted path.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.enum_module:
|
||||||
|
module = self.enum_module
|
||||||
|
else:
|
||||||
|
module = str(self.__class__.__module__) + '.enum'
|
||||||
|
try:
|
||||||
|
return import_module_path(module)
|
||||||
|
except ImportError:
|
||||||
|
return None
|
||||||
|
|
||||||
def get_model_module(self):
|
def get_model_module(self):
|
||||||
"""
|
"""
|
||||||
Imports and returns a reference to the Python module providing schema
|
Imports and returns a reference to the Python module providing schema
|
||||||
|
|
40
edbob/db/extensions/contact/enum.py
Normal file
40
edbob/db/extensions/contact/enum.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/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.contact.enum`` -- Enumerations
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
EMAIL_PREFERENCE_NONE = 0
|
||||||
|
EMAIL_PREFERENCE_TEXT = 1
|
||||||
|
EMAIL_PREFERENCE_HTML = 2
|
||||||
|
EMAIL_PREFERENCE_MOBILE = 3
|
||||||
|
|
||||||
|
EMAIL_PREFERENCE = {
|
||||||
|
EMAIL_PREFERENCE_NONE : "No Emails",
|
||||||
|
EMAIL_PREFERENCE_TEXT : "Text",
|
||||||
|
EMAIL_PREFERENCE_HTML : "HTML",
|
||||||
|
EMAIL_PREFERENCE_MOBILE : "Mobile",
|
||||||
|
}
|
|
@ -35,7 +35,7 @@ import threading
|
||||||
import edbob
|
import edbob
|
||||||
from edbob.errors import email_exception
|
from edbob.errors import email_exception
|
||||||
from edbob.filemon import get_monitor_profiles
|
from edbob.filemon import get_monitor_profiles
|
||||||
from edbob.win32 import file_is_free
|
from edbob.win32 import Service, file_is_free
|
||||||
|
|
||||||
if sys.platform == 'win32': # docs should build for everyone
|
if sys.platform == 'win32': # docs should build for everyone
|
||||||
import win32api
|
import win32api
|
||||||
|
@ -50,36 +50,23 @@ if sys.platform == 'win32': # docs should build for everyone
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FileMonitorService(win32serviceutil.ServiceFramework):
|
class FileMonitorService(Service):
|
||||||
"""
|
"""
|
||||||
Implements edbob's file monitor Windows service.
|
Implements edbob's file monitor Windows service.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_svc_name_ = "Edbob File Monitor"
|
_svc_name_ = 'EdbobFileMonitor'
|
||||||
_svc_display_name_ = "Edbob : File Monitoring Service"
|
_svc_display_name_ = "Edbob : File Monitoring Service"
|
||||||
_svc_description_ = ("Monitors one or more folders for incoming files, "
|
_svc_description_ = ("Monitors one or more folders for incoming files, "
|
||||||
"and performs configured actions as new files arrive.")
|
"and performs configured actions as new files arrive.")
|
||||||
|
|
||||||
appname = 'edbob'
|
|
||||||
|
|
||||||
def __init__(self, args):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# super(FileMonitorService, self).__init__(args)
|
|
||||||
win32serviceutil.ServiceFramework.__init__(self, args)
|
|
||||||
|
|
||||||
# Create "wait stop" event, for main worker loop.
|
|
||||||
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
|
|
||||||
|
|
||||||
def Initialize(self):
|
def Initialize(self):
|
||||||
"""
|
"""
|
||||||
Service initialization.
|
Service initialization.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Read configuration file(s).
|
if not Service.Initialize(self):
|
||||||
edbob.init(self.appname)
|
return False
|
||||||
|
|
||||||
# Read monitor profile(s) from config.
|
# Read monitor profile(s) from config.
|
||||||
self.monitored = get_monitor_profiles(self.appname)
|
self.monitored = get_monitor_profiles(self.appname)
|
||||||
|
@ -118,50 +105,6 @@ class FileMonitorService(win32serviceutil.ServiceFramework):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def SvcDoRun(self):
|
|
||||||
"""
|
|
||||||
This method is invoked when the service starts.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import servicemanager
|
|
||||||
|
|
||||||
# Write start occurrence to Windows Event Log.
|
|
||||||
servicemanager.LogMsg(
|
|
||||||
servicemanager.EVENTLOG_INFORMATION_TYPE,
|
|
||||||
servicemanager.PYS_SERVICE_STARTED,
|
|
||||||
(self._svc_name_, ''))
|
|
||||||
|
|
||||||
# Figure out what we're supposed to be doing.
|
|
||||||
if self.Initialize():
|
|
||||||
|
|
||||||
# Wait infinitely for stop request, while threads do their thing.
|
|
||||||
log.info("SvcDoRun: All threads started; waiting for stop request.")
|
|
||||||
win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
|
|
||||||
log.info("SvcDoRun: Stop request received.")
|
|
||||||
|
|
||||||
else: # Nothing to be done...
|
|
||||||
msg = "Nothing to do! No valid monitor profiles found in config."
|
|
||||||
servicemanager.LogWarningMsg(msg)
|
|
||||||
log.warning("SvcDoRun: %s" % msg)
|
|
||||||
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
|
|
||||||
|
|
||||||
# Write stop occurrence to Windows Event Log.
|
|
||||||
servicemanager.LogMsg(
|
|
||||||
servicemanager.EVENTLOG_INFORMATION_TYPE,
|
|
||||||
servicemanager.PYS_SERVICE_STOPPED,
|
|
||||||
(self._svc_name_, ''))
|
|
||||||
|
|
||||||
def SvcStop(self):
|
|
||||||
"""
|
|
||||||
This method is invoked when the service is requested to stop itself.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Let the SCM know we're trying to stop.
|
|
||||||
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
|
|
||||||
|
|
||||||
# Let worker loop know its job is done.
|
|
||||||
win32event.SetEvent(self.hWaitStop)
|
|
||||||
|
|
||||||
|
|
||||||
def monitor_files(queue, path, profile):
|
def monitor_files(queue, path, profile):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -38,7 +38,9 @@ from edbob.configuration import (
|
||||||
from edbob.exceptions import InitError
|
from edbob.exceptions import InitError
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['init']
|
__all__ = ['init', 'init_modules', 'inited']
|
||||||
|
|
||||||
|
inited = []
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -80,22 +82,40 @@ def init(appname='edbob', *args, **kwargs):
|
||||||
else:
|
else:
|
||||||
config_paths = default_system_paths(appname) + default_user_paths(appname)
|
config_paths = default_system_paths(appname) + default_user_paths(appname)
|
||||||
|
|
||||||
shell = bool(kwargs.get('shell'))
|
service = kwargs.get('service')
|
||||||
for paths in config_paths:
|
if service:
|
||||||
config.read(paths, recurse=not shell)
|
config.read_service(service, config_paths)
|
||||||
|
else:
|
||||||
|
shell = kwargs.get('shell', False)
|
||||||
|
for paths in config_paths:
|
||||||
|
config.read(paths, recurse=not shell)
|
||||||
config.configure_logging()
|
config.configure_logging()
|
||||||
|
|
||||||
default_modules = 'edbob.time'
|
default_modules = 'edbob.time'
|
||||||
modules = config.get('edbob', 'init', default=default_modules)
|
modules = config.get('edbob', 'init', default=default_modules)
|
||||||
if modules:
|
if modules:
|
||||||
for name in modules.split(','):
|
modules = modules.split(',')
|
||||||
name = name.strip()
|
init_modules(modules, config)
|
||||||
|
|
||||||
|
edbob.graft(edbob, locals(), 'config')
|
||||||
|
inited.append('edbob')
|
||||||
|
|
||||||
|
|
||||||
|
def init_modules(names, config=None):
|
||||||
|
"""
|
||||||
|
Initialize the given modules. ``names`` should be a sequence of strings,
|
||||||
|
each of which should be a dotted module name. If ``config`` is not
|
||||||
|
specified, :attr:`edbob.config` is assumed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if config is None:
|
||||||
|
config = edbob.config
|
||||||
|
|
||||||
|
for name in names:
|
||||||
|
name = name.strip()
|
||||||
|
if name not in inited:
|
||||||
module = __import__(name, globals(), locals(), fromlist=['init'])
|
module = __import__(name, globals(), locals(), fromlist=['init'])
|
||||||
if not hasattr(module, 'init'):
|
if not hasattr(module, 'init'):
|
||||||
raise InitError(module)
|
raise InitError(module)
|
||||||
getattr(module, 'init')(config)
|
getattr(module, 'init')(config)
|
||||||
# config.inited.append(name)
|
inited.append(name)
|
||||||
|
|
||||||
# config.inited.append('edbob')
|
|
||||||
edbob.graft(edbob, locals(), 'config')
|
|
||||||
edbob.inited = True
|
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||||
from zope.sqlalchemy import ZopeTransactionExtension
|
from zope.sqlalchemy import ZopeTransactionExtension
|
||||||
|
|
||||||
|
import edbob
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['Session']
|
__all__ = ['Session']
|
||||||
|
|
||||||
|
@ -56,6 +58,7 @@ def includeme(config):
|
||||||
config.include('pyramid_tm')
|
config.include('pyramid_tm')
|
||||||
|
|
||||||
# Configure SQLAlchemy session.
|
# Configure SQLAlchemy session.
|
||||||
|
Session.configure(bind=edbob.engine)
|
||||||
Session.configure(extension=ZopeTransactionExtension())
|
Session.configure(extension=ZopeTransactionExtension())
|
||||||
|
|
||||||
# Configure user authentication / authorization.
|
# Configure user authentication / authorization.
|
||||||
|
@ -69,3 +72,6 @@ def includeme(config):
|
||||||
|
|
||||||
# Add static views.
|
# Add static views.
|
||||||
config.include('edbob.pyramid.static')
|
config.include('edbob.pyramid.static')
|
||||||
|
|
||||||
|
# Add subscriber hooks.
|
||||||
|
config.include('edbob.pyramid.subscribers')
|
||||||
|
|
|
@ -74,12 +74,10 @@ def EnumFieldRenderer(enum):
|
||||||
return ''
|
return ''
|
||||||
if value in enum:
|
if value in enum:
|
||||||
return enum[value]
|
return enum[value]
|
||||||
return value
|
return str(value)
|
||||||
|
|
||||||
def render(self, **kwargs):
|
def render(self, **kwargs):
|
||||||
opts = []
|
opts = [(enum[x], x) for x in sorted(enum)]
|
||||||
for value in sorted(enum):
|
|
||||||
opts.append((enum[value], value))
|
|
||||||
return formalchemy.fields.SelectFieldRenderer.render(self, opts, **kwargs)
|
return formalchemy.fields.SelectFieldRenderer.render(self, opts, **kwargs)
|
||||||
|
|
||||||
return Renderer
|
return Renderer
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<%inherit file="/form.mako" />
|
<%inherit file="/form.mako" />
|
||||||
|
|
||||||
<%def name="title()">${"New "+form.pretty_name if form.creating else form.pretty_name+' : '+str(form.fieldset.model)}</%def>
|
<%def name="title()">${"New "+form.pretty_name if form.creating else form.pretty_name+' : '+h.literal(str(form.fieldset.model))}</%def>
|
||||||
|
|
||||||
${parent.body()}
|
${parent.body()}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
<%inherit file="/base.mako" />
|
|
||||||
${parent.body()}
|
|
28
edbob/pyramid/templates/people/crud.mako
Normal file
28
edbob/pyramid/templates/people/crud.mako
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<%inherit file="/crud.mako" />
|
||||||
|
|
||||||
|
<%def name="crud_name()">Person</%def>
|
||||||
|
|
||||||
|
<%def name="context_menu_items()">
|
||||||
|
<li>${h.link_to("Back to People", url('people'))}</li>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
||||||
|
|
||||||
|
## % if fieldset.edit:
|
||||||
|
## <h2>User Info</h2>
|
||||||
|
## % if user:
|
||||||
|
## ${user.render()|n}
|
||||||
|
## <div class="buttons">
|
||||||
|
## <button type="button" onclick="location.href = '${url('user.edit', uuid=user.model.uuid)}';">Edit User</button>
|
||||||
|
## </div>
|
||||||
|
## % else:
|
||||||
|
## <p>This person does not have a user account.</p>
|
||||||
|
## ${h.form(url('user.new'))}
|
||||||
|
## ${h.hidden('User--person_uuid', value=fieldset.model.uuid)}
|
||||||
|
## ${h.hidden('User--username')}
|
||||||
|
## <div class="buttons">
|
||||||
|
## ${h.submit('submit', "Create User")}
|
||||||
|
## </div>
|
||||||
|
## ${h.end_form()}
|
||||||
|
## % endif
|
||||||
|
## % endif
|
|
@ -1,12 +1,11 @@
|
||||||
<%inherit file="/people/base.mako" />
|
<%inherit file="/grid.mako" />
|
||||||
<%inherit file="/index.mako" />
|
|
||||||
|
|
||||||
<%def name="title()">People</%def>
|
<%def name="title()">People</%def>
|
||||||
|
|
||||||
<%def name="context_menu_items()">
|
<%def name="context_menu_items()">
|
||||||
% if request.has_perm('people.create'):
|
## % if request.has_perm('people.create'):
|
||||||
<li>${h.link_to("Create a new Person", url('person.new'))}</li>
|
## <li>${h.link_to("Create a new Person", url('person.new'))}</li>
|
||||||
% endif
|
## % endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
${parent.body()}
|
${parent.body()}
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
<%inherit file="/people/base.mako" />
|
|
||||||
<%inherit file="/crud.mako" />
|
|
||||||
|
|
||||||
<%def name="crud_name()">Person</%def>
|
|
||||||
|
|
||||||
<%def name="menu()">
|
|
||||||
<p>${h.link_to("Back to People", url('people.list'))}</p>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
${parent.body()}
|
|
||||||
|
|
||||||
% if fieldset.edit:
|
|
||||||
<h2>User Info</h2>
|
|
||||||
% if user:
|
|
||||||
${user.render()|n}
|
|
||||||
<div class="buttons">
|
|
||||||
<button type="button" onclick="location.href = '${url('user.edit', uuid=user.model.uuid)}';">Edit User</button>
|
|
||||||
</div>
|
|
||||||
% else:
|
|
||||||
<p>This person does not have a user account.</p>
|
|
||||||
${h.form(url('user.new'))}
|
|
||||||
${h.hidden('User--person_uuid', value=fieldset.model.uuid)}
|
|
||||||
${h.hidden('User--username')}
|
|
||||||
<div class="buttons">
|
|
||||||
${h.submit('submit', "Create User")}
|
|
||||||
</div>
|
|
||||||
${h.end_form()}
|
|
||||||
% endif
|
|
||||||
% endif
|
|
|
@ -26,155 +26,239 @@
|
||||||
``edbob.pyramid.views.people`` -- Person Views
|
``edbob.pyramid.views.people`` -- Person Views
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import transaction
|
from sqlalchemy import and_
|
||||||
from pyramid.httpexceptions import HTTPFound
|
|
||||||
|
|
||||||
from formalchemy import Field
|
# import transaction
|
||||||
|
# from pyramid.httpexceptions import HTTPFound
|
||||||
|
|
||||||
|
# from formalchemy import Field
|
||||||
|
|
||||||
import edbob
|
import edbob
|
||||||
from edbob.pyramid import filters
|
# from edbob.pyramid import filters
|
||||||
from edbob.pyramid import forms
|
# from edbob.pyramid import forms
|
||||||
from edbob.pyramid import grids
|
# from edbob.pyramid import grids
|
||||||
from edbob.pyramid import Session
|
# from edbob.pyramid import Session
|
||||||
|
from edbob.pyramid.views import SearchableAlchemyGridView, CrudView
|
||||||
|
|
||||||
|
|
||||||
def filter_map():
|
class PeopleGrid(SearchableAlchemyGridView):
|
||||||
return filters.get_filter_map(
|
|
||||||
edbob.Person,
|
|
||||||
ilike=['first_name', 'last_name', 'display_name'])
|
|
||||||
|
|
||||||
def search_config(request, fmap):
|
mapped_class = edbob.Person
|
||||||
return filters.get_search_config(
|
config_prefix = 'people'
|
||||||
'people.list', request, fmap,
|
sort = 'first_name'
|
||||||
include_filter_display_name=True,
|
|
||||||
filter_type_display_name='lk')
|
|
||||||
|
|
||||||
def search_form(config):
|
def join_map(self):
|
||||||
return filters.get_search_form(config)
|
return {
|
||||||
|
'email':
|
||||||
|
lambda q: q.outerjoin(edbob.PersonEmailAddress, and_(
|
||||||
|
edbob.PersonEmailAddress.parent_uuid == edbob.Person.uuid,
|
||||||
|
edbob.PersonEmailAddress.preference == 1)),
|
||||||
|
'phone':
|
||||||
|
lambda q: q.outerjoin(edbob.PersonPhoneNumber, and_(
|
||||||
|
edbob.PersonPhoneNumber.parent_uuid == edbob.Person.uuid,
|
||||||
|
edbob.PersonPhoneNumber.preference == 1)),
|
||||||
|
}
|
||||||
|
|
||||||
def grid_config(request, search, fmap):
|
def filter_map(self):
|
||||||
return grids.get_grid_config(
|
return self.make_filter_map(
|
||||||
'people.list', request, search,
|
ilike=['first_name', 'last_name'],
|
||||||
filter_map=fmap, sort='display_name')
|
email=self.filter_ilike(edbob.PersonEmailAddress.address),
|
||||||
|
phone=self.filter_ilike(edbob.PersonPhoneNumber.number))
|
||||||
|
|
||||||
def sort_map():
|
def filter_config(self):
|
||||||
return grids.get_sort_map(
|
return self.make_filter_config(
|
||||||
edbob.Person,
|
include_filter_first_name=True,
|
||||||
['first_name', 'last_name', 'display_name'])
|
filter_type_first_name='lk',
|
||||||
|
include_filter_last_name=True,
|
||||||
|
filter_type_last_name='lk',
|
||||||
|
filter_label_phone="Phone Number",
|
||||||
|
filter_label_email="Email Address")
|
||||||
|
|
||||||
def query(config):
|
def sort_map(self):
|
||||||
smap = sort_map()
|
return self.make_sort_map(
|
||||||
q = Session.query(edbob.Person)
|
'first_name', 'last_name',
|
||||||
q = filters.filter_query(q, config)
|
email=self.sorter(edbob.PersonEmailAddress.address),
|
||||||
q = grids.sort_query(q, config, smap)
|
phone=self.sorter(edbob.PersonPhoneNumber.number))
|
||||||
return q
|
|
||||||
|
def grid(self):
|
||||||
|
g = self.make_grid()
|
||||||
|
g.configure(
|
||||||
|
include=[
|
||||||
|
g.first_name,
|
||||||
|
g.last_name,
|
||||||
|
g.phone.label("Phone Number"),
|
||||||
|
g.email.label("Email Address"),
|
||||||
|
],
|
||||||
|
readonly=True)
|
||||||
|
g.clickable = True
|
||||||
|
g.click_route_name = 'person.read'
|
||||||
|
return g
|
||||||
|
|
||||||
|
|
||||||
def people(context, request):
|
class PersonCrud(CrudView):
|
||||||
|
|
||||||
fmap = filter_map()
|
mapped_class = edbob.Person
|
||||||
config = search_config(request, fmap)
|
home_route = 'people'
|
||||||
search = search_form(config)
|
|
||||||
config = grid_config(request, search, fmap)
|
|
||||||
people = grids.get_pager(query, config)
|
|
||||||
|
|
||||||
g = forms.AlchemyGrid(
|
def fieldset(self, model):
|
||||||
edbob.Person, people, config,
|
fs = self.make_fieldset(model)
|
||||||
gridurl=request.route_url('people.list'),
|
fs.configure(
|
||||||
objurl='person.edit')
|
include=[
|
||||||
|
fs.first_name,
|
||||||
g.configure(
|
fs.last_name,
|
||||||
include=[
|
fs.phone.label("Phone Number"),
|
||||||
g.first_name,
|
fs.email.label("Email Address"),
|
||||||
g.last_name,
|
])
|
||||||
g.display_name,
|
return fs
|
||||||
],
|
|
||||||
readonly=True)
|
|
||||||
|
|
||||||
grid = g.render(class_='clickable people')
|
|
||||||
return grids.render_grid(request, grid, search)
|
|
||||||
|
|
||||||
|
|
||||||
def person_fieldset(person, request):
|
# def filter_map():
|
||||||
fs = forms.make_fieldset(person, url=request.route_url,
|
# return filters.get_filter_map(
|
||||||
url_action=request.current_route_url(),
|
# edbob.Person,
|
||||||
route_name='people.list')
|
# ilike=['first_name', 'last_name', 'display_name'])
|
||||||
fs.configure(
|
|
||||||
include=[
|
# def search_config(request, fmap):
|
||||||
fs.first_name,
|
# return filters.get_search_config(
|
||||||
fs.last_name,
|
# 'people.list', request, fmap,
|
||||||
fs.display_name,
|
# include_filter_display_name=True,
|
||||||
])
|
# filter_type_display_name='lk')
|
||||||
return fs
|
|
||||||
|
# def search_form(config):
|
||||||
|
# return filters.get_search_form(config)
|
||||||
|
|
||||||
|
# def grid_config(request, search, fmap):
|
||||||
|
# return grids.get_grid_config(
|
||||||
|
# 'people.list', request, search,
|
||||||
|
# filter_map=fmap, sort='display_name')
|
||||||
|
|
||||||
|
# def sort_map():
|
||||||
|
# return grids.get_sort_map(
|
||||||
|
# edbob.Person,
|
||||||
|
# ['first_name', 'last_name', 'display_name'])
|
||||||
|
|
||||||
|
# def query(config):
|
||||||
|
# smap = sort_map()
|
||||||
|
# q = Session.query(edbob.Person)
|
||||||
|
# q = filters.filter_query(q, config)
|
||||||
|
# q = grids.sort_query(q, config, smap)
|
||||||
|
# return q
|
||||||
|
|
||||||
|
|
||||||
def new_person(context, request):
|
# def people(context, request):
|
||||||
|
|
||||||
fs = person_fieldset(edbob.Person, request)
|
# fmap = filter_map()
|
||||||
if not fs.readonly and request.POST:
|
# config = search_config(request, fmap)
|
||||||
fs.rebind(data=request.params)
|
# search = search_form(config)
|
||||||
if fs.validate():
|
# config = grid_config(request, search, fmap)
|
||||||
|
# people = grids.get_pager(query, config)
|
||||||
|
|
||||||
with transaction.manager:
|
# g = forms.AlchemyGrid(
|
||||||
fs.sync()
|
# edbob.Person, people, config,
|
||||||
Session.add(fs.model)
|
# gridurl=request.route_url('people.list'),
|
||||||
Session.flush()
|
# objurl='person.edit')
|
||||||
request.session.flash("%s \"%s\" has been %s." % (
|
|
||||||
fs.crud_title, fs.get_display_text(),
|
|
||||||
'updated' if fs.edit else 'created'))
|
|
||||||
|
|
||||||
return HTTPFound(location=request.route_url('people.list'))
|
# g.configure(
|
||||||
|
# include=[
|
||||||
|
# g.first_name,
|
||||||
|
# g.last_name,
|
||||||
|
# g.display_name,
|
||||||
|
# ],
|
||||||
|
# readonly=True)
|
||||||
|
|
||||||
return {'fieldset': fs, 'crud': True}
|
# grid = g.render(class_='clickable people')
|
||||||
|
# return grids.render_grid(request, grid, search)
|
||||||
|
|
||||||
|
|
||||||
def edit_person(request):
|
# def person_fieldset(person, request):
|
||||||
"""
|
# fs = forms.make_fieldset(person, url=request.route_url,
|
||||||
View for editing a :class:`edbob.Person` instance.
|
# url_action=request.current_route_url(),
|
||||||
"""
|
# route_name='people.list')
|
||||||
|
# fs.configure(
|
||||||
|
# include=[
|
||||||
|
# fs.first_name,
|
||||||
|
# fs.last_name,
|
||||||
|
# fs.display_name,
|
||||||
|
# ])
|
||||||
|
# return fs
|
||||||
|
|
||||||
from edbob.pyramid.views.users import user_fieldset
|
|
||||||
|
|
||||||
uuid = request.matchdict['uuid']
|
# def new_person(context, request):
|
||||||
person = Session.query(edbob.Person).get(uuid) if uuid else None
|
|
||||||
assert person
|
|
||||||
|
|
||||||
fs = person_fieldset(person, request)
|
# fs = person_fieldset(edbob.Person, request)
|
||||||
if request.POST:
|
# if not fs.readonly and request.POST:
|
||||||
fs.rebind(data=request.params)
|
# fs.rebind(data=request.params)
|
||||||
if fs.validate():
|
# if fs.validate():
|
||||||
|
|
||||||
with transaction.manager:
|
# with transaction.manager:
|
||||||
fs.sync()
|
# fs.sync()
|
||||||
fs.model = Session.merge(fs.model)
|
# Session.add(fs.model)
|
||||||
request.session.flash("%s \"%s\" has been %s." % (
|
# Session.flush()
|
||||||
fs.crud_title, fs.get_display_text(),
|
# request.session.flash("%s \"%s\" has been %s." % (
|
||||||
'updated' if fs.edit else 'created'))
|
# fs.crud_title, fs.get_display_text(),
|
||||||
home = request.route_url('people.list')
|
# 'updated' if fs.edit else 'created'))
|
||||||
|
|
||||||
return HTTPFound(location=home)
|
# return HTTPFound(location=request.route_url('people.list'))
|
||||||
|
|
||||||
user = fs.model.user
|
# return {'fieldset': fs, 'crud': True}
|
||||||
if user:
|
|
||||||
user = user_fieldset(user, request)
|
|
||||||
user.readonly = True
|
|
||||||
del user.person
|
|
||||||
del user.password
|
|
||||||
del user.confirm_password
|
|
||||||
|
|
||||||
return {'fieldset': fs, 'crud': True, 'user': user}
|
|
||||||
|
# def edit_person(request):
|
||||||
|
# """
|
||||||
|
# View for editing a :class:`edbob.Person` instance.
|
||||||
|
# """
|
||||||
|
|
||||||
|
# from edbob.pyramid.views.users import user_fieldset
|
||||||
|
|
||||||
|
# uuid = request.matchdict['uuid']
|
||||||
|
# person = Session.query(edbob.Person).get(uuid) if uuid else None
|
||||||
|
# assert person
|
||||||
|
|
||||||
|
# fs = person_fieldset(person, request)
|
||||||
|
# if request.POST:
|
||||||
|
# fs.rebind(data=request.params)
|
||||||
|
# if fs.validate():
|
||||||
|
|
||||||
|
# with transaction.manager:
|
||||||
|
# fs.sync()
|
||||||
|
# fs.model = Session.merge(fs.model)
|
||||||
|
# request.session.flash("%s \"%s\" has been %s." % (
|
||||||
|
# fs.crud_title, fs.get_display_text(),
|
||||||
|
# 'updated' if fs.edit else 'created'))
|
||||||
|
# home = request.route_url('people.list')
|
||||||
|
|
||||||
|
# return HTTPFound(location=home)
|
||||||
|
|
||||||
|
# user = fs.model.user
|
||||||
|
# if user:
|
||||||
|
# user = user_fieldset(user, request)
|
||||||
|
# user.readonly = True
|
||||||
|
# del user.person
|
||||||
|
# del user.password
|
||||||
|
# del user.confirm_password
|
||||||
|
|
||||||
|
# return {'fieldset': fs, 'crud': True, 'user': user}
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
|
|
||||||
config.add_route('people.list', '/people')
|
config.add_route('people', '/people')
|
||||||
config.add_view(people, route_name='people.list', renderer='/people/index.mako',
|
config.add_view(PeopleGrid, route_name='people',
|
||||||
permission='people.list', http_cache=0)
|
renderer='/people/index.mako',
|
||||||
|
permission='people.list')
|
||||||
|
|
||||||
config.add_route('person.new', '/people/new')
|
# config.add_route('people.list', '/people')
|
||||||
config.add_view(new_person, route_name='person.new', renderer='/people/person.mako',
|
# config.add_view(people, route_name='people.list', renderer='/people/index.mako',
|
||||||
permission='people.create', http_cache=0)
|
# permission='people.list', http_cache=0)
|
||||||
|
|
||||||
config.add_route('person.edit', '/people/{uuid}/edit')
|
config.add_route('person.read', '/people/{uuid}')
|
||||||
config.add_view(edit_person, route_name='person.edit', renderer='/people/person.mako',
|
config.add_view(PersonCrud, attr='read', route_name='person.read',
|
||||||
permission='people.edit', http_cache=0)
|
renderer='/people/crud.mako',
|
||||||
|
permission='people.read')
|
||||||
|
|
||||||
|
# config.add_route('person.new', '/people/new')
|
||||||
|
# config.add_view(new_person, route_name='person.new', renderer='/people/person.mako',
|
||||||
|
# permission='people.create', http_cache=0)
|
||||||
|
|
||||||
|
# config.add_route('person.edit', '/people/{uuid}/edit')
|
||||||
|
# config.add_view(edit_person, route_name='person.edit', renderer='/people/person.mako',
|
||||||
|
# permission='people.edit', http_cache=0)
|
||||||
|
|
|
@ -69,7 +69,6 @@ setup(
|
||||||
],
|
],
|
||||||
|
|
||||||
install_requires = requires,
|
install_requires = requires,
|
||||||
tests_require = requires,
|
|
||||||
|
|
||||||
packages = find_packages(),
|
packages = find_packages(),
|
||||||
include_package_data = True,
|
include_package_data = True,
|
||||||
|
|
|
@ -28,16 +28,96 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import logging
|
||||||
|
|
||||||
if sys.platform == 'win32': # docs should build for everyone
|
if sys.platform == 'win32': # docs should build for everyone
|
||||||
import pywintypes
|
import pywintypes
|
||||||
import win32api
|
import win32api
|
||||||
import win32con
|
import win32con
|
||||||
|
import win32event
|
||||||
import win32file
|
import win32file
|
||||||
import win32print
|
import win32print
|
||||||
import win32service
|
import win32service
|
||||||
|
import win32serviceutil
|
||||||
import winerror
|
import winerror
|
||||||
|
|
||||||
|
import edbob
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Service(win32serviceutil.ServiceFramework):
|
||||||
|
"""
|
||||||
|
Base class for Windows service implementations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
appname = 'edbob'
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
"""
|
||||||
|
Constructor.
|
||||||
|
"""
|
||||||
|
|
||||||
|
win32serviceutil.ServiceFramework.__init__(self, args)
|
||||||
|
|
||||||
|
# Create "wait stop" event, for main worker loop.
|
||||||
|
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
|
||||||
|
|
||||||
|
def Initialize(self):
|
||||||
|
"""
|
||||||
|
Service initialization.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Read configuration file(s).
|
||||||
|
edbob.init(self.appname, service=self._svc_name_)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def SvcDoRun(self):
|
||||||
|
"""
|
||||||
|
This method is invoked when the service starts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import servicemanager
|
||||||
|
|
||||||
|
# Write start occurrence to Windows Event Log.
|
||||||
|
servicemanager.LogMsg(
|
||||||
|
servicemanager.EVENTLOG_INFORMATION_TYPE,
|
||||||
|
servicemanager.PYS_SERVICE_STARTED,
|
||||||
|
(self._svc_name_, ''))
|
||||||
|
|
||||||
|
# Figure out what we're supposed to be doing.
|
||||||
|
if self.Initialize():
|
||||||
|
|
||||||
|
# Wait infinitely for stop request, while threads do their thing.
|
||||||
|
log.info("SvcDoRun: All threads started; waiting for stop request.")
|
||||||
|
win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
|
||||||
|
log.info("SvcDoRun: Stop request received.")
|
||||||
|
|
||||||
|
else: # Nothing to be done...
|
||||||
|
msg = "Nothing to do! (Initialization failed.)"
|
||||||
|
servicemanager.LogWarningMsg(msg)
|
||||||
|
log.warning("SvcDoRun: %s" % msg)
|
||||||
|
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
|
||||||
|
|
||||||
|
# Write stop occurrence to Windows Event Log.
|
||||||
|
servicemanager.LogMsg(
|
||||||
|
servicemanager.EVENTLOG_INFORMATION_TYPE,
|
||||||
|
servicemanager.PYS_SERVICE_STOPPED,
|
||||||
|
(self._svc_name_, ''))
|
||||||
|
|
||||||
|
def SvcStop(self):
|
||||||
|
"""
|
||||||
|
This method is invoked when the service is requested to stop itself.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Let the SCM know we're trying to stop.
|
||||||
|
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
|
||||||
|
|
||||||
|
# Let worker loop know its job is done.
|
||||||
|
win32event.SetEvent(self.hWaitStop)
|
||||||
|
|
||||||
|
|
||||||
def RegDeleteTree(key, subkey):
|
def RegDeleteTree(key, subkey):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue