Skip to content

Commit 2e533ed

Browse files
committed
views: FAIR signposting level 1 support (relying on FAIRSignpostingProfileLvl1Serializer)
1 parent 17401b6 commit 2e533ed

File tree

3 files changed

+22
-124
lines changed

3 files changed

+22
-124
lines changed

invenio_app_rdm/records_ui/views/decorators.py

+4-90
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
"""Routes for record-related pages provided by Invenio-App-RDM."""
1111

1212
from functools import wraps
13-
from itertools import islice
1413

1514
from flask import current_app, g, make_response, redirect, request, session, url_for
1615
from flask_login import login_required
@@ -20,11 +19,11 @@
2019
from invenio_communities.proxies import current_communities
2120
from invenio_pidstore.errors import PIDDoesNotExistError
2221
from invenio_rdm_records.proxies import current_rdm_records
23-
from invenio_rdm_records.resources.serializers.utils import get_vocabulary_props
22+
from invenio_rdm_records.resources.serializers.signposting import FAIRSignpostingProfileLvl1Serializer
2423
from invenio_records_resources.services.errors import PermissionDeniedError
2524
from sqlalchemy.orm.exc import NoResultFound
2625

27-
from invenio_app_rdm.urls import download_url_for, export_url_for, record_url_for
26+
from invenio_app_rdm.urls import record_url_for
2827

2928

3029
def service():
@@ -374,81 +373,6 @@ def _get_header(rel, value, link_type=None):
374373
return header
375374

376375

377-
def _get_signposting_cite_as(record):
378-
"""Release self url points to RDM record.
379-
380-
It points to DataCite URL if the integration is enabled, otherwise it points to the HTML URL.
381-
"""
382-
doi_url = record["links"].get("doi")
383-
html_url = record["links"]["self_html"]
384-
return _get_header("cite-as", doi_url or html_url)
385-
386-
387-
def _get_signposting_types(record):
388-
resource_type = record["metadata"]["resource_type"]
389-
props = get_vocabulary_props(
390-
"resourcetypes",
391-
[
392-
"props.schema.org",
393-
],
394-
resource_type["id"],
395-
)
396-
url_schema_org = props.get("schema.org")
397-
return [
398-
_get_header("type", url_schema_org),
399-
_get_header("type", "https://schema.org/AboutPage"),
400-
]
401-
402-
403-
def _get_signposting_authors(record):
404-
authors = []
405-
# Limit authors to the first 10.
406-
for creator in islice(record["metadata"]["creators"], 0, 10):
407-
for identifier in creator["person_or_org"].get("identifiers", []):
408-
if identifier["scheme"] == "orcid":
409-
authors.append(
410-
_get_header(
411-
"author", "https://orcid.org/" + identifier["identifier"]
412-
)
413-
)
414-
return authors
415-
416-
417-
def _get_signposting_describedbys(pid_value):
418-
describedbys = []
419-
for export_format, val in current_app.config.get(
420-
"APP_RDM_RECORD_EXPORTERS", {}
421-
).items():
422-
url = export_url_for(pid_value=pid_value, export_format=export_format)
423-
content_type = val["content-type"]
424-
describedbys.append(_get_header("describedby", url, content_type))
425-
return describedbys
426-
427-
428-
def _get_signposting_licenses(record):
429-
licenses = []
430-
for right in record["metadata"].get("rights", []):
431-
# First try to get `props.url` from the standard licenses,
432-
# then try to get the optional `link` from the custom license.
433-
url = right.get("props", {}).get("url") or right.get("link")
434-
if url:
435-
licenses.append(_get_header("license", url))
436-
return licenses
437-
438-
439-
def _get_signposting_items(files, pid_value):
440-
items = []
441-
# Checking if the user has access to the potentially restricted files.
442-
if files:
443-
# Limiting the iteration to 100 files maximum.
444-
# The `entries` key does not exist if files are not enabled.
445-
for file in islice(files.to_dict().get("entries", []), 0, 100):
446-
url = download_url_for(pid_value=pid_value, filename=file["key"])
447-
items.append(_get_header("item", url, file["mimetype"]))
448-
449-
return items
450-
451-
452376
def _get_signposting_collection(pid_value):
453377
ui_url = record_url_for(pid_value=pid_value)
454378
return _get_header("collection", ui_url, "text/html")
@@ -472,21 +396,11 @@ def view(*args, **kwargs):
472396
response = make_response(f(*args, **kwargs))
473397

474398
# Relies on other decorators having operated before it
475-
pid_value = kwargs["pid_value"]
476399
record = kwargs["record"]
477-
files = kwargs["files"]
478400

479-
signposting_headers = [
480-
_get_signposting_cite_as(record),
481-
*_get_signposting_types(record),
482-
*_get_signposting_authors(record),
483-
*_get_signposting_describedbys(pid_value),
484-
*_get_signposting_licenses(record),
485-
*_get_signposting_items(files, pid_value),
486-
_get_signposting_linkset(pid_value),
487-
]
401+
signposting_headers = FAIRSignpostingProfileLvl1Serializer().serialize_object(record.to_dict())
488402

489-
response.headers["Link"] = " , ".join(signposting_headers)
403+
response.headers["Link"] = signposting_headers
490404

491405
return response
492406

invenio_app_rdm/urls.py

-18
Original file line numberDiff line numberDiff line change
@@ -60,21 +60,3 @@ def download_url_for(pid_value="", filename=""):
6060
)
6161

6262
return "/".join(p.strip("/") for p in [url_prefix, url_path])
63-
64-
65-
def export_url_for(pid_value="", export_format=""):
66-
"""Return url for export route."""
67-
url_prefix = current_app.config.get(f"SITE_UI_URL", "")
68-
69-
# We use [] so that this fails and brings to attention the configuration
70-
# problem if APP_RDM_ROUTES.record_export is missing
71-
# url_path = current_app.config["APP_RDM_ROUTES"]["record_export"].replace(
72-
# "<pid_value>", pid_value
73-
# )
74-
url_path = (
75-
current_app.config["APP_RDM_ROUTES"]["record_export"]
76-
.replace("<pid_value>", pid_value)
77-
.replace("<export_format>", export_format)
78-
)
79-
80-
return "/".join(p.strip("/") for p in [url_prefix, url_path])

tests/ui/test_signposting_ui.py

+18-16
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,26 @@ def test_link_in_landing_page_response_headers(
2424
res = client_http_method(f"/records/{record_with_file.id}")
2525

2626
assert res.headers["Link"].split(" , ") == [
27-
f'<{ui_url}> ; rel="cite-as"',
28-
'<https://schema.org/Photograph> ; rel="type"',
29-
'<https://schema.org/AboutPage> ; rel="type"',
3027
# The test record does not have an author with an identifier.
31-
f'<{ui_url}/export/json> ; rel="describedby" ; type="application/json"',
32-
f'<{ui_url}/export/json-ld> ; rel="describedby" ; type="application/ld+json"',
33-
f'<{ui_url}/export/csl> ; rel="describedby" ; type="application/vnd.citationstyles.csl+json"',
34-
f'<{ui_url}/export/datacite-json> ; rel="describedby" ; type="application/vnd.datacite.datacite+json"',
35-
f'<{ui_url}/export/datacite-xml> ; rel="describedby" ; type="application/vnd.datacite.datacite+xml"',
36-
f'<{ui_url}/export/dublincore> ; rel="describedby" ; type="application/x-dc+xml"',
37-
f'<{ui_url}/export/marcxml> ; rel="describedby" ; type="application/marcxml+xml"',
38-
f'<{ui_url}/export/bibtex> ; rel="describedby" ; type="application/x-bibtex"',
39-
f'<{ui_url}/export/geojson> ; rel="describedby" ; type="application/vnd.geo+json"',
40-
f'<{ui_url}/export/dcat-ap> ; rel="describedby" ; type="application/dcat+xml"',
41-
f'<{ui_url}/export/codemeta> ; rel="describedby" ; type="application/ld+json"',
42-
f'<{ui_url}/export/cff> ; rel="describedby" ; type="application/x-yaml"',
43-
# The test record does not have a license.
28+
# The test record does not have a cite-as since it has no DOI.
29+
f'<{api_url}> ; rel="describedby" ; type="application/dcat+xml"',
30+
f'<{api_url}> ; rel="describedby" ; type="application/json"',
31+
f'<{api_url}> ; rel="describedby" ; type="application/ld+json"',
32+
f'<{api_url}> ; rel="describedby" ; type="application/marcxml+xml"',
33+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.citationstyles.csl+json"',
34+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.datacite.datacite+json"',
35+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.datacite.datacite+xml"',
36+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.geo+json"',
37+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.inveniordm.v1+json"',
38+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.inveniordm.v1.full+csv"',
39+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.inveniordm.v1.simple+csv"',
40+
f'<{api_url}> ; rel="describedby" ; type="application/x-bibtex"',
41+
f'<{api_url}> ; rel="describedby" ; type="application/x-dc+xml"',
42+
f'<{api_url}> ; rel="describedby" ; type="text/x-bibliography"',
4443
f'<{ui_url}/files/{filename}> ; rel="item" ; type="text/plain"',
44+
# The test record does not have a license.
45+
'<https://schema.org/Photograph> ; rel="type"',
46+
'<https://schema.org/AboutPage> ; rel="type"',
4547
f'<{api_url}> ; rel="linkset" ; type="application/linkset+json"',
4648
]
4749

0 commit comments

Comments
 (0)