Source code for MyCapytain.resources.proto.inventory

# -*- coding: utf-8 -*-
"""
.. module:: MyCapytain.resources.proto.inventory
   :synopsis: Prototypes for repository/inventory

.. moduleauthor:: Thibault Clérice <leponteineptique@gmail.com>


"""

from MyCapytain.common.reference import URN, Reference, Citation
from MyCapytain.common.metadata import Metadata
from MyCapytain.errors import InvalidURN
from past.builtins import basestring
from collections import defaultdict
from copy import copy, deepcopy
from lxml import etree
from builtins import object
from six import text_type as str


[docs]class Resource(object): """ Resource represents any resource from the inventory :param resource: Resource representing the TextInventory :type resource: Any """ def __init__(self, resource=None): self.metadata = Metadata() self.resource = None if resource is not None: self.setResource(resource) def __getitem__(self, key): """ Direct key access function for Text objects """ if key == 0: return self elif isinstance(key, int) and 1 <= key <= len(self.parents): r = self.parents[key - 1] if isinstance(r, (list, tuple)): return r[0] else: return r elif isinstance(key, basestring): return self.__urnitem__(key) else: return None def __eq__(self, other): if self is other: return True elif not isinstance(other, self.__class__): return False elif self.resource is None: # Not totally true return hasattr(self, "urn") and hasattr(other, "urn") and self.urn == other.urn return hasattr(self, "urn") and hasattr(other, "urn") and self.urn == other.urn and self.resource == other.resource def __str__(self): raise NotImplementedError()
[docs] def export(self, format=None): raise NotImplementedError()
def __urnitem__(self, key): urn = URN(key) if len(urn) <= 2: raise ValueError("Not valid urn") elif hasattr(self, "urn") and self.urn == urn: return self else: if hasattr(self, "urn"): i = len(self.urn) else: i = 2 if isinstance(self, TextInventory): children = self.textgroups elif isinstance(self, TextGroup): children = self.works elif isinstance(self, Work): children = self.texts order = ["", "", URN.TEXTGROUP, URN.WORK, URN.VERSION] while i <= len(urn) - 1: children = children[urn.upTo(order[i])] if not hasattr(children, "urn") or str(children.urn) != urn.upTo(order[i]): error = "Unrecognized urn at " + [ "URN namespace", "CTS Namespace", "URN Textgroup", "URN Work", "URN Version" ][i] raise ValueError(error) i += 1 return children
[docs] def setResource(self, resource): """ Set the object property resource :param resource: Resource representing the TextInventory :type resource: Any :rtype: Any :returns: Input resource """ self.resource = resource self.parse(resource) return self.resource
[docs] def parse(self, resource): """ Parse the object resource :param resource: Resource representing the TextInventory :type resource: Any :rtype: List """ raise NotImplementedError()
def __getstate__(self, children=True): """ Pickling method to be called upon dumping object :return: """ dic = copy(self.__dict__) if "xml" in dic: dic["xml"] = etree.tostring(dic["xml"], encoding=str) if "resource" in dic: del dic["resource"] """ The resource is unecessary in later than parsing state if "resource" in dic: dic["resource"] = str(dic["resource"]) """ return dic def __setstate__(self, dic): """ :param dic: :return: """ self.__dict__ = dic if "xml" in dic: self.xml = etree.fromstring(dic["xml"]) return self
[docs]class Text(Resource): """ Represents a CTS Text :param resource: Resource representing the TextInventory :type resource: Any :param urn: Identifier of the Text :type urn: str """ def __init__(self, resource=None, urn=None, parents=None, subtype="Edition"): self.resource = None self.citation = None self.lang = None self.urn = None self.docname = None self.parents = list() self.subtype = subtype self.validate = None self.metadata = Metadata(keys=["label", "description", "namespaceMapping"]) if urn is not None: self.urn = URN(urn) if parents is not None: self.parents = parents self.lang = self.parents[0].lang if resource is not None: self.setResource(resource)
[docs] def translations(self, key=None): """ Get translations in given language :param key: Language ISO Code to filter on :return: """ return self.parents[0].getLang(key)
[docs] def editions(self): """ Get all editions of the texts :return: List of editions :rtype: [Text] """ return [ self.parents[0].texts[urn] for urn in self.parents[0].texts if self.parents[0].texts[urn].subtype == "Edition" ]
[docs]def Edition(resource=None, urn=None, parents=None): return Text(resource=resource, urn=urn, parents=parents, subtype="Edition")
[docs]def Translation(resource=None, urn=None, parents=None): return Text(resource=resource, urn=urn, parents=parents, subtype="Translation")
[docs]class Work(Resource): """ Represents a CTS Work CTS Work can be added to each other which would most likely happen if you take your data from multiple API or \ Textual repository. This works close to dictionary update in Python. See update :param resource: Resource representing the TextInventory :type resource: Any :param urn: Identifier of the Work :type urn: str :param parents: List of parents for current object :type parents: Tuple.<TextInventory> """ def __init__(self, resource=None, urn=None, parents=None): self.resource = None self.lang = None self.urn = None self.texts = defaultdict(Text) self.parents = list() self.metadata = Metadata(keys=["title"]) if urn is not None: self.urn = URN(urn) if parents is not None: self.parents = parents if resource is not None: self.setResource(resource)
[docs] def update(self, other): """ Merge two Work Objects. - Original (left Object) keeps his parent. - Added document overwrite text if it already exists :param other: Work object :type other: Work :return: Work Object :rtype Work: """ if not isinstance(other, Work): raise TypeError("Cannot add %s to Work" % type(other)) elif self.urn != other.urn: raise InvalidURN("Cannot add Work %s to Work %s " % (self.urn, other.urn)) self.metadata += other.metadata parents = [self] + self.parents for urn, text in other.texts.items(): if urn in self.texts: self.texts[urn] = deepcopy(text) else: self.texts[urn] = deepcopy(text) self.texts[urn].parents = parents self.texts[urn].resource = None return self
[docs] def getLang(self, key=None): """ Find a translation with given language :param key: Language to find :type key: basestring :rtype: [Text] :returns: List of availables translations """ if key is not None: return [self.texts[urn] for urn in self.texts if self.texts[urn].subtype == "Translation" and self.texts[urn].lang == key] else: return [self.texts[urn] for urn in self.texts if self.texts[urn].subtype == "Translation"]
def __len__(self): """ Get the number of text in the Work :return: Number of texts available in the inventory """ return len(self.texts)
[docs]class TextGroup(Resource): """ Represents a CTS Textgroup CTS TextGroup can be added to each other which would most likely happen if you take your data from multiple API or \ Textual repository. This works close to dictionary update in Python. See update :param resource: Resource representing the TextInventory :type resource: Any :param urn: Identifier of the TextGroup :type urn: str :param parents: List of parents for current object :type parents: Tuple.<TextInventory> """ def __init__(self, resource=None, urn=None, parents=None): self.resource = None self.urn = None self.works = defaultdict(Work) self.parents = list() self.metadata = Metadata(keys=["groupname"]) if urn is not None: self.urn = URN(urn) if parents: self.parents = parents if resource is not None: self.setResource(resource)
[docs] def update(self, other): """ Merge two Textgroup Objects. - Original (left Object) keeps his parent. - Added document merges with work if it already exists :param other: Textgroup object :type other: TextGroup :return: Textgroup Object :rtype: TextGroup """ if not isinstance(other, TextGroup): raise TypeError("Cannot add %s to TextGroup" % type(other)) elif str(self.urn) != str(other.urn): raise InvalidURN("Cannot add TextGroup %s to TextGroup %s " % (self.urn, other.urn)) self.metadata += other.metadata parents = [self] + self.parents for urn, work in other.works.items(): if urn in self.works: self.works[urn].update(deepcopy(work)) else: self.works[urn] = deepcopy(work) self.works[urn].parents = parents self.works[urn].resource = None return self
def __len__(self): """ Get the number of text in the Textgroup :return: Number of texts available in the inventory """ return len([ text for work in self.works.values() for text in work.texts.values() ])
[docs]class TextInventory(Resource): """ Initiate a TextInventory resource :param resource: Resource representing the TextInventory :type resource: Any :param id: Identifier of the TextInventory :type id: str """ def __init__(self, resource=None, id=None): self.resource = None self.textgroups = defaultdict(TextGroup) self.id = id self.parents = list() if resource is not None: self.setResource(resource) def __len__(self): """ Get the number of text in the Inventory :return: Number of texts available in the inventory """ return len([ text for tg in self.textgroups.values() for work in tg.works.values() for text in work.texts.values() ])