Browse Source

Merge branch 'newlodel' of git.labocleo.org:lodel2 into newlodel

Conflicts:
	lodel/leapi/datahandlers/base_classes.py
prieto 8 years ago
parent
commit
b4180fba2c

+ 1
- 1
install/Makefile View File

@@ -5,7 +5,7 @@ all: dyncode
5 5
 dyncode:
6 6
 	$(python) -c 'import lodel_admin; lodel_admin.refresh_dyncode()'
7 7
 
8
-init_db: dyncode
8
+init_db: 
9 9
 	$(python) -c 'import lodel_admin; lodel_admin.init_all_dbs()'
10 10
 
11 11
 list_hooks: dyncode

+ 60
- 0
lodel/leapi/datahandlers/__init__.py View File

@@ -1,2 +1,62 @@
1 1
 from lodel.leapi.datahandlers.base_classes import DataHandler
2 2
 DataHandler.load_base_handlers()
3
+
4
+##@defgroup lodel2_datahandlers Datahandlers
5
+#@ingroup lodel2_leapi
6
+#@ingroup lodel2_em
7
+
8
+##@defgroup lodel2_dh_checks  Datahandlers datas checking
9
+#@ingroup lodel2_datahandlers
10
+
11
+##@package lodel.leapi.datahandlers Lodel2 datahandlers
12
+#
13
+#Datahandlers are object that handles datas check, construction and 
14
+#consistency check
15
+#
16
+
17
+
18
+##@page lodel2_dh_checks_page Datahandlers datas checking
19
+#@ingroup lodel2_dh_checks
20
+#
21
+#@section lodel2_dh_check_mech Datas checking mechanism
22
+#
23
+#The data checking mechanism is divided into 3 stages :
24
+# 1. **value checking** : a basic value check. Example : is len(value) < 52
25
+# 2. **data construction** : for datas that needs to be modified. Example :
26
+#a date that will be transformed into a Datetime object associated with
27
+#a timezone
28
+# 3. **data consistency checking** : perform a consistency checking on the 
29
+#object from the "point of view" of the current field. Example : is the given
30
+#lodel_id an identifier of a Article
31
+#
32
+#@subsection lodel2_dh_check_impl Implementation
33
+#
34
+#Those three stages are implemented by 3 datahandlers methods :
35
+# - @ref base_classes.DataHandler.check_data_value() "check_data_value"
36
+# - @ref base_classes.DataHandler.construct_data() "construct_data"
37
+# - @ref base_classes.DataHandler.check_data_value() "check_data_consitency"
38
+#
39
+#@note To ensure the calls of the base classes methods child classes implements
40
+#those method with a '_' preffix : 
41
+# - @ref base_classes.DataHandler._check_data_value "_check_data_value()"
42
+# - @ref base_classes.DataHandler._construct_data() "_construct_data"
43
+# - @ref base_classes.DataHandler._check_data_value() "_check_data_consitency"
44
+#
45
+#Examples of child classes can be found @ref datahandlers.datas "here"
46
+#
47
+#@subsubsection lodel2_dh_datas_construction Datas construction
48
+#
49
+#Datas construction is a bit tricky. When constructing a data handled by a
50
+#datahandler we may need to have access to other datas in the object (see 
51
+#@ref base_classes.DataHandler.construct_data() "construct_data() arguments").
52
+#
53
+#The problem reside in the construction order, if we need other datas we have
54
+#to be sure that they are allready constructed. To achieve this goal the datas
55
+#dict given as arguement to 
56
+#@ref base_classes.DataHandler.construct_data() "construct_data()" is an
57
+#@ref base_classes.DatasConstructor "DatasConstructor" instance. This class
58
+#check if a data is constructed when trying to access to it, if not it runs
59
+#the corresponding construct_data() (and have a circular dependencies detection
60
+#mechanism)
61
+#
62
+#@see base_classes.DatasConstructor.

+ 126
- 51
lodel/leapi/datahandlers/base_classes.py View File

@@ -16,6 +16,7 @@ class FieldValidationError(Exception):
16 16
     pass
17 17
 
18 18
 ##@brief Base class for all data handlers
19
+#@ingroup lodel2_datahandlers
19 20
 class DataHandler(object):
20 21
     
21 22
     _HANDLERS_MODULES = ('datas_base', 'datas', 'references')
@@ -75,7 +76,10 @@ class DataHandler(object):
75 76
         return self.internal is not False
76 77
 
77 78
     ##@brief calls the data_field defined _check_data_value() method
78
-    # @return tuple (value, error|None)
79
+    #@ingroup lodel2_dh_checks
80
+    #@warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see
81
+    #@ref _construct_data() and @ref lodel2_dh_check_impl )
82
+    #@return tuple (value, error|None)
79 83
     def check_data_value(self, value):
80 84
         if value is None:
81 85
             if not self.nullable:
@@ -83,6 +87,10 @@ class DataHandler(object):
83 87
 
84 88
             return None, None
85 89
         return self._check_data_value(value)
90
+    
91
+    ##@brief Designed to be implemented in child classes
92
+    def _check_data_value(self, value):
93
+        return value, None
86 94
 
87 95
     ##@brief checks if this class can override the given data handler
88 96
     # @param data_handler DataHandler
@@ -93,35 +101,66 @@ class DataHandler(object):
93 101
         return True
94 102
 
95 103
     ##@brief Build field value
96
-    # @param emcomponent EmComponent : An EmComponent child class instance
97
-    # @param fname str : The field name
98
-    # @param datas dict : dict storing fields values (from the component)
99
-    # @param cur_value : the value from the current field (identified by fieldname)
100
-    # @return the value
101
-    # @throw RunTimeError if data construction fails
104
+    #@ingroup lodel2_dh_checks
105
+    #@warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see
106
+    #@ref _construct_data() and @ref lodel2_dh_check_impl )
107
+    #@param emcomponent EmComponent : An EmComponent child class instance
108
+    #@param fname str : The field name
109
+    #@param datas dict : dict storing fields values (from the component)
110
+    #@param cur_value : the value from the current field (identified by fieldname)
111
+    #@return the value
112
+    #@throw RunTimeError if data construction fails
113
+    #@todo raise something else
102 114
     def construct_data(self, emcomponent, fname, datas, cur_value):
103 115
         emcomponent_fields = emcomponent.fields()
104 116
         data_handler = None
105 117
         if fname in emcomponent_fields:
106 118
             data_handler = emcomponent_fields[fname]
107
-
119
+        
120
+        new_val = cur_value
108 121
         if fname in datas.keys():
109
-            return cur_value
122
+            pass
110 123
         elif data_handler is not None and hasattr(data_handler, 'default'):
111
-                return data_handler.default
124
+            new_val = data_handler.default
112 125
         elif data_handler is not None and data_handler.nullable:
113
-                return None
126
+            new_val = None
127
+        return self._construct_data(emcomponent, fname, datas, new_val)
128
+    
129
+    ##@brief Designed to be reimplemented by child classes
130
+    #@param emcomponent EmComponent : An EmComponent child class instance
131
+    #@param fname str : The field name
132
+    #@param datas dict : dict storing fields values (from the component)
133
+    #@param cur_value : the value from the current field (identified by fieldname)
134
+    #@return the value
135
+    #@see construct_data() lodel2_dh_check_impl
136
+    def _construct_data(self, empcomponent, fname, datas, cur_value):
114 137
         return cur_value
138
+        
115 139
 
116 140
     ##@brief Check datas consistency
117
-    # @param emcomponent EmComponent : An EmComponent child class instance
118
-    # @param fname : the field name
119
-    # @param datas dict : dict storing fields values
120
-    # @return an Exception instance if fails else True
121
-    # @todo A implémenter
141
+    #@ingroup lodel2_dh_checks
142
+    #@warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see
143
+    #@ref _construct_data() and @ref lodel2_dh_check_impl )
144
+    #@warning the datas argument looks like a dict but is not a dict
145
+    #see @ref base_classes.DatasConstructor "DatasConstructor" and
146
+    #@ref lodel2_dh_datas_construction "Datas construction section"
147
+    #@param emcomponent EmComponent : An EmComponent child class instance
148
+    #@param fname : the field name
149
+    #@param datas dict : dict storing fields values
150
+    #@return an Exception instance if fails else True
151
+    #@todo A implémenter
122 152
     def check_data_consistency(self, emcomponent, fname, datas):
153
+        return self._check_data_consistency(emcomponent, fname, datas)
154
+    
155
+    ##@brief Designed to be reimplemented by child classes
156
+    #@param emcomponent EmComponent : An EmComponent child class instance
157
+    #@param fname : the field name
158
+    #@param datas dict : dict storing fields values
159
+    #@return an Exception instance if fails else True
160
+    #@see check_data_consistency() lodel2_dh_check_impl
161
+    def _check_data_consistency(self, emcomponent, fname, datas):
123 162
         return True
124
- 
163
+
125 164
     ##@brief make consistency after a query
126 165
     # @param emcomponent EmComponent : An EmComponent child class instance
127 166
     # @param fname : the field name
@@ -139,7 +178,8 @@ class DataHandler(object):
139 178
         if not issubclass(data_handler, DataHandler):
140 179
             raise ValueError("A data handler HAS TO be a child class of DataHandler")
141 180
         cls.__custom_handlers[name] = data_handler
142
-
181
+    
182
+    ##@brief Load all datahandlers
143 183
     @classmethod
144 184
     def load_base_handlers(cls):
145 185
         if cls._base_handlers is None:
@@ -185,13 +225,23 @@ class DataHandler(object):
185 225
         return hash(tuple(hash_dats))
186 226
 
187 227
 ##@brief Base class for datas data handler (by opposition with references)
228
+#@ingroup lodel2_datahandlers
188 229
 class DataField(DataHandler):
189 230
     pass
190 231
 
191 232
 ##@brief Abstract class for all references
233
+#@ingroup lodel2_datahandlers
192 234
 #
193 235
 # References are fields that stores a reference to another
194 236
 # editorial object
237
+#
238
+#
239
+#@todo Check data implementation : check_data = is value an UID or an
240
+#LeObject child instance
241
+#@todo Construct data implementation : transform the data into a LeObject
242
+#instance
243
+#@todo Check data consistency implementation : check that LeObject instance
244
+#is from an allowed class
195 245
 class Reference(DataHandler):
196 246
     base_type="ref"
197 247
 
@@ -210,8 +260,6 @@ class Reference(DataHandler):
210 260
             #if not issubclass(lodel.leapi.leobject.LeObject, back_reference[0]) or not isinstance(back_reference[1], str):
211 261
             #    raise TypeError("Back reference was expected to be a tuple(<class LeObject>, str) but got : (%s, %s)" % (back_reference[0], back_reference[1]))
212 262
         self.__back_reference = back_reference
213
-        self.back_ref = back_reference
214
-
215 263
         super().__init__(internal=internal, **kwargs)
216 264
     
217 265
     @property
@@ -227,11 +275,14 @@ class Reference(DataHandler):
227 275
         self.__back_reference = back_reference
228 276
 
229 277
     ##@brief Check value
230
-    # @param value *
231
-    # @return tuple(value, exception)
232
-    # @todo implement the check when we have LeObject to check value
233
-    def _check_data_value(self, value):
234
-        return value, None
278
+    #@param value *
279
+    #@return tuple(value, exception)
280
+    #@todo implement the check when we have LeObject to check value
281
+    def check_data_value(self, value):
282
+        return super().check_data_value(value)
283
+
284
+
285
+
235 286
         if isinstance(value, lodel.editorial_model.components.EmClass):
236 287
             value = [value]
237 288
         for elt in value:
@@ -243,29 +294,36 @@ class Reference(DataHandler):
243 294
         return value
244 295
 
245 296
     ##@brief Check datas consistency
246
-    # @param emcomponent EmComponent : An EmComponent child class instance
247
-    # @param fname : the field name
248
-    # @param datas dict : dict storing fields values
249
-    # @return an Exception instance if fails else True
250
-    # @todo A implémenter
297
+    #@param emcomponent EmComponent : An EmComponent child class instance
298
+    #@param fname : the field name
299
+    #@param datas dict : dict storing fields values
300
+    #@return an Exception instance if fails else True
301
+    #@todo check for performance issue and check logics
302
+    #@todo Implements consistency checking on value : Check that the given value
303
+    #points onto an allowed class
304
+    #@warning composed uid capabilities broken here
251 305
     def check_data_consistency(self, emcomponent, fname, datas):
306
+        rep = super().check_data_consistency(emcomponent, fname, datas)
307
+        if isinstance(rep, Exception):
308
+            return rep
309
+        if self.back_reference is None:
310
+            return True
311
+        #Checking back reference consistency
312
+
313
+        # !! Reimplement instance fetching in construct data !!
252 314
         dh = emcomponent.field(fname)
253
-        logger.debug(dh)
254
-        logger.info('Warning : multiple uid capabilities are broken here')
255
-        uid = datas[emcomponent.uid_fieldname()[0]]
256
-        target_class = self.back_ref[0]
257
-        target_field = self.back_ref[1]
258
-        target_uidfield = traget_class.uid_fieldname()[0]
259
-        value = datas[emcomponent.data(fname)]
315
+        uid = datas[emcomponent.uid_fieldname()[0]] #multi uid broken here
316
+        target_class = self.back_reference[0]
317
+        target_field = self.back_reference[1]
318
+        target_uidfield = target_class.uid_fieldname()[0] #multi uid broken here
319
+        value = datas[fname]
260 320
         
261
-        obj = target_class.get((target_uidfield , '=', value))
321
+        obj = target_class.get([(target_uidfield , '=', value)])
262 322
         
263 323
         if len(obj) == 0:
264 324
             logger.warning('Object referenced does not exist')
265 325
             return False
266 326
         
267
-        obj.set_data(target_field, uid)
268
-        obj.update()
269 327
         return True
270 328
 
271 329
 ##@brief This class represent a data_handler for single reference to another object
@@ -285,6 +343,7 @@ class SingleRef(Reference):
285 343
 
286 344
 
287 345
 ##@brief This class represent a data_handler for multiple references to another object
346
+#@ingroup lodel2_datahandlers
288 347
 #
289 348
 # The fields using this data handlers are like SingleRef but can store multiple references in one field
290 349
 # @note for the moment split on ',' chars
@@ -297,22 +356,28 @@ class MultipleRef(Reference):
297 356
         super().__init__(**kwargs)
298 357
 
299 358
         
300
-    def _check_data_value(self, value):
359
+    def check_data_value(self, value):
360
+        value, expt = super().check_data_value(value)
361
+        if expt is not None:
362
+            #error in parent
363
+            return value, expt
364
+        elif value is None:
365
+            #none value
366
+            return value, expt
367
+
301 368
         expt = None
302 369
      
303 370
         if isinstance(value, str):
304 371
             value, expt = super()._check_data_value(value)
305 372
         elif not hasattr(value, '__iter__'):
306
-            return None, FieldValidationError("MultipleRef has to be an iterable or a string")
373
+            return None, FieldValidationError("MultipleRef has to be an iterable or a string, '%s' found" % value)
307 374
         if self.max_item is not None:
308 375
             if self.max_item < len(value):
309 376
                 return None, FieldValidationError("Too many items")
310 377
         return value, expt
311 378
 
312
-    def check_data_consistency(self, emcomponent, fname, datas):
313
-        return True
314
-    
315 379
     def construct_data(self, emcomponent, fname, datas, cur_value):
380
+        cur_value = super().construct_data(emcomponent, fname, datas, cur_value)
316 381
         if cur_value == 'None' or cur_value is None or cur_value == '':
317 382
             return None
318 383
         emcomponent_fields = emcomponent.fields()
@@ -336,14 +401,14 @@ class MultipleRef(Reference):
336 401
             l_value = None
337 402
 
338 403
         if l_value is not None:
339
-            if self.back_ref is not None:
340
-                br_class = self.back_ref[0]
404
+            if self.back_reference is not None:
405
+                br_class = self.back_reference[0]
341 406
                 for br_id in l_value:
342 407
                     query_filters = list()
343 408
                     query_filters.append((br_class.uid_fieldname()[0], '=', br_id))
344 409
                     br_obj = br_class.get(query_filters)
345 410
                     if len(br_obj) != 0:
346
-                        br_list = br_obj[0].data(self.back_ref[1])
411
+                        br_list = br_obj[0].data(self.back_reference[1])
347 412
                         if br_list is None:
348 413
                             br_list = list()
349 414
                         if br_id not in br_list:
@@ -354,15 +419,20 @@ class MultipleRef(Reference):
354 419
     # @param emcomponent EmComponent : An EmComponent child class instance
355 420
     # @param fname : the field name
356 421
     # @param datas dict : dict storing fields values
422
+<<<<<<< HEAD
357 423
     # @note Not done in case of delete
358 424
     def make_consistency(self, emcomponent, fname, datas, type_query):
425
+=======
426
+    """
427
+    def make_consistency(self, emcomponent, fname, datas):
428
+>>>>>>> 4acd6f8fbeff935cfb6ba072c9fec31ccb3bbb93
359 429
         dh = emcomponent.field(fname)
360 430
 
361 431
         logger.info('Warning : multiple uid capabilities are broken here')
362 432
         uid = datas[emcomponent.uid_fieldname()[0]]
363
-        if self.back_ref is not None:
364
-            target_class = self.back_ref[0]
365
-            target_field = self.back_ref[1]
433
+        if self.back_reference is not None:
434
+            target_class = self.back_reference[0]
435
+            target_field = self.back_reference[1]
366 436
             target_uidfield = target_class.uid_fieldname()[0]
367 437
             l_value = datas[fname]
368 438
 
@@ -381,6 +451,7 @@ class MultipleRef(Reference):
381 451
                         l_uids_ref.append(uid)
382 452
                         obj[0].set_data(target_field, l_uids_ref)
383 453
                         obj[0].update()
454
+<<<<<<< HEAD
384 455
            
385 456
             if type_query == 'update':
386 457
                 query_filters = list()
@@ -395,8 +466,12 @@ class MultipleRef(Reference):
395 466
                             l_uids_ref.remove(uid)
396 467
                             obj.set_data(target_field, l_uids_ref)
397 468
                             obj.update()
469
+=======
470
+    """
471
+>>>>>>> 4acd6f8fbeff935cfb6ba072c9fec31ccb3bbb93
398 472
                 
399 473
 ## @brief Class designed to handle datas access will fieldtypes are constructing datas
474
+#@ingroup lodel2_datahandlers
400 475
 #
401 476
 # This class is designed to allow automatic scheduling of construct_data calls. 
402 477
 #

+ 1
- 1
lodel/leapi/datahandlers/datas.py View File

@@ -21,7 +21,7 @@ build its content'
21 21
         self._format_string = format_string
22 22
         super().__init__(internal='automatic',**kwargs)
23 23
 
24
-    def construct_data(self, emcomponent, fname, datas, cur_value):
24
+    def _construct_data(self, emcomponent, fname, datas, cur_value):
25 25
         ret = self._format_string % tuple(
26 26
             datas[fname] for fname in self._field_list)
27 27
         if len(ret) > self.max_length:

+ 1
- 1
lodel/leapi/datahandlers/datas_base.py View File

@@ -110,7 +110,7 @@ class DateTime(DataField):
110 110
             error = ValueError("Tue value has to be a string or a datetime")
111 111
         return value, error
112 112
 
113
-    def construct_data(self, emcomponent, fname, datas, cur_value):
113
+    def _construct_data(self, emcomponent, fname, datas, cur_value):
114 114
         if (self.now_on_create and cur_value is None) or self.now_on_update:
115 115
             return datetime.datetime.now()
116 116
         return cur_value

+ 0
- 49
lodel/leapi/datahandlers/references.py View File

@@ -30,9 +30,6 @@ class List(MultipleRef):
30 30
 
31 31
         return val, expt
32 32
 
33
-    def construct_data(self, emcomponent, fname, datas, cur_value):
34
-        return super().construct_data(emcomponent, fname, datas, cur_value)
35
-
36 33
 ##@brief Child class of MultipleRef where references are represented in the form of a python set
37 34
 class Set(MultipleRef):
38 35
 
@@ -53,9 +50,6 @@ class Set(MultipleRef):
53 50
             return None, FieldValidationError("Set or string expected for a set field")
54 51
         return val, expt
55 52
     
56
-    def construct_data(self, emcomponent, fname, datas, cur_value):
57
-        return super().construct_data(emcomponent, fname, datas, cur_value)
58
-    
59 53
 ##@brief Child class of MultipleRef where references are represented in the form of a python dict
60 54
 class Map(MultipleRef):
61 55
 
@@ -77,46 +71,6 @@ class Map(MultipleRef):
77 71
                 None if isinstance(expt, Exception) else value,
78 72
                 expt)
79 73
 
80
-    def construct_data(self, emcomponent, fname, datas, cur_value):
81
-        logger.info('WARNING : not well implemented...list are stored...not dict')
82
-        if cur_value == 'None' or cur_value is None or cur_value == '':
83
-            return None
84
-        emcomponent_fields = emcomponent.fields()
85
-        data_handler = None
86
-        if fname in emcomponent_fields:
87
-            data_handler = emcomponent_fields[fname]
88
-        u_fname = emcomponent.uid_fieldname()
89
-        uidtype = emcomponent.field(u_fname[0]) if isinstance(u_fname, list) else emcomponent.field(u_fname)
90
-
91
-        if isinstance(cur_value, str):
92
-            value = cur_value.split(',')
93
-            l_value = [uidtype.cast_type(uid) for uid in value]
94
-        elif isinstance(cur_value, list):
95
-            l_value = list()
96
-            for value in cur_value:
97
-                if isinstance(value,uidtype.cast_type):
98
-                    l_value.append(value)
99
-                else:
100
-                    raise ValueError("The items must be of the same type, string or %s" % (emcomponent.__name__))
101
-        else:
102
-            l_value = None
103
-
104
-        if l_value is not None:
105
-            if self.back_ref is not None:
106
-                br_class = self.back_ref[0]
107
-                for br_id in l_value:
108
-                    query_filters = list()
109
-                    query_filters.append((br_class.uid_fieldname()[0], '=', br_id))
110
-                    br_obj = br_class.get(query_filters)
111
-                    if len(br_obj) != 0:
112
-                        br_list = br_obj[0].data(self.back_ref[1])
113
-                        if br_list is None:
114
-                            br_list = list()
115
-                        if br_id not in br_list:
116
-                            br_list.append(br_id)
117
-                            logger.info('The referenced object has to be updated')
118
-        return l_value
119
-    
120 74
 ##@brief This Reference class is designed to handler hierarchy with some constraint
121 75
 class Hierarch(MultipleRef):
122 76
     
@@ -137,6 +91,3 @@ class Hierarch(MultipleRef):
137 91
         else:
138 92
             return None, FieldValidationError("Set or string expected for a set field")
139 93
         return val, expt
140
-    
141
-    def construct_data(self, emcomponent, fname, datas, cur_value):
142
-        return super().construct_data(emcomponent, fname, datas, cur_value)

+ 2
- 2
lodel/leapi/leobject.py View File

@@ -496,9 +496,9 @@ construction and consitency when datas are not complete\n")
496 496
     # @param cls
497 497
     # @param datas dict : Datas that have been returned by LeCrud.prepare_datas() method
498 498
     @classmethod
499
-    def make_consistency(cls, datas):
499
+    def make_consistency(cls, datas, type_query = 'insert'):
500 500
         for fname, dh in cls._fields.items():
501
-            ret = dh.make_consistency(cls, fname, datas)
501
+            ret = dh.make_consistency(cls, fname, datas, type_query)
502 502
             
503 503
     ## @brief Add a new instance of LeObject
504 504
     # @return a new uid en case of success, False otherwise

+ 6
- 2
lodel/plugin/datasource_plugin.py View File

@@ -2,6 +2,8 @@ from .plugins import Plugin
2 2
 from .exceptions import *
3 3
 from lodel.settings.validator import SettingValidator
4 4
 
5
+
6
+_glob_typename = 'datasource'
5 7
 ##@brief Designed to handles datasources plugins
6 8
 #
7 9
 #A datasource provide data access to LeAPI typically a connector on a DB
@@ -16,13 +18,15 @@ from lodel.settings.validator import SettingValidator
16 18
 #@todo Write abstract classes for Datasource and MigrationHandler !!!
17 19
 class DatasourcePlugin(Plugin):
18 20
     
21
+    _type_conf_name = _glob_typename
19 22
     ##@brief Stores confspecs indicating where DatasourcePlugin list is stored
20 23
     _plist_confspecs = {
21 24
         'section': 'lodel2',
22 25
         'key': 'datasource_connectors',
23 26
         'default': None,
24
-        'validator': SettingValidator('strip', none_is_valid = False) }
25
-    _type_conf_name = 'datasource'
27
+        'validator': SettingValidator(
28
+            'plugin', none_is_valid = False,
29
+            ptype = _glob_typename) }
26 30
  
27 31
     ##@brief Construct a DatasourcePlugin 
28 32
     #@param name str : plugin name

+ 4
- 2
lodel/plugin/extensions.py View File

@@ -2,13 +2,15 @@ from .plugins import Plugin
2 2
 from .exceptions import *
3 3
 from lodel.settings.validator import SettingValidator
4 4
 
5
+_glob_typename = 'extension'
5 6
 class Extension(Plugin):
6 7
     
7 8
     _plist_confspecs = {
8 9
         'section': 'lodel2',
9 10
         'key': 'extensions',
10 11
         'default': [],
11
-        'validator': SettingValidator('list', none_is_valid = False)}
12
+        'validator': SettingValidator(
13
+            'plugin', none_is_valid = False, ptype = _glob_typename)}
12 14
 
13
-    _type_conf_name = 'extension'
15
+    _type_conf_name = _glob_typename
14 16
 

+ 7
- 2
lodel/plugin/interface.py View File

@@ -2,6 +2,10 @@ from .plugins import Plugin
2 2
 from .exceptions import *
3 3
 from lodel.settings.validator import SettingValidator
4 4
 
5
+_glob_typename = 'ui'
6
+
7
+##@brief Handles interfaces plugin
8
+#@note It's a singleton class. Only 1 interface allowed by instance.
5 9
 class InterfacePlugin(Plugin):
6 10
     
7 11
     ##@brief Singleton instance storage
@@ -11,9 +15,10 @@ class InterfacePlugin(Plugin):
11 15
         'section': 'lodel2',
12 16
         'key': 'interface',
13 17
         'default': None,
14
-        'validator': SettingValidator('strip', none_is_valid = True)}
18
+        'validator': SettingValidator(
19
+            'plugin', none_is_valid = True, ptype = _glob_typename)}
15 20
 
16
-    _type_conf_name = 'ui'
21
+    _type_conf_name = _glob_typename
17 22
     
18 23
     def __init__(self, name):
19 24
         if InterfacePlugin._instance is not None:

+ 4
- 2
lodel/plugin/sessionhandler.py View File

@@ -33,6 +33,7 @@ functions, but no session handler initialized")
33 33
         return super().__getattribute__(name)
34 34
 
35 35
 
36
+_glob_typename = 'session_handler'
36 37
 ##@brief Singleton class designed to handle session handler plugin
37 38
 #
38 39
 #@note This class is a singleton because only one session handler can be
@@ -45,9 +46,10 @@ class SessionHandlerPlugin(Plugin, metaclass=SessionPluginWrapper):
45 46
         'section': 'lodel2',
46 47
         'key': 'session_handler',
47 48
         'default': None,
48
-        'validator': SettingValidator('string', none_is_valid=False)}
49
+        'validator': SettingValidator(
50
+            'plugin', none_is_valid=False,ptype = _glob_typename)}
49 51
 
50
-    _type_conf_name = 'session_handler'
52
+    _type_conf_name = _glob_typename
51 53
             
52 54
     def __init__(self, plugin_name):
53 55
         if self._instance is None:

+ 31
- 22
lodel/settings/validator.py View File

@@ -7,7 +7,6 @@ import socket
7 7
 import inspect
8 8
 import copy
9 9
 
10
-
11 10
 ## @package lodel.settings.validator Lodel2 settings validators/cast module
12 11
 #
13 12
 # Validator are registered in the SettingValidator class.
@@ -29,11 +28,15 @@ class SettingValidator(object):
29 28
     _description = dict()
30 29
     
31 30
     ##@brief Instanciate a validator
32
-    def __init__(self, name, none_is_valid = False):
31
+    #@param name str : validator name
32
+    #@param none_is_valid bool : if True None will be validated
33
+    #@param **kwargs : more arguement for the validator
34
+    def __init__(self, name, none_is_valid = False, **kwargs):
33 35
         if name is not None and name not in self._validators:
34 36
             raise NameError("No validator named '%s'" % name)
35 37
         self.__none_is_valid = none_is_valid
36 38
         self.__name = name
39
+        self._opt_args = kwargs
37 40
 
38 41
     ##@brief Call the validator
39 42
     # @param value *
@@ -45,7 +48,7 @@ class SettingValidator(object):
45 48
         if self.__none_is_valid and value is None:
46 49
             return None
47 50
         try:
48
-            return self._validators[self.__name](value)
51
+            return self._validators[self.__name](value, **self._opt_args)
49 52
         except Exception as e:
50 53
             raise SettingsValidationError(e)
51 54
     
@@ -218,6 +221,9 @@ def host_val(value):
218 221
         msg = "The value '%s' is not a valid host"
219 222
         raise SettingsValidationError(msg % value)
220 223
 
224
+##@brief Validator for Editorial model component
225
+#
226
+# Designed to validate a conf that indicate a class.field in an EM
221 227
 def emfield_val(value):
222 228
     from lodel.plugin.hooks import LodelHook
223 229
     spl = value.split('.')
@@ -239,25 +245,28 @@ def emfield_val(value):
239 245
             raise SettingsValidationError(msg % value)
240 246
     return value
241 247
 
242
-def plugin_val(value):
248
+##@brief Validator for plugin name & optionnaly type
249
+#
250
+#Able to check that the value is a plugin and if it is of a specific type
251
+def plugin_validator(value, ptype = None):
243 252
     from lodel.plugin.hooks import LodelHook
244
-    spl = value.split('.')
245
-    if len(spl) != 2:
246
-        msg = "Expected a value in the form PLUGIN.TYPE but got : %s"
247
-        raise SettingsValidationError(msg % value)
248
-    value = tuple(spl)
249
-    #Late validation hook
250 253
     @LodelHook('lodel2_dyncode_bootstraped')
251
-    def type_check(hookname, caller, payload):
252
-        from lodel import plugin
253
-        typesname = { cls.__name__.lower():cls for cls in plugin.PLUGINS_TYPE}
254
-        if value[1].lower() not in typesname:
255
-            msg = "Following plugin type do not exist in plugin list %s : %s"
256
-            raise SettingsValidationError(msg % value)
257
-        return value
258
-    plug_type_val = plugin_val(value)
259
-    return plug_type_val
260
-
254
+    def plugin_type_checker(hookname, caller, payload):
255
+        from lodel.plugin.plugins import Plugin
256
+        from lodel.plugin.exceptions import PluginError
257
+        try:
258
+            plugin = Plugin.get(value)
259
+        except PluginError:
260
+            msg = "No plugin named %s found"
261
+            msg %= value
262
+            raise SettingsValidationError(msg)
263
+        if plugin._type_conf_name.lower() != ptype.lower():
264
+            msg = "A plugin of type '%s' was expected but found a plugin \
265
+named  '%s' that is a '%s' plugin"
266
+            msg %= (ptype, value, plugin._type_conf_name)
267
+            raise SettingsValidationError(msg)
268
+    return value
269
+        
261 270
 
262 271
 #
263 272
 #   Default validators registration
@@ -265,8 +274,8 @@ def plugin_val(value):
265 274
 
266 275
 SettingValidator.register_validator(
267 276
     'plugin',
268
-    plugin_val,
269
-    'plugin validator')
277
+    plugin_validator,
278
+    'plugin name & type validator')
270 279
 
271 280
 SettingValidator.register_validator(
272 281
     'dummy',

+ 4
- 0
plugins/mongodb_datasource/datasource.py View File

@@ -216,6 +216,7 @@ class MongoDbDatasource(object):
216 216
         mongo_filters = self.__process_filters(
217 217
             target, filters, relational_filters)
218 218
         res = self.__collection(target).update(mongo_filters, upd_datas)
219
+        target.make_consistency(datas=upd_datas, type_query='update')
219 220
         return res['n']
220 221
 
221 222
     ## @brief Inserts a record in a given collection
@@ -224,6 +225,7 @@ class MongoDbDatasource(object):
224 225
     # @return the inserted uid
225 226
     def insert(self, target, new_datas):
226 227
         res = self.__collection(target).insert(new_datas)
228
+        target.make_consistency(datas=new_datas)
227 229
         return str(res)
228 230
 
229 231
     ## @brief Inserts a list of records in a given collection
@@ -232,6 +234,8 @@ class MongoDbDatasource(object):
232 234
     # @return list : list of the inserted records' ids
233 235
     def insert_multi(self, target, datas_list):
234 236
         res = self.__collection(target).insert_many(datas_list)
237
+        for new_datas in datas_list:
238
+            target.make_consistency(datas=new_datas)
235 239
         return list(res.inserted_ids)
236 240
     
237 241
     ##@brief Act on abstract LeObject child

+ 0
- 3
plugins/webui/interface/urls.py View File

@@ -11,11 +11,8 @@ urls = (
11 11
     (r'^/admin/class_admin$', admin_class),
12 12
     (r'/test/(?P<id>.*)$', test),
13 13
     (r'^/test/?$', test),
14
-    #(r'/show/(?P<id>.*)$', show_document),
15 14
     (r'^/list_classes', list_classes),
16 15
     (r'^/list_classes?$', list_classes),
17
-    #(r'^/show_object/(.+)$', show_object),
18 16
     (r'^/show_object?$', show_object),
19
-    #(r'^/show_class/(.+)$', show_class),
20 17
     (r'^/show_class?$', show_class)
21 18
 )

+ 2
- 3
plugins/webui/templates/admin/admin.html View File

@@ -2,10 +2,9 @@
2 2
 {% block title %}- Index{% endblock %}
3 3
 {% block body %}
4 4
 <h1>{{settings.sitename}} administration</h1>
5
-{{url('admin')}}
6 5
 <ul>
7
-    <li><a href="classes_admin">Edit object</a></li>
8
-    <li><a href="object_create">Create object</a></li>
6
+    <li><a href="admin/classes_admin">Edit object</a></li>
7
+    <li><a href="admin/object_create">Create object</a></li>
9 8
 </ul>
10 9
 
11 10
 {% endblock %}

+ 1
- 1
plugins/webui/templates/admin/editable_component.html View File

@@ -12,7 +12,7 @@
12 12
         {% endif %}
13 13
         {% if field.directly_editable %}
14 14
             <input id="{{fieldname}}" name="field_input_{{fieldname}}" type="text" value="{{sval}}" >
15
-            {% set l_classe = field.allowed_classes %}
15
+            {% set l_classe = field.linked_classes %}
16 16
             <p> Please enter uids to instances of {{ l_classe.__name__ }} separated by commas </p>
17 17
         {% else %}
18 18
             <input id="{{fieldname}}" name="field_input_{{fieldname}}" type="text" value="{{sval}}" readonly >

+ 32
- 4
plugins/webui/templates/base.html View File

@@ -4,20 +4,48 @@
4 4
     <meta charset="UTF-8" />
5 5
     <title>{% block title %}{% endblock %}</title>
6 6
     
7
-    <!-- Bootstrap -->
8
-    <link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
9
-    
7
+   <!-- Latest compiled and minified CSS -->
8
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
9
+<!--{{url('/')}} -->
10
+<!-- Optional theme -->
11
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
12
+    <link href="http://127.0.0.1/css/template.css" rel="stylesheet">
10 13
     {% block style %}{% endblock %}
11 14
     {% block scripts %}{% endblock %}
12 15
 </head>
13 16
 <body>
17
+        <!-- Fixed navbar -->
18
+    <nav class="navbar navbar-default navbar-fixed-top">
19
+      <div class="container">
20
+        <div class="navbar-header">
21
+          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
22
+            <span class="sr-only">Toggle navigation</span>
23
+            <span class="icon-bar"></span>
24
+            <span class="icon-bar"></span>
25
+            <span class="icon-bar"></span>
26
+          </button>
27
+          <a class="navbar-brand" href="#">Lodel 2</a>
28
+        </div>
29
+        <div id="navbar" class="navbar-collapse collapse">
30
+          <ul class="nav navbar-nav">
31
+            <li class="active"><a href="lodel_i/">Home</a></li>
32
+            <li><a href="list_classes">All types</a></li>
33
+          </ul>
34
+          <ul class="nav navbar-nav navbar-right">
35
+            <li><a href="admin">Back-end</a></li>
36
+              <li id="signin-nav"><a href="signin">Sign In</a></li>
37
+          </ul>
38
+        </div><!--/.nav-collapse -->
39
+      </div>
40
+    </nav>
14 41
     <div id="content">
15 42
         {% block content %}{% endblock %}
16 43
     </div>
17 44
     <script type="text/javascript">{% block javascript %}{% endblock %}</script>
45
+
18 46
     <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
19 47
     <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
20 48
     <!-- Include all compiled plugins (below), or include individual files as needed -->
21
-    <script src="bootstrap/js/bootstrap.min.js"></script>
49
+   <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
22 50
 </body>
23 51
 </html>

+ 34
- 1
plugins/webui/templates/base_backend.html View File

@@ -3,11 +3,44 @@
3 3
 <head>
4 4
     <meta charset="UTF-8" />
5 5
     <title>{{ settings.sitename }} Admin{% block title %}{% endblock %}</title>
6
-    {% block style %}{% endblock %}
6
+     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
7
+     <link href="http://127.0.0.1/css/template.css" rel="stylesheet">
8
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
9
+    {% block style %}
10
+    {% endblock %}
7 11
     {% block scripts %}{% endblock %}
8 12
 </head>
9 13
 <body>
14
+           <!-- Fixed navbar -->
15
+    <nav class="navbar navbar-default navbar-fixed-top">
16
+      <div class="container">
17
+        <div class="navbar-header">
18
+          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
19
+            <span class="sr-only">Toggle navigation</span>
20
+            <span class="icon-bar"></span>
21
+            <span class="icon-bar"></span>
22
+            <span class="icon-bar"></span>
23
+          </button>
24
+          <a class="navbar-brand" href="#">Lodel 2 Administration</a>
25
+        </div>
26
+        <div id="navbar" class="navbar-collapse collapse">
27
+          <ul class="nav navbar-nav">
28
+            <li class="active"><a href="admin">Home</a></li>
29
+            <li><a href="admin/object_create">Create</a></li>
30
+            <li><a href="admin/classes_admin">Edit</a></li>
31
+          </ul>
32
+          <ul class="nav navbar-nav navbar-right">
33
+            <li><a href="/lodel_i/">Front-End</a></li>
34
+            <li id="signin-nav"><a href="signin">Sign In</a></li>
35
+          </ul>
36
+        </div><!--/.nav-collapse -->
37
+      </div>
38
+    </nav>
10 39
     {% block body %}{% endblock %}
11 40
     <script type="text/javascript">{% block javascript %}{% endblock %}</script>
41
+    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
42
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
43
+    <!-- Include all compiled plugins (below), or include individual files as needed -->
44
+   <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
12 45
 </body>
13 46
 </html>

+ 0
- 0
plugins/webui/templates/empty.html View File


+ 0
- 2
plugins/webui/templates/index/index.html View File

@@ -1,8 +1,6 @@
1 1
 {% extends "base.html" %}
2 2
 {% block title %}Lodel 2 - DASHBOARD{% endblock %}
3 3
 {% block content %}
4
-    DASHBOARD <br />
5
-    {{ lodel.Test().ok() }}
6 4
 <ul>
7 5
     <li><a href="list_classes">Tous les types</a></li>
8 6
 </ul>

+ 0
- 3
settings.ini View File

@@ -1,3 +0,0 @@
1
-[lodel2]
2
-lib_path = /usr/lib/python/lodel2
3
-plugins_path = /usr/lib/python/lodel2/plugins, /usr/share/custom_lodel2/plugins

+ 0
- 45
settings.py View File

@@ -1,45 +0,0 @@
1
-#-*- coding:utf8 -*-
2
-## @package settings Configuration file
3
-
4
-import os
5
-import os.path
6
-
7
-lodel2_lib_path = os.path.dirname(os.path.abspath(__file__))
8
-base_path = os.path.dirname(os.path.abspath(__file__))
9
-debug = False
10
-debug_sql = False
11
-
12
-plugins = ['dummy', 'dummy_auth']
13
-
14
-datasource = {
15
-    'default': {
16
-        'module':None,
17
-        'host': None,
18
-        'user': None,
19
-        'passwd': None,
20
-        'db': None,
21
-    }
22
-}
23
-
24
-migration_options = {
25
-    'dryrun': False,
26
-    'foreign_keys': True,
27
-    'drop_if_exists': False,
28
-}
29
-
30
-em_graph_format = 'png'
31
-em_graph_output = '/tmp/em_%s_graph.png'
32
-
33
-logging = {
34
-    'stderr': {
35
-        'level': 'INFO',
36
-        'context': False,
37
-    },
38
-    'logfile': {
39
-        'level': 'DEBUG',
40
-        'filename': '/tmp/lodel2.log',
41
-        'maxBytes': 1024 * 50, # rotate at 50MB
42
-        'backupCount': 10, # keep at most 10 backup
43
-        'context': True, # if false use a simpler format string
44
-    }
45
-}

+ 2
- 1
tests/leapi/query/test_datasource.py View File

@@ -105,7 +105,8 @@ class LeQueryDatasourceTestCase(unittest.TestCase):
105 105
             [(('alias', {cls: 'firstname'}), '=', 'foo')])
106 106
         self.check_nocall(read = False, exclude = ['delete'])
107 107
         self.check_nocall(read = True)
108
-
108
+    
109
+    @unittest.skip("Waiting references checks stack implementation")
109 110
     def test_insert(self):
110 111
         """ Testing LeInsertQuery mocking datasource """
111 112
         cls = self.dyncode['Person']

Loading…
Cancel
Save