1 '''
2
3 Created on Sep 25, 2009
4
5 @author: Erik De Rijcke
6
7 '''
8 import datetime
9
10 from elixir.entity import Entity, EntityMeta
11
12 from sqlalchemy.types import Date, Unicode
13 from sqlalchemy.sql import and_
14
15 from elixir.fields import Field
16 from elixir.options import using_options
17 from elixir.relationships import ManyToOne, OneToMany
18
19 from camelot.model.authentication import end_of_times
20 from camelot.view.elixir_admin import EntityAdmin
21 from camelot.types import Code, Enumeration
22
23
24
25
26 __status_classes__ = {}
29 """
30 :param cls_name: an Entity class name
31 :return: the status class used for this entity
32 """
33 return __status_classes__[cls_name]
34
36 """Create a class that can be subclassed to provide a class that
37 has a type 3 status with methods to manipulate and review its status
38 :param status_attribute: the name of the type 3 status attribute
39 """
40
41 class Type3StatusMixin(object):
42
43 @property
44 def current_status(self):
45 classified_by = None
46 today = datetime.date.today()
47 for status_history in self.status:
48 if status_history.status_from_date<=today and status_history.status_thru_date>=today:
49 classified_by = status_history.classified_by
50 return classified_by
51
52 def change_status(self, new_status, status_from_date=None, status_thru_date=end_of_times()):
53 from sqlalchemy import orm
54 if not status_from_date:
55 status_from_date = datetime.date.today()
56 mapper = orm.class_mapper(self.__class__)
57 status_property = mapper.get_property('status')
58 status_type = status_property._get_target().class_
59 old_status = status_type.query.filter( and_( status_type.status_for == self,
60 status_type.status_from_date <= status_from_date,
61 status_type.status_thru_date >= status_from_date ) ).first()
62 if old_status != None:
63 old_status.thru_date = datetime.date.today() - datetime.timedelta( days = 1 )
64 old_status.status_thru_date = status_from_date - datetime.timedelta( days = 1 )
65 new_status = status_type( status_for = self,
66 classified_by = new_status,
67 status_from_date = status_from_date,
68 status_thru_date = status_thru_date,
69 from_date = datetime.date.today(),
70 thru_date = end_of_times() )
71 if old_status:
72 self.query.session.flush( [old_status] )
73 self.query.session.flush( [new_status] )
74
75 return Type3StatusMixin
76
77 -def type_3_status( statusable_entity, metadata, collection, verbose_entity_name = None, enumeration=None ):
78 '''
79 Creates a new type 3 status related to the given entity
80 :statusable_entity: A string referring to an entity.
81 :enumeration: if this parameter is used, no status type Entity is created, but the status type is
82 described by the enumeration.
83 '''
84 t3_status_name = statusable_entity + '_status'
85 t3_status_type_name = statusable_entity + '_status_type'
86
87
88 if not enumeration:
89
90 class Type3StatusTypeMeta( EntityMeta ):
91 def __new__( cls, classname, bases, dictionary ):
92 return EntityMeta.__new__( cls, t3_status_type_name,
93 bases, dictionary )
94 def __init__( self, classname, bases, dictionary ):
95 EntityMeta.__init__( self, t3_status_type_name,
96 bases, dictionary )
97
98 class Type3StatusType( Entity, ):
99 using_options( tablename = t3_status_type_name.lower(), metadata=metadata, collection=collection )
100 __metaclass__ = Type3StatusTypeMeta
101
102 code = Field( Code( parts = ['>AAAA'] ), index = True,
103 required = True, unique = True )
104 description = Field( Unicode( 40 ), index = True )
105
106 def __unicode__( self ):
107 return 'Status type: %s : %s' % ( '.'.join( self.code ), self.description )
108
109 class Admin( EntityAdmin ):
110 list_display = ['code', 'description']
111 verbose_name = statusable_entity + ' Status Type'
112 if verbose_entity_name is not None:
113 verbose_name = verbose_entity_name + ' Status Type'
114
115 class Type3StatusMeta( EntityMeta ):
116 def __new__( cls, classname, bases, dictionary ):
117 return EntityMeta.__new__( cls, t3_status_name,
118 bases, dictionary )
119 def __init__( self, classname, bases, dictionary ):
120 EntityMeta.__init__( self, t3_status_name,
121 bases, dictionary )
122
123 class Type3Status( Entity, ):
124 """
125 Status Pattern
126 .. attribute:: status_datetime For statuses that occur at a specific point in time
127 .. attribute:: status_from_date For statuses that require a date range
128 .. attribute:: from_date When a status was enacted or set
129 """
130 using_options( tablename = t3_status_name.lower(), metadata=metadata, collection=collection )
131 __metaclass__ = Type3StatusMeta
132
133 status_datetime = Field( Date, required = False )
134 status_from_date = Field( Date, required = False )
135 status_thru_date = Field( Date, required = False )
136 from_date = Field( Date, required = True, default = datetime.date.today )
137 thru_date = Field( Date, required = True, default = end_of_times )
138
139 status_for = ManyToOne( statusable_entity,
140 ondelete = 'cascade', onupdate = 'cascade' )
141
142 if not enumeration:
143 classified_by = ManyToOne( t3_status_type_name, required = True,
144 ondelete = 'cascade', onupdate = 'cascade' )
145 else:
146 classified_by = Field(Enumeration(enumeration), required=True, index=True)
147
148 class Admin( EntityAdmin ):
149 verbose_name = statusable_entity + ' Status'
150 verbose_name_plural = statusable_entity + ' Statuses'
151 list_display = ['status_from_date', 'status_thru_date', 'classified_by']
152 verbose_name = statusable_entity + ' Status'
153 if verbose_entity_name is not None:
154 verbose_name = verbose_entity_name + ' Status'
155
156 def __unicode__( self ):
157 return u'Status'
158
159 __status_classes__[statusable_entity] = Type3Status
160
161 return t3_status_name
162
163 -def entity_type( typable_entity, metadata, collection, verbose_entity_name = None ):
164 '''
165 Creates a new type related to the given entity.
166 .. typeable_entity:: A string referring to an entity.
167 '''
168 type_name = typable_entity + '_type'
169
170 class TypeMeta( EntityMeta ):
171 def __new__( cls, classname, bases, dictionary ):
172 return EntityMeta.__new__( cls, type_name,
173 bases, dictionary )
174 def __init__( self, classname, bases, dictionary ):
175 EntityMeta.__init__( self, type_name,
176 bases, dictionary )
177
178 class Type( Entity ):
179 using_options( tablename = type_name.lower(), metadata=metadata, collection=collection )
180 __metaclass__ = TypeMeta
181
182 type_description_for = OneToMany( typable_entity )
183 description = Field( Unicode( 48 ), required = True )
184
185 class Admin( EntityAdmin ):
186 verbose_name = typable_entity + ' Type'
187 list_display = ['description', ]
188 verbose_name = typable_entity + ' Type'
189 if verbose_entity_name is not None:
190 verbose_name = verbose_entity_name + ' Type'
191
192 def __unicode__( self ):
193 return u'Type: %s' % ( self.description )
194
195 return type_name
196