Kaynağa Gözat

Begin editorial_model package implementation + tests

Yann Weber 8 yıl önce
ebeveyn
işleme
ba28ec26c0

+ 105
- 0
lodel/editorial_model/component.py Dosyayı Görüntüle

@@ -0,0 +1,105 @@
1
+#-*- coding: utf-8 -*-
2
+
3
+import itertools
4
+
5
+from lodel.utils.mlstring import MlString
6
+
7
+from lodel.editorial_model.exceptions import *
8
+
9
+## @brief Abstract class to represent editorial model components
10
+# @see EmClass EmField
11
+class EmComponent(object):
12
+    
13
+    ## @brief Instanciate an EmComponent
14
+    # @param uid str : uniq identifier
15
+    # @param display_name MlString|str|dict : component display_name
16
+    # @param help_text MlString|str|dict : help_text
17
+    def __init__(self, uid, display_name = None, help_text = None, group = None):
18
+        if self.__class__ == EmComponent:
19
+            raise NotImplementedError('EmComponent is an abstract class')
20
+        self.uid = uid
21
+        self.display_name = None if display_name is None else MlString(display_name)
22
+        self.help_text = None if help_text is None else MlString(help_text)
23
+        self.group = group
24
+    
25
+    def __str__(self):
26
+        if self.display_name is None:
27
+            return str(self.uid)
28
+        return str(self.display_name)
29
+
30
+## @brief Handles editorial model objects classes
31
+#
32
+# @note The inheritance system allow child classes to overwrite parents EmField. But it's maybe not a good idea
33
+class EmClass(EmComponent):
34
+    
35
+    ## @brief Instanciate a new EmClass
36
+    # @param uid str : uniq identifier
37
+    # @param display_name MlString|str|dict : component display_name
38
+    # @param abstract bool : set the class as asbtract if True
39
+    # @param parents list: parent EmClass list or uid list
40
+    # @param help_text MlString|str|dict : help_text
41
+    def __init__(self, uid, display_name = None, help_text = None, abstract = False, parents = None, group = None):
42
+        super().__init__(uid, display_name, help_text, group)
43
+        self.abstract = bool(abstract)
44
+        if parents is not None:
45
+            if not isinstance(parents, list):
46
+                parents = [parents]
47
+            for parent in parents:
48
+                if not isinstance(parent, EmClass):
49
+                    raise ValueError("<class EmClass> expected in parents list, but %s found" % type(parent))
50
+        self.parents = parents
51
+        ## @brief Stores EmFields instances indexed by field uid
52
+        self.__fields = dict() 
53
+    
54
+    ## @brief Property that represent a dict of all fields (the EmField defined in this class and all its parents)
55
+    @property
56
+    def __all_fields(self):
57
+        res = dict()
58
+        for pfields in [ p.__all_fields for p in self.parents]:
59
+            res.update(pfields)
60
+        res.update(self.__fields)
61
+        return res
62
+
63
+    ## @brief EmField getter
64
+    # @param uid None | str : If None returns an iterator on EmField instances else return an EmField instance
65
+    # @param no_parents bool : If True returns only fields defined is this class and not the one defined in parents classes
66
+    # @return An iterator on EmFields instances (if uid is None) else return an EmField instance
67
+    def fields(self, uid = None, no_parents = False):
68
+        fields = self.__fields if no_parents else self.__all_fields
69
+        try:
70
+            return iter(fields.values()) if uid is None else fields[uid]
71
+        except KeyError:
72
+            raise EditorialModelError("No such EmField '%s'" % uid)
73
+
74
+    ## @brief Add a field to the EmClass
75
+    # @param emfield EmField : an EmField instance
76
+    # @warning do not add an EmField allready in another class !
77
+    # @throw EditorialModelException if an EmField with same uid allready in this EmClass (overwritting allowed from parents)
78
+    def add_field(self, emfield):
79
+        if emfield.uid in self.__fields:
80
+            raise EditorialModelException("Duplicated uid '%s' for EmField in this class ( %s )" % (emfield.uid, self))
81
+        self.__fields[emfield.uid] = emfield
82
+    
83
+    ## @brief Create a new EmField and add it to the EmClass
84
+    # @param uid str : the EmField uniq id
85
+    # @param **field_kwargs :  EmField constructor parameters ( see @ref EmField.__init__() ) 
86
+    def new_field(self, uid, **field_kwargs):
87
+        return self.add_field(EmField(uid, **kwargs))
88
+
89
+
90
+## @brief Handles editorial model classes fields
91
+class EmField(EmComponent):
92
+    
93
+    ## @brief Instanciate a new EmField
94
+    # @param uid str : uniq identifier
95
+    # @param display_name MlString|str|dict : field display_name
96
+    # @param data_handler class|str : A DataHandler class or display_name
97
+    # @param help_text MlString|str|dict : help text
98
+    # @param group EmGroup :
99
+    # @param **handler_kwargs : data handler arguments
100
+    def __init__(self, uid, data_handler, display_name = None, help_text = None, group = None, **handler_kwargs):
101
+        super().__init__(uid, display_name, help_text, group)
102
+        self.data_handler = data_handler
103
+        self.data_handler_options = data_handler_options
104
+
105
+    

+ 5
- 0
lodel/editorial_model/exceptions.py Dosyayı Görüntüle

@@ -0,0 +1,5 @@
1
+#-*- coding: utf-8 -*-
2
+
3
+class EditorialModelError(Exception):
4
+    pass
5
+

+ 71
- 0
lodel/editorial_model/model.py Dosyayı Görüntüle

@@ -0,0 +1,71 @@
1
+#-*- coding:utf-8 -*-
2
+from lodel.utils.mlstring import MlString
3
+
4
+from lodel.editorial_model.exceptions import *
5
+from lodel.editorial_model.component import EmClass, EmField
6
+
7
+## @brief Describe an editorial model
8
+class EditorialModel(object):
9
+    
10
+    ## @brief Create a new editorial model
11
+    # @param name MlString|str|dict : the editorial model name
12
+    # @param description MlString|str|dict : the editorial model description
13
+    def __init__(self, name, description = None):
14
+        self.name = MlString(name)
15
+        self.description = MlString(description)
16
+        ## @brief Stores all groups indexed by id
17
+        self.__groups = dict()
18
+        ## @brief Stores all classes indexed by id
19
+        self.__classes = dict()
20
+    
21
+    ## @brief EmClass accessor
22
+    # @param uid None | str : give this argument to get a specific EmClass
23
+    # @return if uid given return an EmClass else return an EmClass iterator
24
+    def classes(self, uid = None):
25
+        try:
26
+            return __elt_getter(self.__classes)
27
+        except KeyError:
28
+            raise EditorialModelException("EmClass not found : '%s'" % uid)
29
+
30
+    ## @brief EmGroup getter
31
+    # @param uid None | str : give this argument to get a specific EmGroup
32
+    # @return if uid given return an EmGroup else return an EmGroup iterator
33
+    def groups(self, uid = None):
34
+        try:
35
+            return __elt_getter(self.__groups)
36
+        except KeyError:
37
+            raise EditorialModelException("EmGroup not found : '%s'" % uid)
38
+
39
+    ## @brief Add a class to the editorial model
40
+    # @param emclass EmClass : the EmClass instance to add
41
+    def add_class(self, emclass):
42
+        if not isinstance(emclass, EmClass):
43
+            raise ValueError("<class EmClass> expected but got %s " % type(emclass))
44
+        if emclass.uid in self.classes:
45
+            raise EditorialModelException('Duplicated uid "%s"' % emclass.uid)
46
+        self.__classes[emclass.uid] = emclass
47
+
48
+    ## @brief Add a new EmClass to the editorial model
49
+    # @param uid str : EmClass uid
50
+    # @param **kwargs : EmClass constructor options ( see @ref lodel.editorial_model.component.EmClass.__init__() )
51
+    def new_class(self, uid, **kwargs):
52
+        return self.add_class(EmClass(uid, **kwargs))
53
+
54
+    # @brief Save a model
55
+    # @param translator module : The translator module to use
56
+    # @param **translator_args
57
+    def save(self, translator, **translator_args):
58
+        return translator.save(self, **kwargs)
59
+    
60
+    ## @brief Load a model
61
+    # @param translator module : The translator module to use
62
+    # @param **translator_args
63
+    @classmethod
64
+    def load(cls, translator, **translator_args):
65
+        return translator.load(**kwargs)
66
+    
67
+    ## @brief Private getter for __groups or __classes
68
+    # @see classes() groups()
69
+    def __elt_getter(self, elts, uid):
70
+        return iter(elts.values) if uid is None else elts[uid]
71
+

+ 4
- 0
lodel/editorial_model/translator/__init__.py Dosyayı Görüntüle

@@ -0,0 +1,4 @@
1
+## @package lodel.editorial_model.translator Editorial model translators
2
+#
3
+# This package contains modules that provides a save and a load function able to load and save
4
+# lodel.editorial_model.model.EditorialModel

+ 1
- 0
lodel/editorial_model/translator/pickle.py Dosyayı Görüntüle

@@ -0,0 +1 @@
1
+#-*- coding: utf-8 -*-

+ 19
- 0
lodel/editorial_model/translator/picklefile.py Dosyayı Görüntüle

@@ -0,0 +1,19 @@
1
+#-*- coding: utf-8 -*-
2
+
3
+import pickle
4
+
5
+## @brief Save a model in a file
6
+# @param model EditorialModel : the model to save
7
+# @param filename str|None : if None return the model as pickle bytes
8
+# @return None if filename is a string, else returns bytes representation of model
9
+def save(self, model, filename = None):
10
+    with open(filename, 'w+') as ffd:
11
+        pickle.dump(model, ffd)
12
+    return filename
13
+
14
+## @brief Load a model from a file
15
+# @param filename str : the filename to use to load the model
16
+def load(self, filename):
17
+    with open(filename, 'r') as ffd:
18
+        edmod = pickle.load(ffd)
19
+    return edmod

+ 0
- 0
tests/__init__.py Dosyayı Görüntüle


+ 20
- 0
tests/test_model.py Dosyayı Görüntüle

@@ -0,0 +1,20 @@
1
+#-*- coding: utf-8 -*-
2
+
3
+import unittest
4
+
5
+from lodel.editorial_model.component import EmComponent, EmClass, EmField
6
+from lodel.utils.mlstring import MlString
7
+
8
+class EmComponentTestCase(unittest.TestCase):
9
+    
10
+    def test_abstract_init(self):
11
+        with self.assertRaises(NotImplementedError):
12
+            EmComponent('test')
13
+
14
+class EmClassTestCase(unittest.TestCase):
15
+
16
+    def test_init(self):
17
+        cls = EmClass('testClass', 'test class', 'A test class')
18
+        self.assertEqual(cls.uid, 'testClass')
19
+        self.assertEqual(cls.display_name, MlString('test class'))
20
+        self.assertEqual(cls.help_text, MlString('A test class'))

Loading…
İptal
Kaydet