Browse Source

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

prieto 8 years ago
parent
commit
b39c279437

+ 1
- 1
Doxyfile View File

763
 # spaces.
763
 # spaces.
764
 # Note: If this tag is empty the current directory is searched.
764
 # Note: If this tag is empty the current directory is searched.
765
 
765
 
766
-INPUT                  = .
766
+INPUT                  =
767
 
767
 
768
 # This tag can be used to specify the character encoding of the source files
768
 # This tag can be used to specify the character encoding of the source files
769
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
769
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

+ 14
- 34
lodel/leapi/leobject.py View File

7
 from lodel.settings import Settings
7
 from lodel.settings import Settings
8
 from lodel.settings.utils import SettingsError
8
 from lodel.settings.utils import SettingsError
9
 from .query import LeInsertQuery, LeUpdateQuery, LeDeleteQuery, LeGetQuery
9
 from .query import LeInsertQuery, LeUpdateQuery, LeDeleteQuery, LeGetQuery
10
+from .exceptions import *
10
 from lodel.plugin.hooks import LodelHook
11
 from lodel.plugin.hooks import LodelHook
11
 
12
 
12
 ##@brief Stores the name of the field present in each LeObject that indicates
13
 ##@brief Stores the name of the field present in each LeObject that indicates
13
 #the name of LeObject subclass represented by this object
14
 #the name of LeObject subclass represented by this object
14
 CLASS_ID_FIELDNAME = "classname"
15
 CLASS_ID_FIELDNAME = "classname"
15
 
16
 
16
-class LeApiErrors(Exception):
17
-    ##@brief Instanciate a new exceptions handling multiple exceptions
18
-    # @param msg str : Exception message
19
-    # @param exceptions dict : A list of data check Exception with concerned field (or stuff) as key
20
-    def __init__(self, msg = "Unknow error", exceptions = None):
21
-        self._msg = msg
22
-        self._exceptions = dict() if exceptions is None else exceptions
23
-
24
-    def __repr__(self):
25
-        return self.__str__()
26
-
27
-    def __str__(self):
28
-        msg = self._msg
29
-        for_iter = self._exceptions.items() if isinstance(self._exceptions, dict) else enumerate(self.__exceptions)
30
-        for obj, expt in for_iter:
31
-            msg += "\n\t{expt_obj} : ({expt_name}) {expt_msg}; ".format(
32
-                    expt_obj = obj,
33
-                    expt_name=expt.__class__.__name__,
34
-                    expt_msg=str(expt)
35
-            )
36
-        return msg
37
-
38
-
39
-##@brief When an error concern a query
40
-class LeApiQueryError(LeApiErrors):
41
-    pass
42
-
43
-
44
-##@brief When an error concerns a datas
45
-class LeApiDataCheckError(LeApiErrors):
46
-    pass
47
-
48
-
49
 ##@brief Wrapper class for LeObject getter & setter
17
 ##@brief Wrapper class for LeObject getter & setter
50
 #
18
 #
51
 # This class intend to provide easy & friendly access to LeObject fields values 
19
 # This class intend to provide easy & friendly access to LeObject fields values 
276
             ds_conf[k] = getattr(ds_conf_old, k)
244
             ds_conf[k] = getattr(ds_conf_old, k)
277
 
245
 
278
         return datasource_class(**ds_conf)
246
         return datasource_class(**ds_conf)
247
+    
248
+    ##@brief Return the uid of the current LeObject instance
249
+    #@return the uid value
250
+    #@warning Broke multiple uid capabilities
251
+    def uid(self):
252
+        return self.data(self._uid[0])
279
 
253
 
280
     ##@brief Read only access to all datas
254
     ##@brief Read only access to all datas
281
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
255
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
290
             raise RuntimeError("The field %s is not initialized yet (and have no value)" % field_name)
264
             raise RuntimeError("The field %s is not initialized yet (and have no value)" % field_name)
291
         return self.__datas[field_name]
265
         return self.__datas[field_name]
292
     
266
     
267
+    ##@brief Read only access to all datas
268
+    #@return a dict representing datas of current instance
269
+    def datas(self):
270
+        return [self.data(fname) for fname in self.fieldnames(True)]
271
+        
272
+    
293
     ##@brief Datas setter
273
     ##@brief Datas setter
294
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
274
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
295
     # @param fname str : field name
275
     # @param fname str : field name
517
             
497
             
518
         try:
498
         try:
519
             result = query.execute(datas)
499
             result = query.execute(datas)
520
-        except Exception as err;
500
+        except Exception as err:
521
             raise err
501
             raise err
522
 
502
 
523
         return result
503
         return result

+ 64
- 47
lodel/leapi/query.py View File

2
 
2
 
3
 import re
3
 import re
4
 import copy
4
 import copy
5
+import inspect
5
 
6
 
6
-from .leobject import LeObject, LeApiErrors, LeApiDataCheckError
7
+from .exceptions import *
7
 from lodel.plugin.hooks import LodelHook
8
 from lodel.plugin.hooks import LodelHook
8
 from lodel import logger
9
 from lodel import logger
9
 
10
 
10
-class LeQueryError(Exception):
11
-    ##@brief Instanciate a new exceptions handling multiple exceptions
12
-    #@param msg str : Exception message
13
-    #@param exceptions dict : A list of data check Exception with concerned
14
-    # field (or stuff) as key
15
-    def __init__(self, msg = "Unknow error", exceptions = None):
16
-        self._msg = msg
17
-        self._exceptions = dict() if exceptions is None else exceptions
18
-
19
-    def __repr__(self):
20
-        return self.__str__()
21
-
22
-    def __str__(self):
23
-        msg = self._msg
24
-        if isinstance(self._exceptions, dict):
25
-            for_iter = self._exceptions.items()
26
-        else:
27
-            for_iter = enumerate(self.__exceptions)
28
-        for obj, expt in for_iter:
29
-            msg += "\n\t{expt_obj} : ({expt_name}) {expt_msg}; ".format(
30
-                    expt_obj = obj,
31
-                    expt_name=expt.__class__.__name__,
32
-                    expt_msg=str(expt)
33
-            )
34
-        return msg
35
 
11
 
36
 ##@todo check datas when running query
12
 ##@todo check datas when running query
37
 class LeQuery(object):
13
 class LeQuery(object):
44
     ##@brief Abstract constructor
20
     ##@brief Abstract constructor
45
     # @param target_class LeObject : class of object the query is about
21
     # @param target_class LeObject : class of object the query is about
46
     def __init__(self, target_class):
22
     def __init__(self, target_class):
23
+        from .leobject import LeObject
47
         if self._hook_prefix is None:
24
         if self._hook_prefix is None:
48
             raise NotImplementedError("Abstract class")
25
             raise NotImplementedError("Abstract class")
49
         if not issubclass(target_class, LeObject):
26
         if not issubclass(target_class, LeObject):
504
 
481
 
505
 
482
 
506
 ##@brief A query to update datas for a given object
483
 ##@brief A query to update datas for a given object
484
+#
485
+#@todo Change behavior, Huge optimization problem when updating using filters
486
+#and not instance. We have to run a GET and then 1 update by fecthed object...
507
 class LeUpdateQuery(LeFilteredQuery):
487
 class LeUpdateQuery(LeFilteredQuery):
508
     
488
     
509
     _hook_prefix = 'leapi_update_'
489
     _hook_prefix = 'leapi_update_'
510
     _data_check_args = { 'complete': True, 'allow_internal': False }
490
     _data_check_args = { 'complete': True, 'allow_internal': False }
511
-
512
-    def __init__(self, target_class, query_filter):
513
-        super().__init__(target_class, query_filter)
514
     
491
     
515
-    ##@brief Called by __query to do severals checks before running the update
516
-    #@warning Optimization issue : each time this method is called we do 
517
-    #a LeGetQuery to fetch ALL datas and construct instances for being able to
518
-    #construct datas and check consistency
519
-    #@todo implementation (waiting for LeApi to be plugged to LeQuery)
520
-    def __fetch_construct_check_update(self, filters, rel_filters, datas):
521
-        """
522
-        instances = self._target_class.get(filters, rel_filters)
523
-        for instance in instances:
524
-            instance.check_datas_value(instance.
525
-        """
526
-        pass
527
-        
492
+    ##@brief Instanciate an update query
493
+    #
494
+    #If a class and not an instance is given, no query_filters are expected
495
+    #and the update will be fast and simple. Else we have to run a get query
496
+    #before updating (to fetch datas, update them and then, construct them
497
+    #and check their consistency)
498
+    #@param target LeObject clas or instance
499
+    #@param query_filters list|None
500
+    #@todo change strategy with instance update. We have to accept datas for
501
+    #the execute method
502
+    def __init__(self, target, query_filters = None):
503
+        ##@brief This attr is set only if the target argument is an 
504
+        #instance of a LeObject subclass
505
+        self.__leobject_datas = None
506
+        target_class = target
507
+        if not inspect.isclass(target):
508
+            if query_filters is not None:
509
+                msg = "No query_filters accepted when an instance is given as \
510
+target to LeUpdateQuery constructor"
511
+                raise AttributeError(msg)
512
+            target_class = target.__class__
513
+            if self.initialized():
514
+                self.__leobject_instance_datas = target.datas()
515
+            else:
516
+                filters = [(target._uid[0], '=', target.uid())]
517
+    
518
+        super().__init__(target_class, query_filters)
528
 
519
 
529
     ##@brief Implements an update query
520
     ##@brief Implements an update query
530
     #@param filters list : see @ref LeFilteredQuery
521
     #@param filters list : see @ref LeFilteredQuery
531
     #@param rel_filters list : see @ref LeFilteredQuery
522
     #@param rel_filters list : see @ref LeFilteredQuery
532
     #@param datas dict : datas to update
523
     #@param datas dict : datas to update
533
     #@returns the number of updated items
524
     #@returns the number of updated items
525
+    #@todo change stategy for instance update. Datas should be allowed 
526
+    #for execute method (and query)
534
     def __query(self, filters, rel_filters, datas):
527
     def __query(self, filters, rel_filters, datas):
535
-        self.__fetch_construct_check_update(filters, rel_filters, datas)
536
-        nb_updated = self._rw_datasource.update(
537
-            self._target_class, filters, rel_filters, datas)
528
+        uid_name = self._target_class._uid[0]
529
+        if self.__leobject_instance is not None:
530
+            #Instance update
531
+            #Building query_filter
532
+            filters = [(
533
+                uid_name, '=', self.__leobject_instance_datas[uid_name])]
534
+            self._rw_datasource.update(
535
+                self._target_class, filters, [],
536
+                self.__leobject_instance_datas)
537
+        else:
538
+            #Update by filters, we have to fetch datas before updating
539
+            res = self._ro_datasource.select(
540
+                self._target_class, self._target_class.fieldnames(True),
541
+                filters, rel_filters)
542
+            #Checking and constructing datas
543
+            upd_datas = dict()
544
+            for res_data in res:
545
+                res_data.update(datas)
546
+                res_datas = self._target_class.prepare_datas(
547
+                    res_data, True, True)
548
+                filters = [(uid_name, '=', res_data[uid_name])]
549
+                self._rw_datasource.update(
550
+                    self._target_class, filters, [],
551
+                    res_datas)
538
         return nb_updated
552
         return nb_updated
539
     
553
     
540
     ## @brief Execute the update query
554
     ## @brief Execute the update query
541
-    def execute(self, datas):
555
+    def execute(self, datas = None):
556
+        if self.__leobject_instance is not None and datas is not None:
557
+            raise AttributeError("No datas expected when running an update \
558
+query on an instance")
542
         return super().execute(datas = datas)
559
         return super().execute(datas = datas)
543
 
560
 
544
 ##@brief A query to delete an object
561
 ##@brief A query to delete an object
639
                 err_l[fieldname] =  expt
656
                 err_l[fieldname] =  expt
640
         if len(err_l) > 0:
657
         if len(err_l) > 0:
641
             msg = "Error while setting field_list in a get query"
658
             msg = "Error while setting field_list in a get query"
642
-            raise LeQueryError(msg = msg, exceptions = err_l)
659
+            raise LeApiQueryErrors(msg = msg, exceptions = err_l)
643
         self.__field_list = list(set(field_list))
660
         self.__field_list = list(set(field_list))
644
     
661
     
645
     ##@brief Execute the get query
662
     ##@brief Execute the get query

+ 10
- 2
plugins/mongodb_datasource/__init__.py View File

1
-from lodel.settings.validator import SettingValidator
2
-
1
+from .utils import connect, get_connection_args
3
 
2
 
4
 __loader__ = "main.py"
3
 __loader__ = "main.py"
5
 __confspec__ = "confspec.py"
4
 __confspec__ = "confspec.py"
13
 #
12
 #
14
 # @return bool|str : True if all the checks are OK, an error message if not
13
 # @return bool|str : True if all the checks are OK, an error message if not
15
 def _activate():
14
 def _activate():
15
+    default_connection_args = get_connection_args()
16
+    connection_check = connect(
17
+        default_connection_args['host'],
18
+        default_connection_args['port'],
19
+        default_connection_args['db_name'],
20
+        default_connection_args['username'],
21
+        default_connection_args['password'])
22
+    if not connection_check:
23
+        return False
16
     return True
24
     return True

+ 3
- 3
plugins/mongodb_datasource/datasource.py View File

11
 
11
 
12
 from lodel import logger
12
 from lodel import logger
13
 
13
 
14
-from .utils import mongodbconnect, object_collection_name, \
15
-    connect, MONGODB_SORT_OPERATORS_MAP, connection_string
14
+from .utils import object_collection_name, connect, \
15
+    MONGODB_SORT_OPERATORS_MAP, connection_string
16
 
16
 
17
 class MongoDbDataSourceError(Exception):
17
 class MongoDbDataSourceError(Exception):
18
     pass
18
     pass
77
     #@param instanciate bool : If true, the records are returned as instances, else they are returned as dict
77
     #@param instanciate bool : If true, the records are returned as instances, else they are returned as dict
78
     #@return list
78
     #@return list
79
     #@todo Implement the relations
79
     #@todo Implement the relations
80
-    def select(self, target, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0, instanciate=True):
80
+    def select(self, target, field_list, filters, rel_filters=None, order=None, group=None, limit=None, offset=0):
81
         collection_name = object_collection_name(target)
81
         collection_name = object_collection_name(target)
82
         collection = self.database[collection_name]
82
         collection = self.database[collection_name]
83
         query_filters = self.__process_filters(
83
         query_filters = self.__process_filters(

+ 2
- 0
plugins/mongodb_datasource/main.py View File

1
 from lodel.plugin import LodelHook
1
 from lodel.plugin import LodelHook
2
 
2
 
3
+
3
 @LodelHook('mongodb_mh_init')
4
 @LodelHook('mongodb_mh_init')
4
 def mongodb_migration_handler_init():
5
 def mongodb_migration_handler_init():
5
     import plugins.mongodb_datasource.migration_handler as migration_handler
6
     import plugins.mongodb_datasource.migration_handler as migration_handler
6
 
7
 
8
+
7
 @LodelHook('mongodb_ds_init')
9
 @LodelHook('mongodb_ds_init')
8
 def mongodb_datasource_init():
10
 def mongodb_datasource_init():
9
     import plugins.mongodb_datasource.datasource as datasource
11
     import plugins.mongodb_datasource.datasource as datasource

+ 20
- 15
plugins/mongodb_datasource/utils.py View File

49
     return connect(host, port, dbname, login, password)
49
     return connect(host, port, dbname, login, password)
50
 '''
50
 '''
51
 
51
 
52
+
52
 def connection_string(host, port, db_name, username, password):
53
 def connection_string(host, port, db_name, username, password):
53
     return 'mongodb://%s:%s@%s:%s' % (username, password, host, port)
54
     return 'mongodb://%s:%s@%s:%s' % (username, password, host, port)
54
 
55
 
56
+
55
 def connect(host, port, db_name, username, password):
57
 def connect(host, port, db_name, username, password):
56
     connection = MongoClient(
58
     connection = MongoClient(
57
         connection_string(host, port, db_name, username, password))
59
         connection_string(host, port, db_name, username, password))
59
     return database
61
     return database
60
 
62
 
61
 
63
 
62
-
63
 ## @brief Returns a collection name given a EmClass
64
 ## @brief Returns a collection name given a EmClass
64
 # @param class_object EmClass
65
 # @param class_object EmClass
65
 # @return str
66
 # @return str
72
 
73
 
73
     return collection_name
74
     return collection_name
74
 
75
 
75
-##@brief Determine a collection field name given a lodel2 fieldname
76
-#@note For the moment this method only return the argument but EVERYWHERE
77
-#in the datasource we should use this method to gather proper fieldnames
78
-#@param fieldname str : A lodel2 fieldname
79
-#@return A string representing a well formated mongodb fieldname
80
-#@see mongo_filednames
76
+
77
+## @brief Determine a collection field name given a lodel2 fieldname
78
+# @note For the moment this method only return the argument but EVERYWHERE
79
+# in the datasource we should use this method to gather proper fieldnames
80
+# @param fieldname str : A lodel2 fieldname
81
+# @return A string representing a well formated mongodb fieldname
82
+# @see mongo_filednames
81
 def mongo_fieldname(fieldname):
83
 def mongo_fieldname(fieldname):
82
     return fieldname
84
     return fieldname
83
-##@brief Same as mongo_fieldname but for list of fields
85
+
86
+
87
+## @brief Same as mongo_fieldname but for list of fields
84
 #
88
 #
85
-#A small utility function
86
-#@param fieldnames iterable : contains str only
87
-#@return a list of converted fildnames (str)
88
-#@see mongo_fieldname
89
-def mongo_filednames(fieldnames):
89
+# A small utility function
90
+# @param fieldnames iterable : contains str only
91
+# @return a list of converted fildnames (str)
92
+# @see mongo_fieldname
93
+def mongo_fieldnames(fieldnames):
90
     return [mongo_fieldname(fname) for fname in fieldnames]
94
     return [mongo_fieldname(fname) for fname in fieldnames]
91
 
95
 
96
+
92
 ## @brief Returns a list of orting options
97
 ## @brief Returns a list of orting options
93
 # @param query_filters_order list
98
 # @param query_filters_order list
94
 # @return list
99
 # @return list
95
 def parse_query_order(query_filters_order):
100
 def parse_query_order(query_filters_order):
96
-    return [    (field, LODEL_SORT_OPERATORS_MAP[direction])
97
-                for field, direction in query_filters_order]
101
+    return [(field, LODEL_SORT_OPERATORS_MAP[direction])
102
+            for field, direction in query_filters_order]

+ 3
- 3
tests/leapi/query/test_get.py View File

5
 import tests.loader_utils
5
 import tests.loader_utils
6
 from tests.leapi.query.utils import dyncode_module as dyncode
6
 from tests.leapi.query.utils import dyncode_module as dyncode
7
 
7
 
8
-from lodel.leapi.leobject import LeApiDataCheckError
9
-from lodel.leapi.query import LeDeleteQuery, LeUpdateQuery, LeGetQuery, LeQueryError
8
+from lodel.leapi.query import LeDeleteQuery, LeUpdateQuery, LeGetQuery
9
+from lodel.leapi.exceptions import LeApiQueryError
10
 
10
 
11
 class LeGetQueryTestCase(unittest.TestCase):
11
 class LeGetQueryTestCase(unittest.TestCase):
12
     
12
     
89
                             ('lodel_id', None,) )
89
                             ('lodel_id', None,) )
90
 
90
 
91
         for bad_field_list in bad_field_lists:
91
         for bad_field_list in bad_field_lists:
92
-            with self.assertRaises(LeQueryError):
92
+            with self.assertRaises(LeApiQueryError):
93
                 LeGetQuery(dyncode.Object, [], field_list = bad_field_list)
93
                 LeGetQuery(dyncode.Object, [], field_list = bad_field_list)
94
 
94
 

Loading…
Cancel
Save