Source code for MyCapytain.common.reference._base

from MyCapytain.common.base import Exportable
from MyCapytain.common.constants import get_graph, Mimetypes, RDF_NAMESPACES
from MyCapytain.errors import CitationDepthError
from copy import copy
from typing import Tuple
from abc import abstractmethod


[docs]class BaseCitationSet(Exportable): """ A citation set is a collection of citations that optionnaly can be matched using a .match() function :param children: List of Citation :type children: [BaseCitation] """ def __repr__(self): return "<MyCapytain.common.reference.BaseCitationSet object at %s>" % id(self) EXPORT_TO = [Mimetypes.JSON.DTS.Std] def __init__(self, children=None): self._children = [] if children: self.children = children @property def children(self): """ Children of a citation :rtype: [BaseCitation] """ return self._children or [] @children.setter def children(self, val: list): """ Sets children :param val: List of citation children """ final_value = [] if val is not None: for citation in val: if citation is None: continue elif not isinstance(citation, (BaseCitation, type(self))): raise TypeError("Citation children should be Citation") else: if isinstance(self, BaseCitation): citation.root = self.root else: citation.root = self final_value.append(citation) self._children = final_value def add_child(self, child): """ Adds a child to the CitationSet :param child: Child citation to add :return: """ if isinstance(child, BaseCitation): self._children.append(child) def __iter__(self): """ Iteration method Loop over the citation children :Example: >>> c = BaseCitationSet(name="line") >>> b = BaseCitationSet(name="poem", children=[c]) >>> b2 = BaseCitationSet(name="paragraph") >>> a = BaseCitationSet(name="book", children=[b]) >>> [e for e in a] == [a, b, c, b2], """ for child in self.children: yield from child @abstractmethod def match(self, passageId): """ Given a specific passageId, matches the citation to a specific citation object :param passageId: Passage Identifier :return: Citation that matches the passageId :rtype: BaseCitation """ @property def depth(self): """ Depth of the citation scheme: if multiple scheme are available, the deepest one from the current node is chosen (if there is two possibilities, a->((b->c)(d->e->f)) 4 is the depth .. example:: If we have a Book, Poem, Line system, and the citation we are looking at is Poem, depth is 2 :rtype: int :return: Depth of the citation scheme """ if len(self.children): return max([child.depth for child in self.children]) else: return 0 def __getitem__(self, item): """ Returns the citations at the given level. :param item: Citation level :type item: int :rtype: list(BaseCitation) or BaseCitation """ if item < 0: _item = self.depth + item if _item < 0: raise CitationDepthError("The negative %s is too small " % _item) item = _item if item == 0: yield from self.children else: for child in self.children: yield from child[item - 1] def __len__(self): """ Number of citation schemes covered by the object :rtype: int :returns: Number of nested citations """ return len(self.children) + sum([len(child) for child in self.children]) def __getstate__(self): """ Pickling method :return: dict """ return copy(self.__dict__) def __setstate__(self, dic): self.__dict__ = dic return self def is_empty(self) -> bool: """ Check if the citation has not been set :return: True if nothing was setup :rtype: bool """ return len(self.children) == 0 def is_root(self) -> bool: """ Check if the Citation is the root :return: """ return True def __export__(self, output=None, context=False, namespace_manager=None, **kwargs): if output == Mimetypes.JSON.DTS.Std: if not namespace_manager: namespace_manager = get_graph().namespace_manager _out = [ cite.export(output, context=False, namespace_manager=namespace_manager) for cite in self.children ] if context: cite_structure_term = str(namespace_manager.qname(RDF_NAMESPACES.DTS.term("citeStructure"))) _out = { "@context": { cite_structure_term.split(":")[0]: str(RDF_NAMESPACES.DTS) }, cite_structure_term: _out } return _out
class BaseCitation(BaseCitationSet): def __repr__(self): """ :return: String representation of the object :rtype: str """ return "<{} name({})>".format(type(self).__name__, self.name) def __init__(self, name: str=None, children: list=None, root: BaseCitationSet=None): """ Initialize a BaseCitation object :param name: Name of the citation level :type name: str :param children: list of children :type children: [BaseCitation] :param root: Root of the citation group :type root: BaseCitationSet """ super(BaseCitation, self).__init__() self._name = None self._root = None self.name = name self.children = children self.root = root def is_root(self) -> str: """ :return: If the current object is the root of the citation set, True :rtype: bool """ return self._root is None def is_set(self) -> bool: """ Checks that the current object is set :rtype: bool """ return self.name is not None @property def root(self) -> BaseCitationSet: """ Returns the root of the citation set :return: Root of the Citation set :rtype: BaseCitationSet """ if self._root is None: return self return self._root @root.setter def root(self, value): """ Set the root to which the current citation is connected to :param value: CitationSet root of the Citation graph :type value: BaseCitationSet :return: """ self._root = value @property def name(self) -> str: """ Type of the citation represented :rtype: str :example: Book, Chapter, Textpart, Section, Poem... """ return self._name @name.setter def name(self, val): self._name = val def __iter__(self): """ Iteration method Loop over the citation children :Example: >>> c = BaseCitation(name="line") >>> b = BaseCitation(name="poem", children=[c]) >>> b2 = BaseCitation(name="paragraph") >>> a = BaseCitation(name="book", children=[b]) >>> [e for e in a] == [a, b, c, b2], """ yield from [self] for child in self.children: yield from child def __export__(self, output=None, context=False, namespace_manager=None, **kwargs): if output == Mimetypes.JSON.DTS.Std: if not namespace_manager: namespace_manager = get_graph().namespace_manager cite_type_term = str(namespace_manager.qname(RDF_NAMESPACES.DTS.term("citeType"))) cite_structure_term = str(namespace_manager.qname(RDF_NAMESPACES.DTS.term("citeStructure"))) _out = { cite_type_term: self.name } if not self.is_empty(): _out[cite_structure_term] = [ cite.export(output, context=False, namespace_manager=namespace_manager) for cite in self.children ] if context: _out["@context"] = {cite_type_term.split(":")[0]: str(RDF_NAMESPACES.DTS)} return _out @property def depth(self) -> int: """ Depth of the citation scheme .. example:: If we have a Book, Poem, Line system, and the citation we are looking at is Poem, depth is 1 :rtype: int :return: Depth of the citation scheme """ if len(self.children): return 1 + max([child.depth for child in self.children]) else: return 1
[docs]class BaseReference(tuple): """ BaseReference represents a passage identifier, range or not It is made of two major properties : .start and .end To check if the object is a range, you can use the method .is_range() """ def __new__(cls, *refs): if len(refs) == 1 and not isinstance(refs[0], tuple): refs = refs[0], None obj = tuple.__new__(cls, refs) return obj def is_range(self) -> int: return bool(self[1]) @property def start(self): """ Quick access property for start part :rtype: str """ return self[0] @property def end(self): """ Quick access property for reference end list :rtype: str """ return self[1]
[docs]class BaseReferenceSet(tuple): """ A BaseReferenceSet is a set of Reference (= a bag of identifier) that can carry citation and level information (what kind of reference is this reference ? Where am I in the levels of the current document ?) It can be iterate like a tuple and has a .citation and .level property """ def __new__(cls, *refs, citation: BaseCitationSet=None, level: int=1): if len(refs) == 1 and not isinstance(refs, BaseReference): refs = refs[0] obj = tuple.__new__(cls, refs) obj._citation = None obj._level = level if citation is not None: obj._citation = citation return obj @property def citation(self) -> BaseCitationSet: return self._citation @property def level(self) -> int: """ Level represents the depth at which lies the reference. eg. depth is the number of floors, level is the actual floor number. """ return self._level def __repr__(self): return "<{typ} ({repr}) level:{level}, citation:{citation}>".format( typ=type(self).__name__, repr=", ".join([str(s) for s in self]), level=self.level, citation=str(self.citation) )
[docs]class NodeId(object): """ Collection of directional references for a Tree :param identifier: Current object identifier :type identifier: str :param children: Current node Children's Identifier :type children: [str] :param parent: Parent of the current node :type parent: str :param siblings: Previous and next node of the current node :type siblings: str :param depth: Depth of the node in the global hierarchy of the text tree :type depth: int """ def __init__(self, identifier=None, children=None, parent=None, siblings=(None, None), depth=None): self._children = children or [] self._parent = parent self._prev_id, self._next_id = siblings self._identifier = identifier self._depth = depth @property def depth(self) -> int: """ Depth of the node in the global hierarchy of the text tree """ return self._depth @property def childIds(self) -> BaseReferenceSet: """ Children Ids """ return self._children @property def firstId(self) -> BaseReference: """ First child Id """ if len(self._children) == 0: return None return self._children[0] @property def lastId(self) -> BaseReference: """ Last child id """ if len(self._children) == 0: return None return self._children[-1] @property def parentId(self) -> BaseReference: """ Parent Id """ return self._parent @property def siblingsId(self) -> Tuple[BaseReference, BaseReference]: """ Siblings Id """ return self.prevId, self.nextId @property def prevId(self) -> BaseReference: """ Previous Id (Sibling) """ return self._prev_id @property def nextId(self) -> BaseReference: """ Next Id """ return self._next_id @property def id(self): """Current object identifier :rtype: str """ return self._identifier