This part is probably gonna be the most technical of all simply because :
- It assumes you are able to read the Python code of Nemo (understand it a little) and some Flask understanding.
- We are gonna need to invent something from the ground up.
We will create 2 pages in this context :
- A page to display a text in full. Maybe some texts would be better served as a complete unit. That's what we'll provide
- A way to serve our tutorial as static pages over our Nemo tutorial
Things are getting a little out of hand : the app.py
is quite filled. So what we'll do before hand is divise things :
- We create a folder
application
- We put a
__init__.py
in it - We put a
corpus.py
in it - We cut/paste the all the resolver related data intop this new file
- We update
app.py
to import the resolverfrom application.corpus import resolver
- We put a
from MyCapytain.resources.prototypes.cts.inventory import CtsTextInventoryCollection, CtsTextInventoryMetadata
from MyCapytain.resolvers.utils import CollectionDispatcher
from capitains_nautilus.cts.resolver import NautilusCTSResolver
# Setting up the collections
general_collection = CtsTextInventoryCollection()
poetry = CtsTextInventoryMetadata("poetry_collection", parent=general_collection)
poetry.set_label("Poetry", "eng")
poetry.set_label("Poésie", "fre")
priapeia = CtsTextInventoryMetadata("priapeia_collection", parent=general_collection)
priapeia.set_label("Priapeia", "eng")
priapeia.set_label("Priapées", "fre")
misc = CtsTextInventoryMetadata("id:misc", parent=general_collection)
misc.set_label("Miscellaneous", "eng")
misc.set_label("Textes Divers", "fre")
organizer = CollectionDispatcher(general_collection, default_inventory_name="id:misc")
@organizer.inventory("priapeia_collection")
def organize_my_priapeia(collection, path=None, **kwargs):
if collection.id.startswith("urn:cts:latinLit:phi1103"):
return True
return False
@organizer.inventory("poetry_collection")
def organize_my_poetry(collection, path=None, **kwargs):
# If we are not dealing with Priapeia
if not collection.id.startswith("urn:cts:latinLit:phi1103"):
# Textgroups have a wonderful shortcut to their editions and translations : .readableDescendants
for text in collection.readableDescendants:
for citation in text.citation:
if citation.name == "line":
return True
return False
# Parsing the data
resolver = NautilusCTSResolver(["corpora/additional-texts", "corpora/priapeia"], dispatcher=organizer)
resolver.parse()
If you have tiny texts, or texts that are meant to be displayed in full, you'll probably appreciate to have the ability to do that. Unfortunately, it is not covered by Nemo in its raw version. So what we'll do is write a new route.
The best, simplest way to build onto Nemo is to extend Nemo. We'll do that simply by creating a new extension.py
file where we'll write :
from flask_nemo import Nemo
class MyNemo(Nemo):
""" We'll write more there later """
and we'll change our app.py
right now to use this thing :
# Before we had
from flask_nemo import Nemo
# Now we have
from extension import MyNemo
# ...
nemo = MyNemo(
name="InstanceNemo",
app=flask_app,
# ...
)
To write our own route, we are gonna need to do few things :
- We'll read and reuse the code of the route for the normal text (The route is
r_passage
). - We'll register the route onto Nemo
Because we are doing a simple copy/edit of Nemo r_passage
route where passage could be empty, I'd propose the following :
from flask_nemo import Nemo
# We added all imports used by r_passage and now r_full_text
from flask import Markup, redirect, url_for
from MyCapytain.resources.prototypes.cts.inventory import CtsWorkMetadata, CtsEditionMetadata
from MyCapytain.errors import UnknownCollection
from MyCapytain.common.constants import Mimetypes
class MyNemo(Nemo):
# We removed the `subreference` parameter of the original function
def r_full_text(self, objectId, lang=None):
""" Retrieve the text of the passage
:param objectId: Collection identifier
:type objectId: str
:param lang: Lang in which to express main data
:type lang: str
:return: Template, collections metadata and Markup object representing the text
:rtype: {str: Any}
"""
collection = self.get_collection(objectId)
if isinstance(collection, CtsWorkMetadata):
editions = [t for t in collection.children.values() if isinstance(t, CtsEditionMetadata)]
if len(editions) == 0:
raise UnknownCollection("This work has no default edition")
# We change the rerouting here to the name of this function
return redirect(url_for(".r_full_text", objectId=str(editions[0].id)))
# We kept `subreference` as None here
text = self.get_passage(objectId=objectId, subreference=None)
passage = self.transform(text, text.export(Mimetypes.PYTHON.ETREE), objectId)
# We removed the self.get_siblings() : full text have no siblings !
return {
"template": "main::text.html",
"objectId": objectId,
"subreference": None,
"collections": {
"current": {
"label": collection.get_label(lang),
"id": collection.id,
"model": str(collection.model),
"type": str(collection.type),
"author": text.get_creator(lang),
"title": text.get_title(lang),
"description": text.get_description(lang),
"citation": collection.citation,
"coins": self.make_coins(collection, text, "", lang=lang)
},
"parents": self.make_parents(collection, lang=lang)
},
"text_passage": Markup(passage),
# We made sure to change the value here to None
"prev": None,
"next": None
}
The most important thing you just saw are the controller functions that are available :
Nemo.get_passage(objectId, subreference)
calls the resolver to retrieve a passage [Doc]Nemo.transform(passage_as_object, text_as_xml_etree_python_objet, objectId)
calls the XSL or any transform function you'd have [Doc]Nemo.get_siblings(objectId, subreference, passage_as_object)
which retrieve the next and previous references [Doc]Nemo.get_collection(objectId)
to retrieve a Metadata collection object [Doc]
Once we have done that, we can add this as a route : Nemo takes its routes from the static property Nemo.ROUTES. We are gonna copy it and add our own new fancy route :
class MyNemo(Nemo):
ROUTES = Nemo.ROUTES + [("/text/<objectId>/full", "r_full_text", ["GET"])]
The route description is written in a tuple where elements :
- is a flask route description
- is the name of the function it should lead to
- is a list of methods it responds to
To add the link, I simply went to the template templates/collection.html
and added a nice little link :
<a class="card-link" href="{{url_for('.r_full_text', objectId=coll.id)}}">Full Text</a>