diff --git a/edbob/__init__.py b/edbob/__init__.py index aa2b830..8ab239f 100644 --- a/edbob/__init__.py +++ b/edbob/__init__.py @@ -34,6 +34,3 @@ from edbob.files import * from edbob.modules import * from edbob.configuration import * from edbob.initialization import * - - -inited = False diff --git a/edbob/configuration.py b/edbob/configuration.py index 4d8edbe..5991f7d 100644 --- a/edbob/configuration.py +++ b/edbob/configuration.py @@ -289,6 +289,25 @@ class AppConfigParser(ConfigParser.SafeConfigParser): self.paths_loaded.append(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): """ Convenience method which will raise an exception if the given option diff --git a/edbob/db/__init__.py b/edbob/db/__init__.py index 84f0d42..c3eac2f 100644 --- a/edbob/db/__init__.py +++ b/edbob/db/__init__.py @@ -37,7 +37,6 @@ import edbob __all__ = ['engines', 'engine', 'Session', 'get_setting', 'save_setting'] -inited = False engines = None engine = None Session = sessionmaker() @@ -71,36 +70,36 @@ def init(config): from edbob.db import enum 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: - keys = keys.split() + keys = keys.split(',') else: keys = ['default'] engines = {} cfg = config.get_dict('edbob.db') for key in keys: + key = key.strip() try: - engines[key] = engine_from_config(cfg, 'sqlalchemy.%s.' % key) + engines[key] = engine_from_config(cfg, '%s.' % key) except KeyError: if key == 'default': try: - engines[key] = engine_from_config(cfg) + engines[key] = engine_from_config(cfg, 'sqlalchemy.') except KeyError: pass engine = engines.get('default') if engine: - Base.metadata.bind = engine + Session.configure(bind=engine) extend_framework() edbob.graft(edbob, edbob.db) edbob.graft(edbob, model) edbob.graft(edbob, enum) - inited = True def get_setting(name, session=None): diff --git a/edbob/db/auth.py b/edbob/db/auth.py index cf1dec8..ad7d72f 100644 --- a/edbob/db/auth.py +++ b/edbob/db/auth.py @@ -85,12 +85,12 @@ def guest_role(session): """ uuid = 'f8a27c98965a11dfaff7001143047286' - admin = session.query(edbob.Role).get(uuid) - if admin: - return admin - admin = edbob.Role(uuid=uuid, name='Guest') - session.add(admin) - return admin + guest = session.query(edbob.Role).get(uuid) + if guest: + return guest + guest = edbob.Role(uuid=uuid, name='Guest') + session.add(guest) + return guest def grant_permission(role, permission, session=None): @@ -114,7 +114,7 @@ def has_permission(obj, perm, session=None): """ if isinstance(obj, edbob.User): - roles = obj.roles + roles = list(obj.roles) elif isinstance(obj, edbob.Role): roles = [obj] elif obj is None: diff --git a/edbob/db/extensions/__init__.py b/edbob/db/extensions/__init__.py index c99c7c7..cf875da 100644 --- a/edbob/db/extensions/__init__.py +++ b/edbob/db/extensions/__init__.py @@ -76,6 +76,11 @@ class Extension(edbob.Object): # :meth:`Extension.get_models_module()` for more info). model_module = '' + # You can set this to any dotted module path you like. If unset a default + # will be assumed, of the form ``.enum`` (see + # :meth:`Extension.get_enum_module()` for more info). + enum_module = '' + # @property # @requires_impl(is_property=True) # def name(self): @@ -119,6 +124,9 @@ class Extension(edbob.Object): """ edbob.graft(edbob, self.get_model_module()) + enum = self.get_enum_module() + if enum: + edbob.graft(edbob, enum) # def extend_mappers(self, metadata): # """ @@ -144,6 +152,26 @@ class Extension(edbob.Object): self.populate_metadata(meta, recurse) 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): """ Imports and returns a reference to the Python module providing schema diff --git a/edbob/db/extensions/contact/enum.py b/edbob/db/extensions/contact/enum.py new file mode 100644 index 0000000..9972273 --- /dev/null +++ b/edbob/db/extensions/contact/enum.py @@ -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 . +# +################################################################################ + +""" +``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", + } diff --git a/edbob/filemon/win32.py b/edbob/filemon/win32.py index a2a85e3..eca8485 100644 --- a/edbob/filemon/win32.py +++ b/edbob/filemon/win32.py @@ -35,7 +35,7 @@ import threading import edbob from edbob.errors import email_exception 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 import win32api @@ -50,36 +50,23 @@ if sys.platform == 'win32': # docs should build for everyone log = logging.getLogger(__name__) -class FileMonitorService(win32serviceutil.ServiceFramework): +class FileMonitorService(Service): """ Implements edbob's file monitor Windows service. """ - _svc_name_ = "Edbob File Monitor" + _svc_name_ = 'EdbobFileMonitor' _svc_display_name_ = "Edbob : File Monitoring Service" _svc_description_ = ("Monitors one or more folders for incoming files, " "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): """ Service initialization. """ - # Read configuration file(s). - edbob.init(self.appname) + if not Service.Initialize(self): + return False # Read monitor profile(s) from config. self.monitored = get_monitor_profiles(self.appname) @@ -118,50 +105,6 @@ class FileMonitorService(win32serviceutil.ServiceFramework): 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): """ diff --git a/edbob/initialization.py b/edbob/initialization.py index 4a93344..410b572 100644 --- a/edbob/initialization.py +++ b/edbob/initialization.py @@ -38,7 +38,9 @@ from edbob.configuration import ( from edbob.exceptions import InitError -__all__ = ['init'] +__all__ = ['init', 'init_modules', 'inited'] + +inited = [] log = logging.getLogger(__name__) @@ -80,22 +82,40 @@ def init(appname='edbob', *args, **kwargs): else: config_paths = default_system_paths(appname) + default_user_paths(appname) - shell = bool(kwargs.get('shell')) - for paths in config_paths: - config.read(paths, recurse=not shell) + service = kwargs.get('service') + if service: + 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() default_modules = 'edbob.time' modules = config.get('edbob', 'init', default=default_modules) if modules: - for name in modules.split(','): - name = name.strip() + modules = modules.split(',') + 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']) if not hasattr(module, 'init'): raise InitError(module) getattr(module, 'init')(config) - # config.inited.append(name) - - # config.inited.append('edbob') - edbob.graft(edbob, locals(), 'config') - edbob.inited = True + inited.append(name) diff --git a/edbob/pyramid/__init__.py b/edbob/pyramid/__init__.py index 64defc6..a3abb88 100644 --- a/edbob/pyramid/__init__.py +++ b/edbob/pyramid/__init__.py @@ -29,6 +29,8 @@ from sqlalchemy.orm import sessionmaker, scoped_session from zope.sqlalchemy import ZopeTransactionExtension +import edbob + __all__ = ['Session'] @@ -56,6 +58,7 @@ def includeme(config): config.include('pyramid_tm') # Configure SQLAlchemy session. + Session.configure(bind=edbob.engine) Session.configure(extension=ZopeTransactionExtension()) # Configure user authentication / authorization. @@ -69,3 +72,6 @@ def includeme(config): # Add static views. config.include('edbob.pyramid.static') + + # Add subscriber hooks. + config.include('edbob.pyramid.subscribers') diff --git a/edbob/pyramid/forms/formalchemy/renderers.py b/edbob/pyramid/forms/formalchemy/renderers.py index 263f728..a51d494 100644 --- a/edbob/pyramid/forms/formalchemy/renderers.py +++ b/edbob/pyramid/forms/formalchemy/renderers.py @@ -74,12 +74,10 @@ def EnumFieldRenderer(enum): return '' if value in enum: return enum[value] - return value + return str(value) def render(self, **kwargs): - opts = [] - for value in sorted(enum): - opts.append((enum[value], value)) + opts = [(enum[x], x) for x in sorted(enum)] return formalchemy.fields.SelectFieldRenderer.render(self, opts, **kwargs) return Renderer diff --git a/edbob/pyramid/templates/edbob/crud.mako b/edbob/pyramid/templates/edbob/crud.mako index 885dba3..4fc6112 100644 --- a/edbob/pyramid/templates/edbob/crud.mako +++ b/edbob/pyramid/templates/edbob/crud.mako @@ -1,5 +1,5 @@ <%inherit file="/form.mako" /> -<%def name="title()">${"New "+form.pretty_name if form.creating else form.pretty_name+' : '+str(form.fieldset.model)} +<%def name="title()">${"New "+form.pretty_name if form.creating else form.pretty_name+' : '+h.literal(str(form.fieldset.model))} ${parent.body()} diff --git a/edbob/pyramid/templates/people/base.mako b/edbob/pyramid/templates/people/base.mako deleted file mode 100644 index 27f7dd9..0000000 --- a/edbob/pyramid/templates/people/base.mako +++ /dev/null @@ -1,2 +0,0 @@ -<%inherit file="/base.mako" /> -${parent.body()} diff --git a/edbob/pyramid/templates/people/crud.mako b/edbob/pyramid/templates/people/crud.mako new file mode 100644 index 0000000..439dcb7 --- /dev/null +++ b/edbob/pyramid/templates/people/crud.mako @@ -0,0 +1,28 @@ +<%inherit file="/crud.mako" /> + +<%def name="crud_name()">Person + +<%def name="context_menu_items()"> +
  • ${h.link_to("Back to People", url('people'))}
  • + + +${parent.body()} + +## % if fieldset.edit: +##

    User Info

    +## % if user: +## ${user.render()|n} +##
    +## +##
    +## % else: +##

    This person does not have a user account.

    +## ${h.form(url('user.new'))} +## ${h.hidden('User--person_uuid', value=fieldset.model.uuid)} +## ${h.hidden('User--username')} +##
    +## ${h.submit('submit', "Create User")} +##
    +## ${h.end_form()} +## % endif +## % endif diff --git a/edbob/pyramid/templates/people/index.mako b/edbob/pyramid/templates/people/index.mako index 577fedf..77b7bad 100644 --- a/edbob/pyramid/templates/people/index.mako +++ b/edbob/pyramid/templates/people/index.mako @@ -1,12 +1,11 @@ -<%inherit file="/people/base.mako" /> -<%inherit file="/index.mako" /> +<%inherit file="/grid.mako" /> <%def name="title()">People <%def name="context_menu_items()"> - % if request.has_perm('people.create'): -
  • ${h.link_to("Create a new Person", url('person.new'))}
  • - % endif +## % if request.has_perm('people.create'): +##
  • ${h.link_to("Create a new Person", url('person.new'))}
  • +## % endif ${parent.body()} diff --git a/edbob/pyramid/templates/people/person.mako b/edbob/pyramid/templates/people/person.mako deleted file mode 100644 index c9f0cf5..0000000 --- a/edbob/pyramid/templates/people/person.mako +++ /dev/null @@ -1,29 +0,0 @@ -<%inherit file="/people/base.mako" /> -<%inherit file="/crud.mako" /> - -<%def name="crud_name()">Person - -<%def name="menu()"> -

    ${h.link_to("Back to People", url('people.list'))}

    - - -${parent.body()} - -% if fieldset.edit: -

    User Info

    - % if user: - ${user.render()|n} -
    - -
    - % else: -

    This person does not have a user account.

    - ${h.form(url('user.new'))} - ${h.hidden('User--person_uuid', value=fieldset.model.uuid)} - ${h.hidden('User--username')} -
    - ${h.submit('submit', "Create User")} -
    - ${h.end_form()} - % endif -% endif diff --git a/edbob/pyramid/views/people.py b/edbob/pyramid/views/people.py index a4c501e..afdf657 100644 --- a/edbob/pyramid/views/people.py +++ b/edbob/pyramid/views/people.py @@ -26,155 +26,239 @@ ``edbob.pyramid.views.people`` -- Person Views """ -import transaction -from pyramid.httpexceptions import HTTPFound +from sqlalchemy import and_ -from formalchemy import Field +# import transaction +# from pyramid.httpexceptions import HTTPFound + +# from formalchemy import Field import edbob -from edbob.pyramid import filters -from edbob.pyramid import forms -from edbob.pyramid import grids -from edbob.pyramid import Session +# from edbob.pyramid import filters +# from edbob.pyramid import forms +# from edbob.pyramid import grids +# from edbob.pyramid import Session +from edbob.pyramid.views import SearchableAlchemyGridView, CrudView -def filter_map(): - return filters.get_filter_map( - edbob.Person, - ilike=['first_name', 'last_name', 'display_name']) +class PeopleGrid(SearchableAlchemyGridView): -def search_config(request, fmap): - return filters.get_search_config( - 'people.list', request, fmap, - include_filter_display_name=True, - filter_type_display_name='lk') + mapped_class = edbob.Person + config_prefix = 'people' + sort = 'first_name' -def search_form(config): - return filters.get_search_form(config) + def join_map(self): + 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): - return grids.get_grid_config( - 'people.list', request, search, - filter_map=fmap, sort='display_name') + def filter_map(self): + return self.make_filter_map( + ilike=['first_name', 'last_name'], + email=self.filter_ilike(edbob.PersonEmailAddress.address), + phone=self.filter_ilike(edbob.PersonPhoneNumber.number)) -def sort_map(): - return grids.get_sort_map( - edbob.Person, - ['first_name', 'last_name', 'display_name']) + def filter_config(self): + return self.make_filter_config( + include_filter_first_name=True, + 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): - smap = sort_map() - q = Session.query(edbob.Person) - q = filters.filter_query(q, config) - q = grids.sort_query(q, config, smap) - return q + def sort_map(self): + return self.make_sort_map( + 'first_name', 'last_name', + email=self.sorter(edbob.PersonEmailAddress.address), + phone=self.sorter(edbob.PersonPhoneNumber.number)) + + 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() - config = search_config(request, fmap) - search = search_form(config) - config = grid_config(request, search, fmap) - people = grids.get_pager(query, config) + mapped_class = edbob.Person + home_route = 'people' - g = forms.AlchemyGrid( - edbob.Person, people, config, - gridurl=request.route_url('people.list'), - objurl='person.edit') - - g.configure( - include=[ - g.first_name, - g.last_name, - g.display_name, - ], - readonly=True) - - grid = g.render(class_='clickable people') - return grids.render_grid(request, grid, search) + def fieldset(self, model): + fs = self.make_fieldset(model) + fs.configure( + include=[ + fs.first_name, + fs.last_name, + fs.phone.label("Phone Number"), + fs.email.label("Email Address"), + ]) + return fs -def person_fieldset(person, request): - fs = forms.make_fieldset(person, url=request.route_url, - url_action=request.current_route_url(), - route_name='people.list') - fs.configure( - include=[ - fs.first_name, - fs.last_name, - fs.display_name, - ]) - return fs +# def filter_map(): +# return filters.get_filter_map( +# edbob.Person, +# ilike=['first_name', 'last_name', 'display_name']) + +# def search_config(request, fmap): +# return filters.get_search_config( +# 'people.list', request, fmap, +# include_filter_display_name=True, +# filter_type_display_name='lk') + +# 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) - if not fs.readonly and request.POST: - fs.rebind(data=request.params) - if fs.validate(): +# fmap = filter_map() +# config = search_config(request, fmap) +# search = search_form(config) +# config = grid_config(request, search, fmap) +# people = grids.get_pager(query, config) - with transaction.manager: - fs.sync() - Session.add(fs.model) - Session.flush() - request.session.flash("%s \"%s\" has been %s." % ( - fs.crud_title, fs.get_display_text(), - 'updated' if fs.edit else 'created')) +# g = forms.AlchemyGrid( +# edbob.Person, people, config, +# gridurl=request.route_url('people.list'), +# objurl='person.edit') - 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): - """ - View for editing a :class:`edbob.Person` instance. - """ +# def person_fieldset(person, request): +# fs = forms.make_fieldset(person, url=request.route_url, +# 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'] - person = Session.query(edbob.Person).get(uuid) if uuid else None - assert person +# def new_person(context, request): - fs = person_fieldset(person, request) - if request.POST: - fs.rebind(data=request.params) - if fs.validate(): +# fs = person_fieldset(edbob.Person, request) +# if not fs.readonly and 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') +# with transaction.manager: +# fs.sync() +# Session.add(fs.model) +# Session.flush() +# request.session.flash("%s \"%s\" has been %s." % ( +# fs.crud_title, fs.get_display_text(), +# 'updated' if fs.edit else 'created')) - return HTTPFound(location=home) +# return HTTPFound(location=request.route_url('people.list')) - 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} - 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): - config.add_route('people.list', '/people') - config.add_view(people, route_name='people.list', renderer='/people/index.mako', - permission='people.list', http_cache=0) + config.add_route('people', '/people') + config.add_view(PeopleGrid, route_name='people', + renderer='/people/index.mako', + permission='people.list') - 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('people.list', '/people') +# config.add_view(people, route_name='people.list', renderer='/people/index.mako', +# permission='people.list', 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) + config.add_route('person.read', '/people/{uuid}') + config.add_view(PersonCrud, attr='read', route_name='person.read', + 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) diff --git a/edbob/scaffolds/edbob/setup.py_tmpl b/edbob/scaffolds/edbob/setup.py_tmpl index 5c54d06..93386aa 100644 --- a/edbob/scaffolds/edbob/setup.py_tmpl +++ b/edbob/scaffolds/edbob/setup.py_tmpl @@ -69,7 +69,6 @@ setup( ], install_requires = requires, - tests_require = requires, packages = find_packages(), include_package_data = True, diff --git a/edbob/win32.py b/edbob/win32.py index 4051118..e74e650 100644 --- a/edbob/win32.py +++ b/edbob/win32.py @@ -28,16 +28,96 @@ import sys import subprocess +import logging if sys.platform == 'win32': # docs should build for everyone import pywintypes import win32api import win32con + import win32event import win32file import win32print import win32service + import win32serviceutil 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): """