[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnue-contrib] r238 - / objectWrapper objectWrapper/.stage objectWrapper
From: |
jamest |
Subject: |
[gnue-contrib] r238 - / objectWrapper objectWrapper/.stage objectWrapper/.stage/objectWrapper objectWrapper/src objectWrapper/src/external objectWrapper/src/util objectWrapper/tests objectWrapper/tests/objects |
Date: |
Sun, 12 Mar 2006 19:40:28 -0600 (CST) |
Author: jamest
Date: 2006-03-12 19:40:27 -0600 (Sun, 12 Mar 2006)
New Revision: 238
Added:
objectWrapper/
objectWrapper/.stage/
objectWrapper/.stage/objectWrapper/
objectWrapper/.stage/objectWrapper/ObjectWrapper.py
objectWrapper/.stage/objectWrapper/__init__.py
objectWrapper/.stage/objectWrapper/datasources.py
objectWrapper/.stage/objectWrapper/external
objectWrapper/.stage/objectWrapper/util
objectWrapper/README
objectWrapper/TODO
objectWrapper/setup-svn
objectWrapper/setup.py
objectWrapper/src/
objectWrapper/src/ObjectWrapper.py
objectWrapper/src/__init__.py
objectWrapper/src/datasources.py
objectWrapper/src/external/
objectWrapper/src/external/__init__.py
objectWrapper/src/external/commonProperty.py
objectWrapper/src/util/
objectWrapper/src/util/__init__.py
objectWrapper/src/util/converters.py
objectWrapper/tests/
objectWrapper/tests/basics.py
objectWrapper/tests/joins.py
objectWrapper/tests/objects/
objectWrapper/tests/objects/__init__.py
objectWrapper/tests/objects/address.py
objectWrapper/tests/objects/baseAddress.py
objectWrapper/tests/objects/invoice.py
objectWrapper/tests/objects/invoiceAddress.py
objectWrapper/tests/objects/invoiceItem.py
objectWrapper/tests/objects/item.py
objectWrapper/tests/objects/state.py
objectWrapper/tests/runAllTests
objectWrapper/tests/util_converters.py
Log:
added objectWrapper to contrib
Added: objectWrapper/.stage/objectWrapper/ObjectWrapper.py
===================================================================
--- objectWrapper/.stage/objectWrapper/ObjectWrapper.py 2006-03-02 13:04:30 UTC
(rev 237)
+++ objectWrapper/.stage/objectWrapper/ObjectWrapper.py 2006-03-13 01:40:27 UTC
(rev 238)
@@ -0,0 +1 @@
+link /home/jamest/svn/gnue-contrib/objectWrapper/src/ObjectWrapper.py
\ No newline at end of file
Property changes on: objectWrapper/.stage/objectWrapper/ObjectWrapper.py
___________________________________________________________________
Name: svn:special
+ *
Added: objectWrapper/.stage/objectWrapper/__init__.py
===================================================================
--- objectWrapper/.stage/objectWrapper/__init__.py 2006-03-02 13:04:30 UTC
(rev 237)
+++ objectWrapper/.stage/objectWrapper/__init__.py 2006-03-13 01:40:27 UTC
(rev 238)
@@ -0,0 +1 @@
+link /home/jamest/svn/gnue-contrib/objectWrapper/src/__init__.py
\ No newline at end of file
Property changes on: objectWrapper/.stage/objectWrapper/__init__.py
___________________________________________________________________
Name: svn:special
+ *
Added: objectWrapper/.stage/objectWrapper/datasources.py
===================================================================
--- objectWrapper/.stage/objectWrapper/datasources.py 2006-03-02 13:04:30 UTC
(rev 237)
+++ objectWrapper/.stage/objectWrapper/datasources.py 2006-03-13 01:40:27 UTC
(rev 238)
@@ -0,0 +1 @@
+link /home/jamest/svn/gnue-contrib/objectWrapper/src/datasources.py
\ No newline at end of file
Property changes on: objectWrapper/.stage/objectWrapper/datasources.py
___________________________________________________________________
Name: svn:special
+ *
Added: objectWrapper/.stage/objectWrapper/external
===================================================================
--- objectWrapper/.stage/objectWrapper/external 2006-03-02 13:04:30 UTC (rev
237)
+++ objectWrapper/.stage/objectWrapper/external 2006-03-13 01:40:27 UTC (rev
238)
@@ -0,0 +1 @@
+link /home/jamest/svn/gnue-contrib/objectWrapper/src/external
\ No newline at end of file
Property changes on: objectWrapper/.stage/objectWrapper/external
___________________________________________________________________
Name: svn:special
+ *
Added: objectWrapper/.stage/objectWrapper/util
===================================================================
--- objectWrapper/.stage/objectWrapper/util 2006-03-02 13:04:30 UTC (rev
237)
+++ objectWrapper/.stage/objectWrapper/util 2006-03-13 01:40:27 UTC (rev
238)
@@ -0,0 +1 @@
+link /home/jamest/svn/gnue-contrib/objectWrapper/src/util
\ No newline at end of file
Property changes on: objectWrapper/.stage/objectWrapper/util
___________________________________________________________________
Name: svn:special
+ *
Added: objectWrapper/README
===================================================================
--- objectWrapper/README 2006-03-02 13:04:30 UTC (rev 237)
+++ objectWrapper/README 2006-03-13 01:40:27 UTC (rev 238)
@@ -0,0 +1,7 @@
+objectWrapper
+
+A simple object relational manager you can use to create an object
+interface to your database. It is based upon GNUe datasources so
+will work with any backend GNUe itself works with.
+
+
Added: objectWrapper/TODO
===================================================================
--- objectWrapper/TODO 2006-03-02 13:04:30 UTC (rev 237)
+++ objectWrapper/TODO 2006-03-13 01:40:27 UTC (rev 238)
@@ -0,0 +1,12 @@
+* postgresql 8.1 doesn't create oids by default which currently forces
+ _primaryKey to be added for any oid-less table
+
+* add cascade on delete of parent object
+
+* find way to ditch the common property calls per class
+
+* load should be able to specify filters to say exactly which fields to load
+
+* find way to set the default db
+
+* objects should have a commit() and rollback()
Added: objectWrapper/setup-svn
===================================================================
--- objectWrapper/setup-svn 2006-03-02 13:04:30 UTC (rev 237)
+++ objectWrapper/setup-svn 2006-03-13 01:40:27 UTC (rev 238)
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+
+if [ `basename \`pwd\`` != 'objectWrapper' ]
+then
+ echo "Please run this script from the web-po svn directory."
+ exit 1
+fi
+
+STAGE=`pwd`/.stage
+TARGET=$STAGE/objectWrapper
+rm -rf $STAGE > /dev/null 2>&1
+mkdir -p $TARGET
+ln -s -f `pwd`/src/* $TARGET
+if [ -d ../../gnue/.cvsdevelbase ]
+ then
+ ln -s -f $TARGET
`pwd`/../../gnue/.cvsdevelbase/gnue/common/contrib/objectWrapper
+ else
+ echo "Could not find gnue-common's setup-svn.py base"
+fi
\ No newline at end of file
Property changes on: objectWrapper/setup-svn
___________________________________________________________________
Name: svn:executable
+ *
Added: objectWrapper/setup.py
===================================================================
--- objectWrapper/setup.py 2006-03-02 13:04:30 UTC (rev 237)
+++ objectWrapper/setup.py 2006-03-13 01:40:27 UTC (rev 238)
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+from distutils.core import setup
+import os
+
+def get_packages (directory, package):
+ content = os.listdir (directory)
+ result = []
+ if "__init__.py" in content:
+ result = [package]
+ for name in content:
+ fullname = os.path.join (directory, name)
+ if os.path.isdir (fullname):
+ result = result + get_packages (fullname, package + "." + name)
+ return result
+
+setup_params = {}
+setup_params['package_dir'] = { 'gnue.common.contrib.objectWrapper' : 'src',
+ }
+packages = []
+for module, directory in setup_params["package_dir"].items ():
+ packages = packages + get_packages (directory, module)
+setup_params ["packages"] = packages
+
+
+setup(name='objectWrapper',
+ version ='1.0',
+ description ='ObjectWrapper',
+ author ='James Thompson',
+ author_email='address@hidden',
+ packages = setup_params["packages"],
+ package_dir = setup_params["package_dir"],
+ )
Property changes on: objectWrapper/setup.py
___________________________________________________________________
Name: svn:executable
+ *
Added: objectWrapper/src/ObjectWrapper.py
===================================================================
--- objectWrapper/src/ObjectWrapper.py 2006-03-02 13:04:30 UTC (rev 237)
+++ objectWrapper/src/ObjectWrapper.py 2006-03-13 01:40:27 UTC (rev 238)
@@ -0,0 +1,568 @@
+#
+# Disable the W0410 as load() requires ** usage to generalize API
+# maybe this should be a single conditional passed in?
+#
+# pylint: disable-msg=W0142
+#
+"""
+Base class use to create a data aware object that utilizes
+GNUe datasources for persistance
+"""
+__revision__ = "$Id: objectWrapper.py 833 2006-03-12 20:27:22Z jamest $"
+
+import copy
+
+from gnue.common.apps import errors
+
+from gnue.common.contrib.objectWrapper.datasources import DBConnections
+from gnue.common.contrib.objectWrapper.datasources import DataSourceWrapper
+from gnue.common.contrib.objectWrapper.util.converters \
+ import convertValueToDecimal, convertMxDatetimeToDatetime, coalesce
+from gnue.common.contrib.objectWrapper.external.commonProperty import
CommonProperty
+
+# ----------------------------------------------------------------------------
+# Exceptions
+# ----------------------------------------------------------------------------
+class ObjectWrapperError(errors.UserError):
+ """
+ An error has occured in the data aware class.
+ """
+ def __init__ (self, message):
+ msg = "ObjectWrapper error: %s" % message
+ errors.UserError.__init__ (self, msg)
+
+
+# ----------------------------------------------------------------------------
+# Main Logic
+# ----------------------------------------------------------------------------
+class ObjectWrapper(object):
+ """
+ Base class to add simple load, post functions to the system.
+ """
+ _tableName = None # The name of the db table to which this object
+ # is associated
+
+ _primaryKeys = None # A common seperated list of primary key fields
+ # used in the table
+
+ _fieldMap = {} # The field map is a dictionary of
+ # <table field name>:<instance variable name>
+ # pairs.
+
+ _joinSingles = {} # A mapping of detail records to be loaded into
+ # the instance. For example this would be used
+ # to load instances of all items that are listed
+ # against an invoice into the invoice instance
+
+ _joinMulti = {} # A mapping of detail records to be loaded into
+ # the instance. For example this would be used
+ # to load instances of all items that are listed
+ # against an invoice into the invoice instance
+
+ _properties = {} #
+
+
+ def __init__(self, connections = None):
+ #
--------------------------------------------------------------------------
+ # Base attributes
+ #
--------------------------------------------------------------------------
+ self._resultSet = None # A handle to the result set that is associated
+ # with this instance via the load() function
+
+ self._recordSet = None # A handle to the record set that is associated
+ # with this instance via the load() function
+
+ self._connectionName = 'gnue' # The default gnue connection name
+
+ self.__datasourceAttributes = { # The attributes that will be used
+ 'connection': self._connectionName, # when setting up connections to
+ 'name': 'dtsource', # the backend
+ 'table': self._tableName, }
+
+ if self._primaryKeys is not None:
+ self.__datasourceAttributes['primarykey'] = self._primaryKeys
+
+
+ if connections is None:
+ gDebug(1,"No connection manager specified. Creating one by default")
+ self.connections = DBConnections()
+ else:
+ self.connections = connections
+
+
+ # Build the internal mappings based upon the contents of
+ # _properties
+
+ if bool(self._properties):
+ self._fieldMap = {}
+ self._joinMulti = {}
+ self._tableFieldList = []
+
+ for prop in self._properties:
+ if prop['style'] == 'field':
+ settings = prop['settings']
+ instanceField = prop['instanceField']
+
+ self._fieldMap[instanceField] = settings
+ self._tableFieldList.append(settings['tableField'])
+
+# if settings['getter'] or settings['setter']:
+# setattr(self, "__%s" % instanceField, None)
+# setattr(self, instanceField,
+# CommonProperty("__%s" % instanceField,
+# settings['getter'],
+# settings['setter']) )
+# print self.__dict__
+
+ elif prop['style'] == 'join':
+ self._joinMulti[prop['instanceField']] = prop['settings']
+
+ # Initialize instance fields to None
+ for instanceField in self._fieldMap.keys():
+# if not (settings['getter'] or settings['setter']):
+ setattr(self, instanceField, None)
+
+ # Initialize joins
+ for joinAttrib in self._joinMulti.keys():
+ joinInfo = self._joinMulti[joinAttrib]
+ if joinInfo['multi']:
+ setattr(self, joinAttrib, [])
+ else:
+ setattr(self, joinAttrib, joinInfo['class']())
+
+ # ---------------------------------------------------------------------------
+ # Loading data
+ # ---------------------------------------------------------------------------
+
+ def load(self, multi=False, conditions = None, order_by = None,
+ **fieldValues):
+ """
+ Populates the current instance with a single record from the table.
+
+ Example Usage::
+ claim = Claim()
+ claim.load(claim_no='99-TEST-001')
+
+ If more than one record is returned then an error will be raised.
+
+ By default this is just a wrapper for the _load() function. Classes
+ that require customized loading should override this function and
+ call _load() as their 1st step.
+
+ @param multi: If true then return a list of all records loaded
+ If false then only one record should load
+ @param conditions: filter contiditions
+ @param order_by: fields passed to sql order by
+ @param fieldValues: Query arguments in the form of tableFieldName=value
+ @return: None
+ """
+ return self._load(conditions=conditions, multi=multi, order_by=order_by,
+ **fieldValues)
+
+
+ def multiLoad(self, **fieldValues):
+ """
+ Returns a list of items with this object being the first in the list.
+
+ Example Usage::
+ items = ClaimActualItems().multiLoad(claim_no='99-TEST-001')
+
+ By default this is just a wrapper for the _load() function. Classes
+ that require customized loading should override this function and
+ call _load() as their 1st step.
+
+ @param fieldValues: Keyword arguments in the form of
tableFieldName=value
+ @return: None
+ """
+ return self._load(multi=True, **fieldValues)
+
+
+ def _load(self, multi = False, conditions = None, order_by = None,
+ **fieldValues):
+ """Base load function. See the load() description for more details.
+
+ @param fieldValues: Keyword arguments in the form of
tableFieldName=value
+ @return: None
+ """
+ #
--------------------------------------------------------------------------
+ # Validations
+ #
--------------------------------------------------------------------------
+ assert bool(self._tableName), 'Table name not defined'
+ assert bool(self._fieldMap), 'Field mapping is not defined'
+
+ # Verify all field names given are valid field names
+ for fieldName in fieldValues.keys():
+ assert fieldName in self._tableFieldList, \
+ 'Invalid field name: %s' % fieldName
+
+ #
--------------------------------------------------------------------------
+ # Pull the requested data and store as part of this instance
+ #
--------------------------------------------------------------------------
+ attributes = copy.copy(self.__datasourceAttributes)
+ if order_by:
+ attributes['order_by'] = order_by
+
+ datasource = DataSourceWrapper(
+ self.connections,
+ attributes=attributes,
+ fields=self._tableFieldList
+ )
+
+ # Load the results
+ if conditions is None:
+ resultSet = datasource.createResultSet(conditions=fieldValues)
+ else:
+ resultSet = datasource.createResultSet(conditions=conditions)
+
+ instanceList = [] # Will store all copies of this classes instances
+ newClass = self # Start by using itself as the initial instance to store
+
+ for recordSet in resultSet:
+ # Populate the instance vars
+ newClass._resultSet = resultSet
+ newClass._recordSet = recordSet
+
+ # Load the variable values
+ for instanceField, settings in self._fieldMap.items():
+ setattr(newClass, instanceField, recordSet[settings['tableField']])
+
+ # Load any detail records
+ if self._joinMulti:
+ for detailVar, detailInfo in self._joinMulti.items():
+ detailInstance = detailInfo['class']()
+
+ conditions = {detailInfo['detaillink'] :
+ getattr(newClass, detailInfo['masterlink']),
+ }
+
+ if bool(detailInfo.get('extraConditions', {})):
+ conditions.update( detailInfo.get('extraConditions', {}) )
+
+ detailList = detailInstance.multiLoad(
+ conditions = conditions
+ )
+ if detailInfo['multi']:
+ setattr(newClass, detailVar, detailList)
+ else:
+ if len(detailList):
+ setattr(newClass, detailVar, detailList[0])
+ else:
+ setattr(self, detailVar, detailInfo['class']())
+
+ if not multi:
+ # We don't need the instance list so just return after verifying only
+ # one value would be returned. We also don't return anything as the
+ # single load just populates the instance that called load()
+ if len(newClass._resultSet) > 1:
+ raise ObjectWrapperError, '%s: load() returned more than one record'
% self.__class__
+ return
+
+ # Build the list
+ instanceList.append(newClass)
+
+ newClass = self.__class__()
+
+ # Only returns from here on multi = True
+ return instanceList
+
+ # ---------------------------------------------------------------------------
+ # Loading data
+ # ---------------------------------------------------------------------------
+
+ def post(self):
+ """
+ Populates the recordSet pulled during the load and posts it to the
+ database table.
+
+ Example Usage::
+ claim = Claim()
+ claim.load(claim_no='99-TEST-001')
+ claim.deduct = '100'
+ claim.post()
+
+ By default this is just a wrapper for the _post() function. Classes
+ that require customized posting should override this function and
+ call _post() as their last step.
+
+ @return: None
+ """
+ self._post()
+
+
+ def _post(self):
+ """Base post function. See the post() description for more details.
+
+ @return: None
+ """
+ #
--------------------------------------------------------------------------
+ # Validations
+ #
--------------------------------------------------------------------------
+ assert bool(self._tableName), 'Table name not defined'
+ assert bool(self._fieldMap), 'Field mapping is not defined'
+
+ #
--------------------------------------------------------------------------
+ # Create a recordset if one doesn't exist
+ #
--------------------------------------------------------------------------
+ if self._resultSet is None:
+ datasource = DataSourceWrapper(
+ self.connections,
+ attributes=self.__datasourceAttributes,
+ fields=self._tableFieldList
+ )
+ self._resultSet = datasource.createEmptyResultSet()
+ self._recordSet = self._resultSet.insertRecord()
+
+ # Pre-post of detail records that need it
----------------------------------
+ self._postDetails(prePost=True)
+
+ #
--------------------------------------------------------------------------
+ # Update the recordSet with the current info
+ #
--------------------------------------------------------------------------
+ for instanceField, settings in self._fieldMap.items():
+ tableField = settings['tableField']
+ oldValue = self._recordSet[tableField]
+ newValue = getattr(self, instanceField)
+
+ if newValue != oldValue:
+ gDebug(10,"Updating %s: %s now %s" % (tableField, oldValue, newValue) )
+ self._recordSet[tableField] = newValue
+
+ # If not deleted then post now, otherwise we'll post after the details
+ if self.isDeleted() is False:
+ self._resultSet.post()
+ self._resultSet.requery(False)
+
+ #
--------------------------------------------------------------------------
+ # Update the instance with any values that changed during the post
+ # (db triggers, default field values, etc)
+ #
--------------------------------------------------------------------------
+ for instanceField, settings in self._fieldMap.items():
+ tableField = settings['tableField']
+ oldValue = getattr(self, instanceField)
+ newValue = self._recordSet[tableField]
+ if newValue != oldValue:
+ gDebug(10,"Backend changed value %s: %s now %s" %
+ (tableField, oldValue, newValue) )
+ setattr(self, instanceField, newValue)
+
+ # Final post of detail records
---------------------------------------------
+ self._postDetails(prePost=False)
+
+ # If deleted then post now after the details are cleaned up
+ if self.isDeleted() is True:
+ self._resultSet.post()
+
+ def _postDetails(self, prePost = False):
+ """
+ Updates the detail records
+
+ @param prePost: Flag indicating whether we post details
+ flagged prepost or details not flagged
+ prepost.
+ @type prePost: boolean
+ """
+ if self._joinMulti:
+ for detailVar, detailInfo in self._joinMulti.items():
+ if detailInfo['prepost'] != prePost:
+ # Skip, as it's not to be posted in this mode
+ continue
+
+ masterValue = getattr(self,detailInfo['masterlink'])
+ detail = getattr(self, detailVar)
+
+ if type(detail) == list:
+ # Mutli join --------------------------------------------------------
+ for index, item in enumerate(detail[:]):
+ # [:] makes a copy of the list to avoid the del in a for loop
issue
+ # Populate detail links if needed
+ detailValue = getattr(item, detailInfo['detaillink'])
+ if detailValue != masterValue:
+ setattr(item, detailInfo['detaillink'], masterValue)
+
+ # Post
+ item.post()
+
+ # Remove the item from the list if deleted
+ if item.isDeleted():
+ detail.remove(item)
+
+ elif detail is not None:
+ # Single join
-------------------------------------------------------
+ if not prePost:
+ detailValue = getattr(detail, detailInfo['detaillink'])
+ if detailValue != masterValue:
+ setattr(detail, detailInfo['detaillink'], masterValue)
+
+ detail.post()
+
+ if prePost:
+ detailValue = getattr(detail, detailInfo['detaillink'])
+ if detailValue != masterValue:
+ setattr(self, detailInfo['masterlink'], detailValue)
+
+ setattr(self, detailVar, None)
+
+ # ---------------------------------------------------------------------------
+ # Deleting data
+ # ---------------------------------------------------------------------------
+ def delete(self):
+ """
+ Tags an instance of an object to be deleted during the next post.
+
+ Example Usage::
+ claim = Claim()
+ claim.load(claim_no='99-TEST-001')
+ claim.delete()
+ claim.post()
+
+ By default this is just a wrapper for the _delete() function. Classes
+ that require customized deleting should override this function and
+ call _delete() as their last step.
+
+ @return: None
+ """
+ self._delete()
+
+
+ def _delete(self):
+ """Base delete function. See the delete() description for more details.
+
+ @return: None
+ """
+ #
--------------------------------------------------------------------------
+ # Validations
+ #
--------------------------------------------------------------------------
+ assert bool(self._tableName), 'Table name not defined'
+ assert bool(self._fieldMap), 'Field mapping is not defined'
+
+ #
--------------------------------------------------------------------------
+ # Create a recordset if one doesn't exist
+ #
--------------------------------------------------------------------------
+ if self._resultSet is None:
+ print "delete() creating record set"
+
+ datasource = DataSourceWrapper(
+ self.connections,
+ attributes=self.__datasourceAttributes,
+ fields=self._tableFieldList
+ )
+ self._resultSet = datasource.createEmptyResultSet()
+ self._recordSet = self._resultSet.insertRecord()
+
+ #
--------------------------------------------------------------------------
+ # Tag the recordset for delete
+ #
--------------------------------------------------------------------------
+ self._recordSet.delete()
+
+ def isDeleted(self):
+ """
+ Return True if the object is deleted.
+ """
+ return self._recordSet.isVoid() or self._recordSet.isDeleted()
+
+ # ---------------------------------------------------------------------------
+ # Type validator
+ # ---------------------------------------------------------------------------
+ def validateTypes(self):
+ """
+ Validates the the instance field types match what is expected.
+
+ Intended to be called by unit tests. Tests Field and Join(multi=False)
+ entries.
+ """
+ for instanceField, settings in self._fieldMap.items():
+ value = getattr(self, instanceField)
+ if bool(settings['typecheck']) and value is not None:
+ actualType = type( value )
+ assert isinstance(value, settings['typecheck']), \
+ 'Field %s is %s not %s' % (instanceField, actualType,
+ settings['typecheck'])
+
+ for detailVar, detailInfo in self._joinMulti.items():
+ if not detailInfo['multi']:
+ item = getattr(self, detailVar)
+ assert isinstance(item, detailInfo['class']), \
+ 'Field %s is %s not %s' % (detailVar, type(item),
+ detailInfo['class'])
+ #
+ def getFieldsAsDict(self):
+ """
+ Returns a list of current Field values as a dict
+
+ Does not return anything for Joins at this time.
+ """
+ dict = {}
+ for instanceField, settings in self._fieldMap.items():
+ dict[instanceField] = getattr(self, instanceField)
+ return dict
+
+# ---------------------------------------------------------------------------
+# Commonly used getters and setters
+# ---------------------------------------------------------------------------
+
+def _getBoolean(self, name):
+ """Accessor for decimal properties"""
+ return getattr(self, name)
+
+def _setBoolean(self, name, value):
+ """Accessor for decimal properties"""
+ setattr(self, name, bool(value))
+
+def _getDatetime(self, name):
+ """Accessor for decimal properties"""
+ return getattr(self, name)
+
+def _setDatetime(self, name, value):
+ """Accessor for decimal properties"""
+ setattr(self, name, convertMxDatetimeToDatetime(value))
+
+def _getDecimal(self, name):
+ """Accessor for decimal properties"""
+ return getattr(self, name)
+
+def _setDecimal(self, name, value):
+ """Accessor for decimal properties"""
+ setattr(self, name, convertValueToDecimal(value))
+
+
+# -----------------------------------------------------------------------------
+# Builder functions
+# -----------------------------------------------------------------------------
+def Field(instanceField,
+ tableField=None, typecheck=None,
+ getter=None, setter=None):
+ """
+ Convenience function that hides the implementation details of
+ defining a field in a objectWrappers _properties.
+ """
+ if tableField is None:
+ tableField = instanceField
+
+ return {'style':'field',
+ 'instanceField' : instanceField,
+ 'settings': {
+ 'tableField' : tableField,
+ 'typecheck': typecheck,
+# 'getter': getter,
+# 'setter': setter,
+ }
+ }
+
+def Join(instanceField, className=None, masterLink=None, detailLink=None,
+ extraConditions=None, multi=True, prePost=False):
+ """
+ Convenience function that hides the implementation details of
+ defining a join in a objectWrappers _properties.
+ """
+ result = {'style' : 'join',
+ 'instanceField' : instanceField,
+ 'settings': {
+ 'class' : className,
+ 'masterlink' : masterLink,
+ 'detaillink' : coalesce(detailLink, masterLink),
+ 'extraConditions' : extraConditions,
+ 'multi' : multi,
+ 'prepost' : prePost
+ }
+ }
+ return result
\ No newline at end of file
Added: objectWrapper/src/__init__.py
===================================================================
Added: objectWrapper/src/datasources.py
===================================================================
--- objectWrapper/src/datasources.py 2006-03-02 13:04:30 UTC (rev 237)
+++ objectWrapper/src/datasources.py 2006-03-13 01:40:27 UTC (rev 238)
@@ -0,0 +1,72 @@
+from gnue.common.datasources.GDataSource import DataSourceWrapper
+from gnue.common.apps import errors
+
+# =============================================================================
+# Exceptions
+# =============================================================================
+
+class StartupError (errors.UserError):
+ pass
+
+
+# ===========================================================================
+# generic functions
+# ===========================================================================
+def getResultSet (connections, connectionName, table, conditions={},
+ readOnly=True, query=True, fields=(), attributes={}):
+ """
+ Generic routine to return a result set.
+
+ @param table: String containing the name of the table to connect to
+ @param conditions: A dictionary of field:value pairs to be used as filter on
+ creation of the result set
+ @param readOnly: Boolean used to set the result set to read only.
+ @param query: Boolean used to determine if the result set should be populated
+ or empty.
+ """
+ finalAttributes={'connection': connectionName,
+ 'name': 'dts%s' % table,
+ 'table': '%s' % table, }
+
+ finalAttributes.update(attributes)
+
+ if fields:
+ datasource = DataSourceWrapper(connections,attributes=finalAttributes,
fields=fields)
+ else:
+ datasource = DataSourceWrapper(connections,attributes=finalAttributes)
+
+ if query:
+ return datasource.createResultSet(conditions=conditions,readOnly=readOnly)
+ else:
+ return datasource.createEmptyResultSet()
+
+# ===========================================================================
+# global connection manager
+# ===========================================================================
+
+import __builtin__
+from gnue.common.datasources import GConnections
+
+__Connections = None
+
+def __getDBConnections():
+ global __Connections
+
+ connections_file = '/usr/local/gnue/etc/connections.conf'
+ if not __Connections:
+ try:
+ __Connections = GConnections.GConnections (connections_file)
+ except GConnections.InvalidFormatError, msg:
+ raise errors.AdminError, \
+ u_("Unable to load the connections definition file.\n\n"
+ "The connections file is in an invalid format.\n%s") % msg
+
+ except IOError, err:
+ raise StartupError, \
+ u_("Unable to load the connections definition file: %s.") \
+ % connections_file
+
+ return __Connections
+
+DBConnections = __getDBConnections
+__builtin__.__dict__ ['DBConnections'] = __getDBConnections
Added: objectWrapper/src/external/__init__.py
===================================================================
Added: objectWrapper/src/external/commonProperty.py
===================================================================
--- objectWrapper/src/external/commonProperty.py 2006-03-02 13:04:30 UTC
(rev 237)
+++ objectWrapper/src/external/commonProperty.py 2006-03-13 01:40:27 UTC
(rev 238)
@@ -0,0 +1,37 @@
+class CommonProperty(object):
+ """A more flexible version of property()
+
+ Saves the name of the managed attribute and uses the saved name
+ in calls to the getter, setter, or destructor. This allows the
+ same function to be used for more than one managed variable.
+
+ As a convenience, the default functions are gettattr, setattr,
+ and delattr. This allows complete access to the managed
+ variable. All or some of those can be overridden to provide
+ custom access behavior.
+
+ """
+
+ def __init__(self, realname, fget=getattr, fset=setattr, fdel=delattr,
doc=None):
+ self.realname = realname
+ self.fget = fget
+ self.fset = fset
+ self.fdel = fdel
+ self.__doc__ = doc or ""
+
+ def __get__(self, obj, objtype=None):
+ if obj is None:
+ return self
+ if self.fget is None:
+ raise AttributeError, "unreadable attribute"
+ return self.fget(obj, self.realname)
+
+ def __set__(self, obj, value):
+ if self.fset is None:
+ raise AttributeError, "can't set attribute"
+ self.fset(obj, self.realname, value)
+
+ def __delete__(self, obj):
+ if self.fdel is None:
+ raise AttributeError, "can't delete attribute"
+ self.fdel(obj, self.realname, value)
Added: objectWrapper/src/util/__init__.py
===================================================================
Added: objectWrapper/src/util/converters.py
===================================================================
--- objectWrapper/src/util/converters.py 2006-03-02 13:04:30 UTC (rev
237)
+++ objectWrapper/src/util/converters.py 2006-03-13 01:40:27 UTC (rev
238)
@@ -0,0 +1,96 @@
+"""
+Converters
+"""
+__revision__ = "$Id"
+
+import datetime
+
+from mx.DateTime import DateTime
+
+from gnue.common.external.decimal import Decimal, ROUND_HALF_UP
+
+# ============================================================================
+# Convert value to Decimal
+# ============================================================================
+def convertValueToDecimal(value):
+ """Accessor for the UnitCost property"""
+
+ # -------------------------------------------------------------------------
+ # Clean up and validate string values
+ # -------------------------------------------------------------------------
+ if isinstance(value, basestring):
+ dollarCount = value.count('$')
+ decimalCount = value.count('.')
+ commaCount = value.count(',')
+ negativeCount = value.count('-')
+
+ if dollarCount > 1 or decimalCount > 1 or negativeCount > 1:
+ raise ValueError
+
+ # Verify Decimals
+ # TODO - bad to assume currency if it's a string
+# if decimalCount:
+# if value[-3] != '.':
+# print "Value was '%s'" % value
+# raise ValueError
+
+ # Remove dollar signs
+ if dollarCount:
+ if not (value.startswith('$') or value.startswith('-$')):
+ raise ValueError
+ else:
+ value = value.replace('$','')
+
+ # Verify and remove commas
+ if decimalCount:
+ whole, fractional = value.split('.')
+ else:
+ whole = value
+ fractional = '0'
+
+ sections = whole.split(',')
+ for index,section in enumerate(sections):
+ if (index > 0) and len(section) > 3:
+ raise ValueError
+ if (index > 0) and (len(section) != 3):
+ raise ValueError
+
+ value = ''.join(sections)
+ value = '.'.join([value,fractional])
+ value = value.strip()
+ finalValue = Decimal(value)
+ elif value is None:
+ finalValue = None
+ else:
+ value = str(value).strip()
+ finalValue = Decimal(value)
+
+ return finalValue
+
+def convertMxDatetimeToDatetime(value):
+ """
+ Convert an mx datetime value to a std python datetime value
+
+ If given a datetime then just return it. This function needs
+ renamed convertToDatetime
+ """
+ if value is None:
+ return value
+ elif isinstance(value, datetime.datetime):
+ return value
+ elif isinstance(value, type(DateTime(2000))):
+ secs, microsecs = str(value.second).split('.')
+ return datetime.datetime(value.year, value.month, value.day,
+ value.hour, value.minute, int(secs),
int(microsecs))
+ else:
+ raise 'Unknown type to convert to datetime'
+
+def coalesce(*values):
+ for value in values:
+ if value is not None:
+ return value
+ return value
+
+def monetize(value):
+ return value.quantize(Decimal('.01'), rounding=ROUND_HALF_UP)
+
Added: objectWrapper/tests/basics.py
===================================================================
--- objectWrapper/tests/basics.py 2006-03-02 13:04:30 UTC (rev 237)
+++ objectWrapper/tests/basics.py 2006-03-13 01:40:27 UTC (rev 238)
@@ -0,0 +1,113 @@
+#
+# FILE:
+# basics.py
+#
+# DESCRIPTION:
+"""
+Test cases for based objectWrapper functionality
+"""
+import unittest
+
+# from gnue.common.apps import GDebug
+# GDebug.setDebug ("3")
+
+from gnue.common.contrib.objectWrapper.ObjectWrapper \
+ import ObjectWrapper, Field, Join
+
+from objects.address import Address
+from objects.item import Item
+from objects.state import State
+# =============================================================================
+# Exceptions
+# =============================================================================
+
+# =============================================================================
+# Test cases
+# =============================================================================
+class TestCase(unittest.TestCase):
+
+ # --------------------------------------------------------------------------
+ # Test Cases
+ # --------------------------------------------------------------------------
+ def testLoading(self):
+ """Verify that basic loading works properly"""
+
+ # Single load
+ object = State()
+ object.load(state = 'KS')
+ self.assertEqual(object.description, 'Kansas')
+
+ # Multi Load
+ object = State()
+ results = object.load(multi = True)
+ self.assertEqual(len(results), 54)
+
+ def testTypeCheck(self):
+ """Verify that the type checker is working """
+
+ object = Address()
+ object.load(address_id = 1)
+ object.validateTypes()
+
+ object = Item()
+ object.load(item_id = 0)
+ object.validateTypes()
+
+ def testPost(self):
+ """Verify that basic posting works properly"""
+
+ # New record
+ object = State()
+ object.state = 'ZZ'
+ object.description = 'Denial'
+ object.post()
+ DBConnections().getConnection('gnue', login=True).commit()
+
+ # Modify (and verify the previous post)
+ object = State()
+ object.load(state = 'ZZ')
+ self.assertEqual(object.description, 'Denial')
+
+ object.description = 'Panic'
+ object.post()
+
+ ## Reload to see if change worked
+ object = State()
+ object.load(state = 'ZZ')
+ self.assertEqual(object.description, 'Panic')
+
+
+ def testGetFieldsAsDict(self):
+ """Testing getFieldsAsDict"""
+ object = Address()
+ object.load(address_id = 1)
+ dict = object.getFieldsAsDict()
+
+ self.assertEqual(dict['address1'] , '6789 N. MAIN')
+ self.assertEqual(dict['address2'] , None)
+ self.assertEqual(dict['city'] , 'ELLSWORTH')
+ self.assertEqual(dict['state'] , 'KS')
+ self.assertEqual(dict['zip'] , '67439')
+
+
+ ## Named with Z so it sorts to the last test to run and cleans up
+ ## the test insert.
+ def testZDelete(self):
+ """
+ Test delete capability
+ """
+ object = State()
+ object.load(state = 'ZZ')
+ object.delete()
+ object.post()
+
+ DBConnections().getConnection('gnue', login=True).commit()
+
+# ----------------------------------------------------------------------------
+# Support code
+# ----------------------------------------------------------------------------
+def suite():
+ suite = unittest.makeSuite(TextTestCase,'test')
+
+if __name__ == "__main__":
+ unittest.main()
Added: objectWrapper/tests/joins.py
===================================================================
--- objectWrapper/tests/joins.py 2006-03-02 13:04:30 UTC (rev 237)
+++ objectWrapper/tests/joins.py 2006-03-13 01:40:27 UTC (rev 238)
@@ -0,0 +1,172 @@
+#
+# FILE:
+# joins.py
+#
+# DESCRIPTION:
+"""
+Test cases for more complex, joined object wrapper objects
+"""
+import unittest
+
+from datetime import datetime
+
+# from gnue.common.apps import GDebug
+# GDebug.setDebug ("3")
+
+from gnue.common.external.decimal import Decimal
+
+from gnue.common.contrib.objectWrapper.ObjectWrapper \
+ import ObjectWrapper, Field, Join
+
+from objects.baseAddress import getCurrentAddress
+from objects.invoice import Invoice
+from objects.invoiceAddress import InvoiceAddress
+
+# =============================================================================
+# Exceptions
+# =============================================================================
+
+# =============================================================================
+# Test Classes
+# =============================================================================
+
+# =============================================================================
+# Test cases
+# =============================================================================
+class TestCase(unittest.TestCase):
+
+ # --------------------------------------------------------------------------
+ # Test Cases
+ # --------------------------------------------------------------------------
+ def testLoad(self):
+ """Verify that basic loading works properly"""
+ object = Invoice()
+ object.load(invoice_id = 1)
+
+ self.assertEquals(object.mdse_total, Decimal('130'))
+ self.assertEquals(len(object.addresses), 2)
+ self.assertEquals(len(object.shippingAddresses), 1)
+ self.assertEquals(len(object.billingAddresses), 1)
+
+ def testTypes(self):
+ """Verify that all fields listed in the claim typecast properly"""
+
+ object = Invoice()
+ object.load(invoice_id = 1)
+ object.validateTypes()
+
+ def testPost(self):
+ """Verify that basic posting works properly"""
+
+ object = Invoice()
+ object.mdse_total = 200
+ object.labor_total = '100'
+ object.grand_total = 300
+
+ newAddress = InvoiceAddress()
+ newAddress.valid_from = datetime.now()
+ newAddress.type = 'SHIP'
+ newAddress.attention1 = 'GNUE CODER'
+ newAddress.streetAddress.address1 ='1357 S. North St.'
+ newAddress.streetAddress.city = 'MANHATTAN'
+ newAddress.streetAddress.state = 'KS'
+ newAddress.streetAddress.zip = '66502'
+
+ object.addresses.append(newAddress)
+
+ # TODO: The oid issue with pg8.1 and datasources
+ # pgsql <= 8.0.x should be fine but I cant test a.t.m.
+ #object.post()
+
+ DBConnections().getConnection('gnue', login=True).commit()
+
+ def testModification(self):
+ """
+ Test modification capability
+ """
+ # Modify ------------------------------------------------------------------
+ object = Invoice()
+ object.load(invoice_id = 1)
+ shipAddress = getCurrentAddress(object.addresses, 'SHIP')
+
+ object.tax_rate = 20
+ object.tax_total = 28
+ shipAddress.streetAddress.address2 = 'SUITE B'
+
+ object.post()
+ DBConnections().getConnection('gnue', login=True).commit()
+
+ # Verify ------------------------------------------------------------------
+ newObject = Invoice()
+ newObject.load(invoice_id = 1)
+ shipAddress = getCurrentAddress(newObject.addresses, 'SHIP')
+
+ self.assertEquals(newObject.tax_rate, Decimal('20'))
+ self.assertEquals(newObject.tax_total, Decimal('28'))
+ self.assertEquals(shipAddress.streetAddress.address2, 'SUITE B')
+
+ # Reset -------------------------------------------------------------------
+ newObject.tax_rate = 10
+ newObject.tax_total = 14
+ shipAddress.streetAddress.address2 = 'SUITE A'
+ newObject.post()
+ DBConnections().getConnection('gnue', login=True).commit()
+
+ #def testInsertWithBlankDetails(self):
+ #"""
+ #Test insert capability when one or more Joins are empty
+ #"""
+ #newObject = Master()
+ #newObject.master_id = 98
+ #newObject.decimal_field = .98
+ #newObject.txt_field = 'Master Test'
+ #newObject.bool_field = True
+
+ #newObject.post()
+
+ #DBConnections().getConnection('epm', login=True).commit()
+
+ #def testGetFieldsAsDict(self):
+ #"""
+ #Test insert capability when one or more Joins are empty
+ #"""
+ #newObject = Master()
+ #newObject.load(master_id = 1)
+ #dict = newObject.getFieldsAsDict()
+ #assert dict['master_id'] == 1, 'Get as Dict Failed'
+ #assert dict['decimal_field'] == .01, 'Get as Dict Failed'
+ #assert dict['txt_field'] == 'Master 1', 'Get as Dict Failed : %s' %
dict['text_field']
+ #assert dict['bool_field'] == True, 'Get as Dict Failed'
+
+
+ ## Named with Z so it sorts to the last test to run and cleans up
+ ## the test insert
+ #def testZDelete(self):
+ #"""
+ #Test delete capability
+ #"""
+ #newObject = Master()
+ #newObject.load(master_id = 99)
+ #newObject.delete()
+ #for item in newObject.details:
+ #item.delete()
+ #newObject.post()
+
+ #newObject = Master()
+ #newObject.load(master_id = 98)
+ #newObject.delete()
+ #for item in newObject.details:
+ #item.delete()
+ #newObject.post()
+
+
+ #DBConnections().getConnection('epm', login=True).commit()
+
+# ----------------------------------------------------------------------------
+# Support code
+# ----------------------------------------------------------------------------
+def suite():
+ suite = unittest.makeSuite(TextTestCase,'test')
+
+if __name__ == "__main__":
+ unittest.main()
Added: objectWrapper/tests/objects/__init__.py
===================================================================
Added: objectWrapper/tests/objects/address.py
===================================================================
--- objectWrapper/tests/objects/address.py 2006-03-02 13:04:30 UTC (rev
237)
+++ objectWrapper/tests/objects/address.py 2006-03-13 01:40:27 UTC (rev
238)
@@ -0,0 +1,38 @@
+"""
+A base payment item to represent the various nearly
+identicle payments used in claims, orders, and invoices
+"""
+__revision__ = "$Id: dbAddress.py 778 2006-02-27 19:52:34Z jamest $"
+
+from datetime import datetime
+
+from gnue.common.contrib.objectWrapper.external.commonProperty \
+ import CommonProperty
+
+from gnue.common.contrib.objectWrapper.ObjectWrapper \
+ import ObjectWrapper, Field, \
+ _getDatetime, _setDatetime
+
+## ============================================================================
+# Main Logic
+# ============================================================================
+class Address(ObjectWrapper):
+ """
+ A base payment item to represent the various nearly
+ identical payments used in claims, orders, and invoices
+ """
+ _tableName = "address"
+
+ _primaryKeys = "address_id"
+
+ _properties = ( Field('address_id', typecheck=int),
+ Field('created', typecheck=datetime),
+ Field('address1', typecheck=basestring),
+ Field('address2', typecheck=basestring),
+ Field('city', typecheck=basestring),
+ Field('state', typecheck=basestring),
+ Field('zip', typecheck=basestring),
+ )
+
+ # Accessors -----------------------------------------------------------------
+ created = CommonProperty('_created', _getDatetime, _setDatetime)
\ No newline at end of file
Added: objectWrapper/tests/objects/baseAddress.py
===================================================================
--- objectWrapper/tests/objects/baseAddress.py 2006-03-02 13:04:30 UTC (rev
237)
+++ objectWrapper/tests/objects/baseAddress.py 2006-03-13 01:40:27 UTC (rev
238)
@@ -0,0 +1,187 @@
+"""
+A standard address format links to a table in a db.
+
+Not used standalone.
+"""
+__revision__ = '$Id$'
+
+from datetime import datetime
+
+from gnue.common.apps import errors
+from gnue.common.contrib.objectWrapper.external.commonProperty \
+ import CommonProperty
+
+from gnue.common.contrib.objectWrapper.ObjectWrapper \
+ import ObjectWrapper, Field, Join, \
+ _getDatetime, _setDatetime
+
+from objects.address import Address
+
+# ============================================================================
+# Exceptions
+# ============================================================================
+class AddressIncompleteError(errors.UserError):
+ """
+ The address is incomplete and cannot be formatted into a valid
+ mailing address.
+ """
+ def __init__ (self, missing):
+ msg = "Address is incomplete. Missing %s" % missing
+ errors.UserError.__init__ (self, msg)
+
+# ============================================================================
+# Main Logic
+# ============================================================================
+class BaseAddress(ObjectWrapper):
+ """
+ Contains the basic template for an address in our system.
+
+ Wraps a standard DBAddress with the additional fields typically
+ used by an address in our system.
+ """
+
+ _properties = ( Field('created' , typecheck=datetime),
+ Field('valid_from' , typecheck=datetime),
+ Field('type' , typecheck=basestring),
+ Field('attention1' , typecheck=basestring),
+ Field('attention2' , typecheck=basestring),
+ Field('address_id' , typecheck=int),
+
+ Join('streetAddress', className=Address,
+ masterLink='address_id', detailLink='address_id',
+ multi=False, prePost=True),
+ )
+ # Accessors -----------------------------------------------------------------
+ created = CommonProperty('_created', _getDatetime, _setDatetime)
+
+ # ===========================================================================
+ # Internal Functions
+ # ===========================================================================
+ def __buildContactList(self, verify = False, companyFirst = False):
+ """
+ Merges the names and company into a list of contact info
+ """
+ contactList = []
+ line = self.attention1.strip().upper()
+ if line:
+ contactList.append(line)
+
+ line = self.attention2.strip().upper()
+ if line:
+ if companyFirst:
+ contactList.insert(0, line)
+ else:
+ contactList.append(line)
+
+ if verify and not contactList:
+ raise AddressIncompleteError, "Contact or Company Name"
+
+ return contactList
+
+
+ def __buildAddressList(self, verify = False):
+ """
+ Merges the address lines into a list of contact info
+ """
+ addressList = []
+
+ address1 = self.streetAddress.address1
+ if address1:
+ line = address1.strip().upper()
+ if line:
+ addressList.append(line)
+
+ address2 = self.streetAddress.address2
+ if address2:
+ line = address2.strip().upper()
+ if line:
+ addressList.append(line)
+
+ if verify and not addressList:
+ raise AddressIncompleteError, "Street Address"
+
+ return addressList
+
+ def __buildCityList(self, verify = False):
+ """
+ Constructs a properly formatted city, state, zip string
+ """
+ city = self.streetAddress.city.strip().upper()
+ state = self.streetAddress.state.strip().upper()
+
+ zipcode = self.streetAddress.zip.strip().upper()
+ zip4 = self.streetAddress.zip4
+ if zip4:
+ zipcode = "%s-%s" % (zipcode, self.streetAddress.zip4.strip().upper())
+
+
+ if verify and "" in (city, state, zipcode):
+ raise AddressIncompleteError, "City, State, or Zip"
+
+ cityList = ["%s, %s %s" % (city, state, zipcode)]
+
+ return cityList
+
+ # ===========================================================================
+ # Internal Functions
+ # ===========================================================================
+ def getMailingFormat(self, companyFirst = False):
+ """
+ Builds a valid mailing address from the available information.
+
+ If any part of the address is incomplete an error is raised.
+
+ @param companyFirst: Should the company name preceed the contact name
+ @return: The formatted address
+ @rtype: A list of strings containing the properly formatted address
+ """
+
+ # Contact lines
+ contactList = self.__buildContactList(verify=True,
+ companyFirst = companyFirst)
+
+ # Address Lines
+ addressList = self.__buildAddressList(True)
+
+ # City Info
+ cityList = self.__buildCityList(True)
+
+ return contactList + addressList + cityList
+
+ def getAddress(self, companyFirst = False):
+ """
+ Builds an address block from the available address information
+
+ If any part of the address is incomplete it is left out. If you
+ need to ensure a valid shipping address use getMailingFormat instead.
+
+ @param companyFirst: Should the company name preceed the contact name
+ @return: The formatted address
+ @rtype: A list of strings containing the properly formatted address
+ """
+ # Contact lines
+ contactList = self.__buildContactList(companyFirst=companyFirst)
+
+ # Address Lines
+ addressList = self.__buildAddressList()
+
+ # City Info
+ cityList = self.__buildCityList()
+
+ return contactList + addressList + cityList
+
+# =============================================================================
+# Module level convenience functions
+# =============================================================================
+def getCurrentAddress(addressList, addressType):
+ """
+ When passed a list of addresses and a type it will return the address
+ that is currently valid based upon the date_valid_from datestamp.
+ """
+ currentAddress = None
+ for address in addressList:
+ if address.type == addressType:
+ if currentAddress is None or \
+ currentAddress.date_valid_from < address.date_valid_from:
+ currentAddress = address
+ return currentAddress
\ No newline at end of file
Added: objectWrapper/tests/objects/invoice.py
===================================================================
--- objectWrapper/tests/objects/invoice.py 2006-03-02 13:04:30 UTC (rev
237)
+++ objectWrapper/tests/objects/invoice.py 2006-03-13 01:40:27 UTC (rev
238)
@@ -0,0 +1,75 @@
+"""
+Representation of a very simple invoice
+"""
+__revision__ = '$Id$'
+
+from datetime import datetime
+
+from gnue.common.external.decimal import Decimal
+
+from objects.invoiceAddress import InvoiceAddress
+from objects.invoiceItem import InvoiceItem
+
+from gnue.common.contrib.objectWrapper.external.commonProperty \
+ import CommonProperty
+
+from gnue.common.contrib.objectWrapper.ObjectWrapper \
+ import ObjectWrapper, Field, Join, \
+ _getDatetime, _setDatetime, _getDecimal, _setDecimal
+
+
+# ============================================================================
+# Main Logic
+# ============================================================================
+class Invoice(ObjectWrapper):
+ """
+ Contains basic information about a single pickticket
+ """
+ _tableName = 'invoice'
+
+ _primaryKeys = 'invoice_id'
+
+ _properties = (Field('invoice_id' , typecheck=int),
+ Field('created' , typecheck=datetime),
+ Field('mdse_total' , typecheck=Decimal),
+ Field('freight_total' , typecheck=Decimal),
+ Field('labor_total' , typecheck=Decimal),
+ Field('subtotal' , typecheck=Decimal),
+ Field('tax_total' , typecheck=Decimal),
+ Field('tax_rate' , typecheck=Decimal),
+ Field('grand_total' , typecheck=Decimal),
+
+ # All addresses
+ Join('addresses', className = InvoiceAddress,
+ masterLink='invoice_id', detailLink='invoice_id'
+ ),
+
+ # Just shipping addresses - for the heck of it
+ Join('shippingAddresses', className = InvoiceAddress,
+ masterLink='invoice_id', detailLink='invoice_id',
+ extraConditions = { 'type' : 'SHIP',
+ }
+ ),
+ # Just billing addresses - for the heck of it
+ Join('billingAddresses', className = InvoiceAddress,
+ masterLink='invoice_id', detailLink='invoice_id',
+ extraConditions = { 'type' : 'BILL',
+ }
+ ),
+
+ Join('items', className=InvoiceItem,
+ masterLink ='invoice_id', detailLink='invoice_id',
+ ),
+ )
+
+ # Accessors -----------------------------------------------------------------
+ created = CommonProperty('_created' , _getDatetime, _setDatetime)
+
+ mdse_total = CommonProperty('_mdse_total' , _getDecimal, _setDecimal)
+ freight_total = CommonProperty('_freight_total', _getDecimal, _setDecimal)
+ labor_total = CommonProperty('_labor_total' , _getDecimal, _setDecimal)
+ subtotal = CommonProperty('_subtotal' , _getDecimal, _setDecimal)
+ tax_total = CommonProperty('_tax_total' , _getDecimal, _setDecimal)
+ tax_rate = CommonProperty('_tax_rate' , _getDecimal, _setDecimal)
+ grand_total = CommonProperty('_grand_total' , _getDecimal, _setDecimal)
+
Added: objectWrapper/tests/objects/invoiceAddress.py
===================================================================
--- objectWrapper/tests/objects/invoiceAddress.py 2006-03-02 13:04:30 UTC
(rev 237)
+++ objectWrapper/tests/objects/invoiceAddress.py 2006-03-13 01:40:27 UTC
(rev 238)
@@ -0,0 +1,24 @@
+"""
+A base claim item to represent the items in claim_equiv_items and
+claim_actual_items
+"""
+__revision__ = "$Id$"
+
+from objects.baseAddress import BaseAddress
+from gnue.common.contrib.objectWrapper.ObjectWrapper \
+ import Field
+
+# ============================================================================
+# Main Logic
+# ============================================================================
+class InvoiceAddress(BaseAddress):
+ """
+ The base class that ClaimEquivItem and ClaimActualItem are based upon.
+ """
+ _tableName = 'invoice_address'
+
+ _primaryKeys = 'invoice_address_id'
+
+ _properties = list(BaseAddress._properties)
+ _properties.append(Field('invoice_address_id', typecheck=int))
+ _properties.append(Field('invoice_id', typecheck=int))
\ No newline at end of file
Added: objectWrapper/tests/objects/invoiceItem.py
===================================================================
--- objectWrapper/tests/objects/invoiceItem.py 2006-03-02 13:04:30 UTC (rev
237)
+++ objectWrapper/tests/objects/invoiceItem.py 2006-03-13 01:40:27 UTC (rev
238)
@@ -0,0 +1,53 @@
+"""
+A base claim item to represent the items in claim_equiv_items and
+claim_actual_items
+"""
+__revision__ = "$Id$"
+
+from gnue.common.external.decimal import Decimal, ROUND_HALF_UP
+
+from gnue.common.contrib.objectWrapper.external.commonProperty \
+ import CommonProperty
+from gnue.common.contrib.objectWrapper.ObjectWrapper \
+ import ObjectWrapper, Field, \
+ _getDecimal, _setDecimal
+
+# ============================================================================
+# Exceptions
+# ============================================================================
+
+
+# ============================================================================
+# Main Logic
+# ============================================================================
+class InvoiceItem(ObjectWrapper):
+ """
+ Claim Item Base Class
+
+ The base class that ClaimEquivItem and ClaimActualItem are based upon.
+ """
+ _tableName = 'invoice_item'
+
+ _primaryKeys = 'invoice_item_id'
+
+ _properties = ( Field('invoice_item_id' , typecheck=int),
+ Field('invoice_id' , typecheck=int),
+ Field('sku' , typecheck=basestring),
+ Field('description' , typecheck=basestring),
+ Field('quantity' , typecheck=int),
+ Field('unit_price' , typecheck=Decimal),
+ )
+
+ # Accessors -----------------------------------------------------------------
+ unit_price = CommonProperty('_unit_price', _getDecimal, _setDecimal)
+
+ def getQuantityPrice(self):
+ """Returns the price of this line item based upon quantity and unit cost
+
+ @return: The total price of the PO line item rounded to the nearest cent.
+ @rtype: Decimal
+ """
+
+ return (self.quantity * self.__unitPrice).quantize(Decimal('.01'),
+ ROUND_HALF_UP)
+
Added: objectWrapper/tests/objects/item.py
===================================================================
--- objectWrapper/tests/objects/item.py 2006-03-02 13:04:30 UTC (rev 237)
+++ objectWrapper/tests/objects/item.py 2006-03-13 01:40:27 UTC (rev 238)
@@ -0,0 +1,39 @@
+"""
+Simple representation of an item in a database.
+"""
+__revision__ = '$Id$'
+
+from datetime import datetime
+
+from gnue.common.external.decimal import Decimal
+
+from gnue.common.contrib.objectWrapper.external.commonProperty \
+ import CommonProperty
+
+from gnue.common.contrib.objectWrapper.ObjectWrapper \
+ import ObjectWrapper, Field, \
+ _getDecimal, _setDecimal, \
+ _getDatetime, _setDatetime
+
+class Item(ObjectWrapper):
+ """
+ Contains basic information about a single purchase order.
+ """
+
+ _tableName = 'items'
+
+
+ _primaryKeys = 'item_id'
+ _properties = (
+ Field('item_id' , typecheck=int),
+ Field('created' , typecheck=datetime),
+ Field('sku' , typecheck=basestring),
+ Field('make' , typecheck=basestring),
+ Field('model' , typecheck=basestring),
+ Field('description' , typecheck=basestring),
+ Field('unit_price' , typecheck=Decimal),
+ )
+
+ created = CommonProperty('_created', _getDatetime, _setDatetime)
+
+ unit_price = CommonProperty('_unit_price', _getDecimal, _setDecimal)
\ No newline at end of file
Added: objectWrapper/tests/objects/state.py
===================================================================
--- objectWrapper/tests/objects/state.py 2006-03-02 13:04:30 UTC (rev
237)
+++ objectWrapper/tests/objects/state.py 2006-03-13 01:40:27 UTC (rev
238)
@@ -0,0 +1,22 @@
+"""
+Represents a US state in our system
+"""
+__revision__ = "$Id: state.py 731 2006-02-16 18:02:02Z jamest $"
+
+from gnue.common.contrib.objectWrapper.ObjectWrapper \
+ import ObjectWrapper, Field
+
+# ============================================================================
+# Main Logic
+# ============================================================================
+class State(ObjectWrapper):
+ """
+ Contains basic information about a single packing slip.
+ """
+ _tableName = 'state'
+
+ _primaryKeys = 'state'
+
+ _properties = ( Field('state', typecheck=basestring),
+ Field('description', typecheck=basestring),
+ )
\ No newline at end of file
Added: objectWrapper/tests/runAllTests
===================================================================
--- objectWrapper/tests/runAllTests 2006-03-02 13:04:30 UTC (rev 237)
+++ objectWrapper/tests/runAllTests 2006-03-13 01:40:27 UTC (rev 238)
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+#
+# Modified test script based upon the alltest.py file included
+# in the python 2.3 unittest examples
+#
+import unittest
+import os, re
+
+def suite():
+ fileFilter = re.compile(".py$")
+
+ allFiles = os.listdir('.')
+ testFiles = filter(fileFilter.search, allFiles)
+ testFiles = [ fileFilter.sub("",filename) for filename in testFiles ]
+
+ suite = unittest.TestSuite()
+ for module in map(__import__, testFiles):
+ suite.addTest(unittest.findTestCases(module))
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
Property changes on: objectWrapper/tests/runAllTests
___________________________________________________________________
Name: svn:executable
+ *
Added: objectWrapper/tests/util_converters.py
===================================================================
--- objectWrapper/tests/util_converters.py 2006-03-02 13:04:30 UTC (rev
237)
+++ objectWrapper/tests/util_converters.py 2006-03-13 01:40:27 UTC (rev
238)
@@ -0,0 +1,81 @@
+#
+# FILE:
+# objects_base_objectWrapper.py
+#
+# DESCRIPTION:
+"""
+Test cases for the objectWrapper converters module
+"""
+# from gnue.common.apps import GDebug
+# GDebug.setDebug ("3")
+
+import unittest
+
+
+from mx.DateTime import DateTime
+import datetime
+
+from gnue.common.external.fixedpoint import FixedPoint
+from gnue.common.external.decimal import Decimal
+
+from gnue.common.contrib.objectWrapper.util.converters \
+ import convertValueToDecimal, convertMxDatetimeToDatetime, \
+ coalesce
+
+# =============================================================================
+# Exceptions
+# =============================================================================
+
+# =============================================================================
+# Test cases
+# =============================================================================
+class TestCase(unittest.TestCase):
+
+ # --------------------------------------------------------------------------
+ # Test Cases
+ # --------------------------------------------------------------------------
+ def testDecimalConverter(self):
+ """Verify that decimal converter works properly"""
+ result = Decimal("100")
+ self.assertEqual (convertValueToDecimal(FixedPoint("100")), result)
+ self.assertEqual (convertValueToDecimal("100"), result)
+ self.assertEqual (convertValueToDecimal(100), result)
+ self.assertEqual (convertValueToDecimal(100.0), result)
+ self.assertEqual (convertValueToDecimal(Decimal("100.0")), result)
+
+ def testCoalesce(self):
+ """Verify that decimal converter works properly"""
+ result = 5
+ self.assertEqual (coalesce(5, None), result)
+ self.assertEqual (coalesce(None, 5), result)
+ self.assertEqual (coalesce(None, None, 5), result)
+ self.assertEqual (coalesce(None, None, 5, 1), result)
+
+ def testMxToDatetime(self):
+ """ Test conversion of mxDateTime to DateTime """
+ value = DateTime(2006,8,6,13,15,54.91)
+
+ match = datetime.datetime(2006, month=8, day=6, hour=13, minute=15,
+ second=54, microsecond=91)
+
+ result = convertMxDatetimeToDatetime(value)
+
+ assert result == match, "mxDateTime to datetime conversion "
+
+ value = DateTime(2006,12,25,13,15,54)
+
+ match = datetime.datetime(2006, month=12, day=25, hour=13, minute=15,
+ second=54)
+
+ result = convertMxDatetimeToDatetime(value)
+
+ assert result == match, "mxDateTime to datetime conversion "
+
+# ----------------------------------------------------------------------------
+# Support code
+# ----------------------------------------------------------------------------
+def suite():
+ suite = unittest.makeSuite(TextTestCase,'test')
+
+if __name__ == "__main__":
+ unittest.main()
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue-contrib] r238 - / objectWrapper objectWrapper/.stage objectWrapper/.stage/objectWrapper objectWrapper/src objectWrapper/src/external objectWrapper/src/util objectWrapper/tests objectWrapper/tests/objects,
jamest <=