|
@@ -11,9 +11,11 @@
|
11
|
11
|
# @note LeObject will be generated by leapi.lefactory.LeFactory
|
12
|
12
|
|
13
|
13
|
import re
|
|
14
|
+import warnings
|
14
|
15
|
|
15
|
16
|
import leapi
|
16
|
17
|
import EditorialModel
|
|
18
|
+from leapi.lecrud import _LeCrud
|
17
|
19
|
from leapi.lefactory import LeFactory
|
18
|
20
|
from EditorialModel.types import EmType
|
19
|
21
|
|
|
@@ -48,7 +50,11 @@ class _LeObject(object):
|
48
|
50
|
|
49
|
51
|
@classmethod
|
50
|
52
|
def fieldtypes(cls):
|
51
|
|
- return super(_LeObject, cls).fieldtypes().update(EditorialModel.classtypes.common_fields)
|
|
53
|
+ if cls._fieldtypes_all is None:
|
|
54
|
+ cls._fieldtypes_all = dict()
|
|
55
|
+ cls._fieldtypes_all.update(cls._uid_fieldtype)
|
|
56
|
+ cls._fieldtypes_all.update(cls._leo_fieldtypes)
|
|
57
|
+ return cls._fieldtypes_all
|
52
|
58
|
|
53
|
59
|
## @brief Creates new entries in the datasource
|
54
|
60
|
# @param datas list : A list a dict with fieldname as key
|
|
@@ -92,8 +98,7 @@ class _LeObject(object):
|
92
|
98
|
# @return bool
|
93
|
99
|
@classmethod
|
94
|
100
|
def delete(cls, letype, filters):
|
95
|
|
- letype, leclass = cls._prepare_targets(letype)
|
96
|
|
- filters,relationnal_filters = cls._prepare_filters(filters, letype, leclass)
|
|
101
|
+ filters,relationnal_filters = cls._prepare_filters(filters)
|
97
|
102
|
return cls._datasource.delete(letype, leclass, filters, relationnal_filters)
|
98
|
103
|
|
99
|
104
|
## @brief Update LeObjects given filters and datas
|
|
@@ -109,50 +114,6 @@ class _LeObject(object):
|
109
|
114
|
letype.check_datas_or_raise(datas, False)
|
110
|
115
|
return cls._datasource.update(letype, leclass, filters, relationnal_filters, datas)
|
111
|
116
|
|
112
|
|
- ## @brief make a search to retrieve a collection of LeObject
|
113
|
|
- # @param query_filters list : list of string of query filters (or tuple (FIELD, OPERATOR, VALUE) ) see @ref leobject_filters
|
114
|
|
- # @param field_list list|None : list of string representing fields see @ref leobject_filters
|
115
|
|
- # @param typename str : The name of the LeType we want
|
116
|
|
- # @param classname str : The name of the LeClass we want
|
117
|
|
- # @param cls
|
118
|
|
- # @return responses ({string:*}): a list of dict with field:value
|
119
|
|
- @classmethod
|
120
|
|
- def get(cls, query_filters, field_list = None, typename = None, classname = None):
|
121
|
|
-
|
122
|
|
- letype,leclass = cls._prepare_targets(typename, classname)
|
123
|
|
-
|
124
|
|
- #Checking field_list
|
125
|
|
- if field_list is None or len(field_list) == 0:
|
126
|
|
- #default field_list
|
127
|
|
- if not (letype is None):
|
128
|
|
- field_list = letype._fields
|
129
|
|
- elif not (leclass is None):
|
130
|
|
- field_list = leclass._fieldtypes.keys()
|
131
|
|
- else:
|
132
|
|
- field_list = list(EditorialModel.classtypes.common_fields.keys())
|
133
|
|
-
|
134
|
|
- #Fetching LeType
|
135
|
|
- if letype is None:
|
136
|
|
- if 'type_id' not in field_list:
|
137
|
|
- field_list.append('type_id')
|
138
|
|
-
|
139
|
|
-
|
140
|
|
- field_list = cls._prepare_field_list(field_list, letype, leclass)
|
141
|
|
-
|
142
|
|
- #preparing filters
|
143
|
|
- filters, relationnal_filters = cls._prepare_filters(query_filters, letype, leclass)
|
144
|
|
-
|
145
|
|
- #Fetching datas from datasource
|
146
|
|
- datas = cls._datasource.get(leclass, letype, field_list, filters, relationnal_filters)
|
147
|
|
-
|
148
|
|
- #Instanciating corresponding LeType child classes with datas
|
149
|
|
- result = list()
|
150
|
|
- for leobj_datas in datas:
|
151
|
|
- letype = cls.uid2leobj(leobj_datas['type_id']) if letype is None else letype
|
152
|
|
- result.append(letype(**leobj_datas))
|
153
|
|
-
|
154
|
|
- return result
|
155
|
|
-
|
156
|
117
|
## @brief Link two leobject together using a rel2type field
|
157
|
118
|
# @param lesup LeType : LeType child class instance linked as superior
|
158
|
119
|
# @param lesub LeType : LeType child class instance linked as subordinate
|
|
@@ -308,20 +269,6 @@ class _LeObject(object):
|
308
|
269
|
return cls._datasource.get_subordinates(leo, nature)
|
309
|
270
|
else:
|
310
|
271
|
return cls._datasource.get_superiors(leo, nature)
|
311
|
|
-
|
312
|
|
- ## @brief Prepare a field_list
|
313
|
|
- # @warning This method assume that letype and leclass are returned from _LeObject._prepare_targets() method
|
314
|
|
- # @param field_list list : List of string representing fields
|
315
|
|
- # @param letype LeType : LeType child class
|
316
|
|
- # @param leclass LeClass : LeClass child class
|
317
|
|
- # @return A well formated field list
|
318
|
|
- @classmethod
|
319
|
|
- def _prepare_field_list(cls, field_list, letype, leclass):
|
320
|
|
- cls._check_fields(letype, leclass, [f for f in field_list if not cls._field_is_relational(f)])
|
321
|
|
- for i, field in enumerate(field_list):
|
322
|
|
- if cls._field_is_relational(field):
|
323
|
|
- field_list[i] = cls._prepare_relational_field(field)
|
324
|
|
- return field_list
|
325
|
272
|
|
326
|
273
|
## @brief Preparing letype and leclass arguments
|
327
|
274
|
#
|
|
@@ -336,7 +283,8 @@ class _LeObject(object):
|
336
|
283
|
# @return a tuple with 2 python classes (LeTypeChild, LeClassChild)
|
337
|
284
|
@classmethod
|
338
|
285
|
def _prepare_targets(cls, letype = None , leclass = None):
|
339
|
|
-
|
|
286
|
+ raise ValueError()
|
|
287
|
+ warnings.warn("_LeObject._prepare_targets is deprecated", DeprecationWarning)
|
340
|
288
|
if not(leclass is None):
|
341
|
289
|
if isinstance(leclass, str):
|
342
|
290
|
leclass = cls.name2class(leclass)
|
|
@@ -372,6 +320,7 @@ class _LeObject(object):
|
372
|
320
|
# @see @ref leobject_filters
|
373
|
321
|
@staticmethod
|
374
|
322
|
def _check_fields(letype, leclass, fields):
|
|
323
|
+ warnings.warn("deprecated", DeprecationWarning)
|
375
|
324
|
#Checking that fields in the query_filters are correct
|
376
|
325
|
if letype is None and leclass is None:
|
377
|
326
|
#Only fields from the object table are allowed
|
|
@@ -388,46 +337,6 @@ class _LeObject(object):
|
388
|
337
|
if field not in field_l:
|
389
|
338
|
raise LeObjectQueryError("No field named '%s' in '%s'"%(field, letype.__name__))
|
390
|
339
|
pass
|
391
|
|
-
|
392
|
|
- ## @brief Prepare filters for datasource
|
393
|
|
- #
|
394
|
|
- # This method divide filters in two categories :
|
395
|
|
- # - filters : standart FIELDNAME OP VALUE filter
|
396
|
|
- # - relationnal_filters : filter on object relation RELATION_NATURE OP VALUE
|
397
|
|
- #
|
398
|
|
- # Both categories of filters are represented in the same way, a tuple with 3 elements (NAME|NAT , OP, VALUE )
|
399
|
|
- #
|
400
|
|
- # @warning This method assume that letype and leclass are returned from _LeObject._prepare_targets() method
|
401
|
|
- # @param filters_l list : This list can contain str "FIELDNAME OP VALUE" and tuples (FIELDNAME, OP, VALUE)
|
402
|
|
- # @param letype LeType|None : needed to check filters
|
403
|
|
- # @param leclass LeClass|None : needed to check filters
|
404
|
|
- # @return a tuple(FILTERS, RELATIONNAL_FILTERS
|
405
|
|
- #
|
406
|
|
- # @see @ref datasource_side
|
407
|
|
- @classmethod
|
408
|
|
- def _prepare_filters(cls, filters_l, letype = None, leclass = None):
|
409
|
|
- filters = list()
|
410
|
|
- for fil in filters_l:
|
411
|
|
- if len(fil) == 3 and not isinstance(fil, str):
|
412
|
|
- filters.append(tuple(fil))
|
413
|
|
- else:
|
414
|
|
- filters.append(cls._split_filter(fil))
|
415
|
|
-
|
416
|
|
- #Checking relational filters (for the moment fields like superior.NATURE)
|
417
|
|
- relational_filters = [ (cls._prepare_relational_field(field), operator, value) for field, operator, value in filters if cls._field_is_relational(field)]
|
418
|
|
- filters = [f for f in filters if not cls._field_is_relational(f[0])]
|
419
|
|
- #Checking the rest of the fields
|
420
|
|
- cls._check_fields(letype, leclass, [ f[0] for f in filters ])
|
421
|
|
-
|
422
|
|
- return (filters, relational_filters)
|
423
|
|
-
|
424
|
|
-
|
425
|
|
- ## @brief Check if a field is relational or not
|
426
|
|
- # @param field str : the field to test
|
427
|
|
- # @return True if the field is relational else False
|
428
|
|
- @staticmethod
|
429
|
|
- def _field_is_relational(field):
|
430
|
|
- return field.startswith('superior.') or field.startswith('subordinate')
|
431
|
340
|
|
432
|
341
|
## @brief Check that a relational field is valid
|
433
|
342
|
# @param field str : a relational field
|
|
@@ -436,48 +345,17 @@ class _LeObject(object):
|
436
|
345
|
def _prepare_relational_field(field):
|
437
|
346
|
spl = field.split('.')
|
438
|
347
|
if len(spl) != 2:
|
439
|
|
- raise LeObjectQueryError("The relationalfield '%s' is not valid"%field)
|
|
348
|
+ return ValueError("The relationalfield '%s' is not valid"%field)
|
440
|
349
|
nature = spl[-1]
|
441
|
350
|
if nature not in EditorialModel.classtypes.EmNature.getall():
|
442
|
|
- raise LeObjectQueryError("'%s' is not a valid nature in the field %s"%(nature, field))
|
|
351
|
+ return ValueError("'%s' is not a valid nature in the field %s"%(nature, field))
|
443
|
352
|
|
444
|
353
|
if spl[0] == 'superior':
|
445
|
354
|
return (REL_SUP, nature)
|
446
|
355
|
elif spl[0] == 'subordinate':
|
447
|
356
|
return (REL_SUB, nature)
|
448
|
357
|
else:
|
449
|
|
- raise LeObjectQueryError("Invalid preffix for relationnal field : '%s'"%spl[0])
|
450
|
|
-
|
451
|
|
- ## @brief Check and split a query filter
|
452
|
|
- # @note The query_filter format is "FIELD OPERATOR VALUE"
|
453
|
|
- # @param query_filter str : A query_filter string
|
454
|
|
- # @param cls
|
455
|
|
- # @return a tuple (FIELD, OPERATOR, VALUE)
|
456
|
|
- @classmethod
|
457
|
|
- def _split_filter(cls, query_filter):
|
458
|
|
- if cls._query_re is None:
|
459
|
|
- cls._compile_query_re()
|
460
|
|
-
|
461
|
|
- matches = cls._query_re.match(query_filter)
|
462
|
|
- if not matches:
|
463
|
|
- raise ValueError("The query_filter '%s' seems to be invalid"%query_filter)
|
464
|
|
-
|
465
|
|
- result = (matches.group('field'), re.sub(r'\s', ' ', matches.group('operator'), count=0), matches.group('value').strip())
|
466
|
|
- for r in result:
|
467
|
|
- if len(r) == 0:
|
468
|
|
- raise ValueError("The query_filter '%s' seems to be invalid"%query_filter)
|
469
|
|
- return result
|
470
|
|
-
|
471
|
|
- ## @brief Compile the regex for query_filter processing
|
472
|
|
- # @note Set _LeObject._query_re
|
473
|
|
- @classmethod
|
474
|
|
- def _compile_query_re(cls):
|
475
|
|
- op_re_piece = '(?P<operator>(%s)'%cls._query_operators[0].replace(' ', '\s')
|
476
|
|
- for operator in cls._query_operators[1:]:
|
477
|
|
- op_re_piece += '|(%s)'%operator.replace(' ', '\s')
|
478
|
|
- op_re_piece += ')'
|
479
|
|
- cls._query_re = re.compile('^\s*(?P<field>(((superior)|(subordinate))\.)?[a-z_][a-z0-9\-_]*)\s*'+op_re_piece+'\s*(?P<value>[^<>=!].*)\s*$', flags=re.IGNORECASE)
|
480
|
|
- pass
|
|
358
|
+ return ValueError("Invalid preffix for relationnal field : '%s'"%spl[0])
|
481
|
359
|
|
482
|
360
|
## @brief Class designed to represent the hierarchy roots
|
483
|
361
|
# @see _LeObject.get_root() _LeObject.is_root()
|