From 3fcc86f77f0f34f147b4cefab345eb271a0ddb86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Fri, 29 Aug 2014 14:12:27 +0200 Subject: [PATCH] add possibility to create a formdef object from a json value (#5348) --- tests/test_formdef_import.py | 54 ++++++++++++++++---- wcs/fields.py | 27 ++++------ wcs/formdef.py | 117 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 153 insertions(+), 45 deletions(-) diff --git a/tests/test_formdef_import.py b/tests/test_formdef_import.py index e476901..4b23688 100644 --- a/tests/test_formdef_import.py +++ b/tests/test_formdef_import.py @@ -22,23 +22,42 @@ def setup_module(module): def teardown_module(module): shutil.rmtree(pub.APP_DIR) -def assert_import_export_works(formdef, include_id=False): +def export_to_indented_xml(formdef, include_id=False): + formdef_xml = formdef.export_to_xml(include_id=include_id) + indent(formdef_xml) + return formdef_xml + +def assert_compare_formdef(formdef1, formdef2, include_id=False): + assert ET.tostring(export_to_indented_xml(formdef1, include_id=include_id)) == \ + ET.tostring(export_to_indented_xml(formdef2, include_id=include_id)) + assert formdef1.export_to_json(include_id=include_id, indent=2) == \ + formdef2.export_to_json(include_id=include_id, indent=2) + +def assert_xml_import_export_works(formdef, include_id=False): formdef2 = FormDef.import_from_xml_tree( formdef.export_to_xml(include_id=include_id), include_id=include_id) - assert ET.tostring(indent(formdef.export_to_xml(include_id=include_id)) - ) == ET.tostring(indent(formdef2.export_to_xml(include_id=include_id))) + assert_compare_formdef(formdef, formdef2, include_id=include_id) + return formdef2 + +def assert_json_import_export_works(formdef, include_id=False): + formdef2 = FormDef.import_from_json( + StringIO.StringIO(formdef.export_to_json(include_id=include_id)), include_id=include_id) + assert_compare_formdef(formdef, formdef2, include_id=include_id) return formdef2 def test_empty(): formdef = FormDef() formdef.name = 'empty' - assert_import_export_works(formdef) + assert_xml_import_export_works(formdef) + assert_json_import_export_works(formdef) def test_text_attributes(): formdef = FormDef() formdef.name = 'Foo' formdef.url_name = 'foo' - f2 = assert_import_export_works(formdef) + f2 = assert_xml_import_export_works(formdef) + assert f2.url_name == formdef.url_name + f2 = assert_json_import_export_works(formdef) assert f2.url_name == formdef.url_name def test_boolean_attributes(): @@ -47,7 +66,10 @@ def test_boolean_attributes(): formdef.url_name = 'foo' formdef.confirmation = True formdef.allow_drafts = True - f2 = assert_import_export_works(formdef) + f2 = assert_xml_import_export_works(formdef) + assert f2.allow_drafts == formdef.allow_drafts + assert f2.confirmation == formdef.confirmation + f2 = assert_json_import_export_works(formdef) assert f2.allow_drafts == formdef.allow_drafts assert f2.confirmation == formdef.confirmation @@ -55,9 +77,11 @@ def test_a_field(): formdef = FormDef() formdef.name = 'Foo' formdef.fields = [ - fields.StringField(type='string', id=1, label='Bar', size=40) + fields.StringField(type='string', id=1, label='Bar', size='40') ] - f2 = assert_import_export_works(formdef) + f2 = assert_xml_import_export_works(formdef) + assert len(f2.fields) == len(formdef.fields) + f2 = assert_json_import_export_works(formdef) assert len(f2.fields) == len(formdef.fields) def test_more_fields(): @@ -70,7 +94,13 @@ def test_more_fields(): fields.DateField(type='date', label='Bar', minimum_date='2014-01-01'), fields.ItemField(type='item', label='Bar', items=['foo', 'bar', 'baz']), ] - f2 = assert_import_export_works(formdef) + f2 = assert_xml_import_export_works(formdef) + assert len(f2.fields) == len(formdef.fields) + assert f2.fields[2].type == formdef.fields[2].type + assert f2.fields[3].minimum_date == formdef.fields[3].minimum_date + assert f2.fields[4].items == formdef.fields[4].items + + f2 = assert_json_import_export_works(formdef) assert len(f2.fields) == len(formdef.fields) assert f2.fields[2].type == formdef.fields[2].type assert f2.fields[3].minimum_date == formdef.fields[3].minimum_date @@ -89,8 +119,12 @@ def test_include_id(): for field in formdef.fields: field.id = formdef.get_new_field_id() formdef.fields[4].id = '10' - f2 = assert_import_export_works(formdef, include_id=True) + f2 = assert_xml_import_export_works(formdef, include_id=True) assert len(f2.fields) == len(formdef.fields) assert f2.fields[0].id == formdef.fields[0].id assert f2.fields[4].id == formdef.fields[4].id + f2 = assert_json_import_export_works(formdef, include_id=True) + assert len(f2.fields) == len(formdef.fields) + assert f2.fields[0].id == formdef.fields[0].id + assert f2.fields[4].id == formdef.fields[4].id diff --git a/wcs/fields.py b/wcs/fields.py index c3bd0a4..f9e48e9 100644 --- a/wcs/fields.py +++ b/wcs/fields.py @@ -125,7 +125,7 @@ class Field: def get_admin_attributes(self): return ['label', 'type'] - def export_to_json(self, charset, include_id=False): + def export_to_json(self, include_id=False): field = {} if include_id: extra_fields = ['id'] @@ -136,25 +136,16 @@ class Field: continue if hasattr(self, attribute) and getattr(self, attribute) is not None: val = getattr(self, attribute) - if type(val) is dict: - if not val: continue - field[attribute] = {} - for k, v in val.items(): - field[attribute][k] = unicode(v, charset, 'replace') - elif type(val) is list: - if not val: continue - field[attribute] = [] - for v in val: - field[attribute].append(unicode(v, charset, 'replace')) - elif type(val) in (str, unicode): - if type(val) is unicode: - field[attribute] = val - else: - field[attribute] = unicode(val, charset, 'replace') - else: - field[attribute] = str(val) + field[attribute] = val return field + def init_with_json(self, elem, include_id=False): + if include_id: + self.id = elem.get('id') + for attribute in self.get_admin_attributes(): + if attribute in elem: + setattr(self, attribute, elem.get(attribute)) + def export_to_xml(self, charset, include_id=False): field = ET.Element('field') if include_id: diff --git a/wcs/formdef.py b/wcs/formdef.py index b3741a9..45091bc 100644 --- a/wcs/formdef.py +++ b/wcs/formdef.py @@ -80,13 +80,13 @@ class FormDef(StorableObject): max_field_id = None - # declarations for serialization - TEXT_ATTRIBUTES = ('name', 'url_name', - 'publication_date', 'expiration_date') - BOOLEAN_ATTRIBUTES = ('discussion', 'detailed_emails', 'disabled', - 'only_allow_one', 'allow_drafts', 'disabled_redirection', - 'always_advertise', 'private_status_and_history') + TEXT_ATTRIBUTES = ['name', 'url_name', + 'publication_date', 'expiration_date', + 'disabled_redirection',] + BOOLEAN_ATTRIBUTES = ['discussion', 'detailed_emails', 'disabled', + 'only_allow_one', 'allow_drafts', + 'always_advertise', 'private_status_and_history'] def migrate(self): changed = False @@ -386,24 +386,104 @@ class FormDef(StorableObject): return d - def export_to_json(self, include_id=False): + def export_to_json(self, include_id=False, indent=None): charset = get_publisher().site_charset root = {} root['name'] = unicode(self.name, charset) + if include_id and self.id: + root['id'] = str(self.id) if self.category: root['category'] = unicode(self.category.name, charset) - for boolean_attribute in self.BOOLEAN_ATTRIBUTES: - value = getattr(self, boolean_attribute) - if value: - value = 'true' - else: - value = 'false' - root[boolean_attribute] = value + root['category_id'] = str(self.category.id) + if self.workflow: + root['workflow'] = unicode(self.workflow.name, charset) + root['workflow_id'] = str(self.workflow.id) + + if self.max_field_id is None and self.fields: + self.max_field_id = max([lax_int(x.id) for x in self.fields]) + + more_attributes = [] + if self.max_field_id: + more_attributes.append('max_field_id') + if self.last_modification_time: + more_attributes.append('last_modification_time') + if include_id: + more_attributes.append('last_modification_user_id') + + for attribute in self.TEXT_ATTRIBUTES + self.BOOLEAN_ATTRIBUTES + more_attributes: + if not hasattr(self, attribute): + continue + root[attribute] = getattr(self, attribute) + root['fields'] = [] - for field in self.fields: - root['fields'].append(field.export_to_json(charset=charset, include_id=include_id)) + if self.fields: + for field in self.fields: + root['fields'].append(field.export_to_json(include_id=include_id)) + + return json.dumps(root, indent=indent) + + def import_from_json(cls, fd, charset=None, include_id=False): + if charset is None: + charset = get_publisher().site_charset + formdef = cls() + + def unicode2str(v): + if isinstance(v, dict): + return dict([(unicode2str(k), unicode2str(v)) for k, v in v.items()]) + elif isinstance(v, list): + return [unicode2str(x) for x in v] + elif isinstance(v, unicode): + return v.encode(charset) + else: + return v + + # we have to make sure all strings are str object, not unicode. + value = unicode2str(json.load(fd)) + + if include_id and 'id' in value: + formdef.id = value.get('id') + + if include_id and 'category_id' in value: + formdef.category_id = value.get('category_id') + elif 'category' in value: + category = value.get('category') + for c in Category.select(): + if c.name == category: + formdef.category_id = c.id + break + + if include_id and 'workflow_id' in value: + formdef.workflow_id = value.get('workflow_id') + elif 'workflow' in value: + workflow = value.get('workflow') + for w in Workflow.select(): + if w.name == workflow: + formdef.workflow_id = w.id + break + + more_attributes = ['max_field_id', 'last_modification_time', + 'last_modification_user_id'] + for attribute in cls.TEXT_ATTRIBUTES + cls.BOOLEAN_ATTRIBUTES + more_attributes: + if attribute in value: + setattr(formdef, attribute, value.get(attribute)) + + formdef.fields = [] + for i, field in enumerate(value.get('fields', [])): + try: + field_o = fields.get_field_class_by_type(field.get('type'))() + except KeyError: + raise ValueError() + field_o.init_with_json(field, include_id=include_id) + if not field_o.id: + # this assumes all fields will have id, or none of them + field_o.id = str(i) + formdef.fields.append(field_o) + + if formdef.fields and not formdef.max_field_id: + formdef.max_field_id = max([lax_int(x.id) for x in formdef.fields])+1 - return json.dumps(root) + return formdef + import_from_json = classmethod(import_from_json) def export_to_xml(self, include_id=False): charset = get_publisher().site_charset @@ -437,6 +517,9 @@ class FormDef(StorableObject): if include_id: elem.attrib['workflow_id'] = str(self.workflow.id) + if self.max_field_id is None and self.fields: + self.max_field_id = max([lax_int(x.id) for x in self.fields]) + if self.max_field_id: ET.SubElement(root, 'max_field_id').text = str(self.max_field_id) -- 2.1.0