Skip to content

Commit

Permalink
adding support for content negotiation to works
Browse files Browse the repository at this point in the history
  • Loading branch information
mfenner committed Mar 12, 2024
1 parent 5ba074d commit e683457
Show file tree
Hide file tree
Showing 12 changed files with 6,886 additions and 18 deletions.
57 changes: 42 additions & 15 deletions api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""Main quart application"""

from hypercorn.config import Config
import logging
from typing import Optional
from datetime import timedelta
import time
import orjson as json
from os import environ
import pydash as py_
from dotenv import load_dotenv
Expand All @@ -29,7 +29,7 @@
worksSelect,
)
from api.typesense import typesense_client as typesense
from commonmeta import doi_from_url
from commonmeta import Metadata, doi_from_url, validate_prefix
from api.utils import (
get_formatted_metadata,
convert_to_commonmeta,
Expand All @@ -51,6 +51,7 @@
)
from api.posts import extract_all_posts, extract_all_posts_by_blog, update_posts
from api.blogs import extract_single_blog, extract_all_blogs
from api.works import SUPPORTED_ACCEPT_HEADERS, get_formatted_work
from api.schema import Blog, Post, Work, PostQuery

config = Config()
Expand Down Expand Up @@ -107,7 +108,7 @@ async def works():
# else _text_match
# )
# order = request.args.get("order") or "desc"

try:
response = (
supabase.table("works")
Expand All @@ -123,19 +124,40 @@ async def works():

@validate_response(Work)
@app.route("/works/<slug>")
async def work(slug):
@app.route("/works/<slug>/<suffix>")
async def work(slug: str, suffix: Optional[str] = None):
"""Get work by slug."""
if not validate_uuid(slug):
locale = request.args.get("locale") or "en-US"
style = request.args.get("style") or "apa"
if validate_uuid(slug):
response = (
supabase.table("works")
.select(worksSelect)
.eq("uuid", slug)
.maybe_single()
.execute()
)
return jsonify(response.data)
elif validate_prefix(slug) and suffix:
doi = f"https://doi.org/{slug}/{suffix}"
response = (
supabase.table("works")
.select(worksSelect)
.eq("id", doi)
.maybe_single()
.execute()
)
else:
logger.warning(f"Invalid slug: {slug}")
return {"error": "An error occured."}, 400
response = (
supabase.table("works")
.select(worksSelect)
.eq("uuid", slug)
.maybe_single()
.execute()
)
return jsonify(response.data)

accept_header = request.accept_mimetypes.best

if accept_header not in SUPPORTED_ACCEPT_HEADERS:
return jsonify(response.data)
subject = Metadata(response.data, via="commonmeta")
result = get_formatted_work(subject, accept_header, style, locale)
return result.strip(), 200, {"Content-Type": accept_header}


@app.route("/blogs/")
Expand Down Expand Up @@ -520,8 +542,13 @@ async def post(slug: str, suffix: Optional[str] = None):
elif format_ == "pdf":
markdown["author"] = format_authors_with_orcid(markdown["author"])
markdown["license"] = {
"text": format_license(markdown["author"], markdown["date"], markdown["rights"]),
"id": "cc-by" if markdown["rights"] == "https://creativecommons.org/licenses/by/4.0/legalcode" else None,
"text": format_license(
markdown["author"], markdown["date"], markdown["rights"]
),
"id": "cc-by"
if markdown["rights"]
== "https://creativecommons.org/licenses/by/4.0/legalcode"
else None,
"link": markdown["rights"],
}
markdown["date"] = format_datetime(markdown["date"], markdown["lang"])
Expand Down
2 changes: 1 addition & 1 deletion api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ def get_formatted_metadata(
content_types = {
"commonmeta": "application/vnd.commonmeta+json",
"bibtex": "application/x-bibtex",
"ris": "application/x-research-infJoo-systems",
"ris": "application/x-research-info-systems",
"csl": "application/vnd.citationstyles.csl+json",
"schema_org": "application/vnd.schemaorg.ld+json",
"datacite": "application/vnd.datacite.datacite+json",
Expand Down
32 changes: 32 additions & 0 deletions api/works.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@
worksSelect,
)

# supported accept headers for content negotiation
SUPPORTED_ACCEPT_HEADERS = [
"application/vnd.commonmeta+json",
"application/x-bibtex",
"application/x-research-info-systems",
"application/vnd.citationstyles.csl+json",
"application/vnd.schemaorg.ld+json",
"application/vnd.datacite.datacite+json",
"application/vnd.crossref.unixref+xml",
"text/x-bibliography",
]

def fetch_single_work(string: str) -> Optional[dict]:
"""Fetch single work."""
Expand Down Expand Up @@ -95,3 +106,24 @@ def update_single_work(string: str) -> Optional[dict]:

work = fetch_single_work(string)
return upsert_single_work(work)


def get_formatted_work(subject, accept_header: str, style:str="apa", locale:str="en-US"):
"""Get formatted work."""
accept_headers = {
"application/vnd.commonmeta+json": "commonmeta",
"application/x-bibtex": "bibtex",
"application/x-research-info-systems": "ris",
"application/vnd.citationstyles.csl+json": "csl",
"application/vnd.schemaorg.ld+json": "schema_org",
"application/vnd.datacite.datacite+json": "datacite",
"application/vnd.crossref.unixref+xml": "crossref_xml",
"text/x-bibliography": "citation",
}
content_type = accept_headers.get(accept_header, "commonmeta")
if content_type == "citation":
# workaround for properly formatting blog posts
subject.type = "JournalArticle"
return subject.write(to="citation", style=style, locale=locale)
else:
return subject.write(to=content_type)
Loading

0 comments on commit e683457

Please sign in to comment.