wcs/wcs/qommon/xml_storage.py

163 lines
6.0 KiB
Python

# w.c.s. - web application for online forms
# Copyright (C) 2005-2014 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import datetime
import xml.etree.ElementTree as ET
from django.utils.encoding import force_str
from quixote import get_publisher
from .misc import indent_xml, xml_node_text
from .storage import Equal, Or, StorableObject
class XmlStorableObject(StorableObject):
XML_NODES = []
@classmethod
def storage_load(cls, fd):
first_byte = fd.read(1)
fd.seek(0)
if first_byte == b'<':
return cls.import_from_xml(fd, include_id=True)
else:
obj = StorableObject.storage_load(fd)
obj._upgrade_must_store = True
return obj
@classmethod
def storage_dumps(cls, object):
return object.export_to_xml_string(include_id=True)
def export_to_xml(self, include_id=False):
charset = get_publisher().site_charset
root = ET.Element(self.xml_root_node)
if include_id and self.id:
root.attrib['id'] = str(self.id)
for attribute in self.XML_NODES:
attribute_name, attribute_type = attribute
if not getattr(self, attribute_name, None):
continue
element = ET.SubElement(root, attribute_name)
export_method = getattr(self, 'export_%s_to_xml' % attribute_type)
export_method(element, attribute_name, charset=charset, include_id=include_id)
return root
def export_str_to_xml(self, element, attribute_name, charset, **kwargs):
element.text = force_str(getattr(self, attribute_name), charset)
def export_int_to_xml(self, element, attribute_name, **kwargs):
element.text = str(getattr(self, attribute_name))
def export_bool_to_xml(self, element, attribute_name, **kwargs):
element.text = 'true' if getattr(self, attribute_name) else 'false'
def export_datetime_to_xml(self, element, attribute_name, **kwargs):
element.text = getattr(self, attribute_name).isoformat()
def export_str_list_to_xml(self, element, attribute_name, **kwargs):
for item in getattr(self, attribute_name, None) or []:
ET.SubElement(element, 'item').text = item
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 export_roles_to_xml(self, element, attribute_name, include_id=False, **kwargs):
for role in getattr(self, attribute_name, None) or []:
sub = ET.SubElement(element, 'role')
if include_id:
sub.attrib['role-id'] = role.id
sub.attrib['role-slug'] = role.slug
sub.text = role.name
@classmethod
def import_from_xml(cls, fd, charset=None, include_id=False):
try:
tree = ET.parse(fd)
except Exception:
raise ValueError()
return cls.import_from_xml_tree(tree, charset=charset, include_id=include_id)
@classmethod
def import_from_xml_tree(cls, tree, include_id=False, charset=None, **kwargs):
if charset is None:
charset = get_publisher().site_charset
obj = 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 tree.tag not in (cls.xml_root_node, cls._names):
# note: cls._names is allowed for compatibility with legacy files.
raise ValueError('root element mismatch (%s vs %s)' % (tree.tag, cls.xml_root_node))
if include_id and tree.attrib.get('id'):
obj.id = tree.attrib.get('id')
for attribute in cls.XML_NODES:
attribute_name, attribute_type = attribute
element = tree.find(attribute_name)
if element is None:
continue
import_method = getattr(obj, 'import_%s_from_xml' % attribute_type)
setattr(
obj,
attribute_name,
import_method(element, charset=charset, include_id=include_id),
)
return obj
def import_str_from_xml(self, element, **kwargs):
return xml_node_text(element)
def import_int_from_xml(self, element, **kwargs):
return int(element.text)
def import_bool_from_xml(self, element, **kwargs):
return bool(element.text == 'true')
def import_datetime_from_xml(self, element, **kwargs):
return datetime.datetime.strptime(element.text[:19], '%Y-%m-%dT%H:%M:%S')
def import_str_list_from_xml(self, element, **kwargs):
value = []
for item in element.findall('item'):
value.append(item.text)
return value
def import_roles_from_xml(self, element, include_id=False, **kwargs):
criterias = []
for sub in element:
if include_id and 'role-id' in sub.attrib:
criterias.append(Equal('id', sub.attrib['role-id']))
elif 'role-slug' in sub.attrib:
criterias.append(Equal('slug', sub.attrib['role-slug']))
else:
role_name = xml_node_text(sub)
if role_name:
criterias.append(Equal('name', role_name))
if not criterias:
return []
def lazy_roles():
return get_publisher().role_class.select([Or(criterias)], order_by='name')
return lazy_roles