diff --git a/ebl/app.py b/ebl/app.py index 72de42234..f52358f66 100644 --- a/ebl/app.py +++ b/ebl/app.py @@ -25,7 +25,7 @@ from ebl.ebl_ai_client import EblAiClient from ebl.files.infrastructure.grid_fs_file_repository import GridFsFileRepository from ebl.files.web.bootstrap import create_files_route -from ebl.markup.web.bootstrap import create_markup_route +from ebl.markup.web.bootstrap import create_markup_routes from ebl.fragmentarium.infrastructure.cropped_sign_images_repository import ( MongoCroppedSignImagesRepository, ) @@ -125,7 +125,7 @@ def create_app(context: Context, issuer: str = "", audience: str = ""): create_files_route(api, context) create_fragmentarium_routes(api, context) create_lemmatization_routes(api, context) - create_markup_route(api, context) + create_markup_routes(api, context) create_afo_register_routes(api, context) return api diff --git a/ebl/bibliography/application/bibliography.py b/ebl/bibliography/application/bibliography.py index a72b0262b..bf4839539 100644 --- a/ebl/bibliography/application/bibliography.py +++ b/ebl/bibliography/application/bibliography.py @@ -27,6 +27,9 @@ def create(self, entry, user: User) -> str: def find(self, id_: str): return self._repository.query_by_id(id_) + def find_many(self, ids: Sequence[str]): + return self._repository.query_by_ids(ids) + def update(self, entry, user: User): old_entry = self._repository.query_by_id(entry["id"]) self._changelog.create( diff --git a/ebl/bibliography/application/bibliography_repository.py b/ebl/bibliography/application/bibliography_repository.py index 85737b192..4d9478b62 100644 --- a/ebl/bibliography/application/bibliography_repository.py +++ b/ebl/bibliography/application/bibliography_repository.py @@ -11,6 +11,10 @@ def create(self, entry) -> str: def query_by_id(self, id_: str): ... + @abstractmethod + def query_by_ids(self, ids: Sequence[str]): + ... + @abstractmethod def update(self, entry) -> None: ... diff --git a/ebl/bibliography/infrastructure/bibliography.py b/ebl/bibliography/infrastructure/bibliography.py index d8c31b888..50f696b07 100644 --- a/ebl/bibliography/infrastructure/bibliography.py +++ b/ebl/bibliography/infrastructure/bibliography.py @@ -64,6 +64,10 @@ def query_by_id(self, id_: str) -> dict: data = self._collection.find_one_by_id(id_) return create_object_entry(data) + def query_by_ids(self, ids: Sequence[str]) -> Sequence[dict]: + data = self._collection.find_many({"_id": {"$in": ids}}) + return [create_object_entry(item) for item in data] + def update(self, entry) -> None: mongo_entry = create_mongo_entry(entry) self._collection.replace_one(mongo_entry) diff --git a/ebl/bibliography/web/bibliography_entries.py b/ebl/bibliography/web/bibliography_entries.py index 54e7e25cd..22b36ae1d 100644 --- a/ebl/bibliography/web/bibliography_entries.py +++ b/ebl/bibliography/web/bibliography_entries.py @@ -1,6 +1,9 @@ import falcon +from falcon_caching import Cache from falcon import Request, Response from falcon.media.validators.jsonschema import validate +import json +from ebl.cache.application.cache import DAILY_TIMEOUT from ebl.bibliography.domain.bibliography_entry import CSL_JSON_SCHEMA from ebl.users.web.require_scope import require_scope @@ -40,6 +43,23 @@ def on_post(self, req: Request, resp: Response, id_: str) -> None: class BibliographyList: + def __init__(self, bibliography: Bibliography, cache: Cache): + self._bibliography = bibliography + self._cache = cache + + def on_get(self, req: Request, resp: Response) -> None: + ids = req.params["ids"].split(",") + cache_key = tuple(sorted(set(ids))) + + if cached := self._cache.get(cache_key): + resp.text = cached + else: + data = json.dumps(self._bibliography.find_many(ids)) + self._cache.set(cache_key, data, timeout=DAILY_TIMEOUT) + resp.text = data + + +class BibliographyAll: def __init__(self, bibliography: Bibliography): self._bibliography = bibliography diff --git a/ebl/bibliography/web/bootstrap.py b/ebl/bibliography/web/bootstrap.py index fa1332aa4..cd9f7eb05 100644 --- a/ebl/bibliography/web/bootstrap.py +++ b/ebl/bibliography/web/bootstrap.py @@ -3,6 +3,7 @@ from ebl.bibliography.web.bibliography_entries import ( BibliographyEntriesResource, BibliographyResource, + BibliographyAll, BibliographyList, ) from ebl.context import Context @@ -12,7 +13,10 @@ def create_bibliography_routes(api: falcon.App, context: Context): bibliography = context.get_bibliography() bibliography_resource = BibliographyResource(bibliography) bibliography_entries = BibliographyEntriesResource(bibliography) - bibliography_all = BibliographyList(bibliography) + bibliography_all = BibliographyAll(bibliography) + bibliography_list = BibliographyList(bibliography, context.cache) + api.add_route("/bibliography", bibliography_resource) api.add_route("/bibliography/all", bibliography_all) + api.add_route("/bibliography/list", bibliography_list) api.add_route("/bibliography/{id_}", bibliography_entries) diff --git a/ebl/cache/application/cache.py b/ebl/cache/application/cache.py index fb561b928..00d643dad 100644 --- a/ebl/cache/application/cache.py +++ b/ebl/cache/application/cache.py @@ -7,6 +7,7 @@ DEFAULT_TIMEOUT: int = 600 +DAILY_TIMEOUT: int = 86400 CONFIG_ENVIRONMENT_VARIABLE: str = "CACHE_CONFIG" DEFAULT_CONFIG: str = '{"CACHE_TYPE": "null"}' diff --git a/ebl/markup/web/bootstrap.py b/ebl/markup/web/bootstrap.py index 7d9a8429e..64dbcbd89 100644 --- a/ebl/markup/web/bootstrap.py +++ b/ebl/markup/web/bootstrap.py @@ -1,8 +1,11 @@ import falcon from falcon import Request, Response +from falcon_caching import Cache +import json from ebl.context import Context from ebl.markup.domain.converters import markup_string_to_json +from ebl.cache.application.cache import DAILY_TIMEOUT class Markup: @@ -12,5 +15,21 @@ def on_get(self, req: Request, resp: Response) -> None: resp.media = markup_string_to_json(req.params["text"]) -def create_markup_route(api: falcon.App, context: Context) -> None: +class CachedMarkup(Markup): + def __init__(self, cache: Cache): + self._cache = cache + + def on_get(self, req: Request, resp: Response) -> None: + cache_key = req.params["text"] + + if cached := self._cache.get(cache_key): + resp.text = cached + else: + data = json.dumps(markup_string_to_json(req.params["text"])) + self._cache.set(cache_key, data, timeout=DAILY_TIMEOUT) + resp.text = data + + +def create_markup_routes(api: falcon.App, context: Context) -> None: api.add_route("/markup", Markup()) + api.add_route("/cached-markup", CachedMarkup(context.cache)) diff --git a/ebl/tests/bibliography/test_bibliography_route.py b/ebl/tests/bibliography/test_bibliography_route.py index 7a3a9fd2c..d0bf901bd 100644 --- a/ebl/tests/bibliography/test_bibliography_route.py +++ b/ebl/tests/bibliography/test_bibliography_route.py @@ -19,6 +19,20 @@ def saved_entry(bibliography, user): return bibliography_entry +@pytest.fixture +def saved_entries(bibliography, user): + number_of_entries = 5 + entries = [ + BibliographyEntryFactory.build(id=f"XY{i+1:05}") + for i in range(number_of_entries) + ] + + for entry in entries: + bibliography.create(entry, user) + + return entries + + def test_get_entry(client, saved_entry): id_ = saved_entry["id"] result = client.simulate_get(f"/bibliography/{id_}") @@ -127,3 +141,11 @@ def test_list_all_bibliography(client, saved_entry): assert result.json == [saved_entry["id"]] assert result.status == falcon.HTTP_OK + + +def test_list_bibliography(client, saved_entries): + ids = [entry["id"] for entry in saved_entries] + result = client.simulate_get(f"/bibliography/list?ids={','.join(ids)}") + + assert result.json == saved_entries + assert result.status == falcon.HTTP_OK