Skip to content

Commit f0e59d2

Browse files
committed
views: FAIR signposting level 1 support
1 parent af97177 commit f0e59d2

File tree

4 files changed

+141
-18
lines changed

4 files changed

+141
-18
lines changed

invenio_rdm_records/resources/serializers/__init__.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
#
3-
# Copyright (C) 2020, 2021 CERN.
3+
# Copyright (C) 2020-2024 CERN.
44
# Copyright (C) 2020 Northwestern University.
55
# Copyright (C) 2021 Graz University of Technology.
66
#
@@ -34,7 +34,10 @@
3434
)
3535
from .marcxml import MARCXMLSerializer
3636
from .schemaorg import SchemaorgJSONLDSerializer
37-
from .signposting import FAIRSignpostingProfileLvl2Serializer
37+
from .signposting import (
38+
FAIRSignpostingProfileLvl1Serializer,
39+
FAIRSignpostingProfileLvl2Serializer,
40+
)
3841
from .ui import UIJSONSerializer
3942

4043
__all__ = (
@@ -47,6 +50,7 @@
4750
"DataPackageSerializer",
4851
"DublinCoreJSONSerializer",
4952
"DublinCoreXMLSerializer",
53+
"FAIRSignpostingProfileLvl1Serializer",
5054
"FAIRSignpostingProfileLvl2Serializer",
5155
"GeoJSONSerializer",
5256
"IIIFCanvasV2JSONSerializer",

invenio_rdm_records/resources/serializers/signposting/__init__.py

+29-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,43 @@
11
# -*- coding: utf-8 -*-
22
#
33
# Copyright (C) 2023 Northwestern University.
4+
# Copyright (C) 2024 CERN.
45
#
56
# Invenio-RDM-Records is free software; you can redistribute it and/or modify
67
# it under the terms of the MIT License; see LICENSE file for more details.
78

89
"""Signposting serializers."""
910

1011
from flask_resources import BaseListSchema, MarshmallowSerializer
11-
from flask_resources.serializers import JSONSerializer
12+
from flask_resources.serializers import JSONSerializer, SimpleSerializer
1213

13-
from .schema import FAIRSignpostingProfileLvl2Schema
14+
from .schema import FAIRSignpostingProfileLvl2Schema, LandingPageLvl1Schema
15+
16+
17+
class FAIRSignpostingProfileLvl1Serializer(MarshmallowSerializer):
18+
"""FAIR Signposting Profile level 1 serializer."""
19+
20+
def __init__(self):
21+
"""Initialise Serializer."""
22+
super().__init__(
23+
format_serializer_cls=SimpleSerializer,
24+
object_schema_cls=LandingPageLvl1Schema,
25+
list_schema_cls=BaseListSchema,
26+
encoder=self.fair_signposting_tostring,
27+
)
28+
29+
@classmethod
30+
def fair_signposting_tostring(cls, record):
31+
"""Stringify a FAIR Signposting record."""
32+
links = []
33+
for rel, values in record.items():
34+
# if rel not in excluded_keys:
35+
for value in values:
36+
link = f'<{value["href"]}> ; rel="{rel}"'
37+
if "type" in value:
38+
link += f' ; type="{value["type"]}"'
39+
links.append(link)
40+
return " , ".join(links)
1441

1542

1643
class FAIRSignpostingProfileLvl2Serializer(MarshmallowSerializer):

invenio_rdm_records/resources/serializers/signposting/schema.py

+33-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
#
33
# Copyright (C) 2023 Northwestern University.
4+
# Copyright (C) 2024 CERN.
45
#
56
# Invenio-RDM-Records is free software; you can redistribute it and/or modify
67
# it under the terms of the MIT License; see LICENSE file for more details.
@@ -20,18 +21,13 @@ class LandingPageSchema(Schema):
2021
Serialization input (`obj`) is a whole record dict projection.
2122
"""
2223

23-
anchor = fields.Method(serialize="serialize_anchor")
2424
author = fields.Method(serialize="serialize_author")
2525
cite_as = fields.Method(data_key="cite-as", serialize="serialize_cite_as")
2626
describedby = fields.Method(serialize="serialize_describedby")
2727
item = fields.Method(serialize="serialize_item")
2828
license = fields.Method(serialize="serialize_license")
2929
type = fields.Method(serialize="serialize_type")
3030

31-
def serialize_anchor(self, obj, **kwargs):
32-
"""Seralize to landing page URL."""
33-
return obj["links"]["self_html"]
34-
3531
def serialize_author(self, obj, **kwargs):
3632
"""Serialize author(s).
3733
@@ -144,6 +140,37 @@ def serialize_type(self, obj, **kwargs):
144140
return result
145141

146142

143+
class LandingPageLvl1Schema(LandingPageSchema):
144+
"""Schema for serialization of link context object for the level 1 landing page.
145+
146+
Serialization input (`obj`) is a whole record dict projection.
147+
"""
148+
149+
linkset = fields.Method(serialize="serialize_linkset")
150+
151+
def serialize_linkset(self, obj, **kwargs):
152+
"""Serialize the linkset URL."""
153+
return [
154+
{
155+
"href": obj["links"]["self"],
156+
"type": "application/linkset+json",
157+
}
158+
]
159+
160+
161+
class LandingPageLvl2Schema(LandingPageSchema):
162+
"""Schema for serialization of link context object for the level 2 landing page.
163+
164+
Serialization input (`obj`) is a whole record dict projection.
165+
"""
166+
167+
anchor = fields.Method(serialize="serialize_anchor")
168+
169+
def serialize_anchor(self, obj, **kwargs):
170+
"""Serialize to landing page URL."""
171+
return obj["links"]["self_html"]
172+
173+
147174
class ContentResourceSchema(Schema):
148175
"""Schema for serialization of link context object for the content resource.
149176
@@ -205,7 +232,7 @@ class FAIRSignpostingProfileLvl2Schema(Schema):
205232

206233
def serialize_linkset(self, obj, **kwargs):
207234
"""Serialize linkset."""
208-
result = [LandingPageSchema().dump(obj)]
235+
result = [LandingPageLvl2Schema().dump(obj)]
209236

210237
content_resource_schema = ContentResourceSchema(context={"record_dict": obj})
211238
result += [

tests/resources/serializers/test_signposting_serializer.py

+73-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
# -*- coding: utf-8 -*-
22
#
33
# Copyright (C) 2023 Northwestern University.
4+
# Copyright (C) 2024 CERN.
45
#
56
# Invenio-RDM-Records is free software; you can redistribute it and/or modify
67
# it under the terms of the MIT License; see LICENSE file for more details.
78

89
"""Resources serializers tests."""
910

1011
from invenio_rdm_records.resources.serializers import (
12+
FAIRSignpostingProfileLvl1Serializer,
1113
FAIRSignpostingProfileLvl2Serializer,
1214
)
1315

@@ -33,10 +35,6 @@ def test_signposting_serializer_full(running_app, full_record_to_dict):
3335
"href": "https://127.0.0.1:5000/api/records/12345-abcde",
3436
"type": "application/ld+json",
3537
},
36-
# {
37-
# "href": "https://127.0.0.1:5000/api/records/12345-abcde",
38-
# "type": "application/linkset+json",
39-
# },
4038
{
4139
"href": "https://127.0.0.1:5000/api/records/12345-abcde",
4240
"type": "application/marcxml+xml",
@@ -125,6 +123,43 @@ def test_signposting_serializer_full(running_app, full_record_to_dict):
125123
assert expected == serialized
126124

127125

126+
def test_signposting_lvl1_serializer_full(running_app, full_record_to_dict):
127+
ui_url = "https://127.0.0.1:5000/records/12345-abcde"
128+
api_url = "https://127.0.0.1:5000/api/records/12345-abcde"
129+
filename = "test.txt"
130+
131+
expected = [
132+
f'<https://orcid.org/0000-0001-8135-3489> ; rel="author"',
133+
f'<https://doi.org/10.1234/12345-abcde> ; rel="cite-as"',
134+
f'<{api_url}> ; rel="describedby" ; type="application/dcat+xml"',
135+
f'<{api_url}> ; rel="describedby" ; type="application/json"',
136+
f'<{api_url}> ; rel="describedby" ; type="application/ld+json"',
137+
f'<{api_url}> ; rel="describedby" ; type="application/marcxml+xml"',
138+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.citationstyles.csl+json"',
139+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.datacite.datacite+json"',
140+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.datacite.datacite+xml"',
141+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.geo+json"',
142+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.inveniordm.v1+json"',
143+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.inveniordm.v1.full+csv"',
144+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.inveniordm.v1.simple+csv"',
145+
f'<{api_url}> ; rel="describedby" ; type="application/x-bibtex"',
146+
f'<{api_url}> ; rel="describedby" ; type="application/x-dc+xml"',
147+
f'<{api_url}> ; rel="describedby" ; type="text/x-bibliography"',
148+
f'<{ui_url}/files/{filename}> ; rel="item" ; type="text/plain"',
149+
'<https://customlicense.org/licenses/by/4.0/> ; rel="license"',
150+
'<https://creativecommons.org/licenses/by/4.0/legalcode> ; rel="license"',
151+
'<https://schema.org/Photograph> ; rel="type"',
152+
'<https://schema.org/AboutPage> ; rel="type"',
153+
f'<{api_url}> ; rel="linkset" ; type="application/linkset+json"',
154+
]
155+
156+
serialized = FAIRSignpostingProfileLvl1Serializer().serialize_object(
157+
full_record_to_dict
158+
)
159+
160+
assert expected == serialized.split(" , ")
161+
162+
128163
def test_signposting_serializer_minimal(running_app, minimal_record_to_dict):
129164
expected = {
130165
"linkset": [
@@ -146,10 +181,6 @@ def test_signposting_serializer_minimal(running_app, minimal_record_to_dict):
146181
"href": "https://127.0.0.1:5000/api/records/67890-fghij",
147182
"type": "application/ld+json",
148183
},
149-
# {
150-
# "href": "https://127.0.0.1:5000/api/records/67890-fghij",
151-
# "type": "application/linkset+json",
152-
# },
153184
{
154185
"href": "https://127.0.0.1:5000/api/records/67890-fghij",
155186
"type": "application/marcxml+xml",
@@ -218,3 +249,37 @@ def test_signposting_serializer_minimal(running_app, minimal_record_to_dict):
218249
serialized = FAIRSignpostingProfileLvl2Serializer().dump_obj(minimal_record_to_dict)
219250

220251
assert expected == serialized
252+
253+
254+
def test_signposting_lvl1_serializer_minimal(running_app, minimal_record_to_dict):
255+
api_url = "https://127.0.0.1:5000/api/records/67890-fghij"
256+
257+
expected = [
258+
# No author since no associated PID
259+
# No cite-as since no DOI
260+
f'<{api_url}> ; rel="describedby" ; type="application/dcat+xml"',
261+
f'<{api_url}> ; rel="describedby" ; type="application/json"',
262+
f'<{api_url}> ; rel="describedby" ; type="application/ld+json"',
263+
f'<{api_url}> ; rel="describedby" ; type="application/marcxml+xml"',
264+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.citationstyles.csl+json"',
265+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.datacite.datacite+json"',
266+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.datacite.datacite+xml"',
267+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.geo+json"',
268+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.inveniordm.v1+json"',
269+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.inveniordm.v1.full+csv"',
270+
f'<{api_url}> ; rel="describedby" ; type="application/vnd.inveniordm.v1.simple+csv"',
271+
f'<{api_url}> ; rel="describedby" ; type="application/x-bibtex"',
272+
f'<{api_url}> ; rel="describedby" ; type="application/x-dc+xml"',
273+
f'<{api_url}> ; rel="describedby" ; type="text/x-bibliography"',
274+
# No files
275+
# No license
276+
'<https://schema.org/Photograph> ; rel="type"',
277+
'<https://schema.org/AboutPage> ; rel="type"',
278+
f'<{api_url}> ; rel="linkset" ; type="application/linkset+json"',
279+
]
280+
281+
serialized = FAIRSignpostingProfileLvl1Serializer().serialize_object(
282+
minimal_record_to_dict
283+
)
284+
285+
assert expected == serialized.split(" , ")

0 commit comments

Comments
 (0)