src/pyams_thesaurus/loader/__init__.py
changeset 0 47700a43ef3f
child 23 9885b9c88b2e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_thesaurus/loader/__init__.py	Tue Apr 14 17:52:05 2015 +0200
@@ -0,0 +1,239 @@
+#
+# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+from datetime import datetime
+from tempfile import TemporaryFile
+
+# import interfaces
+from pyams_thesaurus.interfaces.loader import IThesaurusLoaderHandler, IThesaurusLoader, IThesaurusExporterHandler, \
+    IThesaurusExporter
+from pyams_thesaurus.interfaces.term import IThesaurusLoaderTerm
+from pyams_thesaurus.interfaces.thesaurus import IThesaurusDescription
+from zope.schema.interfaces import IVocabularyFactory
+
+# import packages
+from BTrees.OOBTree import OOBTree
+from pyams_thesaurus.term import ThesaurusTerm
+from pyams_thesaurus.thesaurus import ThesaurusTermsContainer, Thesaurus
+from pyams_utils.request import query_request
+from pyams_utils.unicode import translate_string
+from zope.componentvocabulary.vocabulary import UtilityVocabulary
+from zope.interface import implementer, provider, alsoProvides, noLongerProvides
+from zope.schema.fieldproperty import FieldProperty
+from zope.schema.vocabulary import getVocabularyRegistry
+
+
+#
+# Thesaurus loader classes
+#
+
+@implementer(IThesaurusDescription)
+class ThesaurusLoaderDescription(object):
+    """Thesaurus loader description"""
+
+    title = FieldProperty(IThesaurusDescription['title'])
+    subject = FieldProperty(IThesaurusDescription['subject'])
+    description = FieldProperty(IThesaurusDescription['description'])
+    language = FieldProperty(IThesaurusDescription['language'])
+    creator = FieldProperty(IThesaurusDescription['creator'])
+    publisher = FieldProperty(IThesaurusDescription['publisher'])
+    _created = FieldProperty(IThesaurusDescription['created'])
+
+    @property
+    def created(self):
+        return self._created
+
+    @created.setter
+    def created(self, value):
+        if isinstance(value, str):
+            try:
+                value = datetime.strptime(value, '%Y-%m-%d').date()
+            except ValueError:
+                value = datetime.today().date()
+        self._created = value
+
+
+class ThesaurusLoaderTerm(object):
+    """Thesaurus loader term"""
+
+    def __init__(self, label, alt=None, definition=None, note=None, generic=None, specifics=None,
+                 associations=None, usage=None, used_for=None, created=None, modified=None, weight=0, properties=None):
+        self.label = label
+        self.alt = alt
+        self.definition = definition
+        self.note = note
+        self.generic = generic
+        self.specifics = specifics or []
+        self.associations = associations or []
+        self.usage = usage
+        self.used_for = used_for or []
+        self.created = created
+        self.modified = modified
+        self.weight = int(weight)
+        self.properties = properties or {}
+
+
+@implementer(IThesaurusLoaderHandler)
+class BaseThesaurusLoaderHandler(object):
+    """Base thesaurus loader handler"""
+
+    def __init__(self, configuration):
+        self.configuration = configuration
+
+
+class XMLThesaurusLoaderHandler(BaseThesaurusLoaderHandler):
+    """Base XML thesaurus loader handler"""
+
+
+@implementer(IThesaurusLoader)
+class BaseThesaurusLoader(object):
+    """Base thesaurus loader"""
+
+    handler = None
+
+    def load(self, data, configuration=None):
+        handler = self.handler(configuration)
+        if isinstance(data, tuple):
+            data = data[1]
+        if hasattr(data, 'seek'):
+            data.seek(0)
+        description, terms = handler.read(data)
+        if configuration and configuration.language and not description.language:
+            description.language = configuration.language
+        key_store = OOBTree()
+        store = ThesaurusTermsContainer()
+        # first loop to initialize terms
+        for key, term in terms.items():
+            new_term = ThesaurusTerm(label=term.label,
+                                     alt=term.alt,
+                                     definition=term.definition,
+                                     note=term.note,
+                                     created=term.created,
+                                     modified=term.modified)
+            alsoProvides(new_term, IThesaurusLoaderTerm)
+            key_store[key] = store[term.label] = new_term
+            noLongerProvides(new_term, IThesaurusLoaderTerm)
+        # second loop to update terms links
+        for key, term in terms.items():
+            new_term = key_store[key]
+            if term.generic:
+                target = key_store.get(term.generic)
+                if target is None:
+                    target = ThesaurusTerm(label=term.generic)
+                    alsoProvides(target, IThesaurusLoaderTerm)
+                    key_store[target.label] = store[target.label] = target
+                new_term.generic = target
+            if term.specifics:
+                for specific in term.specifics:
+                    if key_store.get(specific) is None:
+                        target = ThesaurusTerm(label=specific)
+                        alsoProvides(target, IThesaurusLoaderTerm)
+                        key_store[target.label] = store[target.label] = target
+                new_term.specifics = [key_store.get(specific) for specific in term.specifics]
+                for subterm in new_term.specifics:
+                    subterm.generic = new_term
+            if term.associations:
+                for association in term.associations:
+                    if key_store.get(association) is None:
+                        target = ThesaurusTerm(label=association)
+                        alsoProvides(target, IThesaurusLoaderTerm)
+                        key_store[target.label] = store[target.label] = target
+                new_term.associations = [key_store.get(association) for association in term.associations]
+            if term.usage:
+                target = key_store.get(term.usage)
+                if target is None:
+                    target = ThesaurusTerm(label=term.usage)
+                    alsoProvides(target, IThesaurusLoaderTerm)
+                    key_store[target.label] = store[target.label] = target
+                new_term.usage = target
+                target.used_for = set(target.used_for) | {new_term}
+            if term.used_for:
+                for used in term.used_for:
+                    if key_store.get(used) is None:
+                        target = ThesaurusTerm(label=used)
+                        alsoProvides(target, IThesaurusLoaderTerm)
+                        key_store[target.label] = store[target.label] = target
+                new_term.used_for = [key_store.get(used) for used in term.used_for]
+                for synonym in new_term.used_for:
+                    synonym.usage = new_term
+        return Thesaurus(description=description, terms=store)
+
+
+@provider(IVocabularyFactory)
+class ThesaurusLoadersVocabulary(UtilityVocabulary):
+    """Thesaurus loaders vocabulary"""
+
+    interface = IThesaurusLoader
+    nameOnly = True
+
+getVocabularyRegistry().register('PyAMS thesaurus loaders', ThesaurusLoadersVocabulary)
+
+
+#
+# Thesaurus exporters classes
+#
+
+@implementer(IThesaurusExporterHandler)
+class BaseThesaurusExporterHandler(object):
+    """Base thesaurus exporter handler"""
+
+    content_type = None
+
+    def __init__(self, configuration):
+        self.configuration = configuration
+
+
+class XMLThesaurusExporterHandler(BaseThesaurusExporterHandler):
+    """Base XML thesaurus exporter handler"""
+
+    content_type = 'text/xml'
+
+    def _write(self, thesaurus, configuration=None):
+        raise NotImplementedError
+
+    def write(self, thesaurus, output, configuration=None):
+        doc = self._write(thesaurus, configuration)
+        doc.write(output, encoding='utf-8', xml_declaration=True, standalone=True, pretty_print=True)
+        return {'Content-Type': 'text/xml; encoding=utf-8'}
+
+
+@implementer(IThesaurusExporter)
+class BaseThesaurusExporter(object):
+    """Base thesaurus exporter"""
+
+    handler = None
+
+    def export(self, thesaurus, configuration=None):
+        handler = self.handler(configuration)
+        output = TemporaryFile()
+        result = handler.write(thesaurus, output, configuration)
+        request = query_request()
+        if request is not None:
+            filename = translate_string(configuration.filename or (thesaurus.name + '.xml'),
+                                        escape_slashes=True, force_lower=False, spaces='-')
+            request.response.headers.extend({'Content-Type': result.get('Content-Type', 'text/plain'),
+                                             'Content-Disposition': 'attachment; filename="{0}"'.format(filename)})
+        return output
+
+
+@provider(IVocabularyFactory)
+class ThesaurusExportersVocabulary(UtilityVocabulary):
+    """Thesaurus exporters vocabulary"""
+
+    interface = IThesaurusExporter
+    nameOnly = True
+
+getVocabularyRegistry().register('PyAMS thesaurus exporters', ThesaurusExportersVocabulary)