File size: 7,363 Bytes
5162ead
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
af72d76
5162ead
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b55004d
 
5162ead
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7e6c679
 
 
 
 
 
 
 
af72d76
7e6c679
 
af72d76
7e6c679
af72d76
7e6c679
 
 
 
 
 
46002b0
 
 
af72d76
46002b0
 
 
 
 
af72d76
 
46002b0
 
 
 
 
 
 
 
af72d76
46002b0
 
 
 
 
 
 
 
5162ead
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env python3
"""Checks for the code and data relation in the Crossref deposit (2026-09-29):
an article whose author gave a public code and data link carries
isSupplementedBy in its Crossref record, as a DOI when the link is a doi.org
link and as a URI otherwise; every variant validates against the Crossref
5.4.0 schema. Offline and pure: no network, no files written.
Run from the repo root with the venv:
    .venv/bin/python intake/scripts/test_crossref_supplement.py
"""
from __future__ import annotations

import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))

failures: list[str] = []


def check(cond: bool, name: str) -> None:
    print(("  ok   " if cond else "  FAIL ") + name)
    if not cond:
        failures.append(name)


import crossref_deposit as cd  # noqa: E402

BASE = {"sub_id": "ICSAC-SUB-00099", "title": "A test paper", "abstract": "An abstract.",
        "license": "cc-by-4.0", "source": "upload",
        "creators": [{"name": "Test, Author", "orcid": "0000-0002-1825-0097"}]}
ZEN = "https://doi.org/10.5281/zenodo.23042786"


def build(sub: dict, content_type: str = "journal-article") -> str:
    xml = cd.build_deposit_xml(sub, doi="10.5555/icsac.2026.099",
                               landing_url="https://icsacinstitute.org/publications/a-test-paper",
                               pdf_url="https://icsacinstitute.org/papers/ICSAC-SUB-00099.pdf",
                               content_type=content_type)
    try:
        cd.validate_xml(xml)
        ok = True
    except Exception as exc:  # noqa: BLE001
        print(f"       schema: {exc}")
        ok = False
    return xml.decode() if ok else ""


print("1. which identifier")
rel = cd.code_data_relation
check(rel(dict(BASE, code_data={"available": True, "url": ZEN})) == ("doi", "10.5281/zenodo.23042786"),
      "a doi.org link is registered as its DOI")
check(rel(dict(BASE, code_data={"available": True, "url": "https://dx.doi.org/10.5061/dryad.abc123"})) == ("doi", "10.5061/dryad.abc123"),
      "a dx.doi.org link too")
check(rel(dict(BASE, code_data={"available": True, "url": "https://github.com/someone/repo"})) == ("uri", "https://github.com/someone/repo"),
      "any other public link is registered as a URI")
check(rel(dict(BASE, code_data={"available": False, "url": None})) is None, "no link, no relation")
check(rel(dict(BASE)) is None, "a submission from before the field existed has no relation")
check(rel(dict(BASE, code_data={"available": True, "url": "  "})) is None, "a blank link is no link")

print("2. the deposit")
x = build(dict(BASE, code_data={"available": True, "url": ZEN}))
check(bool(x), "an article with a DOI archive validates against the Crossref 5.4.0 schema")
check('<rel:inter_work_relation relationship-type="isSupplementedBy" identifier-type="doi">10.5281/zenodo.23042786</rel:inter_work_relation>' in x,
      "it carries isSupplementedBy -> the archive DOI")
art = x[x.find("<journal_article"):]   # journal_metadata may carry the journal title DOI's own doi_data
check(0 < art.find("<rel:program") < art.find("<doi_data"), "the relation sits before the article's doi_data")
check("hasPreprint" not in x, "an upload with no preprint carries no preprint relation")

x = build(dict(BASE, code_data={"available": True, "url": "https://github.com/someone/repo"}))
check(bool(x) and 'identifier-type="uri">https://github.com/someone/repo<' in x,
      "a GitHub link validates and is registered as a URI")

x = build(dict(BASE, source="doi", doi="10.5281/zenodo.123456",
               code_data={"available": True, "url": ZEN}))
check(bool(x) and x.count("<rel:program") == 1 and x.count("<rel:related_item>") == 2,
      "a preprint and an archive share one relations program, two related items")
check('relationship-type="hasPreprint"' in x and 'relationship-type="isSupplementedBy"' in x,
      "both relations present")

x = build(dict(BASE))
check(bool(x) and "<rel:program" not in x, "no preprint and no archive: no relations program at all")

x = build(dict(BASE, code_data={"available": True, "url": ZEN}), content_type="report-paper")
check(bool(x) and "isSupplementedBy" in x, "report-paper deposits carry the relation and validate too")

x = build(dict(BASE, code_data={"available": True, "url": "https://example.org/data?x=1&y=2"}))
check(bool(x) and "x=1&amp;y=2" in x, "a link with an ampersand is escaped and still validates")

print("3. the journal title DOI (no ISSN yet)")
import config  # noqa: E402
saved = getattr(config, "CROSSREF_JOURNAL_DOI", None)
try:
    config.CROSSREF_JOURNAL_DOI = ""
    x = build(dict(BASE))
    jm = x[x.find("<journal_metadata"):x.find("</journal_metadata>")]
    check(bool(x) and "<doi_data" not in jm, "unset: journal_metadata carries no doi_data (the deposit as before)")
    config.CROSSREF_JOURNAL_DOI = "10.5555/persistence"
    x = build(dict(BASE, code_data={"available": True, "url": ZEN}))
    jm = x[x.find("<journal_metadata"):x.find("</journal_metadata>")]
    check(bool(x) and "<doi>10.5555/persistence</doi>" in jm and "<resource>https://icsacinstitute.org/journal/</resource>" in jm,
          "set: journal_metadata carries the title DOI and the journal page, and validates")
    check(x.count("<doi>10.5555/icsac.2026.099</doi>") == 1, "the article keeps its own DOI")
finally:
    if saved is None:
        delattr(config, "CROSSREF_JOURNAL_DOI") if hasattr(config, "CROSSREF_JOURNAL_DOI") else None
    else:
        config.CROSSREF_JOURNAL_DOI = saved

print("4. the Zenodo archive copy follows the Crossref record")
import repository_deposit as rd  # noqa: E402
sub = dict(BASE, resource_type="preprint", publication_date="2026-09-29")
md = rd._build_metadata(sub, external_doi="10.5555/icsac.2026.099")
check(md.get("upload_type") == "publication" and md.get("publication_type") == "article",
      "with an ICSAC DOI, a form 'preprint' becomes a journal article")
check(md.get("journal_title") == getattr(config, "CROSSREF_JOURNAL_TITLE", "Persistence")
      and md.get("journal_volume") == str(getattr(config, "CROSSREF_JOURNAL_VOLUME", "1")),
      "it names the journal and the volume from config")
check(md.get("doi") == "10.5555/icsac.2026.099", "the external DOI still rides on the draft")
md = rd._build_metadata(dict(sub, resource_type="dataset"), external_doi="10.5555/icsac.2026.099")
check(md.get("upload_type") == "publication" and md.get("publication_type") == "article",
      "any form type is overridden once the work is an ICSAC-registered article")
md = rd._build_metadata(sub)
check(md.get("publication_type") == "preprint" and "journal_title" not in md,
      "without an ICSAC DOI (Zenodo mints its own), the author's type stands")
saved_ct = getattr(config, "CROSSREF_CONTENT_TYPE", None)
try:
    config.CROSSREF_CONTENT_TYPE = "report-paper"
    md = rd._build_metadata(sub, external_doi="10.5555/icsac.2026.099")
    check(md.get("publication_type") == "report" and "journal_title" not in md,
          "a report-paper deployment archives a report, with no journal fields")
finally:
    if saved_ct is None:
        delattr(config, "CROSSREF_CONTENT_TYPE")
    else:
        config.CROSSREF_CONTENT_TYPE = saved_ct

print()
if failures:
    print(f"FAILED: {len(failures)}")
    for f in failures:
        print("  - " + f)
    sys.exit(1)
print("ALL GREEN")