From 2ede7b672ac1091e64e8b8a8721b9d9d7a943311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Tue, 25 Nov 2014 12:13:40 +0100 Subject: [PATCH 2/3] categories: add xml import/export (#4739) --- tests/test_categories.py | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ wcs/categories.py | 63 +++++++++++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 tests/test_categories.py diff --git a/tests/test_categories.py b/tests/test_categories.py new file mode 100644 index 0000000..8cd3139 --- /dev/null +++ b/tests/test_categories.py @@ -0,0 +1,89 @@ +import shutil +import tempfile +from cStringIO import StringIO + +from quixote import cleanup +from wcs import publisher + +from wcs.categories import Category + +def setup_module(module): + cleanup() + + global pub + + publisher.WcsPublisher.APP_DIR = tempfile.mkdtemp() + pub = publisher.WcsPublisher.create_publisher() + + +def teardown_module(module): + shutil.rmtree(pub.APP_DIR) + + +def test_store(): + Category.wipe() + test = Category() + test.id = 1 + test.name = 'Test' + test.description = 'Hello world' + test.store() + test2 = Category.get(1) + assert test.id == test2.id + assert test.name == test2.name + assert test.description == test2.description + + +def test_urlname(): + Category.wipe() + test = Category() + test.id = 1 + test.name = 'Test' + test.description = 'Hello world' + test.store() + test = Category.get(1) + assert test.url_name == 'test' + + +def test_sort_positions(): + Category.wipe() + + categories = [] + for i in range(10): + test = Category() + test.id = i+1 + test.name = 'Test %s' % i + test.position = 10-i + categories.append(test) + + Category.sort_by_position(categories) + assert categories[0].name == 'Test 9' + + +def test_xml_export(): + Category.wipe() + test = Category() + test.id = 1 + test.name = 'Test' + test.description = 'Hello world' + test.store() + test = Category.get(1) + + assert 'Test' in test.export_to_xml_string(include_id=True) + assert ' id="1"' in test.export_to_xml_string(include_id=True) + assert ' id="1"' not in test.export_to_xml_string(include_id=False) + + +def test_xml_import(): + Category.wipe() + test = Category() + test.id = 1 + test.name = 'Test' + test.description = 'Hello world' + test.store() + test = Category.get(1) + + fd = StringIO(test.export_to_xml_string(include_id=True)) + test2 = Category.import_from_xml(fd, include_id=True) + assert test.id == test2.id + assert test.name == test2.name + assert test.description == test2.description diff --git a/wcs/categories.py b/wcs/categories.py index 7cbb250..94a33b3 100644 --- a/wcs/categories.py +++ b/wcs/categories.py @@ -14,11 +14,13 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see . +import xml.etree.ElementTree as ET + from quixote import get_publisher, get_response from quixote.html import TemplateIO, htmltext from qommon.storage import StorableObject -from qommon.misc import simplify, is_user_admin +from qommon.misc import simplify, is_user_admin, indent_xml from qommon.substitution import Substitutions class Category(StorableObject): @@ -28,6 +30,10 @@ class Category(StorableObject): description = None position = None + # declarations for serialization + TEXT_ATTRIBUTES = ['name', 'url_name', 'description'] + INT_ATTRIBUTES = ['position'] + def __init__(self, name = None): StorableObject.__init__(self) self.name = name @@ -75,6 +81,61 @@ class Category(StorableObject): form.store() StorableObject.remove_self(self) + def export_to_xml(self, include_id=False): + charset = get_publisher().site_charset + root = ET.Element('category') + if include_id and self.id: + root.attrib['id'] = str(self.id) + for text_attribute in list(self.TEXT_ATTRIBUTES): + if not hasattr(self, text_attribute) or not getattr(self, text_attribute): + continue + ET.SubElement(root, text_attribute).text = unicode( + getattr(self, text_attribute), charset) + for int_attribute in list(self.INT_ATTRIBUTES): + if not hasattr(self, int_attribute) or not getattr(self, int_attribute): + continue + ET.SubElement(root, int_attribute).text = str(getattr(self, int_attribute)) + return root + + def export_to_xml_string(self, include_id=False): + x = self.export_to_xml(include_id=include_id) + indent_xml(x) + return ET.tostring(x) + + def import_from_xml(cls, fd, charset=None, include_id=False): + try: + tree = ET.parse(fd) + except: + raise ValueError() + return cls.import_from_xml_tree(tree, charset=charset, + include_id=include_id) + import_from_xml = classmethod(import_from_xml) + + def import_from_xml_tree(cls, tree, include_id=False, charset=None): + if charset is None: + charset = get_publisher().site_charset + category = cls() + + # if the tree we get is actually a ElementTree for real, we get its + # root element and go on happily. + if not ET.iselement(tree): + tree = tree.getroot() + + if include_id and tree.attrib.get('id'): + category.id = int(tree.attrib.get('id')) + for text_attribute in list(cls.TEXT_ATTRIBUTES): + value = tree.find(text_attribute) + if value is None: + continue + setattr(category, text_attribute, value.text.encode(charset)) + for int_attribute in list(cls.INT_ATTRIBUTES): + value = tree.find(int_attribute) + if value is None: + continue + setattr(category, int_attribute, int(value.text)) + return category + import_from_xml_tree = classmethod(import_from_xml_tree) + def get_substitution_variables(self, minimal=False): d = { 'category_name': self.name, -- 2.1.3