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

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

@@ -7,45 +7,13 @@ from lodel import logger
7 7
 from lodel.settings import Settings
8 8
 from lodel.settings.utils import SettingsError
9 9
 from .query import LeInsertQuery, LeUpdateQuery, LeDeleteQuery, LeGetQuery
10
+from .exceptions import *
10 11
 from lodel.plugin.hooks import LodelHook
11 12
 
12 13
 ##@brief Stores the name of the field present in each LeObject that indicates
13 14
 #the name of LeObject subclass represented by this object
14 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 17
 ##@brief Wrapper class for LeObject getter & setter
50 18
 #
51 19
 # This class intend to provide easy & friendly access to LeObject fields values 
@@ -276,6 +244,12 @@ raised when trying to import Datasource"
276 244
             ds_conf[k] = getattr(ds_conf_old, k)
277 245
 
278 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 254
     ##@brief Read only access to all datas
281 255
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
@@ -290,6 +264,12 @@ raised when trying to import Datasource"
290 264
             raise RuntimeError("The field %s is not initialized yet (and have no value)" % field_name)
291 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 273
     ##@brief Datas setter
294 274
     # @note for fancy data accessor use @ref LeObject.g attribute @ref LeObjectValues instance
295 275
     # @param fname str : field name
@@ -517,7 +497,7 @@ raised when trying to import Datasource"
517 497
             
518 498
         try:
519 499
             result = query.execute(datas)
520
-        except Exception as err;
500
+        except Exception as err:
521 501
             raise err
522 502
 
523 503
         return result

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

@@ -2,36 +2,12 @@
2 2
 
3 3
 import re
4 4
 import copy
5
+import inspect
5 6
 
6
-from .leobject import LeObject, LeApiErrors, LeApiDataCheckError
7
+from .exceptions import *
7 8
 from lodel.plugin.hooks import LodelHook
8 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 12
 ##@todo check datas when running query
37 13
 class LeQuery(object):
@@ -44,6 +20,7 @@ class LeQuery(object):
44 20
     ##@brief Abstract constructor
45 21
     # @param target_class LeObject : class of object the query is about
46 22
     def __init__(self, target_class):
23
+        from .leobject import LeObject
47 24
         if self._hook_prefix is None:
48 25
             raise NotImplementedError("Abstract class")
49 26
         if not issubclass(target_class, LeObject):
@@ -504,41 +481,81 @@ class LeInsertQuery(LeQuery):
504 481
 
505 482
 
506 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 487
 class LeUpdateQuery(LeFilteredQuery):
508 488
     
509 489
     _hook_prefix = 'leapi_update_'
510 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 520
     ##@brief Implements an update query
530 521
     #@param filters list : see @ref LeFilteredQuery
531 522
     #@param rel_filters list : see @ref LeFilteredQuery
532 523
     #@param datas dict : datas to update
533 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 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 552
         return nb_updated
539 553
     
540 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 559
         return super().execute(datas = datas)
543 560
 
544 561
 ##@brief A query to delete an object
@@ -639,7 +656,7 @@ class LeGetQuery(LeFilteredQuery):
639 656
                 err_l[fieldname] =  expt
640 657
         if len(err_l) > 0:
641 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 660
         self.__field_list = list(set(field_list))
644 661
     
645 662
     ##@brief Execute the get query

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

@@ -1,5 +1,4 @@
1
-from lodel.settings.validator import SettingValidator
2
-
1
+from .utils import connect, get_connection_args
3 2
 
4 3
 __loader__ = "main.py"
5 4
 __confspec__ = "confspec.py"
@@ -13,4 +12,13 @@ __fullname__ = "MongoDB plugin"
13 12
 #
14 13
 # @return bool|str : True if all the checks are OK, an error message if not
15 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 24
     return True

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

@@ -11,8 +11,8 @@ import urllib
11 11
 
12 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 17
 class MongoDbDataSourceError(Exception):
18 18
     pass
@@ -77,7 +77,7 @@ class MongoDbDatasource(object):
77 77
     #@param instanciate bool : If true, the records are returned as instances, else they are returned as dict
78 78
     #@return list
79 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 81
         collection_name = object_collection_name(target)
82 82
         collection = self.database[collection_name]
83 83
         query_filters = self.__process_filters(

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

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

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

@@ -49,9 +49,11 @@ def get_connection_args(connnection_name='default'):
49 49
     return connect(host, port, dbname, login, password)
50 50
 '''
51 51
 
52
+
52 53
 def connection_string(host, port, db_name, username, password):
53 54
     return 'mongodb://%s:%s@%s:%s' % (username, password, host, port)
54 55
 
56
+
55 57
 def connect(host, port, db_name, username, password):
56 58
     connection = MongoClient(
57 59
         connection_string(host, port, db_name, username, password))
@@ -59,7 +61,6 @@ def connect(host, port, db_name, username, password):
59 61
     return database
60 62
 
61 63
 
62
-
63 64
 ## @brief Returns a collection name given a EmClass
64 65
 # @param class_object EmClass
65 66
 # @return str
@@ -72,26 +73,30 @@ def object_collection_name(class_object):
72 73
 
73 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 83
 def mongo_fieldname(fieldname):
82 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 94
     return [mongo_fieldname(fname) for fname in fieldnames]
91 95
 
96
+
92 97
 ## @brief Returns a list of orting options
93 98
 # @param query_filters_order list
94 99
 # @return list
95 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,8 +5,8 @@ import itertools
5 5
 import tests.loader_utils
6 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 11
 class LeGetQueryTestCase(unittest.TestCase):
12 12
     
@@ -89,6 +89,6 @@ class LeGetQueryTestCase(unittest.TestCase):
89 89
                             ('lodel_id', None,) )
90 90
 
91 91
         for bad_field_list in bad_field_lists:
92
-            with self.assertRaises(LeQueryError):
92
+            with self.assertRaises(LeApiQueryError):
93 93
                 LeGetQuery(dyncode.Object, [], field_list = bad_field_list)
94 94
 

Loading…
Cancel
Save