|
@@ -25,7 +25,7 @@ class EmType(EmComponent):
|
25
|
25
|
## Instanciate a new EmType
|
26
|
26
|
# @todo define and check types for icon and sortcolumn
|
27
|
27
|
# @todo better check self.subordinates
|
28
|
|
- def __init__(self, model, uid, name, class_id, fields_list = [], subordinates_list = {}, icon = '0', sortcolumn = 'rank', string = None, help_text = None, date_update = None, date_create = None, rank = None):
|
|
28
|
+ def __init__(self, model, uid, name, class_id, fields_list = [], superiors_list = {}, icon = '0', sortcolumn = 'rank', string = None, help_text = None, date_update = None, date_create = None, rank = None):
|
29
|
29
|
self.class_id = class_id
|
30
|
30
|
self.check_type('class_id', int)
|
31
|
31
|
self.fields_list = fields_list
|
|
@@ -34,14 +34,13 @@ class EmType(EmComponent):
|
34
|
34
|
if not isinstance(l_uid, int):
|
35
|
35
|
raise AttributeError("Excepted fields_list to be a list of integers, but found a +"+str(type(l_uid))+" in it")
|
36
|
36
|
|
37
|
|
- self.subordinates_list = subordinates_list
|
38
|
|
- self.check_type('subordinates_list', dict)
|
39
|
|
- for nature, uids in self.subordinates_list.items():
|
|
37
|
+ self.superiors_list = superiors_list
|
|
38
|
+ self.check_type('superiors_list', dict)
|
|
39
|
+ for nature, sup_uid in self.superiors_list.items():
|
40
|
40
|
if nature not in [EmNature.PARENT, EmNature.TRANSLATION, EmNature.IDENTITY]:
|
41
|
41
|
raise AttributeError("Nature '%s' of subordinates is not allowed !" % nature)
|
42
|
|
- for uid in uids:
|
43
|
|
- if not isinstance(uid, int):
|
44
|
|
- raise AttributeError("Excepted subordinates of nature '%s' to be a list int !" % nature)
|
|
42
|
+ if not isinstance(sup_uid, int):
|
|
43
|
+ raise AttributeError("Excepted subordinates of nature '%s' to be an int !" % nature)
|
45
|
44
|
|
46
|
45
|
self.icon = icon
|
47
|
46
|
self.sortcolumn = sortcolumn
|
|
@@ -79,7 +78,7 @@ class EmType(EmComponent):
|
79
|
78
|
# @todo Check if the type is not linked by any EmClass
|
80
|
79
|
# @todo Check if there is no other ''non-deletion'' conditions
|
81
|
80
|
def delete_check(self):
|
82
|
|
- if sum(self.subordinates_list) > 0:
|
|
81
|
+ if len(self.subordinates()) > 0:
|
83
|
82
|
return False
|
84
|
83
|
#Delete all relation with superiors
|
85
|
84
|
for nature, sups in self.superiors().items():
|
|
@@ -110,52 +109,61 @@ class EmType(EmComponent):
|
110
|
109
|
# Indicates that an optional field is used
|
111
|
110
|
#
|
112
|
111
|
# @param field EmField: The optional field to select
|
113
|
|
- # @return True if success False if failed
|
114
|
112
|
#
|
115
|
113
|
# @throw TypeError if field is not an EmField instance
|
116
|
114
|
# @throw ValueError if field is not optional or is not associated with this type
|
117
|
|
- # @see EmType::_opt_field_act()
|
|
115
|
+ # @throw MigrationHandlerChangeError if migration handler is not happy with the change
|
|
116
|
+ # @see EmType::_change_field_list()
|
118
|
117
|
def select_field(self, field):
|
119
|
118
|
if field.uid in self.fields_list:
|
120
|
119
|
return True
|
121
|
|
- return self._change_field_list(field, True)
|
|
120
|
+ self._change_field_list(field, True)
|
122
|
121
|
|
123
|
122
|
## Unselect_field (Function)
|
124
|
123
|
#
|
125
|
124
|
# Indicates that an optional field will not be used
|
126
|
125
|
#
|
127
|
126
|
# @param field EmField: The optional field to unselect
|
128
|
|
- # @return True if success False if fails
|
129
|
127
|
#
|
130
|
128
|
# @throw TypeError if field is not an EmField instance
|
131
|
129
|
# @throw ValueError if field is not optional or is not associated with this type
|
132
|
|
- # @see EmType::_opt_field_act()
|
|
130
|
+ # @throw MigrationHandlerChangeError if migration handler is not happy with the change
|
|
131
|
+ # @see EmType::_change_field_list()
|
133
|
132
|
def unselect_field(self, field):
|
134
|
133
|
if field.uid not in self.fields_list:
|
135
|
134
|
return True
|
136
|
|
- return self._change_field_list(field, False)
|
|
135
|
+ self._change_field_list(field, False)
|
137
|
136
|
|
138
|
137
|
## @brief Select or unselect an optional field
|
139
|
138
|
# @param field EmField: The EmField to select or unselect
|
140
|
139
|
# @param select bool: If True select field, else unselect it
|
141
|
|
- # @return True if success False if fails
|
142
|
140
|
#
|
143
|
141
|
# @throw TypeError if field is not an EmField instance
|
144
|
142
|
# @throw ValueError if field is not optional or is not associated with this type
|
145
|
|
- def _change_field_list(self, field, add=True): # TODO voir si on conserve l'argument "select"
|
|
143
|
+ # @throw MigrationHandlerChangeError if migration handler is not happy with the change
|
|
144
|
+ def _change_field_list(self, field, select=True):
|
146
|
145
|
if not isinstance(field, EmField):
|
147
|
146
|
raise TypeError("Excepted <class EmField> as field argument. But got " + str(type(field)))
|
148
|
147
|
if not field in self.em_class.fields():
|
149
|
|
- raise ValueError("This field is not part of this type")
|
|
148
|
+ raise ValueError("This field " + str(field) + "is not part of the type " + str(self))
|
150
|
149
|
if not field.optional:
|
151
|
150
|
raise ValueError("This field is not optional")
|
152
|
151
|
|
153
|
|
- if add:
|
154
|
|
- self.fields_list.append(field.uid)
|
155
|
|
- else:
|
156
|
|
- self.fields_list.remove(field.uid)
|
157
|
|
-
|
158
|
|
- return True
|
|
152
|
+ try:
|
|
153
|
+ if select:
|
|
154
|
+ self.fields_list.append(field.uid)
|
|
155
|
+ self.model.migration_handler.register_change(self.model, self.uid, None, {'fields_list': field.uid})
|
|
156
|
+ else:
|
|
157
|
+ self.fields_list.remove(field.uid)
|
|
158
|
+ self.model.migration_handler.register_change(self.model, self.uid, {'fields_list': field.uid}, None)
|
|
159
|
+ except MigrationHandlerChangeError as exception_object:
|
|
160
|
+ if select:
|
|
161
|
+ self.fields_list.remove(field.uid)
|
|
162
|
+ else:
|
|
163
|
+ self.fields_list.append(field.uid)
|
|
164
|
+ raise exception_object
|
|
165
|
+
|
|
166
|
+ self.model.migration_handler.register_model_state(self.model, hash(self.model))
|
159
|
167
|
|
160
|
168
|
## Get the list of associated hooks
|
161
|
169
|
# @note Not conceptualized yet
|
|
@@ -186,22 +194,22 @@ class EmType(EmComponent):
|
186
|
194
|
# EmType instance
|
187
|
195
|
# @throw RuntimeError if a nature fetched from db is not valid
|
188
|
196
|
def subordinates(self):
|
189
|
|
- return { nature: [ self.model.component(tuid) for tuid in self.subordinates_list[nature] ] for nature in self.subordinates_list }
|
|
197
|
+ subordinates = {}
|
|
198
|
+ for em_type in self.model.components(EmType):
|
|
199
|
+ for nature, superior_uid in em_type.superiors_list.items():
|
|
200
|
+ if self.uid == superior_uid:
|
|
201
|
+ if nature in subordinates:
|
|
202
|
+ subordinates[nature].append(em_type)
|
|
203
|
+ else:
|
|
204
|
+ subordinates[nature] = [em_type]
|
|
205
|
+ return subordinates
|
190
|
206
|
|
191
|
207
|
## @brief Get the list of superiors by relation's nature
|
192
|
208
|
# Get a list of EmType that are superiors of this type
|
193
|
209
|
# @return Return a dict with relation nature as keys and an EmType as value
|
194
|
210
|
# @throw RuntimeError if a nature has multiple superiors
|
195
|
211
|
def superiors(self):
|
196
|
|
- superiors = {}
|
197
|
|
- for em_type in self.model.components(EmType):
|
198
|
|
- for nature, sub_uids in em_type.subordinates_list.items():
|
199
|
|
- if self.uid in sub_uids:
|
200
|
|
- if nature in superiors:
|
201
|
|
- raise RuntimeError("Multiple superiors found for relation of nature '%s' for EmType %d"%(nature, self.uid))
|
202
|
|
- else:
|
203
|
|
- superiors[nature] = em_type
|
204
|
|
- return superiors
|
|
212
|
+ return { nature:self.model.component(superior_uid) for nature, superior_uid in self.superiors_list.items() }
|
205
|
213
|
|
206
|
214
|
## Add a superior in the type hierarchy
|
207
|
215
|
# @param em_type EmType: An EmType instance
|
|
@@ -212,11 +220,6 @@ class EmType(EmComponent):
|
212
|
220
|
# @throw ValueError when relation_nature isn't reconized or not allowed for this type
|
213
|
221
|
# @throw ValueError when relation_nature don't allow to link this types together
|
214
|
222
|
def add_superior(self, em_type, relation_nature):
|
215
|
|
- if not isinstance(em_type, EmType) or not isinstance(relation_nature, str):
|
216
|
|
- raise TypeError("Excepted <class EmType> and <class str> as em_type argument. But got : " + str(type(em_type)) + " " + str(type(relation_nature)))
|
217
|
|
- if relation_nature not in EmClassType.natures(self.classtype['name']):
|
218
|
|
- raise ValueError("Invalid nature for add_superior : '" + relation_nature + "'. Allowed relations for this type are " + str(EmClassType.natures(self.classtype['name'])))
|
219
|
|
-
|
220
|
223
|
#Checking that this relation is allowed by the nature of the relation
|
221
|
224
|
att = self.classtype['hierarchy'][relation_nature]['attach']
|
222
|
225
|
if att == 'classtype':
|
|
@@ -225,65 +228,44 @@ class EmType(EmComponent):
|
225
|
228
|
elif self.name != em_type.name:
|
226
|
229
|
raise ValueError("Not allowed to put a different em_type as superior in a relation of nature '" + relation_nature + "'")
|
227
|
230
|
|
228
|
|
- # TODO Réimplémenter
|
229
|
|
- # conn = self.db_engine.connect()
|
230
|
|
- # htable = self._table_hierarchy
|
231
|
|
- # values = {'subordinate_id': self.uid, 'superior_id': em_type.uid, 'nature': relation_nature}
|
232
|
|
- # req = htable.insert(values=values)
|
233
|
|
- #
|
234
|
|
- # try:
|
235
|
|
- # conn.execute(req)
|
236
|
|
- # except sql.exc.IntegrityError:
|
237
|
|
- # ret = False
|
238
|
|
- # else:
|
239
|
|
- # ret = True
|
240
|
|
- # finally:
|
241
|
|
- # conn.close()
|
242
|
|
- #
|
243
|
|
- # return ret
|
|
231
|
+ self._change_superiors_list(em_type, relation_nature, True)
|
244
|
232
|
|
245
|
233
|
## Delete a superior in the type hierarchy
|
246
|
234
|
# @param em_type EmType: An EmType instance
|
|
235
|
+ # @param relation_nature str: The name of the relation's nature
|
247
|
236
|
# @throw TypeError when em_type isn't an EmType instance
|
|
237
|
+ # @throw ValueError when relation_nature isn't reconized or not allowed for this type
|
248
|
238
|
def del_superior(self, em_type, relation_nature):
|
249
|
|
- if not isinstance(em_type, EmType):
|
250
|
|
- raise TypeError("Excepted <class EmType> as argument. But got : " + str(type(em_type)))
|
|
239
|
+ self._change_superiors_list(em_type, relation_nature, False)
|
|
240
|
+
|
|
241
|
+ ## Apply changes to the superiors_list
|
|
242
|
+ # @param em_type EmType: An EmType instance
|
|
243
|
+ # @param relation_nature str: The name of the relation's nature
|
|
244
|
+ # @param add bool: Add or delete relation
|
|
245
|
+ def _change_superiors_list(self, em_type, relation_nature, add=True):
|
|
246
|
+ # check instance of parameters
|
|
247
|
+ if not isinstance(em_type, EmType) or not isinstance(relation_nature, str):
|
|
248
|
+ raise TypeError("Excepted <class EmType> and <class str> as em_type argument. But got : " + str(type(em_type)) + " " + str(type(relation_nature)))
|
|
249
|
+ # check if relation_nature is valid for this type
|
251
|
250
|
if relation_nature not in EmClassType.natures(self.classtype['name']):
|
252
|
251
|
raise ValueError("Invalid nature for add_superior : '" + relation_nature + "'. Allowed relations for this type are " + str(EmClassType.natures(self.classtype['name'])))
|
253
|
252
|
|
254
|
|
- # TODO Réimplémenter
|
255
|
|
- # conn = self.db_engine.connect()
|
256
|
|
- # htable = self._table_hierarchy
|
257
|
|
- # req = htable.delete(htable.c.superior_id == em_type.uid and htable.c.nature == relation_nature)
|
258
|
|
- # conn.execute(req)
|
259
|
|
- # conn.close()
|
260
|
|
-
|
261
|
|
- ## @brief Get the list of linked type
|
262
|
|
- # Types are linked with special fields called relation_to_type fields
|
263
|
|
- # @return a list of EmType
|
264
|
|
- # @see EmFields
|
265
|
|
- def linked_types(self):
|
266
|
|
- return self._linked_types_db() # TODO changer l'appel
|
267
|
|
-
|
268
|
|
- ## @brief Return the list of all the types linked to this type, should they be superiors or subordinates
|
269
|
|
- # @return A list of EmType objects
|
270
|
|
- # def _linked_types_db(self):
|
271
|
|
- # conn = self.db_engine.connect()
|
272
|
|
- # htable = self._table_hierarchy
|
273
|
|
- # req = htable.select(htable.c.superior_id, htable.c.subordinate_id)
|
274
|
|
- # req = req.where(sql.or_(htable.c.subordinate_id == self.uid, htable.c.superior_id == self.uid))
|
275
|
|
- #
|
276
|
|
- # res = conn.execute(req)
|
277
|
|
- # rows = res.fetchall()
|
278
|
|
- # conn.close()
|
279
|
|
- #
|
280
|
|
- # rows = dict(zip(rows.keys(), rows))
|
281
|
|
- # result = []
|
282
|
|
- # for row in rows:
|
283
|
|
- # result.append(EmType(row['subordinate_id'] if row['superior_id'] == self.uid else row['superior_id']))
|
284
|
|
- #
|
285
|
|
- # return result
|
286
|
|
-
|
|
253
|
+ try:
|
|
254
|
+ if add:
|
|
255
|
+ self.superiors_list[relation_nature] = em_type.uid
|
|
256
|
+ self.model.migration_handler.register_change(self.model, self.uid, None, {'superiors_list': {relation_nature: em_type.uid}})
|
|
257
|
+ else:
|
|
258
|
+ del self.superiors_list[relation_nature]
|
|
259
|
+ self.model.migration_handler.register_change(self.model, self.uid, {'superiors_list': {relation_nature: em_type.uid}}, None)
|
|
260
|
+ except MigrationHandlerChangeError as exception_object:
|
|
261
|
+ if add:
|
|
262
|
+ del self.superiors_list[relation_nature]
|
|
263
|
+ else:
|
|
264
|
+ self.superiors_list[relation_nature] = em_type.uid
|
|
265
|
+ raise exception_object
|
|
266
|
+
|
|
267
|
+ self.model.migration_handler.register_model_state(self.model, hash(self.model))
|
|
268
|
+
|
287
|
269
|
## Checks if the EmType is valid
|
288
|
270
|
# @throw EmComponentCheckError if check fails
|
289
|
271
|
def check(self):
|
|
@@ -293,52 +275,50 @@ class EmType(EmComponent):
|
293
|
275
|
raise EmComponentCheckError("class_id contains an uid that does not exists '%d'" % self.class_id)
|
294
|
276
|
if not isinstance(em_class, EditorialModel.classes.EmClass):
|
295
|
277
|
raise EmComponentCheckError("class_id contains an uid from a component that is not an EmClass but a %s" % str(type(emc_class)))
|
296
|
|
-
|
297
|
|
- for i,fuid in enumerate(self.fields_list):
|
298
|
|
- field = self.model.component(fuid)
|
|
278
|
+
|
|
279
|
+ for i,f_uid in enumerate(self.fields_list):
|
|
280
|
+ field = self.model.component(f_uid)
|
299
|
281
|
if not field:
|
300
|
|
- raise EmComponentCheckError("The element %d of selected_field is a non existing uid '%d'"%(i, fuid))
|
|
282
|
+ raise EmComponentCheckError("The element %d of selected_field is a non existing uid '%d'"%(i, f_uid))
|
301
|
283
|
if not isinstance(field, EmField):
|
302
|
284
|
raise EmComponentCheckError("The element %d of selected_field is not an EmField but a %s" % (i, str(type(field)) ))
|
303
|
285
|
if not field.optional:
|
304
|
286
|
raise EmComponentCheckError("The element %d of selected_field is an EmField not optional" % i )
|
305
|
287
|
if field.fieldgroup_id not in [ fg.uid for fg in self.fieldgroups() ]:
|
306
|
288
|
raise EmComponentCheckErrro("The element %d of selected_field is an EmField that is part of an EmFieldGroup that is not associated with this EmType" % i)
|
307
|
|
- for nature in self.subordinates_list:
|
308
|
|
- for i, tuid in enumerate(self.subordinates_list[nature]):
|
309
|
|
- em_type = self.model.component(tuid)
|
310
|
|
- if not em_type:
|
311
|
|
- raise EmComponentCheckError("The element %d of subordinates contains a non existing uid '%d'" % (i, tuid))
|
312
|
|
- if not isinstance(em_type, EmType):
|
313
|
|
- raise EmComponentCheckError("The element %d of subordinates contains a component that is not an EmType but a %s" % (i, str(type(em_type))))
|
314
|
|
- if nature not in EmClassType.natures(self.em_class.classtype):
|
315
|
|
- raise EmComponentCheckError("The relation nature '%s' of the element %d of subordinates is not valid for this EmType classtype '%s'", (nature, i, self.classtype) )
|
316
|
|
-
|
317
|
|
- nat_spec = getattr(EmClassType, self.em_class.classtype)['hierarchy'][nature]
|
318
|
|
-
|
319
|
|
- if nat_spec['attach'] == 'classtype':
|
320
|
|
- if self.classtype != em_type.classtype:
|
321
|
|
- raise EmComponentCheckError("The element %d of subordinates is of '%s' classtype. But the current type is of '%s' classtype, and relation nature '%s' require two EmType of same classtype" % (i, em_type.classtype, self.classtype, nature) )
|
322
|
|
- elif nat_spec['attach'] == 'type':
|
323
|
|
- if self.uid != em_type.uid:
|
324
|
|
- raise EmComponentCheckError("The element %d of subordinates is a different EmType. But the relation nature '%s' require the same EmType" % (i, nature))
|
|
289
|
+
|
|
290
|
+ for nature, sup_uid in self.superiors_list.items():
|
|
291
|
+ em_type = self.model.component(sup_uid)
|
|
292
|
+ if not em_type:
|
|
293
|
+ raise EmComponentCheckError("The superior is a non existing uid '%d'" % (sup_uid))
|
|
294
|
+ if not isinstance(em_type, EmType):
|
|
295
|
+ raise EmComponentCheckError("The superior is a component that is not an EmType but a %s" % (str(type(em_type))))
|
|
296
|
+ if nature not in EmClassType.natures(self.em_class.classtype):
|
|
297
|
+ raise EmComponentCheckError("The relation nature '%s' of the superior is not valid for this EmType classtype '%s'", (nature, self.classtype) )
|
|
298
|
+
|
|
299
|
+ nat_spec = getattr(EmClassType, self.em_class.classtype)['hierarchy'][nature]
|
|
300
|
+
|
|
301
|
+ if nat_spec['attach'] == 'classtype':
|
|
302
|
+ if self.classtype != em_type.classtype:
|
|
303
|
+ raise EmComponentCheckError("The superior is of '%s' classtype. But the current type is of '%s' classtype, and relation nature '%s' require two EmType of same classtype" % (em_type.classtype, self.classtype, nature) )
|
|
304
|
+ elif nat_spec['attach'] == 'type':
|
|
305
|
+ if self.uid != em_type.uid:
|
|
306
|
+ raise EmComponentCheckError("The superior is a different EmType. But the relation nature '%s' require the same EmType" % (nature))
|
|
307
|
+ else:
|
|
308
|
+ raise NotImplementedError("The nature['attach'] '%s' is not implemented in this check !" % nat_spec['attach'])
|
|
309
|
+
|
|
310
|
+ if 'max_depth' in nat_spec and nat_spec['max_depth'] > 0:
|
|
311
|
+ depth = 1
|
|
312
|
+ cur_type = em_type
|
|
313
|
+ while depth >= nat_spec['max_depth']:
|
|
314
|
+ depth +=1
|
|
315
|
+ if len(cur_type.subordinates()[nature]) == 0:
|
|
316
|
+ break
|
325
|
317
|
else:
|
326
|
|
- raise NotImplementedError("The nature['attach'] '%s' is not implemented in this check !" % nat_spec['attach'])
|
327
|
|
-
|
328
|
|
- if 'max_depth' in nat_spec and nat_spec['max_depth'] > 0:
|
329
|
|
- depth = 1
|
330
|
|
- cur_type = em_type
|
331
|
|
- while depth >= nat_spec['max_depth']:
|
332
|
|
- depth +=1
|
333
|
|
- if len(cur_type.subordinates()[nature]) == 0:
|
334
|
|
- break
|
335
|
|
- else:
|
336
|
|
- raise EmComponentCheckError("The relation with the element %d of subordinates has a depth superior than the maximum depth ( %d ) allowed by the relation's nature ( '%s' )" %( i, nat_spec['max_depth'], nature) )
|
337
|
|
-
|
|
318
|
+ raise EmComponentCheckError("The relation with the element %d of subordinates has a depth superior than the maximum depth ( %d ) allowed by the relation's nature ( '%s' )" %( i, nat_spec['max_depth'], nature) )
|
|
319
|
+
|
338
|
320
|
for nature in self.subordinates():
|
339
|
321
|
nat_spec = getattr(EmClassType, self.em_class.classtype)['hierarchy'][nature]
|
340
|
322
|
if 'max_child' in nat_spec and nat_spec['max_child'] > 0:
|
341
|
323
|
if len(self.subordinates()[nature]) > nat_spec['max_child']:
|
342
|
324
|
raise EmComponentCheckError("The EmType has more child than allowed in the relation's nature : %d > %d" (len(self.subordinates()[nature], nat_spec['max_child'])))
|
343
|
|
- #pass
|
344
|
|
-
|