|
@@ -1,6 +1,8 @@
|
1
|
1
|
#-*- coding: utf-8 -*-
|
2
|
2
|
|
3
|
3
|
import itertools
|
|
4
|
+import warnings
|
|
5
|
+import copy
|
4
|
6
|
|
5
|
7
|
from lodel.utils.mlstring import MlString
|
6
|
8
|
|
|
@@ -47,6 +49,8 @@ class EmClass(EmComponent):
|
47
|
49
|
for parent in parents:
|
48
|
50
|
if not isinstance(parent, EmClass):
|
49
|
51
|
raise ValueError("<class EmClass> expected in parents list, but %s found" % type(parent))
|
|
52
|
+ else:
|
|
53
|
+ parents = list()
|
50
|
54
|
self.parents = parents
|
51
|
55
|
## @brief Stores EmFields instances indexed by field uid
|
52
|
56
|
self.__fields = dict()
|
|
@@ -63,11 +67,11 @@ class EmClass(EmComponent):
|
63
|
67
|
## @brief EmField getter
|
64
|
68
|
# @param uid None | str : If None returns an iterator on EmField instances else return an EmField instance
|
65
|
69
|
# @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
|
|
70
|
+ # @return A list on EmFields instances (if uid is None) else return an EmField instance
|
67
|
71
|
def fields(self, uid = None, no_parents = False):
|
68
|
72
|
fields = self.__fields if no_parents else self.__all_fields
|
69
|
73
|
try:
|
70
|
|
- return iter(fields.values()) if uid is None else fields[uid]
|
|
74
|
+ return list(fields.values()) if uid is None else fields[uid]
|
71
|
75
|
except KeyError:
|
72
|
76
|
raise EditorialModelError("No such EmField '%s'" % uid)
|
73
|
77
|
|
|
@@ -79,12 +83,13 @@ class EmClass(EmComponent):
|
79
|
83
|
if emfield.uid in self.__fields:
|
80
|
84
|
raise EditorialModelException("Duplicated uid '%s' for EmField in this class ( %s )" % (emfield.uid, self))
|
81
|
85
|
self.__fields[emfield.uid] = emfield
|
|
86
|
+ emfield._emclass = self
|
82
|
87
|
|
83
|
88
|
## @brief Create a new EmField and add it to the EmClass
|
84
|
89
|
# @param uid str : the EmField uniq id
|
85
|
90
|
# @param **field_kwargs : EmField constructor parameters ( see @ref EmField.__init__() )
|
86
|
91
|
def new_field(self, uid, **field_kwargs):
|
87
|
|
- return self.add_field(EmField(uid, **kwargs))
|
|
92
|
+ return self.add_field(EmField(uid, **field_kwargs))
|
88
|
93
|
|
89
|
94
|
|
90
|
95
|
## @brief Handles editorial model classes fields
|
|
@@ -100,6 +105,88 @@ class EmField(EmComponent):
|
100
|
105
|
def __init__(self, uid, data_handler, display_name = None, help_text = None, group = None, **handler_kwargs):
|
101
|
106
|
super().__init__(uid, display_name, help_text, group)
|
102
|
107
|
self.data_handler = data_handler
|
103
|
|
- self.data_handler_options = data_handler_options
|
|
108
|
+ self.data_handler_options = handler_kwargs
|
|
109
|
+ ## @brief Stores the emclass that contains this field (set by EmClass.add_field() method)
|
|
110
|
+ self._emclass = None
|
104
|
111
|
|
|
112
|
+
|
|
113
|
+## @brief Handles functionnal group of EmComponents
|
|
114
|
+class EmGroup(object):
|
|
115
|
+
|
|
116
|
+ ## @brief Create a new EmGroup
|
|
117
|
+ # @note you should NEVER call the constructor yourself. Use Model.add_group instead
|
|
118
|
+ # @param uid str : Uniq identifier
|
|
119
|
+ # @param depends list : A list of EmGroup dependencies
|
|
120
|
+ # @param display_name MlString|str :
|
|
121
|
+ # @param help_text MlString|str :
|
|
122
|
+ def __init__(self, uid, depends = None, display_name = None, help_text = None):
|
|
123
|
+ self.uid = uid
|
|
124
|
+ ## @brief Stores the list of groups that depends on this EmGroup indexed by uid
|
|
125
|
+ self.required_by = dict()
|
|
126
|
+ ## @brief Stores the list of dependencies (EmGroup) indexed by uid
|
|
127
|
+ self.require = dict()
|
|
128
|
+ ## @brief Stores the list of EmComponent instances contained in this group
|
|
129
|
+ self.__components = set()
|
|
130
|
+
|
|
131
|
+ self.display_name = None if display_name is None else MlString(display_name)
|
|
132
|
+ self.help_text = None if help_text is None else MlString(help_text)
|
|
133
|
+ if depends is not None:
|
|
134
|
+ for grp in depends:
|
|
135
|
+ if not isinstance(grp, EmGroup):
|
|
136
|
+ raise ValueError("EmGroup expected in depends argument but %s found" % grp)
|
|
137
|
+ self.add_dependencie(grp)
|
|
138
|
+
|
|
139
|
+ ## @brief Returns EmGroup dependencie
|
|
140
|
+ # @param recursive bool : if True return all dependencies and their dependencies
|
|
141
|
+ # @return a dict of EmGroup identified by uid
|
|
142
|
+ def dependencies(self, recursive = False):
|
|
143
|
+ res = copy.copy(self.require)
|
|
144
|
+ if not recursive:
|
|
145
|
+ return res
|
|
146
|
+ to_scan = list(res.values())
|
|
147
|
+ while len(to_scan) > 0:
|
|
148
|
+ cur_dep = to_scan.pop()
|
|
149
|
+ for new_dep in cur_dep.require.values():
|
|
150
|
+ if new_dep not in res:
|
|
151
|
+ to_scan.append(new_dep)
|
|
152
|
+ res[new_dep.uid] = new_dep
|
|
153
|
+ return res
|
|
154
|
+
|
|
155
|
+ ## @brief Add components in a group
|
|
156
|
+ # @param components list : EmComponent instance list
|
|
157
|
+ def add_components(self, components):
|
|
158
|
+ for component in components:
|
|
159
|
+ if isinstance(component, EmField):
|
|
160
|
+ if component._emclass is None:
|
|
161
|
+ warnings.warn("Adding an orphan EmField to an EmGroup")
|
|
162
|
+ elif not isinstance(component, EmClass):
|
|
163
|
+ raise EditorialModelError("Expecting components to be a list of EmComponent, but %s found in the list" % type(component))
|
|
164
|
+ self.__components |= set(components)
|
|
165
|
+
|
|
166
|
+ ## @brief Add a dependencie
|
|
167
|
+ # @param em_group EmGroup|iterable : an EmGroup instance or list of instance
|
|
168
|
+ def add_dependencie(self, grp):
|
|
169
|
+ try:
|
|
170
|
+ for group in grp:
|
|
171
|
+ self.add_dependencie(group)
|
|
172
|
+ return
|
|
173
|
+ except TypeError: pass
|
|
174
|
+
|
|
175
|
+ if grp.uid in self.require:
|
|
176
|
+ return
|
|
177
|
+ if self.__circular_dependencie(grp):
|
|
178
|
+ raise EditorialModelError("Circular dependencie detected, cannot add dependencie")
|
|
179
|
+ self.require[grp.uid] = grp
|
|
180
|
+ grp.required_by[self.uid] = self
|
|
181
|
+
|
|
182
|
+ ## @brief Search for circular dependencie
|
|
183
|
+ # @return True if circular dep found else False
|
|
184
|
+ def __circular_dependencie(self, new_dep):
|
|
185
|
+ return self.uid in new_dep.dependencies(True)
|
|
186
|
+
|
|
187
|
+ def __str__(self):
|
|
188
|
+ return "<class EmGroup '%s'>" % self.uid
|
105
|
189
|
|
|
190
|
+ ## @todo better implementation
|
|
191
|
+ def __repr__(self):
|
|
192
|
+ return self.__str__()
|