commit-gnue
[Top][All Lists]
Advanced

[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()





reply via email to

[Prev in Thread] Current Thread [Next in Thread]