Skip to content

Commit

Permalink
Release 1.0.3 (#84)
Browse files Browse the repository at this point in the history
-Added support for .update() on textgroup and work ( Issue #53 )
- Fixed bug in Python 2 where URN != URN would not be the same as not URN == URN
- Now parsing URN from XML on retrieval of inventory for XML Inventory
- Added len to Work and Textgroup (Returns number of text in object)
- Enhanced documentation
- Added Pypy classifier
  • Loading branch information
PonteIneptique committed Sep 20, 2016
1 parent 5322531 commit d5cffe7
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 84 deletions.
2 changes: 1 addition & 1 deletion MyCapytain/__init__.py
Expand Up @@ -10,5 +10,5 @@
"""

__name__ = "MyCapytain"
__version__ = "1.0.2"
__version__ = "1.0.3"
__all__ = ["common", "retrievers", "resources"]
29 changes: 22 additions & 7 deletions MyCapytain/common/reference.py
Expand Up @@ -10,11 +10,9 @@
"""
from __future__ import unicode_literals

from collections import defaultdict
from past.builtins import basestring
from six import text_type as str
from builtins import \
range, object
from builtins import range, object
from copy import copy
import re

Expand Down Expand Up @@ -196,9 +194,18 @@ def __eq__(self, other):
>>> (a == b) == False
>>> (c == b) == True
"""
return (isinstance(other, self.__class__)
return (isinstance(other, type(self))
and self.reference == str(other))

def __ne__(self, other):
""" Inequality checker for Reference object
:param other: An object to be checked against
:rtype: boolean
:returns: Equality between other and self
"""
return not self.__eq__(other)

def __model(self):
""" 3-Tuple model for references
Expand Down Expand Up @@ -431,10 +438,18 @@ def __eq__(self, other):
:Example:
>>> a = URN(urn="urn:cts:latinLit:phi1294.phi002.perseus-lat2:1.1")
>>> b = URN(urn="urn:cts:latinLit:phi1294.phi002:1.1")
>>> (b == a) == False #
>>> (b == a) == False
"""
return isinstance(other, type(self)) and str(self) == str(other)

def __ne__(self, other):
""" Inequality checker for Reference object
:param other: An object to be checked against
:rtype: boolean
:returns: Equality between other and self
"""
return (isinstance(other, self.__class__)
and self.__str__() == str(other))
return not self.__eq__(other)

def __str__(self):
""" Return full initial urn
Expand Down
7 changes: 6 additions & 1 deletion MyCapytain/errors.py
Expand Up @@ -14,4 +14,9 @@ class InvalidSiblingRequest(Exception):
""" This error is thrown when one attempts to get previous or next passage on a passage with a range of different
depth, ex. : 1-2.25
"""
pass
pass


class InvalidURN(Exception):
""" This error is thrown when URN are not valid
"""
8 changes: 6 additions & 2 deletions MyCapytain/resources/inventory.py
Expand Up @@ -236,6 +236,7 @@ def parse(self, resource):
:returns: None
"""
self.xml = xmlparser(resource)
self.urn = URN(self.xml.get("urn"))

if self.subtype == "Translation":
lang = self.xml.get("{http://www.w3.org/XML/1998/namespace}lang")
Expand Down Expand Up @@ -344,6 +345,7 @@ def parse(self, resource):
:param type: basestring, etree._Element
"""
self.xml = xmlparser(resource)
self.urn = URN(self.xml.get("urn"))

lang = self.xml.get("{http://www.w3.org/XML/1998/namespace}lang")
if lang is not None:
Expand Down Expand Up @@ -421,11 +423,13 @@ def export(self, output="xml"):
def parse(self, resource):
""" Parse a resource
:param resource: Element rerpresenting the textgroup
:param type: basestring, etree._Element
:param resource: Element representing the textgroup
:param type: basestring or etree._Element
"""
self.xml = xmlparser(resource)

self.urn = URN(self.xml.get("urn"))

for child in self.xml.xpath("ti:groupname", namespaces=NS):
lg = child.get("{http://www.w3.org/XML/1998/namespace}lang")
if lg is not None:
Expand Down
167 changes: 122 additions & 45 deletions MyCapytain/resources/proto/inventory.py
Expand Up @@ -10,21 +10,22 @@

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
from copy import copy, deepcopy
from lxml import etree
from builtins import object
from six import text_type as str


class Resource(object):
""" Resource represents any resource from the inventory """
def __init__(self, resource=None):
""" Initiate a TextInventory resource
""" Resource represents any resource from the inventory
:param resource: Resource representing the TextInventory
:type resource: Any
"""
: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:
Expand All @@ -43,7 +44,7 @@ def __getitem__(self, key):
elif isinstance(key, basestring):
return self.__urnitem__(key)
else:
None
return None

def __eq__(self, other):
if self is other:
Expand Down Expand Up @@ -144,15 +145,14 @@ def __setstate__(self, dic):

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"):
""" Initiate a Work resource
:param resource: Resource representing the TextInventory
:type resource: Any
:param urn: Identifier of the Text
:type urn: str
"""
self.resource = None
self.citation = None
self.lang = None
self.urn = None
Expand Down Expand Up @@ -203,17 +203,19 @@ def Translation(resource=None, urn=None, parents=None):

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):
""" Initiate a Work resource
: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>
"""
self.resource = None
self.lang = None
self.urn = None
self.texts = defaultdict(Text)
Expand All @@ -229,6 +231,34 @@ def __init__(self, resource=None, urn=None, parents=None):
if resource is not None:
self.setResource(resource)

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

def getLang(self, key=None):
""" Find a translation with given language
Expand All @@ -242,20 +272,29 @@ def getLang(self, key=None):
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)


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):
""" Initiate a TextGroup resource
: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>
"""
self.resource = None
self.urn = None
self.works = defaultdict(Work)
self.parents = list()
Expand All @@ -270,32 +309,70 @@ def __init__(self, resource=None, urn=None, parents=None):
if resource is not None:
self.setResource(resource)

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()
])


class TextInventory(Resource):
""" Represents a CTS Inventory file
""" 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):
""" Initiate a TextInventory resource
:param resource: Resource representing the TextInventory
:type resource: Any
:param id: Identifier of the TextInventory
:type id: str
"""
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()
for tg in self.textgroups.values()
for work in tg.works.values()
for text in work.texts.values()
])
17 changes: 14 additions & 3 deletions setup.py
Expand Up @@ -21,9 +21,20 @@
"mock==1.3.0",
"xmlunittest>=0.3.2"
],
extras_require = {
"DOC" : ["Sphinx==1.3.1"]
extras_require={
"DOC": ["Sphinx==1.3.1"]
},
test_suite="tests",
zip_safe=False
zip_safe=False,
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Intended Audience :: Education",
"Intended Audience :: Information Technology",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Topic :: Software Development :: Libraries",
"Topic :: Text Processing :: Markup :: XML",
"Topic :: Text Processing :: General"
]
)

0 comments on commit d5cffe7

Please sign in to comment.