diff --git a/EditorialModel/backend/json_backend.py b/EditorialModel/backend/json_backend.py index 53f9a97..09fb155 100644 --- a/EditorialModel/backend/json_backend.py +++ b/EditorialModel/backend/json_backend.py @@ -9,17 +9,23 @@ import json import datetime from Lodel.utils.mlstring import MlString +import EditorialModel def date_cast(date): if len(date): - return datetime.datetime(date) + return datetime.datetime.strptime(date, '%c') else: return None +## @brief dirty obsolote cast function def int_or_none(i): - if len(i): + if isinstance(i, int): + return i + elif i is None: + return None + elif len(i): return int(i) else: return None @@ -47,27 +53,35 @@ class EmBackendJson(object): # # @param json_file str: path to the json_file used as data source def __init__(self, json_file): - with open(json_file) as json_data: - self.data = json.loads(json_data.read()) + self.json_file = json_file + pass + + @staticmethod + ## @brief Used by json.dumps to serialize date and datetime + def date_handler(obj): + return obj.strftime('%c') if isinstance(obj, datetime.datetime) or isinstance(obj, datetime.date) else None + ## Loads the data in the data source json file # # @return list def load(self): data = {} - for index, component in self.data.items(): - attributes = {} - for attr_name, attr_value in component.items(): - if attr_name in EmBackendJson.cast_methods: - attributes[attr_name] = EmBackendJson.cast_methods[attr_name](attr_value) - else: - attributes[attr_name] = attr_value - data[int(index)] = attributes + with open(self.json_file) as json_data: + rdata = json.loads(json_data.read()) + for index, component in rdata.items(): + attributes = {} + for attr_name, attr_value in component.items(): + if attr_name in EmBackendJson.cast_methods: + attributes[attr_name] = EmBackendJson.cast_methods[attr_name](attr_value) + else: + attributes[attr_name] = attr_value + data[int(index)] = attributes return data ## Saves the data in the data source json file - # - # @return bool - # @todo à implémenter - def save(self, em): - return True + # @param filename str : The filename to save the EM in (if None use self.json_file provided at init ) + def save(self, em, filename = None): + with open(self.json_file if filename is None else filename, 'w') as fp: + fp.write(json.dumps({ component.uid : component.dumps() for component in em.components() }, default=self.date_handler)) + diff --git a/EditorialModel/components.py b/EditorialModel/components.py index 062aa10..67cc041 100644 --- a/EditorialModel/components.py +++ b/EditorialModel/components.py @@ -56,7 +56,7 @@ class EmComponent(object): ## @brief Return a dict with attributes name as key and attributes value as value # @note Used at creation and deletion to call the migration handler def attr_dump(self): - return {fname: fval for fname, fval in self.__dict__.items() if not (fname.startswith('__') or (fname == 'uid'))} + return {fname: fval for fname, fval in self.__dict__.items() if not (fname.startswith('_') or (fname == 'uid'))} @property ## @brief Provide a uniq name @@ -71,6 +71,23 @@ class EmComponent(object): uname += '_'+self.name return uname + ## @brief dumps attr for serialization + def dumps(self): + #attr = {fname: fval for fname, fval in self.__dict__.items() if not (fname.startswith('_'))} + attr = self.attr_dump + del(attr['model']) + for attr_f in attr: + if isinstance(attr[attr_f], EmComponent): + attr[attr_f] = attr[attr_f].uid + elif isinstance(attr[attr_f], MlString): + attr[attr_f] = attr[attr_f].__str__() + if isinstance(self, EditorialModel.fields.EmField): + attr['component'] = 'EmField' + attr['type'] = self.fieldtype + else: + attr['component'] = self.__class__.__name__ + return attr + ## @brief This function has to be called after the instanciation, checks, and init manipulations are done # @note Create a new attribute _inited that allow __setattr__ to know if it has or not to call the migration handler def init_ended(self): diff --git a/EditorialModel/fields.py b/EditorialModel/fields.py index 5729422..e83da5d 100644 --- a/EditorialModel/fields.py +++ b/EditorialModel/fields.py @@ -22,7 +22,7 @@ class EmField(EmComponent): ## Instanciate a new EmField # @todo define and test type for icon and fieldtype # @warning nullable == True by default - def __init__(self, model, uid, name, fieldgroup_id, optional=False, internal=False, rel_field_id=None, icon='0', string=None, help_text=None, date_update=None, date_create=None, rank=None, nullable = True, default = None, uniq = False, **kwargs): + def __init__(self, model, uid, name, fieldgroup_id, fieldtype, optional=False, internal=False, rel_field_id=None, icon='0', string=None, help_text=None, date_update=None, date_create=None, rank=None, nullable = True, default = None, uniq = False, **kwargs): if self.ftype == None: raise NotImplementedError("Trying to instanciate an EmField and not one of the fieldtypes child classes") @@ -37,7 +37,9 @@ class EmField(EmComponent): self.check_type('rel_field_id', (int, type(None))) self.icon = icon + #Field type elements + self.fieldtype = fieldtype self.nullable = nullable self.default = default self.uniq = uniq diff --git a/EditorialModel/model.py b/EditorialModel/model.py index f9153a2..59db940 100644 --- a/EditorialModel/model.py +++ b/EditorialModel/model.py @@ -77,6 +77,7 @@ class Model(object): if not 'type' in kwargs: raise AttributeError("Missing 'type' from EmField instanciation") cls = EditorialModel.fields.EmField.get_field_class(kwargs['type']) + kwargs['fieldtype'] = kwargs['type'] del(kwargs['type']) else: cls = self.emclass_from_name(cls_name) @@ -103,13 +104,16 @@ class Model(object): component.init_ended() ## Saves data using the current backend - def save(self): - return self.backend.save(self) + # @param filename str | None : if None use the current backend file (provided at backend instanciation) + def save(self, filename = None): + return self.backend.save(self, filename) ## Given a EmComponent child class return a list of instances # @param cls EmComponent : A python class # @return a list of instances or False if the class is not an EmComponent child - def components(self, cls): + def components(self, cls=None): + if cls is None: + return [ self.component(uid) for uid in self._components['uids'] ] key_name = self.name_from_emclass(cls) return False if key_name is False else self._components[key_name]