Changeset 303
- Timestamp:
- 01/31/08 23:58:35 (1 year ago)
- Files:
-
- trunk/src/dm/db.py (modified) (5 diffs)
- trunk/src/dm/dom/base.py (modified) (2 diffs)
- trunk/src/dm/dom/meta.py (modified) (6 diffs)
- trunk/src/dm/dom/registry.py (modified) (4 diffs)
- trunk/src/dm/dom/temporal.py (modified) (2 diffs)
- trunk/src/dm/dom/temporaltest.py (modified) (4 diffs)
- trunk/src/dm/testunit.py (modified) (1 diff)
- trunk/src/dm/timepointtest.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/src/dm/db.py
r302 r303 743 743 ) 744 744 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) 745 750 self.domainObject.id = self.id 746 751 for metaAttr in self.meta.attributes: … … 757 762 mapperValue = self.parent.domainObject 758 763 self.domainObject.parent = mapperValue 759 #message = "Continuing with loaded parent: %s" % (760 # self.domainObject.parent761 #)762 #logger.debug(message)763 764 continue 764 765 if metaAttr.dom.isTemporal: 765 766 # We don't persist temporal attributes on the parent. 766 767 # - 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 ) 771 777 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 ) 775 786 elif metaAttr.isDomainObjectRef: 776 787 mapper = getattr(self, dbName) … … 806 817 "Sets attributes of record object from domain object." 807 818 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) 808 838 for metaAttr in self.meta.attributes: 809 839 domValue = getattr(self.domainObject, metaAttr.domName) … … 812 842 # We don't persist temporal attributes on the parent. 813 843 # - 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) 819 853 elif metaAttr.isDomainObjectRef: 820 854 domainObject = domValue … … 832 866 setattr(self, metaAttr.dbName, domValue) 833 867 isChanged = True 868 if isChangedTemporal: 869 currentVersion.save() 834 870 if isChanged: 835 871 if moddebug and debug: trunk/src/dm/dom/base.py
r299 r303 504 504 isConstant = False 505 505 isUnique = True 506 isTemporal = False 506 507 searchAttributeNames = [] 507 508 startsWithAttributeName = '' … … 672 673 self.decacheItem() 673 674 if self.record: 674 self.deleteTemporal AttributeObjects()675 self.deleteTemporalObjects() 675 676 self.record.domainObject = None 676 677 self.record.destroySelf() 677 678 self.record = None 678 679 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] 684 689 685 690 def purgeAggregates(self): trunk/src/dm/dom/meta.py
r299 r303 23 23 24 24 25 # Todo: Somthing about this being used *only* in the db layer (now). 25 26 class 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 35 class BaseDomainMeta(object): 26 36 "Domain model meta supertype." 27 37 … … 50 60 return spaceSep 51 61 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 52 75 53 class MetaDomainObject( MetaBase):76 class MetaDomainObject(BaseDomainMeta): 54 77 "Models a domain object." 78 # Todo: Rename to DomainObjectMeta? 55 79 56 80 reservedAttrNames = ('id', 'record', 'registerCache', 'projectUrls', 'paths', 'pluginController', 'metaClass', 'meta', 'registerKeyName', 'registerClass', 'registry') … … 73 97 self.isEditable = False 74 98 self.attributesDeferred = {} 99 self.isTemporal = False 75 100 76 101 def __setattr__(self, attrName, attrVal): … … 114 139 115 140 116 class MetaDomainAttr( MetaBase):141 class MetaDomainAttr(BaseDomainMeta): 117 142 "Models a domain object attribute." 118 143 … … 147 172 self.isTemporal = True 148 173 self.isBitemporalActual = False 149 self.temporalDomainClass = None150 174 151 175 def __repr__(self): … … 172 196 setattr(domainObject, self.name, initialValue) 173 197 174 def setTemporalDomainClass(self, domainClass):175 self.temporalDomainClass = domainClass176 177 def createTemporalCollection(self, domainObject):178 if not domainObject:179 raise Exception, "Need a domain object! %s" % domainObject180 register = self.temporalDomainClass.createRegister(181 ownerName='parent',182 owner=domainObject,183 metaAttr=self184 )185 return register186 187 198 def createInitialValue(self, domainObject): 188 199 return None trunk/src/dm/dom/registry.py
r299 r303 2 2 from dm.dom.base import * 3 3 from dm.dom.stateful import * 4 from dm.dom.temporal import TemporalProperty, BitemporalProperty, BitemporalActual 4 from dm.dom.temporal import TemporalProperty, BitemporalProperty, BitemporalActual, TemporalRevision 5 5 from dm.ioc import * 6 6 from dm.exceptions import * … … 45 45 domainClass.meta.isUnique = domainClass.isUnique 46 46 domainClass.meta.isCached = domainClass.isConstant 47 domainClass.meta.isTemporal = domainClass.isTemporal 47 48 classRegister[domainClassName] = domainClass 48 49 domainClass.isRegistered = True … … 51 52 for name in classRegister: 52 53 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) 58 96 59 97 def getDomainClassRegister(self): … … 89 127 raise Exception, "Domain class '%s' is not defined." % className 90 128 return classRegister[className] 91 92 def generateTemporalClass(self, parentMeta, attrMeta):93 if attrMeta.isBitemporalActual:94 temporalBase = BitemporalActual95 elif attrMeta.isBitemporal:96 temporalBase = BitemporalProperty97 else:98 temporalBase = TemporalProperty99 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)105 129 106 130 def setMetaAttributesFromClass(self, domainClass, domainClassMeta): trunk/src/dm/dom/temporal.py
r301 r303 85 85 86 86 87 class BaseTemporal Property(DomainObject):87 class BaseTemporal(DomainObject): 88 88 89 89 isUnique = False 90 90 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 99 91 sortAscending = False 100 92 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 107 class BaseTemporalProperty(BaseTemporal): 108 109 # Overidden by actually recorded meta attribute. 110 recordedValue = String(default='') 101 111 102 112 … … 123 133 return actualsMeta.createTemporalCollection(self) 124 134 135 136 class 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 145 class TemporalRevision(BaseTemporal): 146 147 registerClass = TemporalRevisionCollection 148 149 trunk/src/dm/dom/temporaltest.py
r299 r303 12 12 def suite(): 13 13 suites = [ 14 unittest.makeSuite(TestTemporal), # Example15 14 unittest.makeSuite(TestTemporalProperty), 16 15 unittest.makeSuite(TestBitemporalActual), 17 16 unittest.makeSuite(TestBitemporalProperty), 17 unittest.makeSuite(TestTemporal), # Example of DomainObject with temporal properties. 18 unittest.makeSuite(TestTemporalObjectExample), 18 19 ] 19 20 return unittest.TestSuite(suites) … … 22 23 # Todo: Separate out the testing of indexes from testing of temporal attribute. 23 24 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 26 class 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 34 class 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) 33 81 34 82 … … 43 91 44 92 def tearDown(self): 93 super(TestTemporalProperty, self).tearDown() 45 94 self.timepoint.reset() 46 95 self.collection = None … … 218 267 219 268 269 class 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 220 282 class TestTemporal(TestCase): # Example 221 283 "TestCase for the Temporal class." trunk/src/dm/testunit.py
r299 r303 27 27 from dm.dom.temporaltest import Temporal 28 28 self.registry.registerDomainClass(Temporal) 29 from dm.dom.temporaltest import TemporalObjectExample 30 self.registry.registerDomainClass(TemporalObjectExample) 29 31 self.registry.temporals = Temporal.createRegister() 30 32 from dm.dom.temporal import TemporalProperty, BitemporalProperty, BitemporalActual trunk/src/dm/timepointtest.py
r300 r303 42 42 now1 = self.timepoint.recorded 43 43 import time 44 time.sleep( 2)44 time.sleep(1) 45 45 now2 = self.timepoint.recorded 46 46 self.failUnless(now2 > now1)
