Changeset 303

Show
Ignore:
Timestamp:
01/31/08 23:58:35 (1 year ago)
Author:
johnbywater
Message:

Added support for 'isTemporal' on DomainObject? classes.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/src/dm/db.py

    r302 r303  
    743743            ) 
    744744            logger.debug(message) 
     745        currentVersion = None 
     746        if self.domainObject.meta.isTemporal: 
     747            r = self.domainObject.meta.createTemporalCollection(self.domainObject) 
     748            loadedList = {self.domainObject: self.domainObject} 
     749            currentVersion = r.findFirstDomainObject(loadedList=loadedList) 
    745750        self.domainObject.id = self.id 
    746751        for metaAttr in self.meta.attributes: 
     
    757762                    mapperValue = self.parent.domainObject 
    758763                    self.domainObject.parent = mapperValue 
    759                     #message = "Continuing with loaded parent: %s" % ( 
    760                     #    self.domainObject.parent 
    761                     #) 
    762                     #logger.debug(message) 
    763764                    continue 
    764765            if metaAttr.dom.isTemporal: 
    765766                # We don't persist temporal attributes on the parent. 
    766767                #     - we need to look in the temporal model. 
    767                 r = metaAttr.dom.createTemporalCollection(self.domainObject) 
    768                 mostRecent = r.findFirstDomainObject(loadedList) 
    769                 if mostRecent: 
    770                     mappedValue = mostRecent.recordedValue 
     768                if self.domainObject.meta.isTemporal: 
     769                    r = self.domainObject.meta.createTemporalCollection(self.domainObject) 
     770                    current = r.findFirstDomainObject(loadedList) 
     771                    if current: 
     772                        mappedValue = getattr(current, domName) 
     773                    else: 
     774                        mappedValue = metaAttr.dom.createInitialValue( 
     775                            self.domainObject 
     776                        ) 
    771777                else: 
    772                     mappedValue = metaAttr.dom.createInitialValue( 
    773                         self.domainObject 
    774                     ) 
     778                    r = metaAttr.dom.createTemporalCollection(self.domainObject) 
     779                    current = r.findFirstDomainObject(loadedList) 
     780                    if current: 
     781                        mappedValue = current.recordedValue 
     782                    else: 
     783                        mappedValue = metaAttr.dom.createInitialValue( 
     784                            self.domainObject 
     785                        ) 
    775786            elif metaAttr.isDomainObjectRef: 
    776787                mapper = getattr(self, dbName) 
     
    806817        "Sets attributes of record object from domain object." 
    807818        isChanged = False 
     819        isChangedTemporal = False 
     820        currentVersion = None 
     821        if self.domainObject.meta.isTemporal: 
     822            r = self.domainObject.meta.createTemporalCollection(self.domainObject) 
     823            loadedList = {self.domainObject: self.domainObject} 
     824            currentVersion = r.findFirstDomainObject(loadedList=loadedList) 
     825            if not currentVersion: 
     826                currentVersion = r.create(loadedList=loadedList) 
     827                isChangedTemporal = True 
     828            else: 
     829                for metaAttr in self.domainObject.meta.attributes: 
     830                    if metaAttr.isTemporal: 
     831                        domName = metaAttr.name 
     832                        recordedValue = getattr(currentVersion, domName) 
     833                        actualValue = getattr(self.domainObject, domName) 
     834                        if actualValue != recordedValue: 
     835                            isChangedTemporal = True 
     836                if isChangedTemporal: 
     837                    currentVersion = r.create(loadedList=loadedList) 
    808838        for metaAttr in self.meta.attributes: 
    809839            domValue = getattr(self.domainObject, metaAttr.domName) 
     
    812842                # We don't persist temporal attributes on the parent. 
    813843                #     - we need to look in the temporal model. 
    814                 r = metaAttr.dom.createTemporalCollection(self.domainObject) 
    815                 loadedList = {self.domainObject: self.domainObject} 
    816                 mostRecent = r.findFirstDomainObject(loadedList=loadedList) 
    817                 if mostRecent == None or mostRecent.recordedValue != domValue: 
    818                     r.create(recordedValue=domValue, loadedList=loadedList) 
     844                if self.domainObject.meta.isTemporal: 
     845                    if isChangedTemporal: 
     846                        setattr(currentVersion, metaAttr.domName, domValue) 
     847                else: 
     848                    r = metaAttr.dom.createTemporalCollection(self.domainObject) 
     849                    loadedList = {self.domainObject: self.domainObject} 
     850                    current = r.findFirstDomainObject(loadedList=loadedList) 
     851                    if current == None or current.recordedValue != domValue: 
     852                        r.create(recordedValue=domValue, loadedList=loadedList) 
    819853            elif metaAttr.isDomainObjectRef: 
    820854                domainObject = domValue 
     
    832866                    setattr(self, metaAttr.dbName, domValue) 
    833867                    isChanged = True 
     868        if isChangedTemporal: 
     869            currentVersion.save() 
    834870        if isChanged: 
    835871            if moddebug and debug: 
  • trunk/src/dm/dom/base.py

    r299 r303  
    504504    isConstant = False 
    505505    isUnique = True 
     506    isTemporal = False 
    506507    searchAttributeNames = [] 
    507508    startsWithAttributeName = '' 
     
    672673        self.decacheItem() 
    673674        if self.record: 
    674             self.deleteTemporalAttributeObjects() 
     675            self.deleteTemporalObjects() 
    675676            self.record.domainObject = None 
    676677            self.record.destroySelf() 
    677678            self.record = None 
    678679 
    679     def deleteTemporalAttributeObjects(self): 
    680         for metaAttr in self.meta.attributes: 
    681             if metaAttr.isTemporal: 
    682                 r = metaAttr.createTemporalCollection(self) 
    683                 [i.delete() for i in r] 
     680    def deleteTemporalObjects(self): 
     681        if self.meta.isTemporal: 
     682            register = self.meta.createTemporalCollection(self) 
     683            [i.delete() for i in register] 
     684        else: 
     685            for metaAttr in self.meta.attributes: 
     686                if metaAttr.isTemporal: 
     687                    register = metaAttr.createTemporalCollection(self) 
     688                    [i.delete() for i in register] 
    684689 
    685690    def purgeAggregates(self): 
  • trunk/src/dm/dom/meta.py

    r299 r303  
    2323 
    2424 
     25# Todo: Somthing about this being used *only* in the db layer (now). 
    2526class MetaBase(object): 
     27    "Domain model meta supertype." 
     28 
     29    registry = RequiredFeature('DomainRegistry') 
     30    dictionary = RequiredFeature('SystemDictionary') 
     31    logger = RequiredFeature('Logger') 
     32    debug = RequiredFeature('Debug') 
     33 
     34 
     35class BaseDomainMeta(object): 
    2636    "Domain model meta supertype." 
    2737 
     
    5060        return spaceSep 
    5161 
     62    def setTemporalDomainClass(self, domainClass): 
     63        self.temporalDomainClass = domainClass 
     64 
     65    def createTemporalCollection(self, domainObject): 
     66        if not domainObject: 
     67            raise Exception, "Need a domain object! %s" % domainObject 
     68        register = self.temporalDomainClass.createRegister( 
     69            ownerName='parent', 
     70            owner=domainObject, 
     71            metaAttr=self 
     72        ) 
     73        return register 
     74         
    5275     
    53 class MetaDomainObject(MetaBase): 
     76class MetaDomainObject(BaseDomainMeta): 
    5477    "Models a domain object." 
     78    # Todo: Rename to DomainObjectMeta? 
    5579 
    5680    reservedAttrNames = ('id', 'record', 'registerCache', 'projectUrls', 'paths', 'pluginController', 'metaClass', 'meta', 'registerKeyName', 'registerClass', 'registry')  
     
    7397            self.isEditable = False 
    7498        self.attributesDeferred = {} 
     99        self.isTemporal = False 
    75100 
    76101    def __setattr__(self, attrName, attrVal): 
     
    114139 
    115140 
    116 class MetaDomainAttr(MetaBase): 
     141class MetaDomainAttr(BaseDomainMeta): 
    117142    "Models a domain object attribute." 
    118143 
     
    147172            self.isTemporal = True 
    148173        self.isBitemporalActual = False 
    149         self.temporalDomainClass = None 
    150174 
    151175    def __repr__(self): 
     
    172196            setattr(domainObject, self.name, initialValue)  
    173197     
    174     def setTemporalDomainClass(self, domainClass): 
    175         self.temporalDomainClass = domainClass 
    176  
    177     def createTemporalCollection(self, domainObject): 
    178         if not domainObject: 
    179             raise Exception, "Need a domain object! %s" % domainObject 
    180         register = self.temporalDomainClass.createRegister( 
    181             ownerName='parent', 
    182             owner=domainObject, 
    183             metaAttr=self 
    184         ) 
    185         return register 
    186          
    187198    def createInitialValue(self, domainObject): 
    188199        return None 
  • trunk/src/dm/dom/registry.py

    r299 r303  
    22from dm.dom.base import * 
    33from dm.dom.stateful import * 
    4 from dm.dom.temporal import TemporalProperty, BitemporalProperty, BitemporalActual 
     4from dm.dom.temporal import TemporalProperty, BitemporalProperty, BitemporalActual, TemporalRevision 
    55from dm.ioc import *  
    66from dm.exceptions import *  
     
    4545        domainClass.meta.isUnique = domainClass.isUnique 
    4646        domainClass.meta.isCached = domainClass.isConstant 
     47        domainClass.meta.isTemporal = domainClass.isTemporal 
    4748        classRegister[domainClassName] = domainClass 
    4849        domainClass.isRegistered = True 
     
    5152        for name in classRegister: 
    5253            self.checkHasAsForHasManys(classRegister[name]) 
    53         temporalAttrs = [] 
    54         for attrMeta in domainClass.meta.attributes: 
    55             if attrMeta.isTemporal: 
    56                 parentMeta = domainClass.meta 
    57                 self.generateTemporalClass(parentMeta, attrMeta) 
     54        if domainClass.isTemporal: 
     55            # Group temporal propertes on revision object. 
     56            metaAttrs = [] 
     57            for attrMeta in domainClass.meta.attributes: 
     58                if attrMeta.isTemporal: 
     59                    metaAttrs.append(attrMeta) 
     60            parentMeta = domainClass.meta 
     61            self.generateTemporalRevisionClass(parentMeta, metaAttrs) 
     62        else: 
     63            # Look for independently temporal propertes. 
     64            for attrMeta in domainClass.meta.attributes: 
     65                if attrMeta.isTemporal: 
     66                    parentMeta = domainClass.meta 
     67                    self.generateTemporalPropertyClass(parentMeta, attrMeta) 
     68 
     69    def generateTemporalRevisionClass(self, parentMeta, metaAttrs): 
     70        # No support for bitemporal revisions yet! 
     71        temporalBase = TemporalRevision 
     72        temporalClassName = TemporalRevision.makeTemporalName(parentMeta.name) 
     73        temporalMeta = temporalBase.metaClass(temporalClassName) 
     74        for metaAttr in metaAttrs: 
     75            newAttr = metaAttr.duplicateTemporal() 
     76            setattr(temporalMeta, metaAttr.name, newAttr) 
     77        temporalMeta.parent = HasA(parentMeta.name, isRequired=True) 
     78        temporalClass = self.createDomainClass(temporalMeta, temporalBase) 
     79        parentMeta.setTemporalDomainClass(temporalClass) 
     80 
     81    def generateTemporalPropertyClass(self, parentMeta, attrMeta): 
     82        if attrMeta.isBitemporalActual: 
     83            temporalBase = BitemporalActual 
     84        elif attrMeta.isBitemporal: 
     85            temporalBase = BitemporalProperty 
     86        else: 
     87            temporalBase = TemporalProperty 
     88        temporalClassName = temporalBase.makeTemporalName( 
     89            parentMeta.name, attrMeta.name 
     90        ) 
     91        temporalMeta = temporalBase.metaClass(temporalClassName) 
     92        temporalMeta.recordedValue = attrMeta.duplicateTemporal() 
     93        temporalMeta.parent = HasA(parentMeta.name, isRequired=True) 
     94        temporalClass = self.createDomainClass(temporalMeta, temporalBase) 
     95        attrMeta.setTemporalDomainClass(temporalClass) 
    5896 
    5997    def getDomainClassRegister(self): 
     
    89127            raise Exception, "Domain class '%s' is not defined." % className 
    90128        return classRegister[className] 
    91  
    92     def generateTemporalClass(self, parentMeta, attrMeta): 
    93         if attrMeta.isBitemporalActual: 
    94             temporalBase = BitemporalActual 
    95         elif attrMeta.isBitemporal: 
    96             temporalBase = BitemporalProperty 
    97         else: 
    98             temporalBase = TemporalProperty 
    99         temporalClassName = "%s_tmpr_%s" % (parentMeta.name, attrMeta.name) 
    100         temporalMeta = temporalBase.metaClass(temporalClassName) 
    101         temporalMeta.recordedValue = attrMeta.duplicateTemporal() 
    102         temporalMeta.parent = HasA(parentMeta.name, isRequired=True) 
    103         temporalClass = self.createDomainClass(temporalMeta, temporalBase) 
    104         attrMeta.setTemporalDomainClass(temporalClass) 
    105129 
    106130    def setMetaAttributesFromClass(self, domainClass, domainClassMeta): 
  • trunk/src/dm/dom/temporal.py

    r301 r303  
    8585 
    8686 
    87 class BaseTemporalProperty(DomainObject): 
     87class BaseTemporal(DomainObject): 
    8888 
    8989    isUnique = False 
    9090    isConstant = True 
    91  
    92     dateCreated = DateTime( 
    93         isIndexed=True, 
    94         isRequired=True, 
    95     ) 
    96     parent = Integer(default=1)           # Abstract. 
    97     recordedValue = String(default='')    # Abstract. 
    98  
    9991    sortAscending = False 
    10092    sortOnName = 'id' 
     93 
     94    dateCreated = DateTime(isIndexed=True, isRequired=True) 
     95    # Overidden by HasA(parentClassName) meta attrrbute. 
     96    parent = Integer(default=1)            
     97 
     98    def makeTemporalName(self, className, attrName=None): 
     99        if attrName: 
     100            return "%sHist_%s" % (className, attrName) 
     101        else: 
     102            return "%sHist" % className 
     103 
     104    makeTemporalName = classmethod(makeTemporalName) 
     105 
     106 
     107class BaseTemporalProperty(BaseTemporal): 
     108 
     109    # Overidden by actually recorded meta attribute. 
     110    recordedValue = String(default='') 
    101111     
    102112 
     
    123133        return actualsMeta.createTemporalCollection(self) 
    124134 
     135 
     136class TemporalRevisionCollection(BaseTemporalCollection): 
     137 
     138    def create(self, loadedList={}, dateCreated=None): 
     139        return super(TemporalRevisionCollection, self).create( 
     140            loadedList=loadedList, 
     141            dateCreated=dateCreated, 
     142        ) 
     143 
     144 
     145class TemporalRevision(BaseTemporal): 
     146 
     147    registerClass = TemporalRevisionCollection 
     148 
     149 
  • trunk/src/dm/dom/temporaltest.py

    r299 r303  
    1212def suite(): 
    1313    suites = [ 
    14         unittest.makeSuite(TestTemporal),  # Example 
    1514        unittest.makeSuite(TestTemporalProperty), 
    1615        unittest.makeSuite(TestBitemporalActual), 
    1716        unittest.makeSuite(TestBitemporalProperty), 
     17        unittest.makeSuite(TestTemporal),  # Example of DomainObject with temporal properties. 
     18        unittest.makeSuite(TestTemporalObjectExample), 
    1819    ] 
    1920    return unittest.TestSuite(suites) 
     
    2223# Todo: Separate out the testing of indexes from testing of temporal attribute. 
    2324 
    24 class Temporal(SimpleNamedObject):  # Example 
    25     "Temporally attributed domain object." 
    26  
    27     # Todo: Exception when register key attribute is temporal. 
    28     name = String(default='', isIndexed=True) 
    29     description = String(default='', isTemporal=True) 
    30     firstkiss = DateTime(isTemporal=True, default=mx.DateTime.now()) 
    31     state = HasA('State', isTemporal=True, isRequired=False) 
    32     haircolor = String(default='My Natural Color', isBitemporal=True) 
     25 
     26class TemporalObjectExample(SimpleNamedObject): 
     27 
     28    isTemporal = True 
     29 
     30    title = String(default='Baby Title', isTemporal=True) 
     31    address = String(default='Baby Address', isTemporal=True) 
     32 
     33 
     34class TestTemporalObjectExample(TestCase): 
     35 
     36    timepoint = RequiredFeature('Timepoint') 
     37 
     38    def setUp(self): 
     39        super(TestTemporalObjectExample, self).setUp() 
     40        self.timepoint.reset() 
     41        self.collection = TemporalObjectExample.createRegister(ownerName='parent') 
     42        self.instance = None 
     43 
     44    def tearDown(self): 
     45        super(TestTemporalObjectExample, self).tearDown() 
     46        if self.instance != None: 
     47            self.instance.delete() 
     48        self.timepoint.reset() 
     49 
     50    def test_instance(self): 
     51        self.instance = self.collection.create() 
     52        title1 = "Title1" 
     53        title2 = "Title2" 
     54        title3 = "Title3" 
     55        title4 = "Title4" 
     56        self.instance.title = title1 
     57        self.instance.save() 
     58        instance = self.collection[''] 
     59        self.failUnlessEqual(instance.title, title1) 
     60        revision1 = mx.DateTime.now() 
     61        sleep(1) 
     62        self.instance.title = title2 
     63        self.instance.save() 
     64        self.instance.title = title3 
     65        self.instance.save() 
     66        revision3 = mx.DateTime.now() 
     67        sleep(1) 
     68        self.instance.title = title4 
     69        self.instance.save() 
     70        instance = self.collection[''] 
     71        self.failUnlessEqual(instance.title, title4) 
     72        self.timepoint.recorded = revision1 
     73        instance = self.collection[''] 
     74        self.failUnlessEqual(instance.title, title1) 
     75        self.timepoint.recorded = revision3 
     76        instance = self.collection[''] 
     77        self.failUnlessEqual(instance.title, title3) 
     78        self.timepoint.reset() 
     79        instance = self.collection[''] 
     80        self.failUnlessEqual(instance.title, title4) 
    3381 
    3482 
     
    4391 
    4492    def tearDown(self): 
     93        super(TestTemporalProperty, self).tearDown() 
    4594        self.timepoint.reset() 
    4695        self.collection = None 
     
    218267 
    219268 
     269class Temporal(SimpleNamedObject):  # Example 
     270    "Temporally attributed domain object." 
     271 
     272    # Todo: Exception when register key attribute is temporal. 
     273    name = String(default='', isIndexed=True) 
     274    description = String(default='', isTemporal=True) 
     275    firstkiss = DateTime(isTemporal=True, default=mx.DateTime.now()) 
     276    state = HasA('State', isTemporal=True, isRequired=False) 
     277    haircolor = String(default='My Natural Color', isBitemporal=True) 
     278 
     279 
     280# Todo: Support for passing temporal attribute value as create kwds. 
     281 
    220282class TestTemporal(TestCase):  # Example 
    221283    "TestCase for the Temporal class." 
  • trunk/src/dm/testunit.py

    r299 r303  
    2727        from dm.dom.temporaltest import Temporal 
    2828        self.registry.registerDomainClass(Temporal) 
     29        from dm.dom.temporaltest import TemporalObjectExample 
     30        self.registry.registerDomainClass(TemporalObjectExample) 
    2931        self.registry.temporals = Temporal.createRegister() 
    3032        from dm.dom.temporal import TemporalProperty, BitemporalProperty, BitemporalActual 
  • trunk/src/dm/timepointtest.py

    r300 r303  
    4242        now1 = self.timepoint.recorded 
    4343        import time 
    44         time.sleep(2
     44        time.sleep(1
    4545        now2 = self.timepoint.recorded 
    4646        self.failUnless(now2 > now1)