Skip to content

Commit

Permalink
Commentary (#126)
Browse files Browse the repository at this point in the history
* First implementation of commentary type

Will start with unit tests soon.

* First unit tests for commentary type

* Unit tests finished
  • Loading branch information
sonofmun committed Apr 20, 2017
1 parent a1ada33 commit aee65f7
Show file tree
Hide file tree
Showing 11 changed files with 424 additions and 39 deletions.
9 changes: 6 additions & 3 deletions MyCapytain/resolvers/cts/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from MyCapytain.errors import InvalidURN, UnknownObjectError, UndispatchedTextError
from MyCapytain.resolvers.prototypes import Resolver
from MyCapytain.resources.collections.cts import TextInventory, TextGroup, Work, Citation, Text as InventoryText, \
Translation, Edition
Translation, Edition, Commentary
from MyCapytain.resources.prototypes.cts.inventory import TextInventoryCollection
from MyCapytain.resources.texts.locals.tei import Text
from MyCapytain.resolvers.utils import CollectionDispatcher
Expand Down Expand Up @@ -230,8 +230,8 @@ def __getTextMetadata__(self,
(urn is None or (urn is not None and text.urn.upTo(__PART) == urn)) and
(text.citation is not None) and
(
category not in ["edition", "translation"] or
(category in ["edition", "translation"] and category.lower() == text.subtype.lower())
category not in ["edition", "translation", "commentary"] or
(category in ["edition", "translation", "commentary"] and category.lower() == text.subtype.lower())
)
]
if pagination:
Expand Down Expand Up @@ -308,6 +308,9 @@ def getMetadata(self, objectId=None, **filters):
elif isinstance(text, Translation):
x = Translation(urn=txt_urn, parent=inventory.textgroups[tg_urn].works[wk_urn], lang=text.lang)
x.citation = text.citation
elif isinstance(text, Commentary):
x = Commentary(urn=txt_urn, parent=inventory.textgroups[tg_urn].works[wk_urn], lang=text.lang)
x.citation = text.citation

return inventory[objectId]

Expand Down
23 changes: 22 additions & 1 deletion MyCapytain/resources/collections/cts.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from MyCapytain.resources.prototypes.cts import inventory as cts
from MyCapytain.common.reference import Citation as CitationPrototype
from MyCapytain.common.utils import xmlparser
from MyCapytain.common.constants import NS, Mimetypes
from MyCapytain.common.constants import NS, Mimetypes, NAMESPACES


class Citation(CitationPrototype):
Expand Down Expand Up @@ -114,6 +114,12 @@ def parse_metadata(obj, xml):
if lg is not None:
obj.set_cts_property("label", child.text, lg)

# Added for commentary
for child in xml.xpath("ti:about", namespaces=NS):
#lg = child.get("{http://www.w3.org/XML/1998/namespace}lang")
#if lg is not None:
obj.set_link(NAMESPACES.CTS.term("about"), child.get('urn'))

obj.citation = Citation.ingest(xml, obj.citation, "ti:online/ti:citationMapping/ti:citation")

"""
Expand Down Expand Up @@ -154,6 +160,19 @@ def parse(resource, parent=None):
Translation.parse_metadata(o, xml)
return o

class Commentary(cts.PrototypeCommentary, Text):
""" Create a commentary subtyped PrototypeText object
"""
@staticmethod
def parse(resource, parent=None):
xml = xmlparser(resource)
lang = xml.get("{http://www.w3.org/XML/1998/namespace}lang")

o = Commentary(urn=xml.get("urn"), parent=parent)
if lang is not None:
o.lang = lang
Commentary.parse_metadata(o, xml)
return o

class Work(cts.PrototypeWork):
""" Represents a CTS Textgroup in XML
Expand Down Expand Up @@ -183,6 +202,8 @@ def parse(resource, parent=None):
# Parse children
xpathDict(xml=xml, xpath='ti:edition', cls=Edition, parent=o)
xpathDict(xml=xml, xpath='ti:translation', cls=Translation, parent=o)
# Added for commentary
xpathDict(xml=xml, xpath='ti:commentary', cls=Commentary, parent=o)

return o

Expand Down
63 changes: 62 additions & 1 deletion MyCapytain/resources/prototypes/cts/inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class PrototypeCTSCollection(Collection):
CTSMODEL = "CTSCollection"
DC_TITLE_KEY = None
CTS_PROPERTIES = []
CTS_LINKS = []

EXPORT_TO = [Mimetypes.PYTHON.ETREE]
DEFAULT_EXPORT = Mimetypes.PYTHON.ETREE
Expand Down Expand Up @@ -96,6 +97,40 @@ def set_cts_property(self, prop, value, lang=None):
(self.metadata.asNode(), prop, value)
)

# new for commentary
def get_link(self, prop):
""" Get given link in CTS Namespace
.. example::
collection.get_link("about")
:param prop: Property to get (Without namespace)
:return: whole set of values
:rtype: list
"""
return list([o for o in self.graph.objects(self.metadata.asNode(), prop)])

def set_link(self, prop, value):
""" Set given link in CTS Namespace
.. example::
collection.set_link(NAMESPACES.CTS.about, "urn:cts:latinLit:phi1294.phi002")
:param prop: Property to set (Without namespace)
:param value: Value to set for given property
"""
# https://rdflib.readthedocs.io/en/stable/
# URIRef == identifiers (urn, http, URI in general)
# Literal == String or Number (can have a language)
# BNode == Anonymous nodes (So no specific identifier)
# eg. BNode : Edition(MartialEpigrams:URIRef) ---has_metadata--> Metadata(BNode)
if not isinstance(value, URIRef):
value = URIRef(value)

self.graph.add(
(self.metadata.asNode(), prop, value)
)

def __xml_export_generic__(self, attrs, namespaces=False, lines="\n", members=None):
""" Shared method for Mimetypes.XML.CTS Export
Expand Down Expand Up @@ -151,6 +186,7 @@ class PrototypeText(ResourceCollection, PrototypeCTSCollection):
MODEL_URI = URIRef(NAMESPACES.DTS.resource)
EXPORT_TO = [Mimetypes.XML.CTS]
CTS_PROPERTIES = [NAMESPACES.CTS.label, NAMESPACES.CTS.description]
CTS_LINKS = [NAMESPACES.CTS.about]
SUBTYPE = "unknown"

def __init__(self, urn="", parent=None, lang=None):
Expand Down Expand Up @@ -228,7 +264,7 @@ def __export__(self, output=None, domain="", namespaces=True, lines="\n"):
:type output: basestring
:param domain: Domain to prefix IDs when necessary
:type domain: str
:returns: Desired output formated resource
:returns: Desired output formatted resource
"""
if output == Mimetypes.XML.CTS:
attrs = {"urn": self.id, "xml:lang": self.lang}
Expand All @@ -248,6 +284,17 @@ def __export__(self, output=None, domain="", namespaces=True, lines="\n"):
)
)

for pred in self.CTS_LINKS:
# For each predicate in CTS_LINKS
for obj in self.graph.objects(self.metadata.asNode(), pred):
# For each item in the graph connected to the current item metadata as object through the predicate "pred"
strings.append(
make_xml_node(
self.graph, pred, attributes={"urn": str(obj)}, complete=True
)
# <pref urn="obj.language"/>
)

# Citation !
if self.citation is not None:
strings.append(
Expand Down Expand Up @@ -329,6 +376,20 @@ class PrototypeTranslation(PrototypeText):
MODEL_URI = URIRef(NAMESPACES.DTS.resource)
SUBTYPE = "translation"

class PrototypeCommentary(PrototypeText):
""" Represents a CTS Commentary
:param urn: Identifier of the PrototypeText
:type urn: str
:param parent: Parent of current item
:type parent: PrototypeWork
:param lang: Language of the commentary
:type lang: Lang
"""
TYPE_URI = NAMESPACES.CTS.term("commentary")
MODEL_URI = URIRef(NAMESPACES.DTS.resource)
SUBTYPE = "commentary"


class PrototypeWork(PrototypeCTSCollection):
""" Represents a CTS PrototypeWork
Expand Down
12 changes: 6 additions & 6 deletions tests/resolvers/cts/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,16 +311,16 @@ def test_getMetadata_full(self):
"Members of Inventory should be TextGroups"
)
self.assertEqual(
len(metadata.descendants), 32,
"There should be as many descendants as there is edition, translation, works and textgroup"
len(metadata.descendants), 33,
"There should be as many descendants as there is edition, translation, commentaries, works and textgroup"
)
self.assertEqual(
len(metadata.readableDescendants), 15,
"There should be as many readable descendants as there is edition, translation"
len(metadata.readableDescendants), 16,
"There should be as many readable descendants as there is edition, translation, and commentaries"
)
self.assertEqual(
len([x for x in metadata.readableDescendants if isinstance(x, Text)]), 15,
"There should be 14 editions + 1 translations in readableDescendants"
len([x for x in metadata.readableDescendants if isinstance(x, Text)]), 16,
"There should be 14 editions + 1 translations + 1 commentary in readableDescendants"
)
self.assertEqual(
len(metadata.export(output=Mimetypes.PYTHON.ETREE).xpath("//ti:edition[@urn='urn:cts:latinLit:phi1294.phi002.perseus-lat2']", namespaces=NS)), 1,
Expand Down
71 changes: 46 additions & 25 deletions tests/resolvers/cts/test_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from MyCapytain.resources.prototypes.metadata import Collection
from MyCapytain.resources.collections.cts import TextInventory
from MyCapytain.resources.prototypes.cts.inventory import PrototypeTextGroup, PrototypeText as TextMetadata, \
PrototypeTranslation, PrototypeTextInventory, TextInventoryCollection
PrototypeTranslation, PrototypeTextInventory, TextInventoryCollection, PrototypeCommentary
from MyCapytain.resources.prototypes.text import Passage
from MyCapytain.resolvers.utils import CollectionDispatcher
from unittest import TestCase
Expand Down Expand Up @@ -58,13 +58,17 @@ def test_get_capabilities(self):
["./tests/testing_data/farsiLit"]
)
self.assertEqual(
len(Repository.__getTextMetadata__()[0]), 4,
len(Repository.__getTextMetadata__()[0]), 5,
"General no filter works"
)
self.assertEqual(
len(Repository.__getTextMetadata__(category="edition")[0]), 2,
"Type filter works"
)
self.assertEqual(
len(Repository.__getTextMetadata__(category="commentary")[0]), 1,
"Type filter works"
)
self.assertEqual(
len(Repository.__getTextMetadata__(lang="ger")[0]), 1,
"Filtering on language works"
Expand All @@ -77,6 +81,10 @@ def test_get_capabilities(self):
len(Repository.__getTextMetadata__(category="translation", lang="ger")[0]), 1,
"Type filter + lang works"
)
self.assertEqual(
len(Repository.__getTextMetadata__(category="commentary", lang="lat")[0]), 1,
"Type filter + lang works"
)
self.assertEqual(
len(Repository.__getTextMetadata__(page=1, limit=2, pagination=True)[0]), 2,
"Pagination works without other filters"
Expand All @@ -90,7 +98,7 @@ def test_get_capabilities(self):
"URN Filtering works"
)
self.assertEqual(
len(Repository.__getTextMetadata__(urn="urn:cts:latinLit")[0]), 1,
len(Repository.__getTextMetadata__(urn="urn:cts:latinLit")[0]), 2,
"URN Filtering works"
)
self.assertEqual(
Expand Down Expand Up @@ -420,17 +428,17 @@ def test_getMetadata_full(self):
"Members of Inventory should be TextGroups"
)
self.assertEqual(
len(metadata.descendants), 43,
"There should be as many descendants as there is edition, translation, works and textgroup + 1 for "
"default inventory"
len(metadata.descendants), 44,
"There should be as many descendants as there is edition, translation, commentary, works and textgroup + 1 "
"for default inventory"
)
self.assertEqual(
len(metadata.readableDescendants), 25,
"There should be as many readable descendants as there is edition, translation(25 ed+tr)"
len(metadata.readableDescendants), 26,
"There should be as many readable descendants as there is edition, translation, commentary (26 ed+tr+cm)"
)
self.assertEqual(
len([x for x in metadata.readableDescendants if isinstance(x, TextMetadata)]), 25,
"There should be 24 editions + 1 translations in readableDescendants"
len([x for x in metadata.readableDescendants if isinstance(x, TextMetadata)]), 26,
"There should be 24 editions + 1 translation + 1 commentary in readableDescendants"
)
self.assertEqual(
len(metadata.export(output=Mimetypes.PYTHON.ETREE).xpath(
Expand All @@ -456,16 +464,16 @@ def test_getMetadata_subset(self):
"Members of PrototypeWork should be Texts"
)
self.assertEqual(
len(metadata.descendants), 1,
"There should be as many descendants as there is edition, translation"
len(metadata.descendants), 2,
"There should be as many descendants as there is edition, translation, commentary"
)
self.assertEqual(
len(metadata.readableDescendants), 1,
"There should be 1 edition in readableDescendants"
len(metadata.readableDescendants), 2,
"There should be 1 edition + 1 commentary in readableDescendants"
)
self.assertEqual(
len([x for x in metadata.readableDescendants if isinstance(x, TextMetadata)]), 1,
"There should be 1 edition in readableDescendants"
len([x for x in metadata.readableDescendants if isinstance(x, TextMetadata)]), 2,
"There should be 1 edition + 1 commentary in readableDescendants"
)
self.assertIsInstance(
metadata.parent, PrototypeTextGroup,
Expand All @@ -480,10 +488,10 @@ def test_getMetadata_subset(self):
"//ti:edition[@urn='urn:cts:latinLit:phi1294.phi002.perseus-lat2']", namespaces=NS)), 1,
"There should be one node in exported format corresponding to lat2"
)
self.assertEqual(
self.assertCountEqual(
[x["@id"] for x in metadata.export(output=Mimetypes.JSON.DTS.Std)["@graph"]["dts:members"]],
["urn:cts:latinLit:phi1294.phi002.perseus-lat2"],
"There should be one member in DTS JSON"
["urn:cts:latinLit:phi1294.phi002.opp-eng3", "urn:cts:latinLit:phi1294.phi002.perseus-lat2"],
"There should be two members in DTS JSON"
)

tr = self.resolver.getMetadata(objectId="urn:cts:greekLit:tlg0003.tlg001.opp-fre1")
Expand All @@ -499,6 +507,19 @@ def test_getMetadata_subset(self):
"Description should be the right one"
)

cm = self.resolver.getMetadata(objectId="urn:cts:latinLit:phi1294.phi002.opp-eng3")
self.assertIsInstance(
cm, PrototypeCommentary, "Metadata should be commentary"
)
self.assertEqual(
cm.lang, "eng", "Language is English"
)
self.assertIn(
"Introduction to Martial's Epigrammata",
cm.get_description("eng"),
"Description should be the right one"
)

def test_getSiblings(self):
""" Ensure getSiblings works well """
previous, nextious = self.resolver.getSiblings(
Expand Down Expand Up @@ -618,8 +639,8 @@ def dispatchGreekLit(collection, path=None, **kwargs):
greek_stuff = resolver.getMetadata("urn:perseus:greekLit")
farsi_stuff = resolver.getMetadata("urn:perseus:farsiLit")
self.assertEqual(
len(latin_stuff.readableDescendants), 19,
"There should be 19 readable descendants in Latin"
len(latin_stuff.readableDescendants), 20,
"There should be 20 readable descendants in Latin"
)
self.assertIsInstance(
latin_stuff, PrototypeTextInventory, "should be textinventory"
Expand Down Expand Up @@ -714,8 +735,8 @@ def dispatchGreekLit(collection, path=None, **kwargs):
latin_stuff, greek_stuff, farsi_stuff = TextInventory.parse(latin_stuff), TextInventory.parse(greek_stuff),\
TextInventory.parse(farsi_stuff)
self.assertEqual(
len(latin_stuff.readableDescendants), 19,
"There should be 19 readable descendants in Latin"
len(latin_stuff.readableDescendants), 20,
"There should be 20 readable descendants in Latin"
)
self.assertIsInstance(
latin_stuff, PrototypeTextInventory, "should be textinventory"
Expand All @@ -735,6 +756,6 @@ def dispatchGreekLit(collection, path=None, **kwargs):
get_graph().remove((None, None, None))
all = TextInventory.parse(all)
self.assertEqual(
len(all.readableDescendants), 25,
"There should be all 25 readable descendants in the master collection"
len(all.readableDescendants), 26,
"There should be all 26 readable descendants in the master collection"
)

0 comments on commit aee65f7

Please sign in to comment.