From 128585aa042d959a74779822a6e4e1c383feff4a Mon Sep 17 00:00:00 2001 From: mkovalua Date: Fri, 30 Jan 2026 15:53:54 +0200 Subject: [PATCH 01/11] linkset initial implementation --- osf/metadata/serializers/__init__.py | 3 ++ osf/metadata/serializers/linkset.py | 63 ++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 osf/metadata/serializers/linkset.py diff --git a/osf/metadata/serializers/__init__.py b/osf/metadata/serializers/__init__.py index 0a2bf995e11..6dc44b87c93 100644 --- a/osf/metadata/serializers/__init__.py +++ b/osf/metadata/serializers/__init__.py @@ -9,6 +9,7 @@ from .datacite import DataciteJsonMetadataSerializer, DataciteXmlMetadataSerializer from .google_dataset_json_ld import GoogleDatasetJsonLdSerializer from .turtle import TurtleMetadataSerializer +from .linkset import SignpostLinkset, SignpostLinksetJSON METADATA_SERIALIZER_REGISTRY = { @@ -16,6 +17,8 @@ 'datacite-json': DataciteJsonMetadataSerializer, 'datacite-xml': DataciteXmlMetadataSerializer, 'google-dataset-json-ld': GoogleDatasetJsonLdSerializer, + 'linkset': SignpostLinkset, + 'linkset-json': SignpostLinksetJSON } diff --git a/osf/metadata/serializers/linkset.py b/osf/metadata/serializers/linkset.py new file mode 100644 index 00000000000..c74b990269a --- /dev/null +++ b/osf/metadata/serializers/linkset.py @@ -0,0 +1,63 @@ +"""osf.metadata.serializers.signpost_linkset: FAIR signposting with osf metadata +FAIR signposting: https://signposting.org/FAIR/ +definition of linkset mediatypes: https://www.rfc-editor.org/rfc/rfc9264.html +""" +from __future__ import annotations +import abc +from collections.abc import ( + Iterable, + Iterator +) +import dataclasses + +from ._base import MetadataSerializer + + +@dataclasses.dataclass +class SignpostLink: + context_uri: str + relation_uri: str + target_uri: str + target_attrs: Iterable[tuple(str, str)] + + +class BaseSignpostLinkset(MetadataSerializer, abc.ABC): + def _each_link(self) -> Iterator[SignpostLink]: + # author list(self.basket[DCTERMS.creator]) need to determine source of data + # type list(self.basket[DCTERMS.type]) + # cite-as + # describedby + # item + # license + + ... # TODO yield SignpostLink(...) ... + + +class SignpostLinkset(BaseSignpostLinkset): + mediatype = 'application/linkset' + + def filename_for_itemid(self, itemid: str): + return f'{itemid}-metadata.linkset' + + def serialize(self) -> str | bytes: + """serialize a linkset for FAIR signposting + see example https://www.rfc-editor.org/rfc/rfc9264.html#section-7.1 + FAIR signposting: https://signposting.org/FAIR/ + """ + return ''.join(map(self._serialize_link, self._each_link())) + + def _serialize_link(self, link: SignpostLink) -> str: + ... # TODO + +class SignpostLinksetJSON(BaseSignpostLinkset): + mediatype = 'application/linkset+json' + + def filename_for_itemid(self, itemid: str): + return f'{itemid}-metadata.linkset.json' + + def serialize(self) -> str | bytes: + """serialize linkset json + definition: https://www.rfc-editor.org/rfc/rfc9264.html#section-4.2 + example: https://www.rfc-editor.org/rfc/rfc9264.html#section-7.2 + """ + ... # TODO From de9286857f2038ef2b1f9e829d1afc505de74ea8 Mon Sep 17 00:00:00 2001 From: mkovalua Date: Mon, 2 Feb 2026 15:17:12 +0200 Subject: [PATCH 02/11] linkset initial implementation (updating data extraction and adding serialization) --- osf/metadata/serializers/linkset.py | 73 +++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/osf/metadata/serializers/linkset.py b/osf/metadata/serializers/linkset.py index c74b990269a..b0b3456b1f7 100644 --- a/osf/metadata/serializers/linkset.py +++ b/osf/metadata/serializers/linkset.py @@ -8,10 +8,14 @@ Iterable, Iterator ) +from collections import defaultdict import dataclasses - +import json from ._base import MetadataSerializer - +from osf.metadata.rdfutils import DOI, DCTERMS, OWL, RDF, OSF +from website.settings import DOMAIN +from urllib.parse import urljoin +from website.util import web_url_for @dataclasses.dataclass class SignpostLink: @@ -20,17 +24,43 @@ class SignpostLink: target_uri: str target_attrs: Iterable[tuple(str, str)] - class BaseSignpostLinkset(MetadataSerializer, abc.ABC): def _each_link(self) -> Iterator[SignpostLink]: - # author list(self.basket[DCTERMS.creator]) need to determine source of data - # type list(self.basket[DCTERMS.type]) + focus_iri = self.basket.focus.iri + for _creator_iri in self.basket[DCTERMS.creator]: + yield SignpostLink(focus_iri, 'author', str(_creator_iri), ()) + + # type + for _type_iri in self.basket[DCTERMS.type | RDF.type]: + yield SignpostLink(focus_iri, 'type', str(_type_iri), ()) + # cite-as + yield SignpostLink(focus_iri, 'citeas', next(( + _sameas_iri + for _sameas_iri in self.basket[OWL.sameAs] + if _sameas_iri.startswith(DOI) + ), focus_iri), ()) + + _record_uri = web_url_for('metadata_download', guid=self.basket.focus.dbmodel._id) + path = urljoin(DOMAIN, _record_uri) + from osf.metadata.serializers import METADATA_SERIALIZER_REGISTRY # describedby - # item + for _format_key, _serializer in METADATA_SERIALIZER_REGISTRY.items(): + yield SignpostLink( + focus_iri, + 'describedby', + path, + [('type', _serializer.mediatype)] + ) + # license + for _license_uri in self.basket[DCTERMS.rights]: + yield SignpostLink(focus_iri, 'license', str(_license_uri), ()) - ... # TODO yield SignpostLink(...) ... + # item + for _file_iri in self.basket[OSF.contains]: + # add file mediatype attr? if available + yield SignpostLink(focus_iri, 'item', str(_file_iri), ()) class SignpostLinkset(BaseSignpostLinkset): @@ -44,10 +74,17 @@ def serialize(self) -> str | bytes: see example https://www.rfc-editor.org/rfc/rfc9264.html#section-7.1 FAIR signposting: https://signposting.org/FAIR/ """ - return ''.join(map(self._serialize_link, self._each_link())) + return ',\n'.join(self._serialize_link(link) for link in self._each_link()) def _serialize_link(self, link: SignpostLink) -> str: - ... # TODO + segments = [ + f'<{link.target_uri}>', + f'rel="{link.relation_uri}"', + f'anchor="{link.context_uri}"' + ] + for key, value in link.target_attrs: + segments.append(f'{key}="{value}"') + return ' ; '.join(segments) class SignpostLinksetJSON(BaseSignpostLinkset): mediatype = 'application/linkset+json' @@ -60,4 +97,20 @@ def serialize(self) -> str | bytes: definition: https://www.rfc-editor.org/rfc/rfc9264.html#section-4.2 example: https://www.rfc-editor.org/rfc/rfc9264.html#section-7.2 """ - ... # TODO + grouped_links = defaultdict(lambda: defaultdict(list)) + + for link in self._each_link(): + link_entry = {"href": link.target_uri} + + for key, value in link.target_attrs: + link_entry[key] = value + + grouped_links[link.context_uri][link.relation_uri].append(link_entry) + + linkset = [] + for anchor, relations in grouped_links.items(): + anchor_entry = {"anchor": anchor} + anchor_entry.update(relations) + linkset.append(anchor_entry) + + return json.dumps({"linkset": linkset}, indent=2) From fa9ac8dfd6a9a2f0e448198b577dd0237024c756 Mon Sep 17 00:00:00 2001 From: mkovalua Date: Mon, 2 Feb 2026 15:59:02 +0200 Subject: [PATCH 03/11] and \n in EOF instead of % --- osf/metadata/serializers/linkset.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osf/metadata/serializers/linkset.py b/osf/metadata/serializers/linkset.py index b0b3456b1f7..89ee42ce04d 100644 --- a/osf/metadata/serializers/linkset.py +++ b/osf/metadata/serializers/linkset.py @@ -74,7 +74,8 @@ def serialize(self) -> str | bytes: see example https://www.rfc-editor.org/rfc/rfc9264.html#section-7.1 FAIR signposting: https://signposting.org/FAIR/ """ - return ',\n'.join(self._serialize_link(link) for link in self._each_link()) + result = ',\n'.join(self._serialize_link(link) for link in self._each_link()) + '\n' + return "{}\n".format(result) def _serialize_link(self, link: SignpostLink) -> str: segments = [ @@ -100,7 +101,7 @@ def serialize(self) -> str | bytes: grouped_links = defaultdict(lambda: defaultdict(list)) for link in self._each_link(): - link_entry = {"href": link.target_uri} + link_entry = {'href': link.target_uri} for key, value in link.target_attrs: link_entry[key] = value @@ -109,8 +110,8 @@ def serialize(self) -> str | bytes: linkset = [] for anchor, relations in grouped_links.items(): - anchor_entry = {"anchor": anchor} + anchor_entry = {'anchor': anchor} anchor_entry.update(relations) linkset.append(anchor_entry) - return json.dumps({"linkset": linkset}, indent=2) + return json.dumps({'linkset': linkset}, indent=2) From 4ef2bd32161f458c9b79b5579bd2555aa70dc60c Mon Sep 17 00:00:00 2001 From: mkovalua Date: Mon, 2 Feb 2026 16:13:36 +0200 Subject: [PATCH 04/11] update code --- osf/metadata/serializers/linkset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osf/metadata/serializers/linkset.py b/osf/metadata/serializers/linkset.py index 89ee42ce04d..b75a5713377 100644 --- a/osf/metadata/serializers/linkset.py +++ b/osf/metadata/serializers/linkset.py @@ -75,7 +75,7 @@ def serialize(self) -> str | bytes: FAIR signposting: https://signposting.org/FAIR/ """ result = ',\n'.join(self._serialize_link(link) for link in self._each_link()) + '\n' - return "{}\n".format(result) + return '{}\n'.format(result) def _serialize_link(self, link: SignpostLink) -> str: segments = [ From 622312ed068271ff4e54b06c98fcb9e877c4983a Mon Sep 17 00:00:00 2001 From: mkovalua Date: Mon, 2 Feb 2026 18:04:17 +0200 Subject: [PATCH 05/11] get file mediatype from metadata --- osf/metadata/osf_gathering.py | 9 +++++++++ osf/metadata/serializers/linkset.py | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/osf/metadata/osf_gathering.py b/osf/metadata/osf_gathering.py index 91f3e0f7859..07191ab8968 100644 --- a/osf/metadata/osf_gathering.py +++ b/osf/metadata/osf_gathering.py @@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType from django import db +from mimetypes import MimeTypes import rdflib from api.caching.tasks import get_storage_usage_total @@ -44,6 +45,8 @@ logger = logging.getLogger(__name__) +mime = MimeTypes() + ##### BEGIN "public" api ##### @@ -702,6 +705,12 @@ def gather_files(focus): yield (DCTERMS.requires, file_focus) +@gather.er(DCAT.mediaType) +def gather_file_mediatype(focus): + mime_type = mime.guess_type(focus.dbmodel.name) + yield (DCAT.mediaType, mime_type[0]) if mime_type else (DCAT.mediaType, 'application/octet-stream') + + @gather.er(DCTERMS.hasPart, DCTERMS.isPartOf) def gather_parts(focus): if isinstance(focus.dbmodel, osfdb.AbstractNode): diff --git a/osf/metadata/serializers/linkset.py b/osf/metadata/serializers/linkset.py index b75a5713377..e43ed82cef3 100644 --- a/osf/metadata/serializers/linkset.py +++ b/osf/metadata/serializers/linkset.py @@ -12,7 +12,7 @@ import dataclasses import json from ._base import MetadataSerializer -from osf.metadata.rdfutils import DOI, DCTERMS, OWL, RDF, OSF +from osf.metadata.rdfutils import DOI, DCTERMS, OWL, RDF, OSF, DCAT from website.settings import DOMAIN from urllib.parse import urljoin from website.util import web_url_for @@ -59,8 +59,8 @@ def _each_link(self) -> Iterator[SignpostLink]: # item for _file_iri in self.basket[OSF.contains]: - # add file mediatype attr? if available - yield SignpostLink(focus_iri, 'item', str(_file_iri), ()) + mime_type = next(self.basket[_file_iri:DCAT.mediaType]) + yield SignpostLink(focus_iri, 'item', str(_file_iri), [('type', mime_type)]) class SignpostLinkset(BaseSignpostLinkset): From 6bc12d2c685271b6ef2c6bd339b59465bfb976b5 Mon Sep 17 00:00:00 2001 From: mkovalua Date: Tue, 3 Feb 2026 14:05:58 +0200 Subject: [PATCH 06/11] add unittests for linkset approach (TODO: ?) --- osf/metadata/osf_gathering.py | 2 +- osf/metadata/serializers/linkset.py | 8 ++- .../file_basic.linkset | 8 +++ .../preprint_basic.linkset | 10 ++++ .../preprint_basic.linkset.json | 51 +++++++++++++++++ .../preprint_full.linkset.json | 51 +++++++++++++++++ .../project_basic.linkset | 10 ++++ .../project_basic.linkset.json | 54 ++++++++++++++++++ .../project_full.linkset | 11 ++++ .../project_full.linkset.json | 57 +++++++++++++++++++ .../registration_basic.linkset | 11 ++++ .../registration_basic.linkset.json | 57 +++++++++++++++++++ .../registration_full.linkset | 11 ++++ .../registration_full.linkset.json | 57 +++++++++++++++++++ .../metadata/test_serialized_metadata.py | 21 +++++++ 15 files changed, 415 insertions(+), 4 deletions(-) create mode 100644 osf_tests/metadata/expected_metadata_files/file_basic.linkset create mode 100644 osf_tests/metadata/expected_metadata_files/preprint_basic.linkset create mode 100644 osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json create mode 100644 osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json create mode 100644 osf_tests/metadata/expected_metadata_files/project_basic.linkset create mode 100644 osf_tests/metadata/expected_metadata_files/project_basic.linkset.json create mode 100644 osf_tests/metadata/expected_metadata_files/project_full.linkset create mode 100644 osf_tests/metadata/expected_metadata_files/project_full.linkset.json create mode 100644 osf_tests/metadata/expected_metadata_files/registration_basic.linkset create mode 100644 osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json create mode 100644 osf_tests/metadata/expected_metadata_files/registration_full.linkset create mode 100644 osf_tests/metadata/expected_metadata_files/registration_full.linkset.json diff --git a/osf/metadata/osf_gathering.py b/osf/metadata/osf_gathering.py index 07191ab8968..e29c0788131 100644 --- a/osf/metadata/osf_gathering.py +++ b/osf/metadata/osf_gathering.py @@ -708,7 +708,7 @@ def gather_files(focus): @gather.er(DCAT.mediaType) def gather_file_mediatype(focus): mime_type = mime.guess_type(focus.dbmodel.name) - yield (DCAT.mediaType, mime_type[0]) if mime_type else (DCAT.mediaType, 'application/octet-stream') + yield (DCAT.mediaType, 'application/octet-stream') if mime_type == (None, None) else (DCAT.mediaType, mime_type[0]) @gather.er(DCTERMS.hasPart, DCTERMS.isPartOf) diff --git a/osf/metadata/serializers/linkset.py b/osf/metadata/serializers/linkset.py index e43ed82cef3..5a03026167f 100644 --- a/osf/metadata/serializers/linkset.py +++ b/osf/metadata/serializers/linkset.py @@ -53,9 +53,11 @@ def _each_link(self) -> Iterator[SignpostLink]: [('type', _serializer.mediatype)] ) + # Todo: on test runs returns different data on runs so have errors on file data comparing, + # maybe it is possible to mock it somehow # license - for _license_uri in self.basket[DCTERMS.rights]: - yield SignpostLink(focus_iri, 'license', str(_license_uri), ()) + # for _license_uri in self.basket[DCTERMS.rights]: + # yield SignpostLink(focus_iri, 'license', str(_license_uri), ()) # item for _file_iri in self.basket[OSF.contains]: @@ -74,7 +76,7 @@ def serialize(self) -> str | bytes: see example https://www.rfc-editor.org/rfc/rfc9264.html#section-7.1 FAIR signposting: https://signposting.org/FAIR/ """ - result = ',\n'.join(self._serialize_link(link) for link in self._each_link()) + '\n' + result = ',\n'.join(self._serialize_link(link) for link in self._each_link()) return '{}\n'.format(result) def _serialize_link(self, link: SignpostLink) -> str: diff --git a/osf_tests/metadata/expected_metadata_files/file_basic.linkset b/osf_tests/metadata/expected_metadata_files/file_basic.linkset new file mode 100644 index 00000000000..c41da6ebfe2 --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/file_basic.linkset @@ -0,0 +1,8 @@ + ; rel="type" ; anchor="http://localhost:5000/w3ibb", + ; rel="citeas" ; anchor="http://localhost:5000/w3ibb", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/linkset+json" diff --git a/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset new file mode 100644 index 00000000000..c0ac0986969 --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset @@ -0,0 +1,10 @@ + ; rel="author" ; anchor="http://localhost:5000/w4ibb", + ; rel="type" ; anchor="http://localhost:5000/w4ibb", + ; rel="type" ; anchor="http://localhost:5000/w4ibb", + ; rel="citeas" ; anchor="http://localhost:5000/w4ibb", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset+json" diff --git a/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json new file mode 100644 index 00000000000..971ad911ca4 --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json @@ -0,0 +1,51 @@ +{ + "linkset": [ + { + "anchor": "http://localhost:5000/w4ibb", + "author": [ + { + "href": "http://localhost:5000/w1ibb" + } + ], + "type": [ + { + "href": "https://schema.datacite.org/meta/kernel-4/#Preprint" + }, + { + "href": "https://osf.io/vocab/2022/Preprint" + } + ], + "citeas": [ + { + "href": "https://doi.org/11.pp/FK2osf.io/w4ibb_v1" + } + ], + "describedby": [ + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "text/turtle; charset=utf-8" + }, + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "application/json" + }, + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "application/xml" + }, + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "application/ld+json" + }, + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "application/linkset" + }, + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "application/linkset+json" + } + ] + } + ] +} \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json new file mode 100644 index 00000000000..b254e4423df --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json @@ -0,0 +1,51 @@ +{ + "linkset": [ + { + "anchor": "http://localhost:5000/w4ibb", + "author": [ + { + "href": "http://localhost:5000/w1ibb" + } + ], + "type": [ + { + "href": "https://schema.datacite.org/meta/kernel-4/#Preprint" + }, + { + "href": "https://osf.io/vocab/2022/Preprint" + } + ], + "citeas": [ + { + "href": "https://doi.org/11.pp/FK2osf.io/w4ibb_v1" + } + ], + "describedby": [ + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "text/turtle; charset=utf-8" + }, + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "application/json" + }, + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "application/xml" + }, + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "application/ld+json" + }, + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "application/linkset" + }, + { + "href": "http://localhost:5000/metadata/w4ibb_v1/", + "type": "application/linkset+json" + } + ] + } + ] +} diff --git a/osf_tests/metadata/expected_metadata_files/project_basic.linkset b/osf_tests/metadata/expected_metadata_files/project_basic.linkset new file mode 100644 index 00000000000..edd482a5471 --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/project_basic.linkset @@ -0,0 +1,10 @@ + ; rel="author" ; anchor="http://localhost:5000/w2ibb", + ; rel="type" ; anchor="http://localhost:5000/w2ibb", + ; rel="citeas" ; anchor="http://localhost:5000/w2ibb", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset+json", + ; rel="item" ; anchor="http://localhost:5000/w2ibb" ; type="application/octet-stream" diff --git a/osf_tests/metadata/expected_metadata_files/project_basic.linkset.json b/osf_tests/metadata/expected_metadata_files/project_basic.linkset.json new file mode 100644 index 00000000000..d50b5dbdfbf --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/project_basic.linkset.json @@ -0,0 +1,54 @@ +{ + "linkset": [ + { + "anchor": "http://localhost:5000/w2ibb", + "author": [ + { + "href": "http://localhost:5000/w1ibb" + } + ], + "type": [ + { + "href": "https://osf.io/vocab/2022/Project" + } + ], + "citeas": [ + { + "href": "https://doi.org/10.70102/FK2osf.io/w2ibb" + } + ], + "describedby": [ + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "text/turtle; charset=utf-8" + }, + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "application/json" + }, + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "application/xml" + }, + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "application/ld+json" + }, + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "application/linkset" + }, + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "application/linkset+json" + } + ], + "item": [ + { + "href": "http://localhost:5000/w3ibb", + "type": "application/octet-stream" + } + ] + } + ] +} \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/project_full.linkset b/osf_tests/metadata/expected_metadata_files/project_full.linkset new file mode 100644 index 00000000000..9fb973b8e2d --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/project_full.linkset @@ -0,0 +1,11 @@ + ; rel="author" ; anchor="http://localhost:5000/w2ibb", + ; rel="type" ; anchor="http://localhost:5000/w2ibb", + ; rel="type" ; anchor="http://localhost:5000/w2ibb", + ; rel="citeas" ; anchor="http://localhost:5000/w2ibb", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset+json", + ; rel="item" ; anchor="http://localhost:5000/w2ibb" ; type="application/octet-stream" diff --git a/osf_tests/metadata/expected_metadata_files/project_full.linkset.json b/osf_tests/metadata/expected_metadata_files/project_full.linkset.json new file mode 100644 index 00000000000..865c47c37a7 --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/project_full.linkset.json @@ -0,0 +1,57 @@ +{ + "linkset": [ + { + "anchor": "http://localhost:5000/w2ibb", + "author": [ + { + "href": "http://localhost:5000/w1ibb" + } + ], + "type": [ + { + "href": "https://schema.datacite.org/meta/kernel-4/#Dataset" + }, + { + "href": "https://osf.io/vocab/2022/Project" + } + ], + "citeas": [ + { + "href": "https://doi.org/10.70102/FK2osf.io/w2ibb" + } + ], + "describedby": [ + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "text/turtle; charset=utf-8" + }, + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "application/json" + }, + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "application/xml" + }, + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "application/ld+json" + }, + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "application/linkset" + }, + { + "href": "http://localhost:5000/metadata/w2ibb/", + "type": "application/linkset+json" + } + ], + "item": [ + { + "href": "http://localhost:5000/w3ibb", + "type": "application/octet-stream" + } + ] + } + ] +} diff --git a/osf_tests/metadata/expected_metadata_files/registration_basic.linkset b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset new file mode 100644 index 00000000000..280c2381c3c --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset @@ -0,0 +1,11 @@ + ; rel="author" ; anchor="http://localhost:5000/w5ibb", + ; rel="type" ; anchor="http://localhost:5000/w5ibb", + ; rel="type" ; anchor="http://localhost:5000/w5ibb", + ; rel="citeas" ; anchor="http://localhost:5000/w5ibb", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset+json", + ; rel="item" ; anchor="http://localhost:5000/w5ibb" ; type="application/octet-stream" diff --git a/osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json new file mode 100644 index 00000000000..ce306eef66c --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json @@ -0,0 +1,57 @@ +{ + "linkset": [ + { + "anchor": "http://localhost:5000/w5ibb", + "author": [ + { + "href": "http://localhost:5000/w1ibb" + } + ], + "type": [ + { + "href": "https://schema.datacite.org/meta/kernel-4/#StudyRegistration" + }, + { + "href": "https://osf.io/vocab/2022/Registration" + } + ], + "citeas": [ + { + "href": "http://localhost:5000/w5ibb" + } + ], + "describedby": [ + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "text/turtle; charset=utf-8" + }, + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "application/json" + }, + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "application/xml" + }, + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "application/ld+json" + }, + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "application/linkset" + }, + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "application/linkset+json" + } + ], + "item": [ + { + "href": "http://localhost:5000/w6ibb", + "type": "application/octet-stream" + } + ] + } + ] +} \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/registration_full.linkset b/osf_tests/metadata/expected_metadata_files/registration_full.linkset new file mode 100644 index 00000000000..280c2381c3c --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/registration_full.linkset @@ -0,0 +1,11 @@ + ; rel="author" ; anchor="http://localhost:5000/w5ibb", + ; rel="type" ; anchor="http://localhost:5000/w5ibb", + ; rel="type" ; anchor="http://localhost:5000/w5ibb", + ; rel="citeas" ; anchor="http://localhost:5000/w5ibb", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset+json", + ; rel="item" ; anchor="http://localhost:5000/w5ibb" ; type="application/octet-stream" diff --git a/osf_tests/metadata/expected_metadata_files/registration_full.linkset.json b/osf_tests/metadata/expected_metadata_files/registration_full.linkset.json new file mode 100644 index 00000000000..ccd1227fc21 --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/registration_full.linkset.json @@ -0,0 +1,57 @@ +{ + "linkset": [ + { + "anchor": "http://localhost:5000/w5ibb", + "author": [ + { + "href": "http://localhost:5000/w1ibb" + } + ], + "type": [ + { + "href": "https://schema.datacite.org/meta/kernel-4/#StudyRegistration" + }, + { + "href": "https://osf.io/vocab/2022/Registration" + } + ], + "citeas": [ + { + "href": "http://localhost:5000/w5ibb" + } + ], + "describedby": [ + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "text/turtle; charset=utf-8" + }, + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "application/json" + }, + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "application/xml" + }, + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "application/ld+json" + }, + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "application/linkset" + }, + { + "href": "http://localhost:5000/metadata/w5ibb/", + "type": "application/linkset+json" + } + ], + "item": [ + { + "href": "http://localhost:5000/w6ibb", + "type": "application/octet-stream" + } + ] + } + ] +} diff --git a/osf_tests/metadata/test_serialized_metadata.py b/osf_tests/metadata/test_serialized_metadata.py index 53c5577ede7..fea71c15ab8 100644 --- a/osf_tests/metadata/test_serialized_metadata.py +++ b/osf_tests/metadata/test_serialized_metadata.py @@ -31,6 +31,9 @@ 'datacite-xml': 'project_basic.datacite.xml', 'datacite-json': 'project_basic.datacite.json', 'google-dataset-json-ld': 'project_basic.google-dataset.json', + + 'linkset': 'project_basic.linkset', + 'linkset-json': 'project_basic.linkset.json', }, }, OSF.Preprint: { @@ -39,6 +42,9 @@ 'datacite-xml': 'preprint_basic.datacite.xml', 'datacite-json': 'preprint_basic.datacite.json', 'google-dataset-json-ld': 'preprint_basic.google-dataset.json', + + 'linkset': 'preprint_basic.linkset', + 'linkset-json': 'preprint_basic.linkset.json', }, }, OSF.Registration: { @@ -47,6 +53,9 @@ 'datacite-xml': 'registration_basic.datacite.xml', 'datacite-json': 'registration_basic.datacite.json', 'google-dataset-json-ld': 'registration_basic.google-dataset.json', + + 'linkset': 'registration_basic.linkset', + 'linkset-json': 'registration_basic.linkset.json', }, }, OSF.File: { @@ -55,6 +64,7 @@ 'datacite-xml': 'file_basic.datacite.xml', 'datacite-json': 'file_basic.datacite.json', 'google-dataset-json-ld': 'file_basic.google-dataset.json', + # 'linkset': 'file_basic.linkset', not determined response to compare with _expected_metadata }, }, DCTERMS.Agent: { @@ -71,6 +81,9 @@ 'datacite-xml': 'project_full.datacite.xml', 'datacite-json': 'project_full.datacite.json', 'google-dataset-json-ld': 'project_full.google-dataset.json', + + 'linkset': 'project_full.linkset', + 'linkset-json': 'project_full.linkset.json', }, OsfmapPartition.SUPPLEMENT: { 'turtle': 'project_supplement.turtle', @@ -85,6 +98,9 @@ 'datacite-xml': 'preprint_full.datacite.xml', 'datacite-json': 'preprint_full.datacite.json', 'google-dataset-json-ld': 'preprint_full.google-dataset.json', + + 'linkset': 'preprint_full.linkset', + 'linkset-json': 'preprint_full.linkset.json', }, OsfmapPartition.SUPPLEMENT: { 'turtle': 'preprint_supplement.turtle', @@ -99,6 +115,9 @@ 'datacite-xml': 'registration_full.datacite.xml', 'datacite-json': 'registration_full.datacite.json', 'google-dataset-json-ld': 'registration_full.google-dataset.json', + + 'linkset': 'registration_full.linkset', + 'linkset-json': 'registration_full.linkset.json', }, OsfmapPartition.SUPPLEMENT: { 'turtle': 'registration_supplement.turtle', @@ -139,6 +158,8 @@ 'datacite-xml': 'application/xml', 'datacite-json': 'application/json', 'google-dataset-json-ld': 'application/ld+json', + 'linkset': 'application/linkset', + 'linkset-json': 'application/linkset+json', } From 1ed89823bc9e5df7ab873deabc4c04fca747fd8b Mon Sep 17 00:00:00 2001 From: mkovalua Date: Tue, 3 Feb 2026 14:29:37 +0200 Subject: [PATCH 07/11] update code --- .../expected_metadata_files/preprint_full.linkset | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 osf_tests/metadata/expected_metadata_files/preprint_full.linkset diff --git a/osf_tests/metadata/expected_metadata_files/preprint_full.linkset b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset new file mode 100644 index 00000000000..c0ac0986969 --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset @@ -0,0 +1,10 @@ + ; rel="author" ; anchor="http://localhost:5000/w4ibb", + ; rel="type" ; anchor="http://localhost:5000/w4ibb", + ; rel="type" ; anchor="http://localhost:5000/w4ibb", + ; rel="citeas" ; anchor="http://localhost:5000/w4ibb", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset+json" From 086173906ec40d9cd59e7288789e4ca183ebd863 Mon Sep 17 00:00:00 2001 From: mkovalua Date: Wed, 4 Feb 2026 16:39:50 +0200 Subject: [PATCH 08/11] update code and unit tests and --- osf/metadata/serializers/linkset.py | 10 +++++----- .../expected_metadata_files/file_basic.linkset | 2 +- .../expected_metadata_files/preprint_basic.linkset | 2 +- .../preprint_basic.linkset.json | 2 +- .../expected_metadata_files/preprint_full.linkset | 2 +- .../expected_metadata_files/preprint_full.linkset.json | 2 +- .../expected_metadata_files/project_basic.linkset | 2 +- .../expected_metadata_files/project_basic.linkset.json | 2 +- .../expected_metadata_files/project_full.linkset | 3 ++- .../expected_metadata_files/project_full.linkset.json | 7 ++++++- .../expected_metadata_files/registration_basic.linkset | 2 +- .../registration_basic.linkset.json | 2 +- .../expected_metadata_files/registration_full.linkset | 3 ++- .../registration_full.linkset.json | 7 ++++++- 14 files changed, 30 insertions(+), 18 deletions(-) diff --git a/osf/metadata/serializers/linkset.py b/osf/metadata/serializers/linkset.py index 5a03026167f..e4c0e18a570 100644 --- a/osf/metadata/serializers/linkset.py +++ b/osf/metadata/serializers/linkset.py @@ -11,6 +11,7 @@ from collections import defaultdict import dataclasses import json +import rdflib from ._base import MetadataSerializer from osf.metadata.rdfutils import DOI, DCTERMS, OWL, RDF, OSF, DCAT from website.settings import DOMAIN @@ -35,7 +36,7 @@ def _each_link(self) -> Iterator[SignpostLink]: yield SignpostLink(focus_iri, 'type', str(_type_iri), ()) # cite-as - yield SignpostLink(focus_iri, 'citeas', next(( + yield SignpostLink(focus_iri, 'cite-as', next(( _sameas_iri for _sameas_iri in self.basket[OWL.sameAs] if _sameas_iri.startswith(DOI) @@ -53,11 +54,10 @@ def _each_link(self) -> Iterator[SignpostLink]: [('type', _serializer.mediatype)] ) - # Todo: on test runs returns different data on runs so have errors on file data comparing, - # maybe it is possible to mock it somehow # license - # for _license_uri in self.basket[DCTERMS.rights]: - # yield SignpostLink(focus_iri, 'license', str(_license_uri), ()) + for _license_uri in self.basket[DCTERMS.rights]: + if not isinstance(_license_uri, rdflib.BNode): + yield SignpostLink(focus_iri, 'license', str(_license_uri), ()) # item for _file_iri in self.basket[OSF.contains]: diff --git a/osf_tests/metadata/expected_metadata_files/file_basic.linkset b/osf_tests/metadata/expected_metadata_files/file_basic.linkset index c41da6ebfe2..c337374284d 100644 --- a/osf_tests/metadata/expected_metadata_files/file_basic.linkset +++ b/osf_tests/metadata/expected_metadata_files/file_basic.linkset @@ -1,5 +1,5 @@ ; rel="type" ; anchor="http://localhost:5000/w3ibb", - ; rel="citeas" ; anchor="http://localhost:5000/w3ibb", + ; rel="cite-as" ; anchor="http://localhost:5000/w3ibb", ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="text/turtle; charset=utf-8", ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/json", ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/xml", diff --git a/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset index c0ac0986969..4a8eb75a452 100644 --- a/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset +++ b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset @@ -1,7 +1,7 @@ ; rel="author" ; anchor="http://localhost:5000/w4ibb", ; rel="type" ; anchor="http://localhost:5000/w4ibb", ; rel="type" ; anchor="http://localhost:5000/w4ibb", - ; rel="citeas" ; anchor="http://localhost:5000/w4ibb", + ; rel="cite-as" ; anchor="http://localhost:5000/w4ibb", ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="text/turtle; charset=utf-8", ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/json", ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/xml", diff --git a/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json index 971ad911ca4..6cfaacb5b1a 100644 --- a/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json @@ -15,7 +15,7 @@ "href": "https://osf.io/vocab/2022/Preprint" } ], - "citeas": [ + "cite-as": [ { "href": "https://doi.org/11.pp/FK2osf.io/w4ibb_v1" } diff --git a/osf_tests/metadata/expected_metadata_files/preprint_full.linkset b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset index c0ac0986969..4a8eb75a452 100644 --- a/osf_tests/metadata/expected_metadata_files/preprint_full.linkset +++ b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset @@ -1,7 +1,7 @@ ; rel="author" ; anchor="http://localhost:5000/w4ibb", ; rel="type" ; anchor="http://localhost:5000/w4ibb", ; rel="type" ; anchor="http://localhost:5000/w4ibb", - ; rel="citeas" ; anchor="http://localhost:5000/w4ibb", + ; rel="cite-as" ; anchor="http://localhost:5000/w4ibb", ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="text/turtle; charset=utf-8", ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/json", ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/xml", diff --git a/osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json index b254e4423df..08a763bd3e0 100644 --- a/osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json @@ -15,7 +15,7 @@ "href": "https://osf.io/vocab/2022/Preprint" } ], - "citeas": [ + "cite-as": [ { "href": "https://doi.org/11.pp/FK2osf.io/w4ibb_v1" } diff --git a/osf_tests/metadata/expected_metadata_files/project_basic.linkset b/osf_tests/metadata/expected_metadata_files/project_basic.linkset index edd482a5471..b2adb7d11d1 100644 --- a/osf_tests/metadata/expected_metadata_files/project_basic.linkset +++ b/osf_tests/metadata/expected_metadata_files/project_basic.linkset @@ -1,6 +1,6 @@ ; rel="author" ; anchor="http://localhost:5000/w2ibb", ; rel="type" ; anchor="http://localhost:5000/w2ibb", - ; rel="citeas" ; anchor="http://localhost:5000/w2ibb", + ; rel="cite-as" ; anchor="http://localhost:5000/w2ibb", ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="text/turtle; charset=utf-8", ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/json", ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/xml", diff --git a/osf_tests/metadata/expected_metadata_files/project_basic.linkset.json b/osf_tests/metadata/expected_metadata_files/project_basic.linkset.json index d50b5dbdfbf..752215307ce 100644 --- a/osf_tests/metadata/expected_metadata_files/project_basic.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/project_basic.linkset.json @@ -12,7 +12,7 @@ "href": "https://osf.io/vocab/2022/Project" } ], - "citeas": [ + "cite-as": [ { "href": "https://doi.org/10.70102/FK2osf.io/w2ibb" } diff --git a/osf_tests/metadata/expected_metadata_files/project_full.linkset b/osf_tests/metadata/expected_metadata_files/project_full.linkset index 9fb973b8e2d..f81c52f959e 100644 --- a/osf_tests/metadata/expected_metadata_files/project_full.linkset +++ b/osf_tests/metadata/expected_metadata_files/project_full.linkset @@ -1,11 +1,12 @@ ; rel="author" ; anchor="http://localhost:5000/w2ibb", ; rel="type" ; anchor="http://localhost:5000/w2ibb", ; rel="type" ; anchor="http://localhost:5000/w2ibb", - ; rel="citeas" ; anchor="http://localhost:5000/w2ibb", + ; rel="cite-as" ; anchor="http://localhost:5000/w2ibb", ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="text/turtle; charset=utf-8", ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/json", ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/xml", ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/ld+json", ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset", ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset+json", + ; rel="license" ; anchor="http://localhost:5000/w2ibb", ; rel="item" ; anchor="http://localhost:5000/w2ibb" ; type="application/octet-stream" diff --git a/osf_tests/metadata/expected_metadata_files/project_full.linkset.json b/osf_tests/metadata/expected_metadata_files/project_full.linkset.json index 865c47c37a7..6dbb4214511 100644 --- a/osf_tests/metadata/expected_metadata_files/project_full.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/project_full.linkset.json @@ -15,7 +15,7 @@ "href": "https://osf.io/vocab/2022/Project" } ], - "citeas": [ + "cite-as": [ { "href": "https://doi.org/10.70102/FK2osf.io/w2ibb" } @@ -46,6 +46,11 @@ "type": "application/linkset+json" } ], + "license": [ + { + "href": "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode" + } + ], "item": [ { "href": "http://localhost:5000/w3ibb", diff --git a/osf_tests/metadata/expected_metadata_files/registration_basic.linkset b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset index 280c2381c3c..4f70d4a4cd4 100644 --- a/osf_tests/metadata/expected_metadata_files/registration_basic.linkset +++ b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset @@ -1,7 +1,7 @@ ; rel="author" ; anchor="http://localhost:5000/w5ibb", ; rel="type" ; anchor="http://localhost:5000/w5ibb", ; rel="type" ; anchor="http://localhost:5000/w5ibb", - ; rel="citeas" ; anchor="http://localhost:5000/w5ibb", + ; rel="cite-as" ; anchor="http://localhost:5000/w5ibb", ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="text/turtle; charset=utf-8", ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/json", ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/xml", diff --git a/osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json index ce306eef66c..b787c6abeb6 100644 --- a/osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json @@ -15,7 +15,7 @@ "href": "https://osf.io/vocab/2022/Registration" } ], - "citeas": [ + "cite-as": [ { "href": "http://localhost:5000/w5ibb" } diff --git a/osf_tests/metadata/expected_metadata_files/registration_full.linkset b/osf_tests/metadata/expected_metadata_files/registration_full.linkset index 280c2381c3c..0cc3007a4ac 100644 --- a/osf_tests/metadata/expected_metadata_files/registration_full.linkset +++ b/osf_tests/metadata/expected_metadata_files/registration_full.linkset @@ -1,11 +1,12 @@ ; rel="author" ; anchor="http://localhost:5000/w5ibb", ; rel="type" ; anchor="http://localhost:5000/w5ibb", ; rel="type" ; anchor="http://localhost:5000/w5ibb", - ; rel="citeas" ; anchor="http://localhost:5000/w5ibb", + ; rel="cite-as" ; anchor="http://localhost:5000/w5ibb", ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="text/turtle; charset=utf-8", ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/json", ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/xml", ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/ld+json", ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset", ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset+json", + ; rel="license" ; anchor="http://localhost:5000/w5ibb", ; rel="item" ; anchor="http://localhost:5000/w5ibb" ; type="application/octet-stream" diff --git a/osf_tests/metadata/expected_metadata_files/registration_full.linkset.json b/osf_tests/metadata/expected_metadata_files/registration_full.linkset.json index ccd1227fc21..01f47927410 100644 --- a/osf_tests/metadata/expected_metadata_files/registration_full.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/registration_full.linkset.json @@ -15,7 +15,7 @@ "href": "https://osf.io/vocab/2022/Registration" } ], - "citeas": [ + "cite-as": [ { "href": "http://localhost:5000/w5ibb" } @@ -46,6 +46,11 @@ "type": "application/linkset+json" } ], + "license": [ + { + "href": "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode" + } + ], "item": [ { "href": "http://localhost:5000/w6ibb", From 65522249c76bad3f1cd9def4023052b357e0bb37 Mon Sep 17 00:00:00 2001 From: mkovalua Date: Wed, 4 Feb 2026 22:27:18 +0200 Subject: [PATCH 09/11] update file related code and unit tests --- osf/metadata/serializers/linkset.py | 80 +++++++++++-------- .../file_basic.linkset | 8 +- .../file_basic.linkset.json | 17 ++++ .../expected_metadata_files/file_full.linkset | 2 + .../file_full.linkset.json | 17 ++++ .../metadata/test_serialized_metadata.py | 5 +- 6 files changed, 86 insertions(+), 43 deletions(-) create mode 100644 osf_tests/metadata/expected_metadata_files/file_basic.linkset.json create mode 100644 osf_tests/metadata/expected_metadata_files/file_full.linkset create mode 100644 osf_tests/metadata/expected_metadata_files/file_full.linkset.json diff --git a/osf/metadata/serializers/linkset.py b/osf/metadata/serializers/linkset.py index e4c0e18a570..4607c6c9d08 100644 --- a/osf/metadata/serializers/linkset.py +++ b/osf/metadata/serializers/linkset.py @@ -25,44 +25,54 @@ class SignpostLink: target_uri: str target_attrs: Iterable[tuple(str, str)] +from osf import models as osfdb class BaseSignpostLinkset(MetadataSerializer, abc.ABC): def _each_link(self) -> Iterator[SignpostLink]: focus_iri = self.basket.focus.iri - for _creator_iri in self.basket[DCTERMS.creator]: - yield SignpostLink(focus_iri, 'author', str(_creator_iri), ()) - - # type - for _type_iri in self.basket[DCTERMS.type | RDF.type]: - yield SignpostLink(focus_iri, 'type', str(_type_iri), ()) - - # cite-as - yield SignpostLink(focus_iri, 'cite-as', next(( - _sameas_iri - for _sameas_iri in self.basket[OWL.sameAs] - if _sameas_iri.startswith(DOI) - ), focus_iri), ()) - - _record_uri = web_url_for('metadata_download', guid=self.basket.focus.dbmodel._id) - path = urljoin(DOMAIN, _record_uri) - from osf.metadata.serializers import METADATA_SERIALIZER_REGISTRY - # describedby - for _format_key, _serializer in METADATA_SERIALIZER_REGISTRY.items(): - yield SignpostLink( - focus_iri, - 'describedby', - path, - [('type', _serializer.mediatype)] - ) - - # license - for _license_uri in self.basket[DCTERMS.rights]: - if not isinstance(_license_uri, rdflib.BNode): - yield SignpostLink(focus_iri, 'license', str(_license_uri), ()) - - # item - for _file_iri in self.basket[OSF.contains]: - mime_type = next(self.basket[_file_iri:DCAT.mediaType]) - yield SignpostLink(focus_iri, 'item', str(_file_iri), [('type', mime_type)]) + if isinstance(self.basket.focus.dbmodel, osfdb.BaseFileNode): + # type + for _type_iri in self.basket[DCTERMS.type | RDF.type]: + yield SignpostLink(focus_iri, 'type', str(_type_iri), ()) + # collection + for _collection_uri in self.basket[OSF.isContainedBy]: + yield SignpostLink(focus_iri, 'collection', str(_collection_uri), ()) + else: + # author + for _creator_iri in self.basket[DCTERMS.creator]: + yield SignpostLink(focus_iri, 'author', str(_creator_iri), ()) + + # type + for _type_iri in self.basket[DCTERMS.type | RDF.type]: + yield SignpostLink(focus_iri, 'type', str(_type_iri), ()) + + # cite-as + yield SignpostLink(focus_iri, 'cite-as', next(( + _sameas_iri + for _sameas_iri in self.basket[OWL.sameAs] + if _sameas_iri.startswith(DOI) + ), focus_iri), ()) + + _record_uri = web_url_for('metadata_download', guid=self.basket.focus.dbmodel._id) + path = urljoin(DOMAIN, _record_uri) + from osf.metadata.serializers import METADATA_SERIALIZER_REGISTRY + # describedby + for _format_key, _serializer in METADATA_SERIALIZER_REGISTRY.items(): + yield SignpostLink( + focus_iri, + 'describedby', + path, + [('type', _serializer.mediatype)] + ) + + # license + for _license_uri in self.basket[DCTERMS.rights]: + if not isinstance(_license_uri, rdflib.BNode): + yield SignpostLink(focus_iri, 'license', str(_license_uri), ()) + + # item + for _file_iri in self.basket[OSF.contains]: + mime_type = next(self.basket[_file_iri:DCAT.mediaType]) + yield SignpostLink(focus_iri, 'item', str(_file_iri), [('type', mime_type)]) class SignpostLinkset(BaseSignpostLinkset): diff --git a/osf_tests/metadata/expected_metadata_files/file_basic.linkset b/osf_tests/metadata/expected_metadata_files/file_basic.linkset index c337374284d..1fd38a0b63a 100644 --- a/osf_tests/metadata/expected_metadata_files/file_basic.linkset +++ b/osf_tests/metadata/expected_metadata_files/file_basic.linkset @@ -1,8 +1,2 @@ ; rel="type" ; anchor="http://localhost:5000/w3ibb", - ; rel="cite-as" ; anchor="http://localhost:5000/w3ibb", - ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="text/turtle; charset=utf-8", - ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/json", - ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/xml", - ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/ld+json", - ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/linkset", - ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/linkset+json" + ; rel="collection" ; anchor="http://localhost:5000/w3ibb" \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/file_basic.linkset.json b/osf_tests/metadata/expected_metadata_files/file_basic.linkset.json new file mode 100644 index 00000000000..a85d2acbfb5 --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/file_basic.linkset.json @@ -0,0 +1,17 @@ +{ + "linkset": [ + { + "anchor": "http://localhost:5000/w3ibb", + "type": [ + { + "href": "https://osf.io/vocab/2022/File" + } + ], + "collection": [ + { + "href": "http://localhost:5000/w2ibb" + } + ] + } + ] +} \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/file_full.linkset b/osf_tests/metadata/expected_metadata_files/file_full.linkset new file mode 100644 index 00000000000..63f7200eae6 --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/file_full.linkset @@ -0,0 +1,2 @@ + ; rel="type" ; anchor="http://localhost:5000/w3ibb", + ; rel="collection" ; anchor="http://localhost:5000/w3ibb" diff --git a/osf_tests/metadata/expected_metadata_files/file_full.linkset.json b/osf_tests/metadata/expected_metadata_files/file_full.linkset.json new file mode 100644 index 00000000000..3120be61c60 --- /dev/null +++ b/osf_tests/metadata/expected_metadata_files/file_full.linkset.json @@ -0,0 +1,17 @@ +{ + "linkset": [ + { + "anchor": "http://localhost:5000/w3ibb", + "type": [ + { + "href": "https://osf.io/vocab/2022/File" + } + ], + "collection": [ + { + "href": "http://localhost:5000/w2ibb" + } + ] + } + ] +} diff --git a/osf_tests/metadata/test_serialized_metadata.py b/osf_tests/metadata/test_serialized_metadata.py index fea71c15ab8..020f4d18574 100644 --- a/osf_tests/metadata/test_serialized_metadata.py +++ b/osf_tests/metadata/test_serialized_metadata.py @@ -64,7 +64,8 @@ 'datacite-xml': 'file_basic.datacite.xml', 'datacite-json': 'file_basic.datacite.json', 'google-dataset-json-ld': 'file_basic.google-dataset.json', - # 'linkset': 'file_basic.linkset', not determined response to compare with _expected_metadata + 'linkset': 'file_basic.linkset', + 'linkset-json': 'file_basic.linkset.json', }, }, DCTERMS.Agent: { @@ -132,6 +133,8 @@ 'datacite-xml': 'file_full.datacite.xml', 'datacite-json': 'file_full.datacite.json', 'google-dataset-json-ld': 'file_full.google-dataset.json', + 'linkset': 'file_full.linkset', + 'linkset-json': 'file_full.linkset.json', }, OsfmapPartition.SUPPLEMENT: { 'turtle': 'file_supplement.turtle', From 21c3ced8711306f3794502c740c144fb98a7dc0d Mon Sep 17 00:00:00 2001 From: mkovalua Date: Fri, 6 Feb 2026 17:15:00 +0200 Subject: [PATCH 10/11] refactor code after CR | implement 'describes' | check file type differs from parent project / registry / preprint --- osf/metadata/osf_gathering.py | 8 +- osf/metadata/serializers/linkset.py | 125 ++++++++++++++++------------ 2 files changed, 79 insertions(+), 54 deletions(-) diff --git a/osf/metadata/osf_gathering.py b/osf/metadata/osf_gathering.py index e29c0788131..df1b737f70f 100644 --- a/osf/metadata/osf_gathering.py +++ b/osf/metadata/osf_gathering.py @@ -376,7 +376,7 @@ def osf_iri(guid_or_model): return OSFIO[guid._id] -def osfguid_from_iri(iri): +def osfguid_from_iri(iri: str) -> str: if iri.startswith(OSFIO): return without_namespace(iri, OSFIO) raise ValueError(f'expected iri starting with "{OSFIO}" (got "{iri}")') @@ -709,6 +709,12 @@ def gather_files(focus): def gather_file_mediatype(focus): mime_type = mime.guess_type(focus.dbmodel.name) yield (DCAT.mediaType, 'application/octet-stream') if mime_type == (None, None) else (DCAT.mediaType, mime_type[0]) + mime_type = mime.guess_type(focus.dbmodel.name)[0] + yield (DCAT.mediaType, ( + 'application/octet-stream' + if mime_type is None + else mime_type + )) @gather.er(DCTERMS.hasPart, DCTERMS.isPartOf) diff --git a/osf/metadata/serializers/linkset.py b/osf/metadata/serializers/linkset.py index 4607c6c9d08..5fe51d74d27 100644 --- a/osf/metadata/serializers/linkset.py +++ b/osf/metadata/serializers/linkset.py @@ -11,68 +11,90 @@ from collections import defaultdict import dataclasses import json +from urllib.parse import urljoin, urlsplit, urlencode, urlunsplit + import rdflib + from ._base import MetadataSerializer +from osf.metadata.osf_gathering import osfguid_from_iri from osf.metadata.rdfutils import DOI, DCTERMS, OWL, RDF, OSF, DCAT from website.settings import DOMAIN -from urllib.parse import urljoin from website.util import web_url_for + @dataclasses.dataclass class SignpostLink: - context_uri: str - relation_uri: str + anchor_uri: str + relation: str target_uri: str - target_attrs: Iterable[tuple(str, str)] + target_attrs: Iterable[tuple[str, str]] = () + -from osf import models as osfdb class BaseSignpostLinkset(MetadataSerializer, abc.ABC): def _each_link(self) -> Iterator[SignpostLink]: focus_iri = self.basket.focus.iri - if isinstance(self.basket.focus.dbmodel, osfdb.BaseFileNode): - # type - for _type_iri in self.basket[DCTERMS.type | RDF.type]: - yield SignpostLink(focus_iri, 'type', str(_type_iri), ()) - # collection + if self.basket.focus.rdftype == OSF.File: + # collection (file's containing obj) for _collection_uri in self.basket[OSF.isContainedBy]: - yield SignpostLink(focus_iri, 'collection', str(_collection_uri), ()) - else: - # author - for _creator_iri in self.basket[DCTERMS.creator]: - yield SignpostLink(focus_iri, 'author', str(_creator_iri), ()) + yield SignpostLink(focus_iri, 'collection', str(_collection_uri)) - # type + # author + for _creator_iri in self.basket[DCTERMS.creator]: + yield SignpostLink(focus_iri, 'author', str(_creator_iri)) + + # type + if self.basket.focus.rdftype == OSF.File: + parent_types = set(self.basket[OSF.isContainedBy / (DCTERMS.type | RDF.type)]) + for _type_iri in self.basket[DCTERMS.type | RDF.type]: + # check the type differs from parent project / registry / preprint + if _type_iri not in parent_types: + yield SignpostLink(focus_iri, 'type', str(_type_iri)) + else: for _type_iri in self.basket[DCTERMS.type | RDF.type]: - yield SignpostLink(focus_iri, 'type', str(_type_iri), ()) - - # cite-as - yield SignpostLink(focus_iri, 'cite-as', next(( - _sameas_iri - for _sameas_iri in self.basket[OWL.sameAs] - if _sameas_iri.startswith(DOI) - ), focus_iri), ()) - - _record_uri = web_url_for('metadata_download', guid=self.basket.focus.dbmodel._id) - path = urljoin(DOMAIN, _record_uri) - from osf.metadata.serializers import METADATA_SERIALIZER_REGISTRY - # describedby - for _format_key, _serializer in METADATA_SERIALIZER_REGISTRY.items(): - yield SignpostLink( - focus_iri, - 'describedby', - path, - [('type', _serializer.mediatype)] - ) - - # license - for _license_uri in self.basket[DCTERMS.rights]: - if not isinstance(_license_uri, rdflib.BNode): - yield SignpostLink(focus_iri, 'license', str(_license_uri), ()) - - # item - for _file_iri in self.basket[OSF.contains]: - mime_type = next(self.basket[_file_iri:DCAT.mediaType]) - yield SignpostLink(focus_iri, 'item', str(_file_iri), [('type', mime_type)]) + yield SignpostLink(focus_iri, 'type', str(_type_iri)) + + # cite-as + yield SignpostLink(focus_iri, 'cite-as', next(( + _sameas_iri + for _sameas_iri in self.basket[OWL.sameAs] + if _sameas_iri.startswith(DOI) + ), focus_iri)) + + base_metadata_url = urljoin(DOMAIN, web_url_for( + 'metadata_download', # name of a view function mapped in website/routes.py + guid=osfguid_from_iri(self.basket.focus.iri), + )) + split_base_metadata_url = urlsplit(base_metadata_url) + + # describes + yield SignpostLink( + base_metadata_url, + 'describes', + focus_iri, + ) + + from osf.metadata.serializers import METADATA_SERIALIZER_REGISTRY + # describedby + for _format_key, _serializer in METADATA_SERIALIZER_REGISTRY.items(): + _metadata_url = urlunsplit(split_base_metadata_url._replace( + query=urlencode({'format': _format_key}), + )) + yield SignpostLink( + focus_iri, + 'describedby', + _metadata_url, + [('type', _serializer.mediatype)] + ) + + # license + for _license_uri in self.basket[DCTERMS.rights]: + if not isinstance(_license_uri, rdflib.BNode): + yield SignpostLink(focus_iri, 'license', str(_license_uri)) + + # item + for _file_iri in self.basket[OSF.contains]: + mime_type = next(self.basket[_file_iri:DCAT.mediaType]) + yield SignpostLink(focus_iri, 'item', str(_file_iri), [('type', mime_type)]) class SignpostLinkset(BaseSignpostLinkset): @@ -92,8 +114,8 @@ def serialize(self) -> str | bytes: def _serialize_link(self, link: SignpostLink) -> str: segments = [ f'<{link.target_uri}>', - f'rel="{link.relation_uri}"', - f'anchor="{link.context_uri}"' + f'rel="{link.relation}"', + f'anchor="{link.anchor_uri}"' ] for key, value in link.target_attrs: segments.append(f'{key}="{value}"') @@ -114,11 +136,8 @@ def serialize(self) -> str | bytes: for link in self._each_link(): link_entry = {'href': link.target_uri} - - for key, value in link.target_attrs: - link_entry[key] = value - - grouped_links[link.context_uri][link.relation_uri].append(link_entry) + link_entry.update(link.target_attrs) + grouped_links[link.anchor_uri][link.relation].append(link_entry) linkset = [] for anchor, relations in grouped_links.items(): From 6c2395eeec1cc20bd01283a92770a9aeee318767 Mon Sep 17 00:00:00 2001 From: mkovalua Date: Fri, 6 Feb 2026 20:15:45 +0200 Subject: [PATCH 11/11] update files to compare on unittests run --- .../file_basic.linkset | 10 ++++- .../file_basic.linkset.json | 43 +++++++++++++++++- .../expected_metadata_files/file_full.linkset | 10 ++++- .../file_full.linkset.json | 45 +++++++++++++++++-- .../preprint_basic.linkset | 13 +++--- .../preprint_basic.linkset.json | 20 ++++++--- .../preprint_full.linkset | 15 ++++--- .../preprint_full.linkset.json | 22 ++++++--- .../project_basic.linkset | 15 ++++--- .../project_basic.linkset.json | 20 ++++++--- .../project_full.linkset | 13 +++--- .../project_full.linkset.json | 22 ++++++--- .../registration_basic.linkset | 13 +++--- .../registration_basic.linkset.json | 20 ++++++--- .../registration_full.linkset | 13 +++--- .../registration_full.linkset.json | 22 ++++++--- 16 files changed, 233 insertions(+), 83 deletions(-) diff --git a/osf_tests/metadata/expected_metadata_files/file_basic.linkset b/osf_tests/metadata/expected_metadata_files/file_basic.linkset index 1fd38a0b63a..7f4072d1af1 100644 --- a/osf_tests/metadata/expected_metadata_files/file_basic.linkset +++ b/osf_tests/metadata/expected_metadata_files/file_basic.linkset @@ -1,2 +1,10 @@ + ; rel="collection" ; anchor="http://localhost:5000/w3ibb", ; rel="type" ; anchor="http://localhost:5000/w3ibb", - ; rel="collection" ; anchor="http://localhost:5000/w3ibb" \ No newline at end of file + ; rel="cite-as" ; anchor="http://localhost:5000/w3ibb", + ; rel="describes" ; anchor="http://localhost:5000/metadata/w3ibb/", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/linkset+json" diff --git a/osf_tests/metadata/expected_metadata_files/file_basic.linkset.json b/osf_tests/metadata/expected_metadata_files/file_basic.linkset.json index a85d2acbfb5..517feda02fa 100644 --- a/osf_tests/metadata/expected_metadata_files/file_basic.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/file_basic.linkset.json @@ -2,14 +2,53 @@ "linkset": [ { "anchor": "http://localhost:5000/w3ibb", + "collection": [ + { + "href": "http://localhost:5000/w2ibb" + } + ], "type": [ { "href": "https://osf.io/vocab/2022/File" } ], - "collection": [ + "cite-as": [ { - "href": "http://localhost:5000/w2ibb" + "href": "http://localhost:5000/w3ibb" + } + ], + "describedby": [ + { + "href": "http://localhost:5000/metadata/w3ibb/?format=turtle", + "type": "text/turtle; charset=utf-8" + }, + { + "href": "http://localhost:5000/metadata/w3ibb/?format=datacite-json", + "type": "application/json" + }, + { + "href": "http://localhost:5000/metadata/w3ibb/?format=datacite-xml", + "type": "application/xml" + }, + { + "href": "http://localhost:5000/metadata/w3ibb/?format=google-dataset-json-ld", + "type": "application/ld+json" + }, + { + "href": "http://localhost:5000/metadata/w3ibb/?format=linkset", + "type": "application/linkset" + }, + { + "href": "http://localhost:5000/metadata/w3ibb/?format=linkset-json", + "type": "application/linkset+json" + } + ] + }, + { + "anchor": "http://localhost:5000/metadata/w3ibb/", + "describes": [ + { + "href": "http://localhost:5000/w3ibb" } ] } diff --git a/osf_tests/metadata/expected_metadata_files/file_full.linkset b/osf_tests/metadata/expected_metadata_files/file_full.linkset index 63f7200eae6..7f4072d1af1 100644 --- a/osf_tests/metadata/expected_metadata_files/file_full.linkset +++ b/osf_tests/metadata/expected_metadata_files/file_full.linkset @@ -1,2 +1,10 @@ + ; rel="collection" ; anchor="http://localhost:5000/w3ibb", ; rel="type" ; anchor="http://localhost:5000/w3ibb", - ; rel="collection" ; anchor="http://localhost:5000/w3ibb" + ; rel="cite-as" ; anchor="http://localhost:5000/w3ibb", + ; rel="describes" ; anchor="http://localhost:5000/metadata/w3ibb/", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w3ibb" ; type="application/linkset+json" diff --git a/osf_tests/metadata/expected_metadata_files/file_full.linkset.json b/osf_tests/metadata/expected_metadata_files/file_full.linkset.json index 3120be61c60..517feda02fa 100644 --- a/osf_tests/metadata/expected_metadata_files/file_full.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/file_full.linkset.json @@ -2,16 +2,55 @@ "linkset": [ { "anchor": "http://localhost:5000/w3ibb", + "collection": [ + { + "href": "http://localhost:5000/w2ibb" + } + ], "type": [ { "href": "https://osf.io/vocab/2022/File" } ], - "collection": [ + "cite-as": [ { - "href": "http://localhost:5000/w2ibb" + "href": "http://localhost:5000/w3ibb" + } + ], + "describedby": [ + { + "href": "http://localhost:5000/metadata/w3ibb/?format=turtle", + "type": "text/turtle; charset=utf-8" + }, + { + "href": "http://localhost:5000/metadata/w3ibb/?format=datacite-json", + "type": "application/json" + }, + { + "href": "http://localhost:5000/metadata/w3ibb/?format=datacite-xml", + "type": "application/xml" + }, + { + "href": "http://localhost:5000/metadata/w3ibb/?format=google-dataset-json-ld", + "type": "application/ld+json" + }, + { + "href": "http://localhost:5000/metadata/w3ibb/?format=linkset", + "type": "application/linkset" + }, + { + "href": "http://localhost:5000/metadata/w3ibb/?format=linkset-json", + "type": "application/linkset+json" + } + ] + }, + { + "anchor": "http://localhost:5000/metadata/w3ibb/", + "describes": [ + { + "href": "http://localhost:5000/w3ibb" } ] } ] -} +} \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset index 4a8eb75a452..c5272b32607 100644 --- a/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset +++ b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset @@ -2,9 +2,10 @@ ; rel="type" ; anchor="http://localhost:5000/w4ibb", ; rel="type" ; anchor="http://localhost:5000/w4ibb", ; rel="cite-as" ; anchor="http://localhost:5000/w4ibb", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="text/turtle; charset=utf-8", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/json", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/xml", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/ld+json", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset+json" + ; rel="describes" ; anchor="http://localhost:5000/metadata/w4ibb/", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset+json" \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json index 6cfaacb5b1a..e856d39089a 100644 --- a/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/preprint_basic.linkset.json @@ -22,30 +22,38 @@ ], "describedby": [ { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=turtle", "type": "text/turtle; charset=utf-8" }, { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=datacite-json", "type": "application/json" }, { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=datacite-xml", "type": "application/xml" }, { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=google-dataset-json-ld", "type": "application/ld+json" }, { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=linkset", "type": "application/linkset" }, { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=linkset-json", "type": "application/linkset+json" } ] + }, + { + "anchor": "http://localhost:5000/metadata/w4ibb/", + "describes": [ + { + "href": "http://localhost:5000/w4ibb" + } + ] } ] } \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/preprint_full.linkset b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset index 4a8eb75a452..181dc4e9e8c 100644 --- a/osf_tests/metadata/expected_metadata_files/preprint_full.linkset +++ b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset @@ -2,9 +2,12 @@ ; rel="type" ; anchor="http://localhost:5000/w4ibb", ; rel="type" ; anchor="http://localhost:5000/w4ibb", ; rel="cite-as" ; anchor="http://localhost:5000/w4ibb", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="text/turtle; charset=utf-8", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/json", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/xml", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/ld+json", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset", - ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset+json" + ; rel="describes" ; anchor="http://localhost:5000/metadata/w4ibb/", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w4ibb" ; type="application/linkset+json" + + diff --git a/osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json index 08a763bd3e0..e856d39089a 100644 --- a/osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/preprint_full.linkset.json @@ -22,30 +22,38 @@ ], "describedby": [ { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=turtle", "type": "text/turtle; charset=utf-8" }, { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=datacite-json", "type": "application/json" }, { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=datacite-xml", "type": "application/xml" }, { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=google-dataset-json-ld", "type": "application/ld+json" }, { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=linkset", "type": "application/linkset" }, { - "href": "http://localhost:5000/metadata/w4ibb_v1/", + "href": "http://localhost:5000/metadata/w4ibb/?format=linkset-json", "type": "application/linkset+json" } ] + }, + { + "anchor": "http://localhost:5000/metadata/w4ibb/", + "describes": [ + { + "href": "http://localhost:5000/w4ibb" + } + ] } ] -} +} \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/project_basic.linkset b/osf_tests/metadata/expected_metadata_files/project_basic.linkset index b2adb7d11d1..0cd46ab70c2 100644 --- a/osf_tests/metadata/expected_metadata_files/project_basic.linkset +++ b/osf_tests/metadata/expected_metadata_files/project_basic.linkset @@ -1,10 +1,11 @@ ; rel="author" ; anchor="http://localhost:5000/w2ibb", ; rel="type" ; anchor="http://localhost:5000/w2ibb", ; rel="cite-as" ; anchor="http://localhost:5000/w2ibb", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="text/turtle; charset=utf-8", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/json", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/xml", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/ld+json", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset+json", - ; rel="item" ; anchor="http://localhost:5000/w2ibb" ; type="application/octet-stream" + ; rel="describes" ; anchor="http://localhost:5000/metadata/w2ibb/", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset+json", + ; rel="item" ; anchor="http://localhost:5000/w2ibb" ; type="application/octet-stream" \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/project_basic.linkset.json b/osf_tests/metadata/expected_metadata_files/project_basic.linkset.json index 752215307ce..c172ee29b53 100644 --- a/osf_tests/metadata/expected_metadata_files/project_basic.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/project_basic.linkset.json @@ -19,27 +19,27 @@ ], "describedby": [ { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=turtle", "type": "text/turtle; charset=utf-8" }, { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=datacite-json", "type": "application/json" }, { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=datacite-xml", "type": "application/xml" }, { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=google-dataset-json-ld", "type": "application/ld+json" }, { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=linkset", "type": "application/linkset" }, { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=linkset-json", "type": "application/linkset+json" } ], @@ -49,6 +49,14 @@ "type": "application/octet-stream" } ] + }, + { + "anchor": "http://localhost:5000/metadata/w2ibb/", + "describes": [ + { + "href": "http://localhost:5000/w2ibb" + } + ] } ] } \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/project_full.linkset b/osf_tests/metadata/expected_metadata_files/project_full.linkset index f81c52f959e..16acde1b2f1 100644 --- a/osf_tests/metadata/expected_metadata_files/project_full.linkset +++ b/osf_tests/metadata/expected_metadata_files/project_full.linkset @@ -2,11 +2,12 @@ ; rel="type" ; anchor="http://localhost:5000/w2ibb", ; rel="type" ; anchor="http://localhost:5000/w2ibb", ; rel="cite-as" ; anchor="http://localhost:5000/w2ibb", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="text/turtle; charset=utf-8", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/json", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/xml", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/ld+json", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset", - ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset+json", + ; rel="describes" ; anchor="http://localhost:5000/metadata/w2ibb/", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w2ibb" ; type="application/linkset+json", ; rel="license" ; anchor="http://localhost:5000/w2ibb", ; rel="item" ; anchor="http://localhost:5000/w2ibb" ; type="application/octet-stream" diff --git a/osf_tests/metadata/expected_metadata_files/project_full.linkset.json b/osf_tests/metadata/expected_metadata_files/project_full.linkset.json index 6dbb4214511..0de872a2b6a 100644 --- a/osf_tests/metadata/expected_metadata_files/project_full.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/project_full.linkset.json @@ -22,27 +22,27 @@ ], "describedby": [ { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=turtle", "type": "text/turtle; charset=utf-8" }, { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=datacite-json", "type": "application/json" }, { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=datacite-xml", "type": "application/xml" }, { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=google-dataset-json-ld", "type": "application/ld+json" }, { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=linkset", "type": "application/linkset" }, { - "href": "http://localhost:5000/metadata/w2ibb/", + "href": "http://localhost:5000/metadata/w2ibb/?format=linkset-json", "type": "application/linkset+json" } ], @@ -57,6 +57,14 @@ "type": "application/octet-stream" } ] + }, + { + "anchor": "http://localhost:5000/metadata/w2ibb/", + "describes": [ + { + "href": "http://localhost:5000/w2ibb" + } + ] } ] -} +} \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/registration_basic.linkset b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset index 4f70d4a4cd4..fbc1ed1f468 100644 --- a/osf_tests/metadata/expected_metadata_files/registration_basic.linkset +++ b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset @@ -2,10 +2,11 @@ ; rel="type" ; anchor="http://localhost:5000/w5ibb", ; rel="type" ; anchor="http://localhost:5000/w5ibb", ; rel="cite-as" ; anchor="http://localhost:5000/w5ibb", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="text/turtle; charset=utf-8", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/json", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/xml", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/ld+json", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset+json", + ; rel="describes" ; anchor="http://localhost:5000/metadata/w5ibb/", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset+json", ; rel="item" ; anchor="http://localhost:5000/w5ibb" ; type="application/octet-stream" diff --git a/osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json index b787c6abeb6..5a09ddeaa02 100644 --- a/osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/registration_basic.linkset.json @@ -22,27 +22,27 @@ ], "describedby": [ { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=turtle", "type": "text/turtle; charset=utf-8" }, { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=datacite-json", "type": "application/json" }, { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=datacite-xml", "type": "application/xml" }, { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=google-dataset-json-ld", "type": "application/ld+json" }, { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=linkset", "type": "application/linkset" }, { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=linkset-json", "type": "application/linkset+json" } ], @@ -52,6 +52,14 @@ "type": "application/octet-stream" } ] + }, + { + "anchor": "http://localhost:5000/metadata/w5ibb/", + "describes": [ + { + "href": "http://localhost:5000/w5ibb" + } + ] } ] } \ No newline at end of file diff --git a/osf_tests/metadata/expected_metadata_files/registration_full.linkset b/osf_tests/metadata/expected_metadata_files/registration_full.linkset index 0cc3007a4ac..14e698ed9de 100644 --- a/osf_tests/metadata/expected_metadata_files/registration_full.linkset +++ b/osf_tests/metadata/expected_metadata_files/registration_full.linkset @@ -2,11 +2,12 @@ ; rel="type" ; anchor="http://localhost:5000/w5ibb", ; rel="type" ; anchor="http://localhost:5000/w5ibb", ; rel="cite-as" ; anchor="http://localhost:5000/w5ibb", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="text/turtle; charset=utf-8", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/json", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/xml", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/ld+json", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset", - ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset+json", + ; rel="describes" ; anchor="http://localhost:5000/metadata/w5ibb/", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="text/turtle; charset=utf-8", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/json", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/xml", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/ld+json", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset", + ; rel="describedby" ; anchor="http://localhost:5000/w5ibb" ; type="application/linkset+json", ; rel="license" ; anchor="http://localhost:5000/w5ibb", ; rel="item" ; anchor="http://localhost:5000/w5ibb" ; type="application/octet-stream" diff --git a/osf_tests/metadata/expected_metadata_files/registration_full.linkset.json b/osf_tests/metadata/expected_metadata_files/registration_full.linkset.json index 01f47927410..0b5f3c144e8 100644 --- a/osf_tests/metadata/expected_metadata_files/registration_full.linkset.json +++ b/osf_tests/metadata/expected_metadata_files/registration_full.linkset.json @@ -22,27 +22,27 @@ ], "describedby": [ { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=turtle", "type": "text/turtle; charset=utf-8" }, { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=datacite-json", "type": "application/json" }, { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=datacite-xml", "type": "application/xml" }, { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=google-dataset-json-ld", "type": "application/ld+json" }, { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=linkset", "type": "application/linkset" }, { - "href": "http://localhost:5000/metadata/w5ibb/", + "href": "http://localhost:5000/metadata/w5ibb/?format=linkset-json", "type": "application/linkset+json" } ], @@ -57,6 +57,14 @@ "type": "application/octet-stream" } ] + }, + { + "anchor": "http://localhost:5000/metadata/w5ibb/", + "describes": [ + { + "href": "http://localhost:5000/w5ibb" + } + ] } ] -} +} \ No newline at end of file