--- /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)