Skip to content

Commit

Permalink
Nearly working collection
Browse files Browse the repository at this point in the history
  • Loading branch information
PonteIneptique committed Aug 26, 2024
1 parent 54807c4 commit 08ace00
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 15 deletions.
63 changes: 53 additions & 10 deletions dapitains/app/app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, Any
from typing import Dict, Any, Optional

from sqlalchemy.orm.collections import collection

Expand All @@ -18,11 +18,45 @@
from dapitains.app.database import db, Collection, Navigation
from dapitains.app.ingest import get_nav


def msg_4xx(string, code=404) -> Response:
return Response(json.dumps({"message": string}), status=code, mimetype="application/json")


def navigation_view(resource, ref, start, end, tree, down, templates):
def collection_view(identifier: Optional[str], nav: str, templates: Dict[str, str]) -> Response:
if not identifier:
coll: Collection = db.session.query(Collection).filter(~Collection.parents.any()).first()
else:
coll = Collection.query.where(Collection.identifier==identifier).first()
if coll is None:
return msg_4xx("Unknown collection")
out = coll.json()

if nav == 'children':
related_collections = db.session.query(Collection).filter(
Collection.parents.any(id=coll.id)
).all()
elif nav == 'parents':
related_collections = db.session.query(Collection).filter(
Collection.children.any(id=coll.id)
).all()
else:
return msg_4xx(f"nav parameter has a wrong value {nav}", code=400)

return Response(json.dumps({
"@context": "https://distributed-text-services.github.io/specifications/context/1-alpha1.json",
"dtsVersion": "1-alpha",
**out,
"totalParents": coll.total_parents,
"totalChildren": coll.total_children,
"member": [
related.json()
for related in related_collections
]
}), mimetype="application/json", status=200)


def navigation_view(resource, ref, start, end, tree, down, templates: Dict[str, str]) -> Response:
if not resource:
return msg_4xx("Resource parameter was not provided")

Expand All @@ -49,30 +83,39 @@ def navigation_view(resource, ref, start, end, tree, down, templates):
refs = nav.references[tree]
paths = nav.paths[tree]
members, start, end = get_nav(refs=refs, paths=paths, start_or_ref=start or ref, end=end, down=down)
print(templates)
return {
return Response(json.dumps({
"@context": "https://distributed-text-services.github.io/specifications/context/1-alpha1.json",
"dtsVersion": "1-alpha",
"@type": "Navigation",
"@id": "https://example.org/api/dts/navigation/?resource=https://en.wikisource.org/wiki/Dracula&down=1",
"resource": collection.json(inject=templates), # To Do: implement and inject URI templates
"member": members
}
}), mimetype="application/json", status=200)


def create_app(
app: Flask,
use_query: bool = False,
# navigation_template: str = "/navigation?resource=https://en.wikisource.org/wiki/Dracula{&ref,down,start,end,tree,page}"
use_query: bool = False
) -> (Flask, SQLAlchemy):
"""
Initialisation of the DB is up to you
"""
navigation_template = uritemplate.URITemplate("/navigation/{?resource}{&ref,start,end,tree,down}")
collection_template = uritemplate.URITemplate("/navigation/collection/{?id,page,nav}")
collection_template = uritemplate.URITemplate("/navigation/collection/{?id,nav}")
document_template = uritemplate.URITemplate("/document/{?resource}{&ref,start,end,tree}")

@app.route("/collection/")
def collection_route():
resource = request.args.get("id")
nav = request.args.get("nav")

return collection_view(resource, nav, templates={
"navigation": navigation_template.partial({"resource": resource}).uri,
"collection": collection_template.partial({"id": resource}).uri,
"document": document_template.partial({"resource": resource}).uri,
})

@app.route("/navigation/")
def navigation_route():
resource = request.args.get("resource")
Expand Down Expand Up @@ -109,7 +152,7 @@ def navigation_route():
db.drop_all()
db.create_all()

catalog, _ = ingest_catalog("/home/thibault/dev/MyDapytains/tests/catalog/example-collection.xml")
catalog, _ = ingest_catalog(f"{basedir}/../../tests/catalog/example-collection.xml")
store_catalog(catalog)

app.run()
app.run()
17 changes: 16 additions & 1 deletion dapitains/app/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.ext.mutable import MutableDict, Mutable
from sqlalchemy.types import TypeDecorator, TEXT
from sqlalchemy import func
import click
except ImportError:
print("This part of the package can only be imported with the web requirements.")
Expand Down Expand Up @@ -45,6 +46,7 @@ def process_result_value(self, value, dialect):
return None
return json.loads(value, cls=CustomKeyJSONDecoder)


class Collection(db.Model):
__tablename__ = 'collections'

Expand All @@ -60,7 +62,6 @@ class Collection(db.Model):
# One-to-one relationship with Navigation
navigation = db.relationship('Navigation', uselist=False, backref='collection', lazy=True)


parents = db.relationship(
'Collection',
secondary=parent_child_association,
Expand All @@ -69,9 +70,22 @@ class Collection(db.Model):
backref='children'
)

@property
def total_children(self):
return db.session.query(func.count(parent_child_association.c.child_id)).filter(
parent_child_association.c.parent_id == self.id
).scalar()

@property
def total_parents(self):
return db.session.query(func.count(parent_child_association.c.parent_id)).filter(
parent_child_association.c.child_id == self.id
).scalar()

def json(self, inject: Optional[Dict[str, Any]] = None):
data = {
"@type": "Resource" if self.resource else "Collection",
"@id": self.identifier,
"title": self.title,
**(inject or {})
}
Expand All @@ -97,6 +111,7 @@ def from_class(cls, obj: abstracts.Collection) -> "Collection":
extensions=[ext.json() for ext in obj.extension]
)


class Navigation(db.Model):
__tablename__ = 'navigations'

Expand Down
16 changes: 13 additions & 3 deletions dapitains/app/ingest.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
from typing import Dict, List, Optional, Any, Tuple
from dapitains.app.database import Collection, Navigation, db
from dapitains.app.database import Collection, Navigation, db, parent_child_association
from dapitains.metadata.xml_parser import Catalog
from dapitains.tei.document import Document
import copy
import tqdm


def store_catalog(catalog: Catalog):
for identifier, collection in catalog.objects.items():
keys = {}
for identifier, collection in tqdm.tqdm(catalog.objects.items(), desc="Parsing all collections"):
coll_db = Collection.from_class(collection)
db.session.add(coll_db)
db.session.flush()
keys[coll_db.identifier] = coll_db.id
if collection.resource:
doc = Document(collection.filepath)
references = {
Expand All @@ -17,10 +21,16 @@ def store_catalog(catalog: Catalog):
}
paths = {key: generate_paths(tree) for key, tree in references.items()}
nav = Navigation(collection_id=coll_db.id, paths=paths, references=references)
print(nav.paths)
db.session.add(nav)
db.session.commit()

for parent, child in catalog.relationships:
insert_statement = parent_child_association.insert().values(
parent_id=keys[parent],
child_id=keys[child]
)
db.session.execute(insert_statement)



def get_member_by_path(data: List[Dict[str, Any]], path: List[int]) -> Optional[Dict[str, Any]]:
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ saxonche==12.5.0
lxml
flask
flask-sqlalchemy
click
click
uritemplate

0 comments on commit 08ace00

Please sign in to comment.