Bladeren bron

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

prieto 8 jaren geleden
bovenliggende
commit
2bd1cbdc61

+ 17
- 2
install/loader.py Bestand weergeven

@@ -1,6 +1,6 @@
1 1
 #-*- coding: utf-8 -*-
2 2
 
3
-import sys, os
3
+import sys, os, os.path
4 4
 #
5 5
 # Bootstraping
6 6
 #
@@ -19,7 +19,8 @@ except ImportError:
19 19
 # Loading settings
20 20
 #
21 21
 from lodel.settings.settings import Settings as settings
22
-settings('conf.d')
22
+if not settings.started():
23
+    settings('conf.d')
23 24
 from lodel.settings import Settings
24 25
 
25 26
 #Starts hooks
@@ -36,6 +37,20 @@ def start():
36 37
 
37 38
 if __name__ == '__main__':
38 39
     start()
40
+
41
+    if Settings.runtest:
42
+        import unittest
43
+        import tests
44
+        loader = unittest.TestLoader()
45
+        test_dir = os.path.join(LODEL2_LIB_ABS_PATH, 'tests')
46
+        suite = loader.discover(test_dir)
47
+        print("DEBUG  : failfast  = ", '-f' in sys.argv, sys.argv)
48
+        runner = unittest.TextTestRunner(
49
+            failfast = '-f' in sys.argv,
50
+            verbosity = 2 if '-v' in sys.argv else 1)
51
+        runner.run(suite)
52
+        exit()
53
+
39 54
     LodelHook.call_hook('lodel2_loader_main', '__main__', None)
40 55
     #Run interative python
41 56
     import code

+ 45
- 68
lodel/leapi/leobject.py Bestand weergeven

@@ -26,21 +26,23 @@ class LeObjectValues(object):
26 26
     # @param set_callback method : The LeObject.set_datas() method of corresponding LeObject class
27 27
     # @param get_callback method : The LeObject.get_datas() method of corresponding LeObject class
28 28
     def __init__(self, fieldnames_callback, set_callback, get_callback):
29
-        self.__setter = set_callback
30
-        self.__getter = get_callback
29
+        self._setter = set_callback
30
+        self._getter = get_callback
31 31
     
32 32
     ##@brief Provide read access to datas values
33 33
     # @note Read access should be provided for all fields
34 34
     # @param fname str : Field name
35 35
     def __getattribute__(self, fname):
36
-        return self.__getter(fname)
36
+        getter = super().__getattribute__('_getter')
37
+        return getter(fname)
37 38
     
38 39
     ##@brief Provide write access to datas values
39 40
     # @note Write acces shouldn't be provided for internal or immutable fields
40 41
     # @param fname str : Field name
41 42
     # @param fval * : the field value
42 43
     def __setattribute__(self, fname, fval):
43
-        return self.__setter(fname, fval)
44
+        setter = super().__getattribute__('_setter')
45
+        return setter(fname, fval)
44 46
         
45 47
 
46 48
 class LeObject(object):
@@ -56,17 +58,26 @@ class LeObject(object):
56 58
     ##@breif Read & write datasource ( see @ref lodel2_datasources )
57 59
     _rw_datasource = None
58 60
 
59
-    ##@brief Construct an object representing an Editorial component
60
-    # @note Can be considered as EmClass instance
61
-    def __init__(self, **kwargs):
62
-        if self._abstract:
63
-            raise NotImplementedError("%s is abstract, you cannot instanciate it." % self.__class__.__name__ )
61
+    def __new__(cls, **kwargs):
62
+        
63
+        self = object.__new__(cls)
64 64
         ##@brief A dict that stores fieldvalues indexed by fieldname
65 65
         self.__datas = { fname:None for fname in self._fields }
66 66
         ##@brief Store a list of initianilized fields when instanciation not complete else store True
67 67
         self.__initialized = list()
68 68
         ##@brief Datas accessor. Instance of @ref LeObjectValues
69 69
         self.d = LeObjectValues(self.fieldnames, self.set_data, self.data)
70
+        for fieldname, fieldval in kwargs.items():
71
+            self.__datas[fieldname] = fieldval
72
+            self.__initialized.append(fieldname)
73
+        self.__set_initialized()
74
+        return self
75
+
76
+    ##@brief Construct an object representing an Editorial component
77
+    # @note Can be considered as EmClass instance
78
+    def __init__(self, **kwargs):
79
+        if self._abstract:
80
+            raise NotImplementedError("%s is abstract, you cannot instanciate it." % self.__class__.__name__ )
70 81
 
71 82
         # Checks that uid is given
72 83
         for uid_name in self._uid:
@@ -75,25 +86,24 @@ class LeObject(object):
75 86
             self.__datas[uid_name] = kwargs[uid_name]
76 87
             del(kwargs[uid_name])
77 88
             self.__initialized.append(uid_name)
78
-        
89
+
79 90
         # Processing given fields
80 91
         allowed_fieldnames = self.fieldnames(include_ro = False)
81
-        err_list = list()
92
+        err_list = dict()
82 93
         for fieldname, fieldval in kwargs.items():
83 94
             if fieldname not in allowed_fieldnames:
84 95
                 if fieldname in self._fields:
85
-                    err_list.append(
86
-                        LeApiError("Value given for internal field : '%s'" % fieldname)
87
-                    )
96
+                    err_list[fieldname] = LeApiError(
97
+                        "Value given but the field is internal")
88 98
                 else:
89
-                    err_list.append(
90
-                        LeApiError("Unknown fieldname : '%s'" % fieldname)
91
-                    )
99
+                    err_list[fieldname] = LeApiError(
100
+                        "Unknown fieldname : '%s'" % fieldname)
92 101
             else:
93 102
                 self.__datas[fieldname] = fieldval
94 103
                 self.__initialized.append(fieldname)
95 104
         if len(err_list) > 0:
96
-            raise LeApiErrors(err_list)
105
+            raise LeApiErrors(msg = "Unable to __init__ %s" % self.__class__,
106
+                exceptions = err_list)
97 107
         self.__set_initialized()
98 108
     
99 109
     #-----------------------------------#
@@ -268,8 +278,8 @@ raised when trying to import Datasource"
268 278
     
269 279
     ##@brief Read only access to all datas
270 280
     #@return a dict representing datas of current instance
271
-    def datas(self):
272
-        return [self.data(fname) for fname in self.fieldnames(True)]
281
+    def datas(self, internal = False):
282
+        return {fname:self.data(fname) for fname in self.fieldnames(internal)}
273 283
         
274 284
     
275 285
     ##@brief Datas setter
@@ -485,7 +495,7 @@ raised when trying to import Datasource"
485 495
     ## @brief Update an instance of LeObject
486 496
     #
487 497
     #@param datas : list of new datas 
488
-    def update(self, datas):
498
+    def update(self, datas = None):
489 499
         datas = self.datas(internal=False) if datas is None else datas
490 500
         uids = self._uid
491 501
         query_filter = list()
@@ -493,7 +503,7 @@ raised when trying to import Datasource"
493 503
             query_filter.append((uid, '=', self.data(uid)))
494 504
         
495 505
         try:
496
-            query = LeUpdateQuery(cls, query_filter)
506
+            query = LeUpdateQuery(self.__class__, query_filter)
497 507
         except Exception as err:
498 508
             raise err
499 509
             
@@ -538,43 +548,6 @@ raised when trying to import Datasource"
538 548
             deleted += result
539 549
         return deleted
540 550
             
541
-    
542
-    ## @brief Load an instance of LeObject
543
-    #@param uid a list of tuple (uid_field_nane, value) ou a single value
544
-    #@return an instance of a subclass of LeObject
545
-    @classmethod
546
-    def load(cls, uid_tuples):
547
-        query_filter = list()
548
-        uids = cls._uid
549
-        if uids.isinstance(tuple):
550
-            if not uid_tuples.isinstance(list):
551
-                raise AttributeError ("In %s:load : uid must be a list of tuple" % cls.__name__)
552
-            elif len(uid_tuples) != len(uids):
553
-                raise AttributeError ("In %s:load : must have %d uid fields" % len(uids))
554
-            for fieldname, fieldvalue in uid_tuples:
555
-                if fieldname in uids:
556
-                    dhdl = cls.data_handler(fieldname)
557
-                    if dhdl.check_data_value(fieldvalue)[1] is None:
558
-                        query_filter.append((fieldname, '=', fieldvalue))
559
-                    else:
560
-                        raise AttributeError("n %s:load :%s not a valid value for %s" % (fieldvalue, fieldname))
561
-                else:
562
-                    raise AttributeError ("In %s:load :%s not a uid field for class %s" % (fieldname, cls.__name__))
563
-        else:
564
-            dhdl = cls.data_handler(uids)
565
-            if dhdl.check_data_value(uid_tuples)[1] is None:
566
-                query_filter.append((uids, '=', uid_tuples))
567
-            else:
568
-                raise AttributeError("n %s:load :%s not a valid value for %s" % (uid_tuples, uids))
569
-        query = LeGetQuery(cls, query_filter, limit = 1)
570
-        try:
571
-            result=query.execute()
572
-        except LeQueryError as err:
573
-            print("Unable to load object of type %s" % cls.__name__)
574
-            raise err
575
-        
576
-        return cls.name2class(res[CLASS_IDENTIFIER])(result[0])
577
-        
578 551
     ## @brief Get instances of LeObject
579 552
     #
580 553
     #@param target_class LeObject : class of object the query is about
@@ -590,16 +563,19 @@ raised when trying to import Datasource"
590 563
     #@return a list of items (lists of (fieldname, fieldvalue))
591 564
     @classmethod
592 565
     def get(cls, query_filters, field_list=None, order=None, group=None, limit=None, offset=0):
593
-        if isinstance(cls._uids, tuple):
594
-            for uid in cls._uids:
595
-                if uid not in field_list:
596
-                    raise AttributeError("In %s:get : Cannot instanciate a LeObject without it's identifier" % cls.__name__)
566
+        if field_list is None:
567
+            field_list = cls.fieldnames(True)
597 568
         else:
598
-            if uid not in field_list:
599
-                raise AttributeError("In %s:get : Cannot instanciate a LeObject without it's identifier" % cls.__name__)
600
-
569
+            for uid in [ uidname
570
+                for uidname in cls.uid_fieldname()
571
+                if uidname not in field_list ]:
572
+                field_list.append(uid)
573
+            if CLASS_ID_FIELDNAME not in field_list:
574
+                field_list.append(CLASS_ID_FIELDNAME)
601 575
         try:
602
-            query = LeGetQuery(cls, query_filter, field_list = field_list, order = order, group = group, limit = limit, offset = offset)
576
+            query = LeGetQuery(
577
+                cls, query_filters = query_filters, field_list = field_list,
578
+                order = order, group = group, limit = limit, offset = offset)
603 579
         except ValueError as err:
604 580
             raise err
605 581
             
@@ -610,7 +586,8 @@ raised when trying to import Datasource"
610 586
         
611 587
         objects = list()
612 588
         for res in result:
613
-            inst = cls.name2class(res[CLASS_ID_FIELDNAME])(res)
589
+            res_cls = cls.name2class(res[CLASS_ID_FIELDNAME])
590
+            inst = res_cls.__new__(res_cls,**res)
614 591
             objects.append(inst)
615 592
         
616 593
         return objects

+ 18
- 17
lodel/leapi/query.py Bestand weergeven

@@ -3,6 +3,7 @@
3 3
 import re
4 4
 import copy
5 5
 import inspect
6
+import warnings
6 7
 
7 8
 from .exceptions import *
8 9
 from lodel.plugin.hooks import LodelHook
@@ -35,18 +36,18 @@ class LeQuery(object):
35 36
     # @return the query result
36 37
     # @see LeQuery._query()
37 38
     #
38
-    def execute(self, **datas):
39
+    def execute(self, datas):
39 40
         if not datas is None:
40 41
             self._target_class.check_datas_value(
41
-                                                    datas['datas'],
42
+                                                    datas,
42 43
                                                     **self._data_check_args)
43
-            self._target_class.prepare_datas(datas['datas']) #not yet implemented
44
+            self._target_class.prepare_datas(datas) #not yet implemented
44 45
         if self._hook_prefix is None:
45 46
             raise NotImplementedError("Abstract method")
46 47
         LodelHook.call_hook(    self._hook_prefix+'_pre',
47 48
                                 self._target_class,
48 49
                                 datas)
49
-        ret = self._query(**datas)
50
+        ret = self._query(datas = datas)
50 51
         ret = LodelHook.call_hook(  self._hook_prefix+'_post',
51 52
                                     self._target_class,
52 53
                                     ret)
@@ -119,7 +120,6 @@ class LeFilteredQuery(LeQuery):
119 120
         try:
120 121
 
121 122
             filters, rel_filters = self._query_filter
122
-            #res = super().execute(filters = filters, rel_filters = rel_filters)
123 123
             res = super().execute(datas)
124 124
         except Exception as e:
125 125
             #restoring filters even if an exception is raised
@@ -199,7 +199,7 @@ class LeFilteredQuery(LeQuery):
199 199
                 for tclass, tfield in ref_dict.items():
200 200
                     query = LeGetQuery(
201 201
                         target_class = tclass,
202
-                        query_filter = [(tfield, op, value)],
202
+                        query_filters = [(tfield, op, value)],
203 203
                         field_list = [tfield])
204 204
                     subq.append((rfield, query))
205 205
         self.subqueries = subq
@@ -213,7 +213,7 @@ class LeFilteredQuery(LeQuery):
213 213
 
214 214
     def __repr__(self):
215 215
         res = "<{classname} target={target_class} query_filter={query_filter}"
216
-        res = ret.format(
216
+        res = res.format(
217 217
             classname=self.__class__.__name__,
218 218
             query_filter = self._query_filter,
219 219
             target_class = self._target_class)
@@ -465,8 +465,8 @@ class LeInsertQuery(LeQuery):
465 465
     def _query(self, datas):
466 466
         datas = self._target_class.prepare_datas(datas, True, False)
467 467
         nb_inserted = self._rw_datasource.insert(self._target_class,datas)
468
-        if nb_inserted < 0:
469
-            raise LeQueryError("Insertion error")
468
+        if nb_inserted <= 0:
469
+            raise LeApiQueryError("Insertion error")
470 470
         return nb_inserted
471 471
     """
472 472
     ## @brief Implements an insert query operation, with multiple insertions
@@ -475,7 +475,7 @@ class LeInsertQuery(LeQuery):
475 475
         nb_inserted = self._datasource.insert_multi(
476 476
             self._target_class,datas_list)
477 477
         if nb_inserted < 0:
478
-            raise LeQueryError("Multiple insertions error")
478
+            raise LeApiQueryError("Multiple insertions error")
479 479
         return nb_inserted
480 480
     """
481 481
 
@@ -571,14 +571,15 @@ class LeDeleteQuery(LeFilteredQuery):
571 571
         super().__init__(target_class, query_filter)
572 572
 
573 573
     ## @brief Execute the delete query
574
-    def execute(self):
574
+    def execute(self, datas = None):
575 575
         return super().execute()
576 576
     
577 577
     ##@brief Implements delete query operations
578 578
     #@param filters list : see @ref LeFilteredQuery
579 579
     #@param rel_filters list : see @ref LeFilteredQuery
580 580
     #@returns the number of deleted items
581
-    def _query(self, filters, rel_filters):
581
+    def _query(self, datas = None):
582
+        filters, rel_filters = self._query_filter
582 583
         nb_deleted = self._rw_datasource.delete(
583 584
             self._target_class, filters, rel_filters)
584 585
         return nb_deleted
@@ -597,8 +598,8 @@ class LeGetQuery(LeFilteredQuery):
597 598
     #@param group list : A list of field names or tuple (FIELDNAME,[ASC | DESC])
598 599
     #@param limit int : The maximum number of returned results
599 600
     #@param offset int : offset
600
-    def __init__(self, target_class, query_filter, **kwargs):
601
-        super().__init__(target_class, query_filter)
601
+    def __init__(self, target_class, query_filters, **kwargs):
602
+        super().__init__(target_class, query_filters)
602 603
         
603 604
         ##@brief The fields to get
604 605
         self.__field_list = None
@@ -630,7 +631,7 @@ class LeGetQuery(LeFilteredQuery):
630 631
             self.__group = kwargs['group']
631 632
         if 'limit' in kwargs:
632 633
             try:
633
-                self.__limit = int(kwargs[limit])
634
+                self.__limit = int(kwargs['limit'])
634 635
                 if self.__limit <= 0:
635 636
                     raise ValueError()
636 637
             except ValueError:
@@ -648,7 +649,7 @@ class LeGetQuery(LeFilteredQuery):
648 649
     ##@brief Set the field list
649 650
     # @param field_list list | None : If None use all fields
650 651
     # @return None
651
-    # @throw LeQueryError if unknown field given
652
+    # @throw LeApiQueryError if unknown field given
652 653
     def set_field_list(self, field_list):
653 654
         err_l = dict()
654 655
         for fieldname in field_list:
@@ -664,7 +665,7 @@ class LeGetQuery(LeFilteredQuery):
664 665
         self.__field_list = list(set(field_list))
665 666
     
666 667
     ##@brief Execute the get query
667
-    def execute(self):
668
+    def execute(self, datas = None):
668 669
         return super().execute()
669 670
 
670 671
     ##@brief Implements select query operations

+ 2
- 0
lodel/settings/validator.py Bestand weergeven

@@ -308,6 +308,8 @@ LODEL2_CONF_SPECS = {
308 308
                         SettingValidator('strip')),
309 309
         'lib_path': (   None,
310 310
                         SettingValidator('path')),
311
+        'runtest': (    False,
312
+                        SettingValidator('bool')),
311 313
     },
312 314
     'lodel2.logging.*' : {
313 315
         'level': (  'ERROR',

+ 4
- 4
plugins/dummy_datasource/datasource.py Bestand weergeven

@@ -27,7 +27,7 @@ class DummyDatasource(object):
27 27
     #@param relational_filters list : List of relational filters
28 28
     #@return int : number of deleted records
29 29
     def delete(self, target, filters, relational_filters):
30
-        pass
30
+        return 0
31 31
 
32 32
     ## @brief updates records according to given filters
33 33
     #@param target Emclass : class of the object to insert
@@ -36,18 +36,18 @@ class DummyDatasource(object):
36 36
     #@param upd_datas dict : datas to update (new values)
37 37
     #@return int : Number of updated records
38 38
     def update(self, target, filters, relational_filters, upd_datas):
39
-        pass
39
+        return 0
40 40
 
41 41
     ## @brief Inserts a record in a given collection
42 42
     # @param target Emclass : class of the object to insert
43 43
     # @param new_datas dict : datas to insert
44 44
     # @return the inserted uid
45 45
     def insert(self, target, new_datas):
46
-        pass
46
+        return 0
47 47
 
48 48
     ## @brief Inserts a list of records in a given collection
49 49
     # @param target Emclass : class of the objects inserted
50 50
     # @param datas_list list : list of dict
51 51
     # @return list : list of the inserted records' ids
52 52
     def insert_multi(self, target, datas_list):
53
-        pass
53
+        return 0

+ 24
- 9
plugins/mongodb_datasource/datasource.py Bestand weergeven

@@ -2,16 +2,15 @@
2 2
 
3 3
 import re
4 4
 import warnings
5
-import bson
6 5
 from bson.son import SON
7 6
 from collections import OrderedDict
8 7
 import pymongo
9 8
 from pymongo.errors import BulkWriteError
10
-import urllib
11 9
 
12 10
 from lodel import logger
13 11
 
14
-from .utils import object_collection_name, connect, \
12
+from . import utils
13
+from .utils import object_collection_name,\
15 14
     MONGODB_SORT_OPERATORS_MAP, connection_string
16 15
 
17 16
 class MongoDbDataSourceError(Exception):
@@ -51,8 +50,9 @@ class MongoDbDatasource(object):
51 50
         self.__read_only = bool(read_only)
52 51
         ##@brief Uniq ID for mongodb connection
53 52
         self.__conn_hash= None
54
-        ##@brief Stores the connection to MongoDB
55
-        self.database = self.__connect(username, password)
53
+        ##@brief Stores the database cursor
54
+        self.database = self.__connect(
55
+            username, password, ro = self.__read_only)
56 56
 
57 57
     ##@brief Destructor that attempt to close connection to DB
58 58
     #
@@ -64,6 +64,7 @@ class MongoDbDatasource(object):
64 64
         if self._connections[self.__conn_hash]['conn_count'] <= 0:
65 65
             self._connections[self.__conn_hash]['db'].close()
66 66
             del(self._connections[self.__conn_hash])
67
+            logger.info("Closing connection to database")
67 68
 
68 69
     ##@brief returns a selection of documents from the datasource
69 70
     #@param target Emclass
@@ -176,12 +177,26 @@ class MongoDbDatasource(object):
176 177
     #@param ro bool : If True the Datasource is for read only, else the
177 178
     def __connect(self, username, password, ro):
178 179
         conn_string = connection_string(
179
-            username = username, password = password, **self.__db_infos)
180
-        conn_string += "__ReadOnly__:"+self.__read_only
181
-        self.__conf_hash = conn_h = hash(conn_string)
180
+            username = username, password = password,
181
+            host = self.__db_infos['host'],
182
+            port = self.__db_infos['port'])
183
+
184
+        conn_string += "__ReadOnly__:"+str(self.__read_only)
185
+        self.__conn_hash = conn_h = hash(conn_string)
182 186
         if conn_h in self._connections:
183 187
             self._connections[conn_h]['conn_count'] += 1
184
-            return self._connections[conn_h]['db']
188
+            return self._connections[conn_h]['db'][self.__db_infos['db_name']]
189
+        else:
190
+            logger.info("Opening a new connection to database")
191
+            self._connections[conn_h] = {
192
+                'conn_count': 1,
193
+                'db': utils.connection(
194
+                    host = self.__db_infos['host'],
195
+                    port = self.__db_infos['port'],
196
+                    username = username, 
197
+                    password = password)}
198
+            return self._connections[conn_h]['db'][self.__db_infos['db_name']]
199
+                    
185 200
 
186 201
     ##@brief Return a pymongo collection given a LeObject child class
187 202
     #@param leobject LeObject child class (no instance)

+ 19
- 7
plugins/mongodb_datasource/migration_handler.py Bestand weergeven

@@ -2,7 +2,7 @@
2 2
 import datetime
3 3
 
4 4
 from lodel.editorial_model.components import EmClass, EmField
5
-
5
+from lodel.editorial_model.model import EditorialModel
6 6
 from .utils import get_connection_args, connect, collection_prefix, object_collection_name, mongo_fieldname
7 7
 from lodel.leapi.datahandlers.base_classes import DataHandler
8 8
 from lodel.plugin import LodelHook
@@ -16,7 +16,7 @@ class MigrationHandlerError(Exception):
16 16
     pass
17 17
 
18 18
 @LodelHook('mongodb_mh_init_db')
19
-def mongodb_mh_init_db(conn_args=None):
19
+def mongodb_mh_init_db(editorial_model, conn_args=None):
20 20
     connection_args = get_connection_args('default') if conn_args is None else get_connection_args(conn_args['name'])
21 21
     migration_handler = MongoDbMigrationHandler(connection_args)
22 22
     migration_handler._install_collections()
@@ -26,13 +26,15 @@ class MongoDbMigrationHandler(object):
26 26
 
27 27
     COMMANDS_IFEXISTS_DROP = 'drop'
28 28
     COMMANDS_IFEXISTS_NOTHING = 'nothing'
29
-    INIT_COLLECTIONS_NAMES = ['object', 'relation', 'entitie', 'person', 'text', 'entry']
29
+    #INIT_COLLECTIONS_NAMES = ['object', 'relation', 'entitie', 'person', 'text', 'entry']
30 30
     MIGRATION_HANDLER_DEFAULT_SETTINGS = {'dry_run': False, 'foreign_keys': True, 'drop_if_exists': False}
31 31
 
32 32
     ## @brief Constructs a MongoDbMigrationHandler
33 33
     # @param conn_args dict : a dictionary containing the connection options
34 34
     # @param **kwargs : extra arguments
35
-    def __init__(self, conn_args=None, **kwargs):
35
+    def __init__(self, editorial_model, conn_args=None, **kwargs):
36
+        self.editorial_model = editorial_model
37
+
36 38
         conn_args = get_connection_args() if conn_args is None else conn_args
37 39
 
38 40
         if len(conn_args.keys()) == 0:
@@ -55,11 +57,21 @@ class MongoDbMigrationHandler(object):
55 57
         self.drop_if_exists = kwargs['drop_if_exists'] if 'drop_is_exists' in kwargs else \
56 58
             MongoDbMigrationHandler.MIGRATION_HANDLER_DEFAULT_SETTINGS['drop_if_exists']
57 59
 
60
+        self.init_collections_names = self._set_init_collection_names()
58 61
         #self._install_collections()
59 62
 
63
+    def _set_init_collection_names(self):
64
+        collection_names = []
65
+        init_collections = self.editorial_model.all_classes()
66
+        for init_collection in init_collections.items():
67
+            if init_collection.abstract:
68
+                collection_names.append(init_collection.uid)
69
+        return collection_names
70
+
60 71
     ## @brief Installs the basis collections of the database
61 72
     def _install_collections(self):
62
-        for collection_name in MongoDbMigrationHandler.INIT_COLLECTIONS_NAMES:
73
+        init_collection_names = self.init_collections_names
74
+        for collection_name in init_collection_names:
63 75
             prefix = collection_prefix['object'] if collection_name != 'relation' else collection_prefix['relation']
64 76
             collection_to_create = "%s%s" % (prefix, collection_name)
65 77
             self._create_collection(collection_name=collection_to_create)
@@ -110,7 +122,7 @@ class MongoDbMigrationHandler(object):
110 122
             component_class_name = 'emfield'
111 123
 
112 124
         if component_class_name:
113
-            handler_func('_'+component_class_name.lower()+'_'+state_change)
125
+            handler_func = '_'+component_class_name.lower()+'_'+state_change
114 126
             if hasattr(self, handler_func):
115 127
                 getattr(self, handler_func)(model, uid, initial_state, new_state)
116 128
         else:
@@ -129,7 +141,7 @@ class MongoDbMigrationHandler(object):
129 141
     ## @brief deletes a collection corresponding to a given uid
130 142
     # @see register_change()
131 143
     def _emclass_delete(self, model, uid, initial_state, new_state):
132
-        if uid not in MongoDbMigrationHandler.INIT_COLLECTIONS_NAMES:
144
+        if uid not in self.init_collections_names:
133 145
             collection_name = object_collection_name(model.classes(uid))
134 146
             self._delete_collection(collection_name)
135 147
 

+ 29
- 16
plugins/mongodb_datasource/utils.py Bestand weergeven

@@ -41,23 +41,36 @@ def get_connection_args(connnection_name='default'):
41 41
     return {'host': 'localhost', 'port': 28015, 'login': 'lodel_admin', 'password': 'lapwd', 'dbname': 'lodel'}
42 42
 
43 43
 
44
-## @brief Creates a connection to a MongoDb Database
45
-# @param connection_name str
46
-# @return MongoClient
47
-'''def mongodbconnect(connection_name):
48
-    login, password, host, port, dbname = get_connection_args(connection_name)
49
-    return connect(host, port, dbname, login, password)
50
-'''
51
-
52
-
53
-def connection_string(host, port, db_name, username, password):
54
-    return 'mongodb://%s:%s@%s:%s' % (username, password, host, port)
55
-
56
-
44
+def connection_string(host, port, username, password):
45
+    ret = 'mongodb://'
46
+    if username != None:
47
+        ret += username
48
+        if password != None:
49
+            ret += ':'+password
50
+        ret+='@'
51
+    elif password != None:
52
+        raise RuntimeError("Password given but no username given...")
53
+    host = 'localhost' if host is None else host
54
+    ret += host
55
+    if port != None:
56
+        ret += ':'+str(port)
57
+    return ret
58
+
59
+##@brief Return an instanciated MongoClient
60
+#@param host str : hostname or ip
61
+#@param port int : port
62
+#@param username str | None: username
63
+#@param password str|None : password
57 64
 def connection(host, port, username, password):
58
-    return MongoClient(connection_string(host, port, '', username, password))
59
-
60
-
65
+    conn_str = connection_string(host, port, username, password)
66
+    return MongoClient(conn_str)
67
+
68
+##@brief Return a database cursor
69
+#@param host str : hostname or ip
70
+#@param port int : port
71
+#@param db_name str : database name
72
+#@param username str | None: username
73
+#@param password str|None : password
61 74
 def connect(host, port, db_name, username, password):
62 75
     conn = connection(host, port, username, password)
63 76
     database = conn[db_name]

+ 1
- 21
plugins/webui/__init__.py Bestand weergeven

@@ -1,22 +1,2 @@
1
-from lodel.settings.validator import SettingValidator
2
-
3 1
 __loader__ = 'main.py'
4
-
5
-CONFSPEC = {
6
-    'lodel2.webui': {
7
-        'standalone': ( False,
8
-                        SettingValidator('bool')),
9
-        'listen_address': ( '127.0.0.1',
10
-                            SettingValidator('dummy')),
11
-        'listen_port': (    '9090',
12
-                            SettingValidator('int')),
13
-    },
14
-    'lodel2.webui.sessions': {
15
-        'directory': (  '/tmp/lodel2_session',
16
-                        SettingValidator('path')),
17
-        'expiration': ( 900,
18
-                        SettingValidator('int')),
19
-        'file_template': (  'lodel2_%s.sess',
20
-                            SettingValidator('dummy')),
21
-    }
22
-}
2
+__confspec__ = 'confspec.py'

+ 20
- 0
plugins/webui/confspec.py Bestand weergeven

@@ -0,0 +1,20 @@
1
+from lodel.settings.validator import SettingValidator
2
+
3
+CONFSPEC = {
4
+    'lodel2.webui': {
5
+        'standalone': ( False,
6
+                        SettingValidator('bool')),
7
+        'listen_address': ( '127.0.0.1',
8
+                            SettingValidator('dummy')),
9
+        'listen_port': (    '9090',
10
+                            SettingValidator('int')),
11
+    },
12
+    'lodel2.webui.sessions': {
13
+        'directory': (  '/tmp/lodel2_session',
14
+                        SettingValidator('path')),
15
+        'expiration': ( 900,
16
+                        SettingValidator('int')),
17
+        'file_template': (  'lodel2_%s.sess',
18
+                            SettingValidator('dummy')),
19
+    }
20
+}

+ 14
- 5
plugins/webui/interface/controllers.py Bestand weergeven

@@ -1,26 +1,35 @@
1 1
 # -*- coding: utf-8 -*-
2
-import os.path
3 2
 
4 3
 from werkzeug.wrappers import Response
5 4
 from .template.loader import TemplateLoader
6 5
 
7 6
 # This module contains the web UI controllers that will be called from the web ui class
8 7
 
9
-def get_response(tpl, mimetype = 'text/html', status_code = 200):
8
+
9
+def get_response(tpl, mimetype='text/html', status_code=200):
10 10
     loader = TemplateLoader()
11
-    response = Response(    loader.render_to_response(tpl),
12
-                            mimetype = 'text/html')
11
+    response = Response(loader.render_to_response(tpl), mimetype=mimetype)
13 12
     response.status_code = status_code
14 13
     return response
15 14
 
15
+
16 16
 def admin(request):
17 17
     return get_response('admin/admin.html')
18 18
 
19
+
19 20
 def index(request):
20 21
     return get_response('index/index.html')
21 22
 
23
+
22 24
 def not_found(request):
23
-    return get_response('errors/404.html', status_code = 404)
25
+    return get_response('errors/404.html', status_code=404)
26
+
24 27
 
25 28
 def test(request):
26 29
     return get_response('test.html')
30
+
31
+
32
+def list_classes(request):
33
+    # TODO Add the method to get the classes list
34
+
35
+    return get_response('list_classes.html')

+ 15
- 1
plugins/webui/interface/router.py Bestand weergeven

@@ -3,12 +3,26 @@ import re
3 3
 
4 4
 from .controllers import *
5 5
 from .urls import urls
6
+from lodel.settings import Settings
7
+
8
+
9
+def format_url_rule(url_rule):
10
+    if url_rule == '^$':
11
+        return "^%s$" % Settings.sitename
12
+
13
+    formatted_rule = ''
14
+    if url_rule.startswith('^'):
15
+        formatted_rule += "^"
16
+
17
+    formatted_rule += "%s/%s" % (Settings.sitename, url_rule)
18
+    return formatted_rule
6 19
 
7 20
 
8 21
 def get_controller(request):
22
+
9 23
     url_rules = []
10 24
     for url in urls:
11
-        url_rules.append((url[0], url[1]))
25
+        url_rules.append((format_url_rule(url[0]), url[1]))
12 26
 
13 27
     # Returning the right controller to call
14 28
     for regex, callback in url_rules:

+ 2
- 2
plugins/webui/templates/errors/404.html Bestand weergeven

@@ -1,5 +1,5 @@
1
-{% extends "templates/base.html" %}
1
+{% extends "base.html" %}
2 2
 {% block title %}Error 404{% endblock %}
3 3
 {% block content %}
4
-    <h1>404 - File Not Found</h1>
4
+    <h3>404 - File Not Found</h3>
5 5
 {% endblock %}

+ 3
- 1
runtest Bestand weergeven

@@ -53,6 +53,8 @@ cp -R examples $testdir
53 53
 cp -R tests $testdir
54 54
 cd $testdir
55 55
 rm -R conf.d && mv tests/tests_conf.d conf.d
56
-python3 -W ignore -m unittest $@
56
+make
57
+#python3 -W ignore -m unittest $@
58
+python3 loader.py $@
57 59
 
58 60
 rm -Rf $testdir

+ 0
- 4
tests/__init__.py Bestand weergeven

@@ -1,4 +0,0 @@
1
-try:
2
-    import loader
3
-except ImportError:
4
-    print("This tests are designed to be runned with the runtest utility")

+ 76
- 10
tests/leapi/test_leobject.py Bestand weergeven

@@ -3,7 +3,7 @@ from unittest import mock
3 3
 from unittest.mock import patch
4 4
 
5 5
 import tests.loader_utils
6
-from tests.leapi.query.utils import dyncode_module as dyncode
6
+import leapi_dyncode as dyncode
7 7
 
8 8
 from lodel.leapi.leobject import LeObject
9 9
 from lodel.leapi.query import LeDeleteQuery, LeUpdateQuery, LeGetQuery, \
@@ -88,10 +88,6 @@ but invalid name %s was given" % badname):
88 88
         self.assertEqual(set(fnames),
89 89
             {'lastname', 'linked_texts', 'firstname', 'alias'})
90 90
 
91
-    def test_insert(self):
92
-        """ Testing insert method """
93
-        dyncode.Person.insert({'lastname': 'foo', 'firstname': 'bar'})
94
-    
95 91
     def test_bad_insert(self):
96 92
         """ Insert with bad arguments """
97 93
         badargs = [
@@ -154,8 +150,7 @@ class LeObjectQueryMockTestCase(unittest.TestCase):
154 150
                 lodel_id = 1, firstname = "foo", lastname = "bar")
155 151
             ret = inst.delete()
156 152
             self.assertEqual(ret, 1, 'Bad return value forwarding')
157
-            mock_execute.assert_called_once_with(
158
-                dyncode.Person, [('lodel_id', '=', 1)], [])
153
+            mock_execute.assert_called_once_with()
159 154
 
160 155
     def test_delete_bundle(self):
161 156
         """ Checking that LeObject delete_bundle method calls LeDeleteQuery
@@ -171,9 +166,80 @@ class LeObjectQueryMockTestCase(unittest.TestCase):
171 166
                 dyncode.Person, ['lodel_id > 1'])
172 167
 
173 168
         with patch.object(
174
-            LeDeleteQuery, 'execute', return_value = None) as mock_execute:
169
+            LeDeleteQuery, 'execute', return_value = 1) as mock_execute:
175 170
             
176 171
             dyncode.Person.delete_bundle(['lodel_id > 1'])
177
-            mock_init.assert_called_once_with(
178
-                dyncode.Person, [('lodel_id', '>', 1)], [])
172
+            mock_execute.assert_called_once_with()
179 173
 
174
+    def test_update_instance(self):
175
+        """ Checking that LeObject update method calls LeUpdateQuery
176
+            correctly """
177
+        with patch.object(
178
+            LeUpdateQuery, '__init__', return_value = None) as mock_init:
179
+            with patch.object(
180
+                LeObject, 'datas', return_value = {
181
+                    'lodel_id': 1, 'firstname': 'foo', 'lastname': 'bar',
182
+                    'fullname': 'Foo Bar', 'alias': None }) as mock_datas:
183
+            
184
+                inst = dyncode.Person(
185
+                    lodel_id = 1, firstname = "foo", lastname = "bar")
186
+                try:
187
+                   inst.update()
188
+                except AttributeError:
189
+                    pass
190
+                mock_init.assert_called_once_with(
191
+                    dyncode.Person, [('lodel_id', '=', 1)])
192
+        
193
+        with patch.object(
194
+            LeUpdateQuery, 'execute', return_value = None) as mock_update:
195
+            with patch.object(
196
+                LeObject, 'datas', return_value = {
197
+                    'lodel_id': 1, 'firstname': 'foo', 'lastname': 'bar',
198
+                    'fullname': 'Foo Bar', 'alias': None }) as mock_datas:
199
+            
200
+                inst = dyncode.Person(
201
+                    lodel_id = 1, firstname = "foo", lastname = "bar")
202
+                inst.update()
203
+                mock_update.assert_called_once_with({
204
+                    'lodel_id': 1, 'firstname': 'foo', 'lastname': 'bar',
205
+                    'fullname': 'Foo Bar', 'alias': None })
206
+                    
207
+    
208
+    def test_get(self):
209
+        """ Checking that LeObject.get method calls LeGetQuery
210
+            correctly """
211
+        get_args = {
212
+            'query_filters': ['lodel_id = 1'],
213
+            'field_list': ['firstname'],
214
+            'order': ['firstname'],
215
+            'group': ['alias'],
216
+            'limit': 42,
217
+            'offset': 24}
218
+
219
+        with patch.object(
220
+            LeGetQuery, '__init__', return_value = None) as mock_init:
221
+            
222
+            try:
223
+                dyncode.Person.get(**get_args)
224
+            except AttributeError:
225
+                pass
226
+
227
+            mock_init.assert_called_once_with(
228
+                dyncode.Person,
229
+                **get_args)
230
+
231
+        ret_val = [{
232
+            'lodel_id': 1,
233
+            'firstname': 'foo',
234
+            'lastname': 'bar',
235
+            'fullname': 'foo bar',
236
+            'alias': None,
237
+            'classname': 'Person'}]
238
+        with patch.object(
239
+            LeGetQuery, 'execute', return_value = ret_val) as mock_execute:
240
+            results = dyncode.Person.get(**get_args)
241
+            mock_execute.assert_called_once_with()
242
+            res = results[0]
243
+            self.assertEqual(res.d.lodel_id, 1)
244
+            self.assertEqual(res.d.firstname, 'foo')
245
+            self.assertEqual(res.d.lastname, 'bar')

+ 1
- 0
tests/tests_conf.d/lodel2.ini Bestand weergeven

@@ -3,6 +3,7 @@ debug = False
3 3
 sitename = noname
4 4
 plugins_path = /foo/plugins
5 5
 plugins = dummy, dummy_datasource
6
+runtest=True
6 7
 
7 8
 [lodel2.logging.stderr]
8 9
 level = Error

Loading…
Annuleren
Opslaan