sprox-0.6.4/ 0000755 0000767 0000024 00000000000 11236057646 012472 5 ustar cperkins1 staff sprox-0.6.4/MANIFEST.in 0000644 0000767 0000024 00000000054 11124254300 014205 0 ustar cperkins1 staff recursive-include sprox/widgets/templates *
sprox-0.6.4/PKG-INFO 0000644 0000767 0000024 00000001253 11236057646 013570 0 ustar cperkins1 staff Metadata-Version: 1.0
Name: sprox
Version: 0.6.4
Summary: A package for creation of web widgets directly from database schema.
Home-page: http://www.sprox.org
Author: Christopher Perkins 2007-2009 major contributions by Michael Brickenstein
Author-email: chris@percious.com
License: MIT
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Software Development :: Libraries :: Python Modules
sprox-0.6.4/setup.cfg 0000644 0000767 0000024 00000000201 11236057646 014304 0 ustar cperkins1 staff [egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
[aliases]
release = egg_info -rDb "" sdist bdist_egg register upload
sprox-0.6.4/setup.py 0000644 0000767 0000024 00000003141 11224440262 014166 0 ustar cperkins1 staff #setup.py
from setuptools import setup, find_packages
from sprox.release import __version__
setup(
name="sprox",
version=__version__,
zip_safe=False,
include_package_data=True,
description="""A package for creation of web widgets directly from database schema.""",
author="Christopher Perkins 2007-2009 major contributions by Michael Brickenstein",
author_email="chris@percious.com",
license="MIT",
url="http://www.sprox.org",
install_requires=['sqlalchemy>=0.5',
'tw.forms>=0.9.7.2',
],
packages = find_packages(),
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
"Topic :: Software Development :: Libraries :: Python Modules",
],
entry_points = """[toscawidgets]
# Use 'widgets' to point to the module where widgets should be imported
# from to register in the widget browser
widgets = sprox.widgets
# Use 'samples' to point to the module where widget examples
# should be imported from to register in the widget browser
# samples = tw.samples
# Use 'resources' to point to the module where resources
# should be imported from to register in the widget browser
#resources = sprox.widgets.resources
"""
# entry_points="""
# [paste.paster_create_template]
# dbsprockets=sprox.instance.newSprox:Template
# """,
)
sprox-0.6.4/sprox/ 0000755 0000767 0000024 00000000000 11236057646 013645 5 ustar cperkins1 staff sprox-0.6.4/sprox/__init__.py 0000644 0000767 0000024 00000000000 11123744670 015737 0 ustar cperkins1 staff sprox-0.6.4/sprox/configbase.py 0000644 0000767 0000024 00000015573 11211237554 016321 0 ustar cperkins1 staff from formencode.validators import Validator
from tw.api import Widget
from sprox.providerselector import ProviderTypeSelector
class ConfigBaseError(Exception):pass
class ConfigBase(object):
"""
Base class for all configurable classes in Sprox.
:Modifiers:
+-----------------------------------+--------------------------------------------+------------------------------+
| Name | Description | Default |
+===================================+============================================+==============================+
| __entity__ (__model__) | Entity used for metadata extraction. | None |
+-----------------------------------+--------------------------------------------+------------------------------+
| __sprox_id_ | Id for use in the widget. | None |
+-----------------------------------+--------------------------------------------+------------------------------+
| __provider_type_selector_type__ | A type for selecting the provider. | ProviderTypeSelector |
+-----------------------------------+--------------------------------------------+------------------------------+
| __field_order__ | A list of ordered field names. | None |
+-----------------------------------+--------------------------------------------+------------------------------+
| __hide_fields__ | Fields marked as hidden. | [] |
+-----------------------------------+--------------------------------------------+------------------------------+
| __add_fields__ | Additional fields to add to the config. | {} |
+-----------------------------------+--------------------------------------------+------------------------------+
| __disable_fields__ | Field marked as disabled. | [] |
+-----------------------------------+--------------------------------------------+------------------------------+
| __omit_fields__ | Fields removed from the field list. | [] |
+-----------------------------------+--------------------------------------------+------------------------------+
| __limit_fields__ | Limit the field list to this. | None |
+-----------------------------------+--------------------------------------------+------------------------------+
| __field_attrs__ | attr parmater to set to the field. | {} |
+-----------------------------------+--------------------------------------------+------------------------------+
| __metadata_type__ | Metadata associated with this config. | None |
+-----------------------------------+--------------------------------------------+------------------------------+
"""
# what object does will this object use for metadata extraction
# model and entity are one in the same
__model__ = __entity__ = None
# this is used by catwalk's validate decorator to lookup the sprocket in the cache
__sprox_id__ = None
#How should we select a provider
__provider_type_selector_type__ = ProviderTypeSelector
# field overrides
__field_order__ = None
__hide_fields__ = None
__disable_fields__ = None
__omit_fields__ = None
__add_fields__ = None
__limit_fields__ = None
__field_attrs__ = None
__metadata_type__ = None
def __init__(self, provider_hint=None, **provider_hints):
#map __model__ to __entity__, this may be deprecated
if self.__entity__ is None and self.__model__ is not None:
self.__entity__ = self.__model__
self.__provider_type_selector__ = self.__provider_type_selector_type__()
self.provider_hint = provider_hint
self.provider_hints = provider_hints
self._do_init_attrs()
def __remove_duplicates(self, l):
l2 = []
for i in l:
if i not in l2 and i is not None:
l2.append(i)
return l2
@property
def __fields__(self):
return self._do_get_fields()
def _do_get_fields(self):
fields = []
if self.__field_order__ is not None:
#this makes sure all the ordered fields bubble to the start of the list
fields.extend(self.__field_order__)
if self.__limit_fields__ is not None:
fields.extend(self.__limit_fields__)
fields.extend(self.__hide_fields__)
fields.extend(self.__add_fields__.keys())
fields = self.__remove_duplicates(fields)
return fields
else:
fields = self.__metadata__.keys()
fields.extend(self.__add_fields__.keys())
fields.extend(self.__hide_fields__)
if self.__field_order__ is not None:
fields = set(fields)
field_order = set(self.__field_order__)
extra_fields = fields.difference(field_order)
fields = self.__field_order__+list(extra_fields)
for field in self.__omit_fields__:
while field in fields:
fields.remove(field)
r = []
for field in fields:
if field not in r and field is not None:
r.append(field)
return r
@property
def __metadata__(self):
if not hasattr(self, '___metadata__'):
if self.__metadata_type__ is None:
raise ConfigBaseError('You must define a __metadata_type__ attribute for this object')
self.___metadata__=self.__metadata_type__(self.__provider__, self.__entity__)
return self.___metadata__
@property
def __provider__(self):
if self.__entity__ is None:
raise ConfigBaseError('You must define a __entity__ attribute for this object')
return self.__provider_type_selector__.get_selector(self.__entity__).get_provider(self.__entity__,
self.provider_hint,
**self.provider_hints)
def _do_init_attrs(self):
if self.__hide_fields__ is None:
self.__hide_fields__ = []
if self.__disable_fields__ is None:
self.__disable_fields__ = []
if self.__omit_fields__ is None:
self.__omit_fields__ = []
if self.__add_fields__ is None:
self.__add_fields__ = {}
if self.__field_attrs__ is None:
self.__field_attrs__ = {}
sprox-0.6.4/sprox/dojo/ 0000755 0000767 0000024 00000000000 11236057646 014600 5 ustar cperkins1 staff sprox-0.6.4/sprox/dojo/__init__.py 0000644 0000767 0000024 00000000000 11131150323 016653 0 ustar cperkins1 staff sprox-0.6.4/sprox/dojo/fillerbase.py 0000755 0000767 0000024 00000001566 11211004607 017253 0 ustar cperkins1 staff """
fillerbase Module
Classes to help fill widgets with data
Copyright (c) 2008 Christopher Perkins
Original Version by Christopher Perkins 2008
Released under MIT license.
"""
from sprox.fillerbase import TableFiller
class DojoTableFiller(TableFiller):
def get_value(self, value=None, **kw):
offset = kw.get('start', None)
limit = kw.get('count', None)
order_by = kw.get('sort', None)
desc = False
if order_by is not None and order_by.startswith('-'):
order_by = order_by[1:]
desc = True
items = super(DojoTableFiller, self).get_value(value, limit=limit, offset=offset, order_by=order_by, desc=desc, **kw)
count = self.get_count()
identifier = self.__provider__.get_primary_field(self.__entity__)
return dict(identifier=identifier, numRows=count, totalCount=count, items=items)
sprox-0.6.4/sprox/dojo/formbase.py 0000644 0000767 0000024 00000016166 11236045316 016751 0 ustar cperkins1 staff """
fillerbase Module
Classes to help fill widgets with data
Copyright (c) 2008 Christopher Perkins
Original Version by Christopher Perkins 2008
Released under MIT license.
"""
from sprox.formbase import FormBase, EditableForm, AddRecordForm
from sprox.widgetselector import SAWidgetSelector
from sprox.widgets.dojo import SproxDojoSelectShuttleField, SproxDojoSortedSelectShuttleField
class DojoSAWidgetSelector(SAWidgetSelector):
"""Dojo-Specific Widget Selector"""
default_multiple_select_field_widget_type = SproxDojoSelectShuttleField
class DojoFormBase(FormBase):
"""FormBase for Dojo
see :class:`sprox.formbase.FormBase`
"""
__widget_selector_type__ = DojoSAWidgetSelector
class DojoEditableForm(EditableForm):
"""Creates a form for editing records that has select shuttles for the multiple relations.
:Modifiers:
see :class:`sprox.formbase.FormBase`
:Usage:
>>> from sprox.dojo.formbase import DojoEditableForm
>>> from formencode import Schema
>>> from formencode.validators import FieldsMatch
>>> class Form(DojoEditableForm):
... __model__ = User
... __limit_fields__ = ['user_name', 'groups']
>>> edit_form = Form()
>>> print edit_form() # doctest: +XML
"""
__widget_selector_type__ = DojoSAWidgetSelector
class DojoAddRecordForm(AddRecordForm):
"""
Creates a form for adding records that has select shuttles for the multiple relations.
:Modifiers:
see :class:`sprox.formbase.FormBase`
:Usage:
>>> from sprox.dojo.formbase import DojoAddRecordForm
>>> from formencode import Schema
>>> from formencode.validators import FieldsMatch
>>> class Form(DojoAddRecordForm):
... __model__ = User
... __limit_fields__ = ['user_name', 'groups']
>>> add_form = Form()
>>> print add_form() # doctest: +XML
"""
__widget_selector_type__ = DojoSAWidgetSelector
sprox-0.6.4/sprox/dojo/sprockets.py 0000644 0000767 0000024 00000003604 11131150323 017146 0 ustar cperkins1 staff """
Sprockets Module
This is sort of like the central nervous system of sprox. Views and
Sessions are collected in separate caches and served up as sprockets.
The cache objects may be solidified at some point with a parent class.
They work for right now.
Classes:
Name Description
SprocketCache A cache of Sprockets
Sprocket A binding of Filler and View configs
ConfigCache Individual configs cached
Functions:
None
Copyright (c) 2007 Christopher Perkins
Original Version by Christopher Perkins 2007
Released under MIT license.
"""
from sprox.sprockets import ConfigCache, SprocketCache
from sprox.providerselector import SAORMSelector
from sprox.formbase import FormBase, AddRecordForm, EditableForm
from sprox.entitiesbase import EntitiesBase, EntityDefBase
from sprox.fillerbase import ModelsFiller, ModelDefFiller, EditFormFiller, AddFormFiller, FormFiller
from fillerbase import DojoTableFiller
from tablebase import DojoTableBase
class ViewCache(ConfigCache):
default_configs = { 'model_view' : EntitiesBase,
'edit' : EditableForm,
'add' : AddRecordForm,
'listing' : DojoTableBase,
'metadata' : EntityDefBase,
}
json_url = 'data'
def __getitem__(self, key):
view = super(ViewCache, self).__getitem__(key)
return view
class FillerCache(ConfigCache):
default_configs = { 'model_view' : ModelsFiller,
'metadata' : ModelDefFiller,
'view' : FormFiller,
'listing' : DojoTableFiller,
'edit' : EditFormFiller,
'add' : AddFormFiller,
}
class DojoSprocketCache(SprocketCache):
view_type = ViewCache
filler_type = FillerCache sprox-0.6.4/sprox/dojo/tablebase.py 0000755 0000767 0000024 00000002632 11211004607 017060 0 ustar cperkins1 staff from tw.dojo import DojoJsonRestStore
from sprox.widgets.dojo import SproxEditableDojoGrid, SproxDojoGrid
from sprox.tablebase import TableBase
from sprox.metadata import FieldsMetadata
class DojoTableBase(TableBase):
"""This class allows you to credate a table widget.
:Modifiers:
see modifiers in :mod:`sprox.tablebase`
"""
#object overrides
__base_widget_type__ = SproxDojoGrid
__url__ = None
__column_options__ = {}
def _do_get_widget_args(self):
args = super(DojoTableBase, self)._do_get_widget_args()
if self.__url__ is not None:
args['action'] = self.__url__
args['columns'] = self.__fields__
args['column_options'] = self.__column_options__
args['headers'] = self.__headers__
args['jsId'] = self.__sprox_id__
return args
"""
Experimental for next version. Will not be included in 0.5"""
class DojoEditableTableBase(TableBase):
__base_widget_type__ = SproxEditableDojoGrid
__url__ = None
__column_options__ = {}
def _do_get_widget_args(self):
args = super(DojoEditableTableBase, self)._do_get_widget_args()
if self.__url__ is not None:
args['action'] = self.__url__
args['columns'] = self.__fields__
args['column_options'] = self.__column_options__
args['headers'] = self.__headers__
args['jsId'] = self.__sprox_id__
return args
sprox-0.6.4/sprox/dummyentity.py 0000644 0000767 0000024 00000000027 11133413505 016570 0 ustar cperkins1 staff class DummyEntity:pass
sprox-0.6.4/sprox/entitiesbase.py 0000644 0000767 0000024 00000006512 11135127077 016674 0 ustar cperkins1 staff import inspect
from sprox.widgets import ContainerWidget, TableWidget
from viewbase import ViewBase
from widgetselector import EntitiesViewWidgetSelector, EntityDefWidgetSelector
from sprox.metadata import EntitiesMetadata, FieldsMetadata
class EntityDefBase(ViewBase):
"""This view can display all of the entities for a given provider.
:Modifiers:
see :mod:`sprox.viewbase.ViewBase`
:Usage:
>>> from sprox.entitiesbase import EntityDefBase
>>> class UserEntityDef(EntityDefBase):
... __entity__ = User
>>> base = UserEntityDef(session)
>>> print base()
Name
Definition
password
Unicode(length=40)
user_id
Integer()
user_name
Unicode(length=16)
email_address
Unicode(length=255)
display_name
Unicode(length=255)
created
DateTime(timezone=False)
town_id
Integer()
town
relation
password
relation
groups
relation
"""
__base_widget_type__ = TableWidget
__widget_selector_type__ = EntityDefWidgetSelector
__metadata_type__ = FieldsMetadata
from dummyentity import DummyEntity
class EntitiesBase(ViewBase):
"""This view can display all of the entities for a given provider.
:Modifiers:
see :mod:`sprox.viewbase.ViewBase`
:Usage:
>>> from sprox.entitiesbase import EntitiesBase
>>> class MyEntitiesBase(EntitiesBase):
... __entity__ = User
>>> base = MyEntitiesBase(session, metadata=metadata)
>>> print base()
"""
__entity__ = DummyEntity
__base_widget_type__ = ContainerWidget
__widget_selector_type__ = EntitiesViewWidgetSelector
__metadata_type__ = EntitiesMetadata
sprox-0.6.4/sprox/fillerbase.py 0000644 0000767 0000024 00000024643 11212321666 016326 0 ustar cperkins1 staff """
fillerbase Module
Classes to help fill widgets with data
Copyright (c) 2008 Christopher Perkins
Original Version by Christopher Perkins 2008
Released under MIT license.
"""
from configbase import ConfigBase, ConfigBaseError
from metadata import FieldsMetadata
from genshi import XML
import inspect
class FillerBase(ConfigBase):
"""
:Modifiers:
see :mod:`sprox.configbase`.
The base filler class.
:Arguments:
values
pass through of values. This is typically a set of default values that is updated by the
filler. This is useful when updating an existing form.
kw
Set of keyword arguments for assisting the fill. This is for instance information like offset
and limit for a TableFiller.
:Usage:
>>> filler = FillerBase()
>>> filler.get_value()
{}
"""
def get_value(self, values=None, **kw):
"""
The main function for getting data to fill widgets,
"""
if values is None:
values = {}
return values
class ModelsFiller(FillerBase):
pass
class ModelDefFiller(FillerBase):
pass
class FormFiller(FillerBase):
__metadata_type__ = FieldsMetadata
def get_value(self, values=None, **kw):
values = super(FormFiller, self).get_value(values)
values['sprox_id'] = self.__sprox_id__
return values
class TableFiller(FillerBase):
"""
This is the base class for generating table data for use in table widgets. The TableFiller uses
it's provider to obtain a dictionary of information about the __entity__ this Filler defines.
This class is especially useful when you need to return a json stream, because it allows for
customization of attributes. A package which has similar functionality to this is TurboJson,
but TurboJson is rules-based, where the semantics for generating dictionaries follows the same
:mod:`sprox.configbase` methodology.
Modifiers defined in this class
+-----------------------------------+--------------------------------------------+------------------------------+
| Name | Description | Default |
+===================================+============================================+==============================+
| __actions__ | An overridable function to define how to | a function that creates an |
| | display action links in the form. | edit and delete link. |
+-----------------------------------+--------------------------------------------+------------------------------+
| __metadata_type__ | How should we get data from the provider. | FieldsMetadata |
+-----------------------------------+--------------------------------------------+------------------------------+
| __possible_field_names__ | See explanation below. | See below. |
+-----------------------------------+--------------------------------------------+------------------------------+
see modifiers also in :mod:`sprox.configbase`.
:Relations:
By default, TableFiller will populate relations (join or foreign_key) with either the value
from the related table, or a comma-separated list of values. These values are derived from
the related object given the field names provided by the __possible_field_names__ modifier.
For instance, if you have a User class which is related to Groups, the groups item in the result
dictionaries will be populated with Group.group_name. The default field names are:
_name, name, description, title.
:RESTful Actions:
By default, Table filler provides an "__actions__" item in the resultant dictionary list. This provides
and edit, and (javascript) delete link which provide edit and DELETE functionality as HTML verbs in REST.
For more information on developing RESTful URLs, please visit `http://microformats.org/wiki/rest/urls `_ .
:Usage:
Here is how we would get the values to fill up a user's table, minus the action column, and created date.
>>> class UsersFiller(TableFiller):
... __model__ = User
... __actions__ = False
... __omit_fields__ = ['created']
>>> users_filler = UsersFiller(session)
>>> value = users_filler.get_value(values={}, limit=20, offset=0)
>>> print value #doctest: +IGNORE_WHITESPACE
[{'town': u'Arvada', 'user_id': u'1', 'user_name': u'asdf',
'town_id': u'1', 'groups': u'4', '_password': '******', 'password': '******',
'email_address': u'asdf@asdf.com', 'display_name': u'None'}]
"""
__actions__ = True
__metadata_type__ = FieldsMetadata
__possible_field_names__ = ['_name', 'name', 'description', 'title']
def _get_list_data_value(self, field, values):
l = []
for value in values:
if not isinstance(value, basestring):
name = self.__provider__.get_view_field_name(value.__class__, self.__possible_field_names__)
l.append(unicode(getattr(value, name)))
else:
#this is needed for postgres to see array values
return values
return ', '.join(l)
def _get_relation_value(self, field, value):
#this may be needed for catwalk, but I am not sure what conditions cause it to be needed
#if value is None:
# return None
name = self.__provider__.get_view_field_name(value.__class__, self.__possible_field_names__)
return getattr(value, name)
def get_count(self):
"""Returns the total number of items possible for retrieval. This can only be
executed after a get_value() call. This call is useful for creating pagination in the context
of a user interface.
"""
if not hasattr(self, '__count__'):
raise ConfigBaseError('Count not yet set for filler. try calling get_value() first.')
return self.__count__
def _do_get_fields(self):
fields = super(TableFiller, self)._do_get_fields()
if '__actions__' not in self.__omit_fields__ and '__actions__' not in fields:
fields.insert(0, '__actions__')
return fields
def __actions__(self, obj):
"""Override this function to define how action links should be displayed for the given record."""
primary_fields = self.__provider__.get_primary_fields(self.__entity__)
pklist = '/'.join(map(lambda x: str(getattr(obj, x)), primary_fields))
value = '
"""
__metadata_type__ = FieldsMetadata
__widget_selector_type__ = RecordViewWidgetSelector
__base_widget_type__ = RecordViewWidget
def _do_get_field_widget_args(self, field_name, field):
"""Override this method do define how this class gets the field
widget arguemnts
"""
args = super(RecordViewBase, self)._do_get_field_widget_args( field_name, field)
args['field_name'] = field_name
if self.__provider__.is_relation(self.__entity__, field_name):
args['entity'] = self.__entity__
args['field_name'] = field_name
return args sprox-0.6.4/sprox/release.py 0000644 0000767 0000024 00000000026 11236056672 015633 0 ustar cperkins1 staff __version__ = "0.6.4"
sprox-0.6.4/sprox/saormprovider.py 0000644 0000767 0000024 00000032426 11226720162 017107 0 ustar cperkins1 staff """
saprovider Module
this contains the class which allows dbsprockets to interface with sqlalchemy.
Classes:
Name Description
SAProvider sqlalchemy metadata/crud provider
Exceptions:
None
Functions:
None
Copyright (c) 2007 Christopher Perkins
Original Version by Christopher Perkins 2007
Released under MIT license.
"""
import inspect
import re
from sqlalchemy import and_, or_, DateTime, Date, Interval, Binary, MetaData, desc as _desc
from sqlalchemy.engine import Engine
from sqlalchemy.orm.session import Session
from sqlalchemy.orm.scoping import ScopedSession
from sqlalchemy.orm import class_mapper, Mapper, PropertyLoader, _mapper_registry, SynonymProperty, object_mapper
from sqlalchemy.orm.exc import UnmappedClassError, NoResultFound, UnmappedInstanceError
from sprox.iprovider import IProvider
from cgi import FieldStorage
from datetime import datetime, timedelta
from warnings import warn
class SAORMProviderError(Exception):pass
class SAORMProvider(IProvider):
def __init__(self, hint=None, **hints):
"""
initialize me with a engine, bound metadata or bound session object
or a combination of the above.
"""
#if hint is None and len(hints) == 0:
# raise SAORMProviderError('You must provide a hint to this provider')
self.engine, self.session, self.metadata = self._get_engine(hint, hints)
def _get_engine(self, hint, hints):
metadata = hints.get('metadata', None)
engine = hints.get('engine', None)
session = hints.get('session', None)
if isinstance(hint, Engine):
engine=hint
if isinstance(hint, MetaData):
metadata=hint
if isinstance(hint, (Session, ScopedSession)):
session = hint
if session is not None and engine is None:
engine = session.bind
if metadata is not None and engine is None:
engine = metadata.bind
return engine, session, metadata
def get_fields(self, entity):
if inspect.isfunction(entity):
entity = entity()
mapper = class_mapper(entity)
field_names = mapper.c.keys()
for prop in mapper.iterate_properties:
try:
getattr(mapper.c, prop.key)
field_names.append(prop.key)
except AttributeError:
mapper.get_property(prop.key)
field_names.append(prop.key)
return field_names
def get_entity(self, name):
for mapper in _mapper_registry:
if mapper.class_.__name__ == name:
engine = mapper.tables[0].bind
if engine is not None and mapper.tables[0].bind != self.engine:
continue
return mapper.class_
raise KeyError('could not find model by the name %s'%(name))
def get_entities(self):
entities = []
for mapper in _mapper_registry:
engine = mapper.tables[0].bind
if engine is not None and mapper.tables[0].bind != self.engine:
continue
entities.append(mapper.class_.__name__)
return entities
def get_field(self, entity, name):
mapper = class_mapper(entity)
try:
return getattr(mapper.c, name)
except AttributeError:
return mapper.get_property(name)
def is_binary(self, entity, name):
field = self.get_field(entity, name)
if isinstance(field, PropertyLoader):
field = field.local_side[0]
# I am unsure what this is needed for, so it will be removed in the next version, and is for now
# commented until someone reports a bug.
#if not hasattr(field, 'type'):
# return False
return isinstance(field.type, Binary)
def is_nullable(self, entity, name):
field = self.get_field(entity, name)
if isinstance(field, SynonymProperty):
return
if isinstance(field, PropertyLoader):
return field.local_side[0].nullable
return field.nullable
def get_primary_fields(self, entity):
#for now we are only supporting entities with a single primary field
return [self.get_primary_field(entity)]
def get_primary_field(self, entity):
#sometimes entities get surrounded by functions, not sure why.
if inspect.isfunction(entity):
entity = entity()
mapper = class_mapper(entity)
for field_name in self.get_fields(entity):
value = getattr(mapper.c, field_name)
if value.primary_key:
return value.key
def get_view_field_name(self, entity, possible_names):
fields = self.get_fields(entity)
view_field = None
for column_name in possible_names:
for actual_name in fields:
if column_name == actual_name:
view_field = actual_name
break
if view_field:
break;
for actual_name in fields:
if column_name in actual_name:
view_field = actual_name
break
if view_field:
break;
if view_field is None:
view_field = fields[0]
return view_field
def get_dropdown_options(self, entity, field_name, view_names=None):
if view_names is None:
view_names = ['_name', 'name', 'description', 'title']
if self.session is None:
warn('No dropdown options will be shown for %s. '
'Try passing the session into the initialization'
'of your form base object so that this sprocket'
'can have values in the drop downs'%entity)
return []
field = self.get_field(entity, field_name)
target_field = entity
if isinstance(field, PropertyLoader):
target_field = field.argument
if inspect.isfunction(target_field):
target_field = target_field()
#some kind of relation
if isinstance(target_field, Mapper):
target_field = target_field.class_
pk_name = self.get_primary_field(target_field)
view_name = self.get_view_field_name(target_field, view_names)
rows = self.session.query(target_field).all()
return [(getattr(row, pk_name), getattr(row, view_name)) for row in rows]
def get_relations(self, entity):
mapper = class_mapper(entity)
return [prop.key for prop in mapper.iterate_properties if isinstance(prop, PropertyLoader)]
def is_relation(self, entity, field_name):
mapper = class_mapper(entity)
if isinstance(mapper.get_property(field_name), PropertyLoader):
return True
def is_unique(self, entity, field_name, value):
field = getattr(entity, field_name)
try:
self.session.query(entity).filter(field==value).one()
except NoResultFound:
return True
return False
def get_synonyms(self, entity):
mapper = class_mapper(entity)
return [prop.key for prop in mapper.iterate_properties if isinstance(prop, SynonymProperty)]
def _modify_params_for_relationships(self, entity, params, delete_first=True):
mapper = class_mapper(entity)
relations = self.get_relations(entity)
for relation in relations:
if relation in params:
prop = mapper.get_property(relation)
target = prop.argument
if inspect.isfunction(target):
target = target()
value = params[relation]
if value:
if prop.uselist and isinstance(value, list):
target_obj = []
for v in value:
try:
object_mapper(v)
target_obj.append(v)
except UnmappedInstanceError:
target_obj.append(self.session.query(target).get(v))
elif prop.uselist:
try:
object_mapper(value)
target_obj = [value]
except UnmappedInstanceError:
target_obj = [self.session.query(target).get(value)]
else:
try:
object_mapper(value)
target_obj = value
except UnmappedInstanceError:
target_obj = self.session.query(target).get(value)
params[relation] = target_obj
else:
del params[relation]
return params
def create(self, entity, params):
params = self._modify_params_for_dates(entity, params)
params = self._modify_params_for_relationships(entity, params)
obj = entity()
for key, value in params.iteritems():
if value is not None:
setattr(obj, key, value)
self.session.add(obj)
self.session.flush()
return obj
def dictify(self, obj):
if obj is None:
return {}
r = {}
mapper = class_mapper(obj.__class__)
for prop in mapper.iterate_properties:
value = getattr(obj, prop.key)
if value is not None:
if isinstance(prop, PropertyLoader):
klass = prop.argument
if isinstance(klass, Mapper):
klass = klass.class_
pk_name = self.get_primary_field(klass)
if isinstance(value, list):
#joins
value = [getattr(value, pk_name) for value in value]
else:
#fks
value = getattr(value, pk_name)
r[prop.key] = value
return r
def get_default_values(self, entity, params):
return params
def get(self, entity, params):
pk_name = self.get_primary_field(entity)
obj = self.session.query(entity).get(params[pk_name])
return self.dictify(obj)
def query(self, entity, limit=None, offset=None, limit_fields=None, order_by=None, desc=False, **kw):
query = self.session.query(entity)
count = query.count()
if order_by is not None:
field = self.get_field(entity, order_by)
if desc:
field = _desc(field)
query = query.order_by(field)
if offset is not None:
query = query.offset(offset)
if limit is not None:
query = query.limit(limit)
objs = query.all()
return count, objs
def _modify_params_for_dates(self, entity, params):
mapper = class_mapper(entity)
for key, value in params.iteritems():
if key in mapper.c and value is not None:
field = mapper.c[key]
if hasattr(field, 'type') and isinstance(field.type, DateTime) and not isinstance(value, datetime):
dt = datetime.strptime(value[:19], '%Y-%m-%d %H:%M:%S')
params[key] = dt
if hasattr(field, 'type') and isinstance(field.type, Date) and not isinstance(value, datetime):
dt = datetime.strptime(value, '%Y-%m-%d')
params[key] = dt
if hasattr(field, 'type') and isinstance(field.type, Interval) and not isinstance(value, timedelta):
d = re.match(
r'((?P\d+) days, )?(?P\d+):'
r'(?P\d+):(?P\d+)',
str(value)).groupdict(0)
dt = timedelta(**dict(( (key, int(value))
for key, value in d.items() )))
params[key] = dt
return params
def _remove_related_empty_params(self, obj, params):
entity = obj.__class__
mapper = class_mapper(entity)
relations = self.get_relations(entity)
for relation in relations:
#clear out those items which are not found in the params list.
if relation not in params or not params[relation]:
related_items = getattr(obj, relation)
if related_items is not None:
if hasattr(related_items, '__iter__'):
setattr(obj, relation, [])
else:
setattr(obj, relation, None)
def update(self, entity, params):
params = self._modify_params_for_dates(entity, params)
params = self._modify_params_for_relationships(entity, params)
pk_name = self.get_primary_field(entity)
obj = self.session.query(entity).get(params[pk_name])
relations = self.get_relations(entity)
for key, value in params.iteritems():
setattr(obj, key, value)
self._remove_related_empty_params(obj, params)
self.session.flush()
return obj
def delete(self, entity, params):
pk_name = self.get_primary_field(entity)
obj = self.session.query(entity).get(params[pk_name])
self.session.delete(obj)
return obj
sprox-0.6.4/sprox/sprockets.py 0000644 0000767 0000024 00000010540 11131150323 016210 0 ustar cperkins1 staff """
Sprockets Module
This is sort of like the central nervous system of sprox. Views and
Sessions are collected in separate caches and served up as sprockets.
The cache objects may be solidified at some point with a parent class.
They work for right now.
Classes:
Name Description
SprocketCache A cache of Sprockets
Sprocket A binding of Filler and View configs
ConfigCache Individual configs cached
Functions:
None
Copyright (c) 2007 Christopher Perkins
Original Version by Christopher Perkins 2007
Released under MIT license.
"""
from providerselector import SAORMSelector
from formbase import FormBase, AddRecordForm, EditableForm
from tablebase import TableBase
from entitiesbase import EntitiesBase, EntityDefBase
from fillerbase import ModelsFiller, ModelDefFiller, EditFormFiller, AddFormFiller, FormFiller, TableFiller
class ConfigCacheError(Exception): pass
class ConfigCache(object):
default_configs = {}
def __init__(self, session, metadata=None):
self.session = session
self.metadata = metadata
self._provider = SAORMSelector.get_provider(hint=session.bind, metadata=self.metadata)
separator = '__'
def _split_identifiers(self, key):
separator = '__'
if self.separator not in key:
identifier = None
view_type = key
else:
view_type, identifier = key.split(self.separator)
return view_type, identifier
def _get(self, key):
view_type, identifier = self._split_identifiers(key)
if view_type not in self.default_configs:
raise ConfigCacheError('view_type:%s not found in default bases'%view_type)
base = self.default_configs[view_type]
if key != 'model_view':
base.__entity__ = self._provider.get_entity(identifier)
base.__provider__ = self._provider
base.__sprox_id__ = key
return base(self.session)
def __getitem__(self, key):
#xxx: turn caching back on
#if key in self.__dict__:
# return self.__dict__[key]
view = self._get(key)
#self.__dict__[key] = view
return view
class ViewCache(ConfigCache):
default_configs = { 'model_view' : EntitiesBase,
'edit' : EditableForm,
'add' : AddRecordForm,
'listing' : TableBase,
'metadata' : EntityDefBase,
}
class FillerCache(ConfigCache):
"""
Container for Fillers
"""
default_configs = { 'model_view' : ModelsFiller,
'metadata' : ModelDefFiller,
'view' : FormFiller,
'listing' : TableFiller,
'edit' : EditFormFiller,
'add' : AddFormFiller,
}
class Sprocket:
"""Association between a view and a sessionConfig"""
def __init__(self, view, filler):
"""Construct a Sprocket Object
view
a ``view`` object which has been instantiated from a ``ViewConfig``
filler
defines how the view should be filled
"""
self.view = view
self.filler = filler
class SprocketCache(object):
"""Set of Associations between widgets and the method to obtain their data
caching is disabled for now
"""
def __init__(self, session, metadata=None):
self.views = self.view_type(session, metadata=metadata)
self.fillers = self.filler_type(session, metadata=metadata)
view_type = ViewCache
filler_type = FillerCache
sprocket_type = Sprocket
def __getitem__(self, key):
"""
"""
#xxx: enable caching
#if key in self.__dict__:
# import pdb; pdb.set_trace()
# return self.__dict__.__getitem__(key)
sprocket = self._get_sprocket(key)
#self.__dict__[key] = sprocket
return sprocket
def _get_sprocket(self, key):
view = self.views[key]
filler = self.fillers[key]
return self.sprocket_type(view, filler)
#xxx: enable caching
#def __setitem__(self, key, item):
# return
#if not isinstance(item, Sprocket):
# raise TypeError('item must be of type Sprocket')
#return self.__dict__.__setitem__(key, item)
sprox-0.6.4/sprox/tablebase.py 0000644 0000767 0000024 00000023370 11211240544 016126 0 ustar cperkins1 staff from sprox.widgets import SproxDataGrid
from viewbase import ViewBase
from metadata import FieldsMetadata
from tw.api import Widget
from operator import itemgetter
class TableBase(ViewBase):
"""This class allows you to create a table widget.
:Modifiers:
+-----------------------------------+--------------------------------------------+------------------------------+
| Name | Description | Default |
+===================================+============================================+==============================+
| __base_widget_type__ | Base widget for fields to go into. | SproxDataGrid |
+-----------------------------------+--------------------------------------------+------------------------------+
| __metadata_type__ | Type the widget is based on. | FieldsMetadata |
+-----------------------------------+--------------------------------------------+------------------------------+
| __headers__ | A dictionay of field/header pairs. | {} |
+-----------------------------------+--------------------------------------------+------------------------------+
| __column_widths__ | A dictionay of field/width(string) pairs. | {} |
+-----------------------------------+--------------------------------------------+------------------------------+
| __default_column_width__ | Header size to use when not specified in | '10em' |
| | __column_widths__ | |
+-----------------------------------+--------------------------------------------+------------------------------+
| __xml_fields__ | fields whos values should show as html | |
+-----------------------------------+--------------------------------------------+------------------------------+
see modifiers in :mod:`sprox.viewbase`
Here is an example listing of the towns in the test database.
>>> from sprox.tablebase import TableBase
>>> class TownTable(TableBase):
... __model__ = Town
>>> town_table = TownTable(session)
>>> print town_table()
actions
town_id
name
No Records Found.
As you can see, this is none too interesting, because there is no data in the table.
Here is how we fill the table with data using TableFillerBase
>>> from sprox.fillerbase import TableFiller
>>> class TownFiller(TableFiller):
... __model__ = Town
>>> town_filler = TownFiller(session)
>>> value = town_filler.get_value()
>>> print town_table.__widget__(value=value) #doctest: +SKIP
And now you can see the table has some data in it, and some restful links to the data. But what if you don't want those links?
You can omit the links by adding '__actions__' to omitted fields as follows:
>>> class TownTable(TableBase):
... __model__ = Town
... __omit_fields__ = ['__actions__']
>>> town_table = TownTable(session)
>>> print town_table.__widget__(value=value)
town_id
name
1
Arvada
2
Denver
3
Golden
4
Boulder
"""
#object overrides
__base_widget_type__ = SproxDataGrid
__metadata_type__ = FieldsMetadata
__headers__ = None
__column_widths__ = None
__xml_fields__ = None
__default_column_width__ = "10em"
def _do_get_fields(self):
fields = super(TableBase, self)._do_get_fields()
if '__actions__' not in self.__omit_fields__ and '__actions__' not in fields:
fields.insert(0, '__actions__')
if '__actions__' not in self.__headers__:
self.__headers__['__actions__'] = 'actions'
return fields
def _do_init_attrs(self):
super(TableBase, self)._do_init_attrs()
if self.__headers__ is None:
self.__headers__ = {}
if self.__column_widths__ is None:
self.__column_widths__ = {}
if self.__xml_fields__ is None:
self.__xml_fields__ = {}
def _do_get_widget_args(self):
args = super(TableBase, self)._do_get_widget_args()
args['pks'] = None
args['column_widths'] = self.__column_widths__
args['default_column_width'] = self.__default_column_width__
# Okay: the trunk version of Sprox creates its args['fields']
# very simply, as a list of lambdas that do an item lookup on
# the field name. This means that TableBase inherently ignores
# any attempt to specify widgets for displaying or formatting
# model values; in fact, the field widget list that the
# TableBase grows at some point during processing appears to be
# completely ignored. (You will see if it you add "print"
# statements around your code to watch how your tables are
# behaving.)
# To make widgets active, we need to put the widgets in place of
# the lambdas in the the args['fields'] that we build. There
# are challenges to building this list, however:
# 1. The widgets supplied by default are useless. Instead of
# being something that can actually display text, the list of
# widgets somehow winds up just being bare Widget instances,
# which seem to display absolutely nothing when rendered.
# Therefore, when we see a bare Widget supplied for a
# particular column, we need to ignore it.
# 2. Some fields (like '__action__') do not even appear in our
# list of fields-plus-widgets called self.__fields__, so we
# have to be willing to fall back to a lambda in that case
# too.
# 3. For some reason, the list of field widgets is not always
# present, so we have to wastefully re-compute it here by
# calling the _do_get_field_widgets() method. It would be
# nice if this were produced once before these computations
# started and could be relied upon to be present, but it's
# not so far as I could tell. But, then, I was reading code
# late at night.
# Anyway, with the stanza below, I am able to provide my table
# classes with a __field_widget_types__ dictionary and have
# those fields rendered differently.
field_headers = [ self.__headers__.get(field, field)
for field in self.__fields__ ]
field_widget_dict = self._do_get_field_widgets(self.__fields__)
field_widgets = []
for field in self.__fields__:
widget = field_widget_dict.get(field, None)
if widget is None or widget.__class__ is Widget: # yuck
widget = itemgetter(field)
field_widgets.append(widget)
args['fields'] = zip(field_headers, field_widgets)
# And, now back to our regularly-scheduled trunk-derived Sprox.
if '__actions__' not in self.__omit_fields__:
args['pks'] = self.__provider__.get_primary_fields(self.__entity__)
args['xml_fields'] = self.__xml_fields__
return args
sprox-0.6.4/sprox/test/ 0000755 0000767 0000024 00000000000 11236057646 014624 5 ustar cperkins1 staff sprox-0.6.4/sprox/test/__init__.py 0000644 0000767 0000024 00000000000 11131150323 016677 0 ustar cperkins1 staff sprox-0.6.4/sprox/test/base.py 0000644 0000767 0000024 00000012544 11217752012 016102 0 ustar cperkins1 staff import os, re
from copy import copy
from difflib import unified_diff
from sqlalchemy import *
from sqlalchemy.orm import *
from model import *
from cStringIO import StringIO
from cgi import FieldStorage
from tw import framework
framework.default_view = 'mako'
#try:
import xml.etree.ElementTree as etree
#except ImportError:
# import cElementTree as etree
session = None
engine = None
connect = None
sorted_user_columns = ['_password', 'created', 'display_name', 'email_address',
'groups', 'password', 'sprox_id', 'town',
'town_id', 'user_id', 'user_name']
def remove_whitespace_nodes(node):
new_node = copy(node)
new_node._children = []
if new_node.text and new_node.text.strip() == '':
new_node.text = ''
if new_node.tail and new_node.tail.strip() == '':
new_node.tail = ''
for child in node.getchildren():
if child is not None:
child = remove_whitespace_nodes(child)
new_node.append(child)
return new_node
def remove_namespace(doc):
"""Remove namespace in the passed document in place."""
for elem in doc.getiterator():
match = re.match('(\{.*\})(.*)', elem.tag)
if match:
elem.tag = match.group(2)
def replace_escape_chars(needle):
needle = needle.replace(' ', ' ')
return needle
def fix_xml(needle):
needle = replace_escape_chars(needle)
needle_node = etree.fromstring(needle)
needle_node = remove_whitespace_nodes(needle_node)
remove_namespace(needle_node)
needle_s = etree.tostring(needle_node)
return needle_s
def in_xml(needle, haystack):
needle_s, haystack_s = map(fix_xml, (needle, haystack))
return needle_s in haystack_s
def eq_xml(needle, haystack):
needle_s, haystack_s = map(fix_xml, (needle, haystack))
return needle_s == haystack_s
def assert_in_xml(needle, haystack):
assert in_xml(needle, haystack), "%s not found in %s"%(needle, haystack)
def assert_eq_xml(needle, haystack):
assert eq_xml(needle, haystack), "%s does not equal %s"%(needle, haystack)
database_setup=False
def setup_database():
global session, engine, database_setup, connect, metadata
#singletonizes things
if not database_setup:
engine = create_engine(os.environ.get('DBURL', 'sqlite://'), strategy="threadlocal")
connect = engine.connect()
# print 'testing on', engine
metadata.bind = engine
metadata.drop_all()
metadata.create_all()
Session = sessionmaker(bind=engine, autoflush=True, autocommit=False)
session = Session()
database_setup = True
return session, engine, metadata
records_setup = None
def setup_records(session):
session.expunge_all()
user = User()
user.user_name = u'asdf'
user.email_address = u"asdf@asdf.com"
user.password = u"asdf"
session.add(user)
arvada = Town(name=u'Arvada')
session.add(arvada)
session.flush()
user.town = arvada
session.add(Town(name=u'Denver'))
session.add(Town(name=u'Golden'))
session.add(Town(name=u'Boulder'))
#test_table.insert(values=dict(BLOB=FieldStorage('asdf', StringIO()).value)).execute()
#user_reference_table.insert(values=dict(user_id=user.user_id)).execute()
# print user.user_id
for i in range (5):
group = Group(group_name=unicode(i))
session.add(group)
user.groups.append(group)
session.flush()
return user
def teardown_database():
pass
#metadata.drop_all()
def _reassign_from_metadata():
global visits_table, visit_identity_table, groups_table, town_table
global users_table, permissions_table, user_group_table
global group_permission_table, test_table
visits_table = metadata.tables['visit']
visit_identity_table = metadata.tables['visit_identity']
groups_table = metadata.tables['tg_group']
town_table = metadata.tables['town_table']
users_table = metadata.tables['tg_user']
permissions_table = metadata.tables['permission']
user_group_table = metadata.tables['user_group']
group_permission_table = metadata.tables['group_permission']
test_table = metadata.tables['test_table']
def setup_reflection():
#if os.environ.get('AUTOLOAD', False):
metadata.clear()
metadata.reflect()
_reassign_from_metadata()
clear_mappers()
tables = metadata.tables
mapper(Town, tables['town_table'])
mapper(Example, tables['test_table'])
mapper(Visit, tables['visit'])
mapper(VisitIdentity, tables['visit_identity'],
properties=dict(users=relation(User, backref='visit_identity')))
mapper(User, tables['tg_user'])
mapper(Group, tables['tg_group'],
properties=dict(users=relation(User,
secondary=tables['user_group'],
backref='groups')))
mapper(Permission, tables['permission'],
properties=dict(groups=relation(Group,
secondary=tables['group_permission'],
backref='permissions')))
class SproxTest(object):
def setup(self):
self.session = session
self.engine = engine
try:
self.user = setup_records(session)
except:
self.session.rollback()
def teardown(self):
self.session.rollback()
if __name__ == '__main__':
setupDatabase()
sprox-0.6.4/sprox/test/model.py 0000644 0000767 0000024 00000017073 11226740743 016302 0 ustar cperkins1 staff #most of this file was taken from turbogears default template
from hashlib import sha1
import os
import md5
import sha
from datetime import datetime
#from sqlalchemy.types import *
from sqlalchemy import *
from sqlalchemy.orm import relation, backref, synonym
from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base, synonym_for
DeclarativeBase = declarative_base()
metadata = DeclarativeBase.metadata
# This is the association table for the many-to-many relationship between
# groups and permissions.
group_permission_table = Table('tg_group_permission', metadata,
Column('group_id', Integer, ForeignKey('tg_group.group_id',
onupdate="CASCADE", ondelete="CASCADE")),
Column('permission_id', Integer, ForeignKey('tg_permission.permission_id',
onupdate="CASCADE", ondelete="CASCADE"))
)
# This is the association table for the many-to-many relationship between
# groups and members - this is, the memberships.
user_group_table = Table('tg_user_group', metadata,
Column('user_id', Integer, ForeignKey('tg_user.user_id',
onupdate="CASCADE", ondelete="CASCADE")),
Column('group_id', Integer, ForeignKey('tg_group.group_id',
onupdate="CASCADE", ondelete="CASCADE"))
)
# auth model
class Group(DeclarativeBase):
"""An ultra-simple group definition.
"""
__tablename__ = 'tg_group'
group_id = Column(Integer, autoincrement=True, primary_key=True)
group_name = Column(Unicode(16), unique=True)
display_name = Column(Unicode(255))
created = Column(DateTime, default=datetime.now)
users = relation('User', secondary=user_group_table, backref='groups')
def __repr__(self):
return '' % self.group_name
class Town(DeclarativeBase):
__tablename__ = 'town'
town_id = Column(Integer, primary_key=True)
name = Column(String(32))
class User(DeclarativeBase):
"""Reasonably basic User definition. Probably would want additional
attributes.
"""
__tablename__ = 'tg_user'
user_id = Column(Integer, autoincrement=True, primary_key=True)
user_name = Column(Unicode(16), unique=True)
email_address = Column(Unicode(255), unique=True)
display_name = Column(Unicode(255))
_password = Column('password', Unicode(40))
created = Column(DateTime, default=datetime.now)
town_id = Column(Integer, ForeignKey('town.town_id'))
town = relation(Town)
def __repr__(self):
return '' % (
self.email_address, self.display_name)
@property
def permissions(self):
perms = set()
for g in self.groups:
perms = perms | set(g.permissions)
return perms
@classmethod
def by_email_address(cls, email):
"""A class method that can be used to search users
based on their email addresses since it is unique.
"""
return DBSession.query(cls).filter(cls.email_address==email).first()
@classmethod
def by_user_name(cls, username):
"""A class method that permits to search users
based on their user_name attribute.
"""
return DBSession.query(cls).filter(cls.user_name==username).first()
def _set_password(self, password):
"""encrypts password on the fly using the encryption
algo defined in the configuration
"""
#unfortunately, this causes coverage not to work
#self._password = self._encrypt_password(algorithm, password)
def _get_password(self):
"""returns password
"""
return self._password
password = synonym('password', descriptor=property(_get_password,
_set_password))
def _encrypt_password(self, algorithm, password):
"""Hash the given password with the specified algorithm. Valid values
for algorithm are 'md5' and 'sha1'. All other algorithm values will
be essentially a no-op."""
hashed_password = password
if isinstance(password, unicode):
password_8bit = password.encode('UTF-8')
else:
password_8bit = password
#creates a salted sha password
salt = sha1()
salt.update(os.urandom(60))
hash = sha1()
hash.update(password_8bit + salt.hexdigest())
hashed_password = salt.hexdigest() + hash.hexdigest()
# make sure the hased password is an UTF-8 object at the end of the
# process because SQLAlchemy _wants_ a unicode object for Unicode columns
if not isinstance(hashed_password, unicode):
hashed_password = hashed_password.decode('UTF-8')
return hashed_password
def validate_password(self, password):
"""Check the password against existing credentials.
this method _MUST_ return a boolean.
@param password: the password that was provided by the user to
try and authenticate. This is the clear text version that we will
need to match against the (possibly) encrypted one in the database.
@type password: unicode object
"""
hashed_pass = sha1()
hashed_pass.update(password + self.password[:40])
return self.password[40:] == hashed_pass.hexdigest()
class Permission(DeclarativeBase):
"""A relationship that determines what each Group can do
"""
__tablename__ = 'tg_permission'
permission_id = Column(Integer, autoincrement=True, primary_key=True)
permission_name = Column(Unicode(16), unique=True)
description = Column(Unicode(255))
groups = relation(Group, secondary=group_permission_table,
backref='permissions')
class Example(DeclarativeBase):
__tablename__ = 'example'
example_id = Column(Integer, primary_key=True)
created = Column(DateTime, default=datetime.now)
blob = Column(BLOB )
binary = Column(Binary )
boolean = Column(Boolean )
char = Column(CHAR(200) )
cLOB = Column(CLOB(200) )
date_ = Column( DATE )
datetime_ = Column( DATETIME )
decimal = Column(DECIMAL )
date = Column(Date )
dateTime = Column(DateTime )
float__ = Column( FLOAT )
float_ = Column(Float )
int_ = Column(INT )
integer = Column(Integer, default=10)
interval = Column(Interval )
# (NCHAR = #Column NCHAR )
numeric = Column(Numeric )
pickletype = Column(PickleType )
smallint = Column(SMALLINT )
smalliunteger = Column(SmallInteger )
string = Column(String(20) )
text = Column(TEXT(20) )
time_ = Column(TIME )
time = Column(Time )
timestamp = Column(TIMESTAMP )
unicode_ = Column(Unicode(200) )
varchar = Column(VARCHAR(200) )
password = Column(String(20) )
class Document(DeclarativeBase):
__tablename__ = 'document'
document_id = Column(Integer, primary_key=True)
created = Column(DateTime, default=datetime.now)
blob = Column(BLOB )
owner = Column(Integer, ForeignKey('tg_user.user_id'))
url = Column(String(500))
def _get_address(self):
return self.url
def _set_address(self, value):
self.url = value
address = synonym('address', descriptor=property(_get_address,
_set_address))
sprox-0.6.4/sprox/util.py 0000644 0000767 0000024 00000001313 11131150323 015146 0 ustar cperkins1 staff """
util Module
this contains the class which allows dbsprockets to interface with sqlalchemy.
Classes:
Name Description
MultiDict A class that allows dicts with multiple keys of the same value
Exceptions:
None
Functions:
None
Copyright (c) 2007 Christopher Perkins
Original Version by Christopher Perkins 2007
Released under MIT license.
"""
from copy import deepcopy, copy
class MultiDict(dict):
def __setitem__(self, key, value):
self.setdefault(key, []).append(value)
def iteritems(self):
for key in self:
values = dict.__getitem__(self, key)
for value in values:
yield (key, value)
sprox-0.6.4/sprox/validators.py 0000644 0000767 0000024 00000001256 11131150323 016347 0 ustar cperkins1 staff """
validators Module
Copyright (c) 2008 Christopher Perkins
Original Version by Christopher Perkins 2008
Released under MIT license.
"""
from formencode import FancyValidator, Invalid
class UniqueValue(FancyValidator):
def __init__(self, provider, entity, field_name, *args, **kw):
self.provider = provider
self.entity = entity
self.field_name = field_name
FancyValidator.__init__(self, *args, **kw)
def _to_python(self, value, state):
if not self.provider.is_unique(self.entity, self.field_name, value):
raise Invalid(
'That value already exists',
value, state)
return value
sprox-0.6.4/sprox/validatorselector.py 0000644 0000767 0000024 00000005643 11131150323 017731 0 ustar cperkins1 staff """
validatorselecter Module
this contains the class which allows the ViewConfig to select the appropriate validator for the given field
Classes:
Name Description
ValidatorSelecter Parent Class
SAValidatorSelector Selecter Based on sqlalchemy field types
DatabaseViewValidatorSelector Database View always selects the same validator
TableDefValidatorSelector Table def fields use the same validator
Exceptions:
None
Functions:
None
Copyright (c) 2007 Christopher Perkins
Original Version by Christopher Perkins 2007
Released under MIT license.
"""
from sqlalchemy.schema import Column
from sqlalchemy.types import *
from sqlalchemy.types import String as StringType
from tw.forms.validators import *
from formencode.compound import All
from formencode import Invalid
from formencode.validators import StringBool
from sqlalchemy.orm import PropertyLoader, SynonymProperty
class ValidatorSelector(object):
_name_based_validators = {}
def __new__(cls, *args, **kw):
bases = cls.mro()
chained_attrs = ['_name_based_validators']
for base in bases:
for attr in chained_attrs:
if hasattr(base, attr):
current = getattr(cls, attr)
current.update(getattr(base, attr))
return object.__new__(cls, *args, **kw)
def __getitem__(self, field):
return self.select(field)
def __init__(self, *args, **kw):
pass
@property
def name_based_validators(self):
validators = self._do_get_name_based_validators()
validators.update(self._name_based_validators)
return validators
def select(self, field):
return UnicodeString
def _do_get_name_based_validators(self):
return {}
class SAValidatorSelector(ValidatorSelector):
default_validators = {
StringType: UnicodeString,
Integer: Int,
Numeric: Number,
DateTime: DateValidator,
Date: DateValidator,
Time: DateValidator,
# Binary: UnicodeString,
PickleType: UnicodeString,
# Boolean: UnicodeString,
# NullType: TextField
}
_name_based_validators = {'email_address':Email}
def __init__(self, provider):
self.provider = provider
def select(self, field):
if isinstance(field, PropertyLoader):
return
if isinstance(field, SynonymProperty):
return
#do not validate boolean or binary arguments
if isinstance(field.type, (Boolean, Binary)):
return None
if field.name in self.name_based_validators:
return self.name_based_validators[field.name]
type_ = StringType
for t in self.default_validators.keys():
if isinstance(field.type, t):
type_ = t
break
validator_type = self.default_validators[type_]
return validator_type
sprox-0.6.4/sprox/viewbase.py 0000644 0000767 0000024 00000021637 11213551634 016024 0 ustar cperkins1 staff import inspect
from tw.api import Widget
from tw.forms import HiddenField
from configbase import ConfigBase, ConfigBaseError
from sqlalchemy.orm import PropertyLoader
from widgetselector import WidgetSelector
class ClassViewer(object):
"""class wrapper to expose items of a class. Needed to pass classes to TW as params"""
def __init__(self, klass):
self.__name__ = klass.__name__
class ViewBaseError(Exception):pass
class ViewBase(ConfigBase):
"""
:Modifiers:
+-----------------------------------+--------------------------------------------+------------------------------+
| Name | Description | Default |
+===================================+============================================+==============================+
| __widget_selector_type__ | What class to use for widget selection. | WidgetSelector |
+-----------------------------------+--------------------------------------------+------------------------------+
| __field_widgets__ | A dictionary of widgets to replace the | {} |
| | ones that would be chosen by the selector | |
+-----------------------------------+--------------------------------------------+------------------------------+
| __field_widget_types__ | A dictionary of types of widgets, allowing | {} |
| | sprox to determine the widget args | |
+-----------------------------------+--------------------------------------------+------------------------------+
| __field_widget_args__ | A dictionary of types of args for widgets, | {} |
| | you to override the args sent to the fields| |
+-----------------------------------+--------------------------------------------+------------------------------+
| __base_widget_type__ | The base widget for this config | Widget |
+-----------------------------------+--------------------------------------------+------------------------------+
| __base_widget_args__ | Args to pass into the widget overrides any | {} |
| | defaults that are set in sprox creation | |
+-----------------------------------+--------------------------------------------+------------------------------+
| __widget_selector__ | an instantiated object to use for widget | None |
| | selection. | |
+-----------------------------------+--------------------------------------------+------------------------------+
Also, see the :mod:`sprox.configbase` modifiers.
"""
__field_widgets__ = None
__field_widget_types__ = None
__field_widget_args__ = None
__ignore_field_names__ = None
#object overrides
__base_widget_type__ = Widget
__base_widget_args__ = None
__widget_selector_type__ = WidgetSelector
__widget_selector__ = None
def _do_init_attrs(self):
super(ViewBase, self)._do_init_attrs()
if self.__base_widget_args__ is None:
self.__base_widget_args__ = {}
if self.__field_widgets__ is None:
self.__field_widgets__ = {}
if self.__field_widget_args__ is None:
self.__field_widget_args__ = {}
if self.__field_widget_types__ is None:
self.__field_widget_types__ = {}
if self.__widget_selector__ is None:
self.__widget_selector__ = self.__widget_selector_type__()
if self.__ignore_field_names__ is None:
self.__ignore_field_names__ = ['sprox_id', '_method']
for attr in dir(self):
if not attr.startswith('__'):
value = getattr(self, attr)
if isinstance(value, Widget):
if not getattr(value, 'id', None):
raise ViewBaseError('Widgets must provide an id argument for use as a field within a ViewBase')
self.__add_fields__[attr] = value
try:
if issubclass(value, Widget):
self.__field_widget_types__[attr] = value
except TypeError:
pass
@property
def __widget__(self):
widget = getattr(self, '___widget__', None)
if not widget:
self.___widget__ = self.__base_widget_type__(**self.__widget_args__)
widget = self.___widget__
return self.___widget__
#try to act like a widget as much as possible
def __call__(self, *args, **kw):
return self.__widget__.__call__(*args, **kw)
@property
def __widget_args__(self):
return self._do_get_widget_args()
def _do_get_widget_args(self):
widget_dict = self._do_get_field_widgets(self.__fields__)
field_widgets = []
for key in self.__fields__:
if key not in widget_dict:
continue
value = widget_dict[key]
#sometimes a field will have two widgets associated with it (disabled fields)
if hasattr(value,'__iter__'):
field_widgets.extend(value)
continue
field_widgets.append(value)
d = dict(children=field_widgets)
d.update(self.__base_widget_args__)
return d
def _do_get_disabled_fields(self):
return self.__disable_fields__
def _do_get_field_widget_args(self, field_name, field):
# toscawidgets does not like ids that have '.' in them. This does not
# work for databases with schemas.
field_name = field_name.replace('.', '_')
args = {}
#this is sort of a hack around TW evaluating _some_ params that are classes.
entity = field
if inspect.isclass(field):
entity = ClassViewer(field)
args = {'id':field_name, 'identity':self.__entity__.__name__+'_'+field_name, 'entity':entity}
if field_name in self.__field_attrs__:
args['attrs'] = self.__field_attrs__[field_name]
if isinstance(field, PropertyLoader):
args['provider'] = self.__provider__
args['nullable'] = self.__provider__.is_nullable(self.__entity__, field_name)
if field_name in self.__field_widget_args__:
args.update(self.__field_widget_args__[field_name])
return args
def __create_hidden_fields(self):
fields = {}
fields['sprox_id'] = HiddenField(id='sprox_id')
for field in self.__hide_fields__:
if field not in self.__omit_fields__:
args = {}
if field in self.__field_widget_args__:
args.update(self.__field_widget_args__[field])
fields[field] = HiddenField(id=field, identifier=field, **args)
return fields
def _do_get_field_widgets(self, fields):
metadata_keys = self.__metadata__.keys()
widgets = {}
for field_name in fields:
if field_name in self.__field_widgets__:
widgets[field_name] = self.__field_widgets__[field_name]
continue
if field_name in self.__add_fields__:
widget = self.__add_fields__[field_name]
if widget is None:
widget = Widget(field_name)
widgets[field_name] = widget
continue
if field_name in self.__ignore_field_names__:
continue
if field_name in self.__hide_fields__:
continue
if field_name not in metadata_keys:
continue
field = self.__metadata__[field_name]
if inspect.isclass(field):
identifier = ClassViewer(field)
field_widget_type = self.__field_widget_types__.get(field_name,
self.__widget_selector__.select(field))
field_widget_args = self._do_get_field_widget_args(field_name, field)
if field_name in self._do_get_disabled_fields():
# in this case, we display the current field, disabling it, and also add
# a hidden field into th emix
field_widget_args['disabled'] = True
widgets[field_name] = (HiddenField(id=field_name.replace('.','_'), identifier=field_name), field_widget_type(**field_widget_args))
else:
widgets[field_name] = field_widget_type(**field_widget_args)
widgets.update(self.__create_hidden_fields())
return widgets
sprox-0.6.4/sprox/widgets/ 0000755 0000767 0000024 00000000000 11236057646 015313 5 ustar cperkins1 staff sprox-0.6.4/sprox/widgets/__init__.py 0000644 0000767 0000024 00000000025 11123744670 017414 0 ustar cperkins1 staff from widgets import * sprox-0.6.4/sprox/widgets/dojo.py 0000755 0000767 0000024 00000007434 11220007356 016615 0 ustar cperkins1 staff """Dojo Specific Widgets"""
from tw.dojo import DojoQueryReadStore, DojoBase, grid_css, tundragrid_css, DojoJsonRestStore, buildService
from tw.dojo.selectshuttle import DojoSelectShuttleField, DojoSortedSelectShuttleField
from tw.api import JSSource
from sprox.widgets import PropertyMixin
class SproxDojoGrid(DojoBase):
engine_name=None
available_engines = ['mako','genshi']
css = [grid_css, tundragrid_css]
require = ['dojox.grid.DataGrid', 'dojox.data.QueryReadStore']
dojoType = 'dojox.grid.DataGrid'
params = ['id', 'attrs', 'columns', 'jsId', 'action',
'rowsPerPage', 'model', 'delayScroll', 'cssclass', 'actions',
'columnResizing', 'columnReordering', 'column_widths', 'default_column_width', 'headers','column_options', 'default_column_options','dojoStoreType','dojoStoreWidget'
]
delayScroll = "true"
cssclass="sprox-dojo-grid"
rowsPerPage = 20
columns = []
columnReordering = "false"
columnResizing="false"
column_widths = {}
column_options = {}
default_column_options = {}
headers = {}
default_column_width = "10em"
include_dynamic_js_calls = True
action='.json'
model = None
actions = True
dojoStoreType = 'dojox.data.QueryReadStore'
dojoStoreWidget = None
template = "sprox.widgets.templates.dojogrid"
def update_params(self,d):
d['dojoStoreWidget']=DojoQueryReadStore()
super(SproxDojoGrid, self).update_params(d)
class DojoJsonRestStoreInstance(JSSource):
engine_name='mako'
require = ['twdojo.data.TWDojoRestStore']
target='.json'
url = '.json'
idAttribute = 'id'
autoSave = 'true'
source_vars = ["varId","target","url","idAttribute","autoSave"]
src = """
dojo.require("twdojo.data.TWDojoRestStore")
var ${varId}=new twdojo.data.TWDojoRestStore({target:"${target}",autoSave:"${autoSave and 'true' or 'false'}", service:buildService("${url}"),idAttribute:"${idAttribute}"})
"""
class SproxEditableDojoGrid(DojoBase):
engine_name=None
available_engines = ['genshi', 'mako']
css = [grid_css, tundragrid_css]
require = ['dojox.grid.DataGrid', 'twdojo.data.TWDojoRestStore']
dojoType = 'dojox.grid.DataGrid'
params = ['id', 'attrs', 'columns', 'jsId', 'action',
'rowsPerPage', 'model', 'delayScroll', 'cssclass', 'actions',
'columnResizing', 'columnReordering', 'column_widths', 'default_column_width', 'headers','column_options', 'default_column_options','dojoStoreType','dojoStoreWidget'
]
delayScroll = "true"
cssclass="sprox-dojo-grid"
rowsPerPage = 20
columns = []
columnReordering = "false"
columnResizing="false"
column_widths = {}
column_options = {}
default_column_options = {'editable':"true"}
headers = {}
default_column_width = "10em"
include_dynamic_js_calls = True
action='.json'
model = None
actions = True
dojoStoreType = 'twdojo.data.TWDojoRestStore'
dojoStoreWidget = None
template = "sprox.widgets.templates.dojogrid"
def __init__(self,**kw):
super(SproxEditableDojoGrid, self).__init__(**kw)
storeName = kw.get('jsId','') or ''
storeName = storeName+'_store'
self.javascript.append(buildService)
self.javascript.append(DojoJsonRestStoreInstance(varId=storeName,target=kw['action'],url=kw['action']))
class SproxDojoSelectShuttleField(DojoSelectShuttleField, PropertyMixin):
def update_params(self, d):
self._my_update_params(d)
super(SproxDojoSelectShuttleField, self).update_params(d)
class SproxDojoSortedSelectShuttleField(DojoSortedSelectShuttleField, PropertyMixin):
def update_params(self, d):
self._my_update_params(d)
super(SproxDojoSortedSelectShuttleField, self).update_params(d)
sprox-0.6.4/sprox/widgets/templates/ 0000755 0000767 0000024 00000000000 11236057646 017311 5 ustar cperkins1 staff sprox-0.6.4/sprox/widgets/templates/__init__.py 0000644 0000767 0000024 00000000000 11123744670 021403 0 ustar cperkins1 staff sprox-0.6.4/sprox/widgets/templates/__init__.pyc 0000644 0000767 0000024 00000000237 11123754614 021561 0 ustar cperkins1 staff ³ò
¸ÉOIc @ s d S( N( ( ( ( sH /Users/cperkins1/nrel/pdil/src/sprox/sprox/widgets/templates/__init__.pys s sprox-0.6.4/sprox/widgets/templates/checkbox.html 0000644 0000767 0000024 00000000457 11123744670 021766 0 ustar cperkins1 staff