Skip to content

Publication

Publication Documentation sub-package for MaRDMO.

Provides everything needed to retrieve and document publications within RDMO:

  • :mod:~MaRDMO.publication.models — Dataclasses for Author, Journal, and Publication entities populated from SPARQL results, Crossref, DataCite, DOI resolution, zbMath, and ORCID APIs.
  • :mod:~MaRDMO.publication.handlers — Signal handlers that fetch publication metadata and fill the questionnaire when a citation ID is saved.
  • :mod:~MaRDMO.publication.providers — RDMO optionset provider for searching publications across MaRDI Portal and Wikidata.
  • :mod:~MaRDMO.publication.worker — Background worker that retrieves full publication metadata (authors, journals) and writes it to the questionnaire.
  • :mod:~MaRDMO.publication.utils — Helper functions for querying external citation APIs, resolving ORCiDs, and cleaning bibliographic data.
  • :mod:~MaRDMO.publication.constants — Field mappings and relation lists used when writing publication data to the questionnaire and the MaRDI Portal.

Handlers

Module containing handlers for the Publication documentation sub-package.

Listens to RDMO model signals to keep publication-related questionnaire state consistent. For example, cleans up dependent Value objects when a publication set entry is deleted.

Provides:

  • :class:Information — signal receiver class that wires up publication-set relations in the questionnaire (relation method)
  • publication_set_deletepost_delete receiver that removes orphaned values when a publication value set is removed

Information

Class containing functions, querying external sources for specific entities and integrating the related metadata into the questionnaire.

Source code in MaRDMO/publication/handlers.py
 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
class Information:
    '''Class containing functions, querying external sources for specific
       entities and integrating the related metadata into the questionnaire.'''

    def __init__(self):
        '''Load publication questions, ontology registries, and the base URI.'''
        self.questions = get_questions('publication')
        self.mathalgodb = get_mathalgodb()
        self.mathmoddb = get_mathmoddb()
        self.base = BASE_URI

    def citation(self, instance):
        '''Handle a Publication ID-field save: populate citation metadata from the
        MaRDI Portal or Wikidata.

        Extracts project, text, external_id, set_index, catalog, and snapshot
        from *instance* and delegates to :meth:`fill_citation`.

        Args:
            instance: RDMO :class:`~rdmo.projects.models.Value` that was just saved.
        '''
        self.fill_citation(
            project     = instance.project,
            text        = instance.text,
            external_id = instance.external_id,
            set_index   = instance.set_index,
            catalog     = str(getattr(instance.project, 'catalog', '')),
            snapshot    = instance.snapshot,
        )

    def fill_citation(self, project, text, external_id, set_index, catalog='', snapshot=None):
        '''Populate publication citation metadata from the MaRDI Portal or Wikidata.

        Cleans any previously stored background data, then (if *text* is set
        and not ``"not found"``) adds basic label/description, fetches SPARQL
        citation data, stores references, and writes catalog-specific
        Publication–Entity / Publication–Algorithm / Publication–Benchmark/Software
        relations.

        Args:
            project:     RDMO project instance.
            text:        Raw ID-field text (``"Label (Description) [source]"``).
            external_id: External ID string in ``"source:id"`` format.
            set_index:   Set-index of the publication page.
            catalog:     Active catalog URI string (used for relation dispatch).
            snapshot:    RDMO snapshot (``None`` for the current snapshot).
        '''

        publication = self.questions["Publication"]

        clean_background_data(
            key_dict  = ITEMINFOS | CITATIONINFOS | LANGUAGES | JOURNALS | AUTHORS,
            questions = publication,
            project   = project,
            snapshot  = snapshot,
            set_index = set_index
        )

        # Stop if no Text or 'not found' in ID Field
        if not text or text == 'not found':
            return

        # Add basic Information
        add_basics(
            project   = project,
            text      = text,
            questions = self.questions,
            item_type = 'Publication',
            index     = (set_index, 0)
        )

        # Get Source and ID of selected Publication
        source, identifier = external_id.split(':')

        # Query the External Source
        query = get_sparql_query(
            f'publication/queries/doi_from_{source}.sparql'
        ).format(
            identifier,
            **get_items(),
            **get_properties()
        )

        results = query_sparql(query, get_url(source, 'sparql'))

        if not results:
            return

        # Structure the data
        data = Publication.from_query(results)

        add_references(
            project   = project,
            data      = data,
            uri       = f'{BASE_URI}{publication["Reference"]["uri"]}',
            set_index = set_index
        )

        if source != 'mardi':
            return

        # For Models: add Publication–Entity Relations
        if catalog.endswith(('mardmo-model-catalog', 'mardmo-model-basics-catalog')):
            add_relations_flexible(
                project   = project,
                data      = data,
                props     = {'keys': PROPS['P2E'], 'mapping': self.mathmoddb},
                index     = {'set_prefix': set_index},
                statement = {
                    'relation': f'{BASE_URI}{publication["P2E"]["uri"]}',
                    'relatant': f'{BASE_URI}{publication["EntityRelatant"]["uri"]}',
                },
            )

        # For Algorithms: add Publication–Algorithm and Publication–Benchmark/Software Relations
        if catalog.endswith('mardmo-algorithm-catalog'):
            add_relations_flexible(
                project   = project,
                data      = data,
                props     = {'keys': PROPS['P2A'], 'mapping': self.mathalgodb},
                index     = {'set_prefix': set_index},
                statement = {
                    'relation': f'{BASE_URI}{publication["P2A"]["uri"]}',
                    'relatant': f'{BASE_URI}{publication["ARelatant"]["uri"]}',
                },
            )
            add_relations_flexible(
                project   = project,
                data      = data,
                props     = {'keys': PROPS['P2BS'], 'mapping': self.mathalgodb},
                index     = {'set_prefix': set_index},
                statement = {
                    'relation': f'{BASE_URI}{publication["P2BS"]["uri"]}',
                    'relatant': f'{BASE_URI}{publication["BSRelatant"]["uri"]}',
                },
            )

__init__()

Load publication questions, ontology registries, and the base URI.

Source code in MaRDMO/publication/handlers.py
49
50
51
52
53
54
def __init__(self):
    '''Load publication questions, ontology registries, and the base URI.'''
    self.questions = get_questions('publication')
    self.mathalgodb = get_mathalgodb()
    self.mathmoddb = get_mathmoddb()
    self.base = BASE_URI

citation(instance)

Handle a Publication ID-field save: populate citation metadata from the MaRDI Portal or Wikidata.

Extracts project, text, external_id, set_index, catalog, and snapshot from instance and delegates to :meth:fill_citation.

Parameters:

Name Type Description Default
instance

RDMO :class:~rdmo.projects.models.Value that was just saved.

required
Source code in MaRDMO/publication/handlers.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def citation(self, instance):
    '''Handle a Publication ID-field save: populate citation metadata from the
    MaRDI Portal or Wikidata.

    Extracts project, text, external_id, set_index, catalog, and snapshot
    from *instance* and delegates to :meth:`fill_citation`.

    Args:
        instance: RDMO :class:`~rdmo.projects.models.Value` that was just saved.
    '''
    self.fill_citation(
        project     = instance.project,
        text        = instance.text,
        external_id = instance.external_id,
        set_index   = instance.set_index,
        catalog     = str(getattr(instance.project, 'catalog', '')),
        snapshot    = instance.snapshot,
    )

fill_citation(project, text, external_id, set_index, catalog='', snapshot=None)

Populate publication citation metadata from the MaRDI Portal or Wikidata.

Cleans any previously stored background data, then (if text is set and not "not found") adds basic label/description, fetches SPARQL citation data, stores references, and writes catalog-specific Publication–Entity / Publication–Algorithm / Publication–Benchmark/Software relations.

Parameters:

Name Type Description Default
project

RDMO project instance.

required
text

Raw ID-field text ("Label (Description) [source]").

required
external_id

External ID string in "source:id" format.

required
set_index

Set-index of the publication page.

required
catalog

Active catalog URI string (used for relation dispatch).

''
snapshot

RDMO snapshot (None for the current snapshot).

None
Source code in MaRDMO/publication/handlers.py
 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
def fill_citation(self, project, text, external_id, set_index, catalog='', snapshot=None):
    '''Populate publication citation metadata from the MaRDI Portal or Wikidata.

    Cleans any previously stored background data, then (if *text* is set
    and not ``"not found"``) adds basic label/description, fetches SPARQL
    citation data, stores references, and writes catalog-specific
    Publication–Entity / Publication–Algorithm / Publication–Benchmark/Software
    relations.

    Args:
        project:     RDMO project instance.
        text:        Raw ID-field text (``"Label (Description) [source]"``).
        external_id: External ID string in ``"source:id"`` format.
        set_index:   Set-index of the publication page.
        catalog:     Active catalog URI string (used for relation dispatch).
        snapshot:    RDMO snapshot (``None`` for the current snapshot).
    '''

    publication = self.questions["Publication"]

    clean_background_data(
        key_dict  = ITEMINFOS | CITATIONINFOS | LANGUAGES | JOURNALS | AUTHORS,
        questions = publication,
        project   = project,
        snapshot  = snapshot,
        set_index = set_index
    )

    # Stop if no Text or 'not found' in ID Field
    if not text or text == 'not found':
        return

    # Add basic Information
    add_basics(
        project   = project,
        text      = text,
        questions = self.questions,
        item_type = 'Publication',
        index     = (set_index, 0)
    )

    # Get Source and ID of selected Publication
    source, identifier = external_id.split(':')

    # Query the External Source
    query = get_sparql_query(
        f'publication/queries/doi_from_{source}.sparql'
    ).format(
        identifier,
        **get_items(),
        **get_properties()
    )

    results = query_sparql(query, get_url(source, 'sparql'))

    if not results:
        return

    # Structure the data
    data = Publication.from_query(results)

    add_references(
        project   = project,
        data      = data,
        uri       = f'{BASE_URI}{publication["Reference"]["uri"]}',
        set_index = set_index
    )

    if source != 'mardi':
        return

    # For Models: add Publication–Entity Relations
    if catalog.endswith(('mardmo-model-catalog', 'mardmo-model-basics-catalog')):
        add_relations_flexible(
            project   = project,
            data      = data,
            props     = {'keys': PROPS['P2E'], 'mapping': self.mathmoddb},
            index     = {'set_prefix': set_index},
            statement = {
                'relation': f'{BASE_URI}{publication["P2E"]["uri"]}',
                'relatant': f'{BASE_URI}{publication["EntityRelatant"]["uri"]}',
            },
        )

    # For Algorithms: add Publication–Algorithm and Publication–Benchmark/Software Relations
    if catalog.endswith('mardmo-algorithm-catalog'):
        add_relations_flexible(
            project   = project,
            data      = data,
            props     = {'keys': PROPS['P2A'], 'mapping': self.mathalgodb},
            index     = {'set_prefix': set_index},
            statement = {
                'relation': f'{BASE_URI}{publication["P2A"]["uri"]}',
                'relatant': f'{BASE_URI}{publication["ARelatant"]["uri"]}',
            },
        )
        add_relations_flexible(
            project   = project,
            data      = data,
            props     = {'keys': PROPS['P2BS'], 'mapping': self.mathalgodb},
            index     = {'set_prefix': set_index},
            statement = {
                'relation': f'{BASE_URI}{publication["P2BS"]["uri"]}',
                'relatant': f'{BASE_URI}{publication["BSRelatant"]["uri"]}',
            },
        )

publication_set_delete(sender, **kwargs)

Signal handler: delete hidden citation data when a Publication set page is removed.

Connected to the post_delete signal on :class:~rdmo.projects.models.Value. When the deleted value corresponds to the top-level Publication attribute, removes all background citation fields (item info, language, journal, author) for that set index.

Parameters:

Name Type Description Default
sender

The :class:~rdmo.projects.models.Value model class.

required
**kwargs

Signal keyword arguments; "instance" holds the deleted value.

{}
Source code in MaRDMO/publication/handlers.py
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
@receiver(post_delete, sender=Value)
def publication_set_delete(sender, **kwargs):
    '''Signal handler: delete hidden citation data when a Publication set page is removed.

    Connected to the ``post_delete`` signal on :class:`~rdmo.projects.models.Value`.
    When the deleted value corresponds to the top-level Publication attribute,
    removes all background citation fields (item info, language, journal, author)
    for that set index.

    Args:
        sender: The :class:`~rdmo.projects.models.Value` model class.
        **kwargs: Signal keyword arguments; ``"instance"`` holds the deleted value.
    '''
    instance = kwargs.get("instance", None)
    questions = get_questions('publication')
    if instance and instance.attribute.uri == f'{BASE_URI}{questions["Publication"]["uri"]}':
        clean_background_data(
            key_dict  = ITEMINFOS | CITATIONINFOS | LANGUAGES | JOURNALS | AUTHORS,
            questions = questions["Publication"],
            project   = instance.project,
            snapshot  = instance.snapshot,
            set_index = instance.set_index
        )

Models

Dataclasses that represent bibliographic entities in the Publication catalog.

Each class maps to one entity type retrieved from external sources (Crossref, ZbMATH, Wikidata, ORCID) during publication metadata collection. Instances are populated from API responses and carry all fields needed to fill questionnaire answers and produce export entries.

Provides:

  • :class:Author — person entity with ORCID, ZbMATH, and Wikidata identifiers
  • :class:Journal — publication venue with ISSN and portal identifiers
  • :class:Publication — central bibliographic record linking authors and journal

Author dataclass

Data Class For Authors

Source code in MaRDMO/publication/models.py
 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
@dataclass
class Author:
    '''Data Class For Authors'''
    id: Optional[str]
    label: Optional[str]
    description: Optional[str]
    orcid_id: Optional[str]
    zbmath_id: Optional[str]
    wikidata_id: Optional[str]

    @classmethod
    def from_query(cls, raw: str) -> 'Author':
        '''Parse a ``<|>``-delimited SPARQL result string into an Author instance.

        Args:
            raw: Space-and-pipe-delimited string with six fields:
                 identifier, label, description, orcid_id, zbmath_id, wikidata_id.

        Returns:
            Author instance populated from the parsed fields.
        '''
        identifier, label, description, orcid_id, zbmath_id, wikidata_id = raw.split(" <|> ")

        if label and not identifier:
            identifier = 'no author found'

        if not description:
            if orcid_id:
                description = f'scientist (ORCID iD {orcid_id})'
            elif zbmath_id:
                description = f'scientist (zbMath ID {orcid_id})'
            else:
                description = 'scientist'

        return cls(
            id = identifier,
            label = label,
            description = description,
            orcid_id = orcid_id,
            zbmath_id = zbmath_id,
            wikidata_id = wikidata_id
        )

    @classmethod
    def from_crossref(cls, raw: str) -> 'Author':
        '''Build an Author instance from a single Crossref contributor dict.

        Args:
            raw: Dict-like object from the Crossref ``author`` list with optional
                 keys ``given``, ``family``, and ``ORCID``.

        Returns:
            Author instance; ``id`` is ``'no author found'`` when a name is present
            but no MaRDI Portal entry exists yet.
        '''
        # Get Label
        label = None
        if raw.get('given') and raw.get('family'):
            label = f"{raw['given']} {raw['family']}"

        # Get MaRDI Portal ID
        identifier = None
        if label:
            identifier = 'no author found'

        # Get ORCID ID
        orcid_id = None
        if raw.get('ORCID'):
            orcid_id = raw.get('ORCID').split('/')[-1]

        # Get ZBMath ID
        zbmath_id = None

        # Get Wikidata ID
        wikidata_id = None

        # Get Description
        description = None
        if label:
            if orcid_id:
                description = f'scientist (ORCID iD {orcid_id})'
            else:
                description = 'scientist'


        return cls(
            id = identifier,
            label = label,
            description = description,
            orcid_id = orcid_id,
            zbmath_id = zbmath_id,
            wikidata_id = wikidata_id
        )

    @classmethod
    def from_datacite(cls, raw: str) -> 'Author':
        '''Build an Author instance from a single DataCite creator dict.

        Args:
            raw: Dict-like object from the DataCite ``creators`` list with optional
                 keys ``givenName``, ``familyName``, and ``nameIdentifiers``.

        Returns:
            Author instance; ``id`` is ``'no author found'`` when a name is present
            but no MaRDI Portal entry exists yet.
        '''
        # Get Label
        label = None
        if raw.get('givenName') and raw.get('familyName'):
            label = f"{raw['givenName']} {raw['familyName']}"

        # Get MaRDI Portal ID
        identifier = None
        if label:
            identifier = 'no author found'

        # Get ORCID ID
        orcid_id = None
        for name_identifier in raw.get('nameIdentifiers', []):
            if name_identifier.get('nameIdentifierScheme') == 'ORCID':
                orcid_uri = name_identifier.get('nameIdentifier', '')
                orcid_id = orcid_uri.split('/')[-1]
                break

        # Get ZBMath ID
        zbmath_id = None

        # Get Wikidata ID
        wikidata_id = None

        # Get Description
        description = None
        if label:
            if orcid_id:
                description = f'scientist (ORCID iD {orcid_id})'
            else:
                description = 'scientist'

        return cls(
            id = identifier,
            label = label,
            description = description,
            orcid_id = orcid_id,
            zbmath_id = zbmath_id,
            wikidata_id = wikidata_id
        )

    @classmethod
    def from_doi(cls, raw: str) -> 'Author':
        '''Build an Author instance from a single DOI API author dict.

        Args:
            raw: Dict-like object from the DOI API ``author`` list with optional
                 keys ``given``, ``family``, and ``ORCID``.

        Returns:
            Author instance; ``id`` is ``'no author found'`` when a name is present
            but no MaRDI Portal entry exists yet.
        '''
        # Get Label
        label = None
        if raw.get('given') and raw.get('family'):
            label = f"{raw['given']} {raw['family']}"

        # Get MaRDI Portal ID
        identifier = None
        if label:
            identifier = 'no author found'

        # Get ORCID ID
        orcid_id = None
        if raw.get('ORCID'):
            orcid_id = raw.get('ORCID').split('/')[-1]

        # Get ZBMath ID
        zbmath_id = None

        # Get Wikidata ID
        wikidata_id = None

        # Get Description
        description = None
        if label:
            if orcid_id:
                description = f'scientist (ORCID iD {orcid_id})'
            else:
                description = 'scientist'

        return cls(
            id = identifier,
            label = label,
            description = description,
            orcid_id = orcid_id,
            zbmath_id = zbmath_id,
            wikidata_id = wikidata_id
        )

    @classmethod
    def from_zbmath(cls, raw: str) -> 'Author':
        '''Build an Author instance from a single zbMath author dict.

        Args:
            raw: Dict-like object from the zbMath ``contributors.authors`` list
                 with optional keys ``name`` and ``codes``.

        Returns:
            Author instance; ``id`` is ``'no author found'`` when a name is present
            but no MaRDI Portal entry exists yet.
        '''
        # Get Label
        label = None
        if raw.get('name'):
            label = " ".join(reversed(raw['name'].split(", ")))

        # Get MaRDI Portal ID
        identifier = None
        if label:
            identifier = 'no author found'

        # Get ORCID ID
        orcid_id = None

        # Get zbMath ID
        zbmath_id = None
        if raw.get('codes'):
            zbmath_id = raw['codes'][0]

        # Get Wikidata ID
        wikidata_id = None

        # Get Description
        description = None
        if label:
            if zbmath_id:
                description = f'scientist (zbMath ID {zbmath_id})'
            else:
                description = 'scientist'

        return cls(
            id = identifier,
            label = label,
            description = description,
            orcid_id = orcid_id,
            zbmath_id = zbmath_id,
            wikidata_id = wikidata_id
        )

    @classmethod
    def from_orcid(cls, raw: str) -> 'Author':
        '''Build an Author instance from an ORCID API person record.

        Args:
            raw: Dict-like object from the ORCID API with an optional ``name``
                 sub-dict containing ``given-names``, ``family-name``, and ``path``.

        Returns:
            Author instance; ``id`` is ``'no author found'`` when a name is present
            but no MaRDI Portal entry exists yet.
        '''

        label = None
        given = family = None
        orcid_id = zbmath_id = wikidata_id = None

        # Get Name & ORCID ID
        if raw.get('name'):
            name = raw.get('name', {})
            if name.get('given-names', {}):
                given = name.get('given-names', {}).get('value')
            if name.get('family-name', {}):
                family = name.get('family-name', {}).get('value')
            orcid_id = name.get('path')
            if given and family:
                label = f"{given} {family}"

        # Get MaRDI Portal ID
        identifier = None
        if label:
            identifier = 'no author found'

        # Get Description
        description = None
        if label:
            if orcid_id:
                description = f'scientist (ORCID iD {orcid_id})'
            else:
                description = 'scientist'

        return cls(
            id = identifier,
            label = label,
            description = description,
            orcid_id = orcid_id,
            zbmath_id = zbmath_id,
            wikidata_id = wikidata_id
        )

from_crossref(raw) classmethod

Build an Author instance from a single Crossref contributor dict.

Parameters:

Name Type Description Default
raw str

Dict-like object from the Crossref author list with optional keys given, family, and ORCID.

required

Returns:

Type Description
Author

Author instance; id is 'no author found' when a name is present

Author

but no MaRDI Portal entry exists yet.

Source code in MaRDMO/publication/models.py
 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
@classmethod
def from_crossref(cls, raw: str) -> 'Author':
    '''Build an Author instance from a single Crossref contributor dict.

    Args:
        raw: Dict-like object from the Crossref ``author`` list with optional
             keys ``given``, ``family``, and ``ORCID``.

    Returns:
        Author instance; ``id`` is ``'no author found'`` when a name is present
        but no MaRDI Portal entry exists yet.
    '''
    # Get Label
    label = None
    if raw.get('given') and raw.get('family'):
        label = f"{raw['given']} {raw['family']}"

    # Get MaRDI Portal ID
    identifier = None
    if label:
        identifier = 'no author found'

    # Get ORCID ID
    orcid_id = None
    if raw.get('ORCID'):
        orcid_id = raw.get('ORCID').split('/')[-1]

    # Get ZBMath ID
    zbmath_id = None

    # Get Wikidata ID
    wikidata_id = None

    # Get Description
    description = None
    if label:
        if orcid_id:
            description = f'scientist (ORCID iD {orcid_id})'
        else:
            description = 'scientist'


    return cls(
        id = identifier,
        label = label,
        description = description,
        orcid_id = orcid_id,
        zbmath_id = zbmath_id,
        wikidata_id = wikidata_id
    )

from_datacite(raw) classmethod

Build an Author instance from a single DataCite creator dict.

Parameters:

Name Type Description Default
raw str

Dict-like object from the DataCite creators list with optional keys givenName, familyName, and nameIdentifiers.

required

Returns:

Type Description
Author

Author instance; id is 'no author found' when a name is present

Author

but no MaRDI Portal entry exists yet.

Source code in MaRDMO/publication/models.py
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
@classmethod
def from_datacite(cls, raw: str) -> 'Author':
    '''Build an Author instance from a single DataCite creator dict.

    Args:
        raw: Dict-like object from the DataCite ``creators`` list with optional
             keys ``givenName``, ``familyName``, and ``nameIdentifiers``.

    Returns:
        Author instance; ``id`` is ``'no author found'`` when a name is present
        but no MaRDI Portal entry exists yet.
    '''
    # Get Label
    label = None
    if raw.get('givenName') and raw.get('familyName'):
        label = f"{raw['givenName']} {raw['familyName']}"

    # Get MaRDI Portal ID
    identifier = None
    if label:
        identifier = 'no author found'

    # Get ORCID ID
    orcid_id = None
    for name_identifier in raw.get('nameIdentifiers', []):
        if name_identifier.get('nameIdentifierScheme') == 'ORCID':
            orcid_uri = name_identifier.get('nameIdentifier', '')
            orcid_id = orcid_uri.split('/')[-1]
            break

    # Get ZBMath ID
    zbmath_id = None

    # Get Wikidata ID
    wikidata_id = None

    # Get Description
    description = None
    if label:
        if orcid_id:
            description = f'scientist (ORCID iD {orcid_id})'
        else:
            description = 'scientist'

    return cls(
        id = identifier,
        label = label,
        description = description,
        orcid_id = orcid_id,
        zbmath_id = zbmath_id,
        wikidata_id = wikidata_id
    )

from_doi(raw) classmethod

Build an Author instance from a single DOI API author dict.

Parameters:

Name Type Description Default
raw str

Dict-like object from the DOI API author list with optional keys given, family, and ORCID.

required

Returns:

Type Description
Author

Author instance; id is 'no author found' when a name is present

Author

but no MaRDI Portal entry exists yet.

Source code in MaRDMO/publication/models.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
@classmethod
def from_doi(cls, raw: str) -> 'Author':
    '''Build an Author instance from a single DOI API author dict.

    Args:
        raw: Dict-like object from the DOI API ``author`` list with optional
             keys ``given``, ``family``, and ``ORCID``.

    Returns:
        Author instance; ``id`` is ``'no author found'`` when a name is present
        but no MaRDI Portal entry exists yet.
    '''
    # Get Label
    label = None
    if raw.get('given') and raw.get('family'):
        label = f"{raw['given']} {raw['family']}"

    # Get MaRDI Portal ID
    identifier = None
    if label:
        identifier = 'no author found'

    # Get ORCID ID
    orcid_id = None
    if raw.get('ORCID'):
        orcid_id = raw.get('ORCID').split('/')[-1]

    # Get ZBMath ID
    zbmath_id = None

    # Get Wikidata ID
    wikidata_id = None

    # Get Description
    description = None
    if label:
        if orcid_id:
            description = f'scientist (ORCID iD {orcid_id})'
        else:
            description = 'scientist'

    return cls(
        id = identifier,
        label = label,
        description = description,
        orcid_id = orcid_id,
        zbmath_id = zbmath_id,
        wikidata_id = wikidata_id
    )

from_orcid(raw) classmethod

Build an Author instance from an ORCID API person record.

Parameters:

Name Type Description Default
raw str

Dict-like object from the ORCID API with an optional name sub-dict containing given-names, family-name, and path.

required

Returns:

Type Description
Author

Author instance; id is 'no author found' when a name is present

Author

but no MaRDI Portal entry exists yet.

Source code in MaRDMO/publication/models.py
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
@classmethod
def from_orcid(cls, raw: str) -> 'Author':
    '''Build an Author instance from an ORCID API person record.

    Args:
        raw: Dict-like object from the ORCID API with an optional ``name``
             sub-dict containing ``given-names``, ``family-name``, and ``path``.

    Returns:
        Author instance; ``id`` is ``'no author found'`` when a name is present
        but no MaRDI Portal entry exists yet.
    '''

    label = None
    given = family = None
    orcid_id = zbmath_id = wikidata_id = None

    # Get Name & ORCID ID
    if raw.get('name'):
        name = raw.get('name', {})
        if name.get('given-names', {}):
            given = name.get('given-names', {}).get('value')
        if name.get('family-name', {}):
            family = name.get('family-name', {}).get('value')
        orcid_id = name.get('path')
        if given and family:
            label = f"{given} {family}"

    # Get MaRDI Portal ID
    identifier = None
    if label:
        identifier = 'no author found'

    # Get Description
    description = None
    if label:
        if orcid_id:
            description = f'scientist (ORCID iD {orcid_id})'
        else:
            description = 'scientist'

    return cls(
        id = identifier,
        label = label,
        description = description,
        orcid_id = orcid_id,
        zbmath_id = zbmath_id,
        wikidata_id = wikidata_id
    )

from_query(raw) classmethod

Parse a <|>-delimited SPARQL result string into an Author instance.

Parameters:

Name Type Description Default
raw str

Space-and-pipe-delimited string with six fields: identifier, label, description, orcid_id, zbmath_id, wikidata_id.

required

Returns:

Type Description
Author

Author instance populated from the parsed fields.

Source code in MaRDMO/publication/models.py
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
@classmethod
def from_query(cls, raw: str) -> 'Author':
    '''Parse a ``<|>``-delimited SPARQL result string into an Author instance.

    Args:
        raw: Space-and-pipe-delimited string with six fields:
             identifier, label, description, orcid_id, zbmath_id, wikidata_id.

    Returns:
        Author instance populated from the parsed fields.
    '''
    identifier, label, description, orcid_id, zbmath_id, wikidata_id = raw.split(" <|> ")

    if label and not identifier:
        identifier = 'no author found'

    if not description:
        if orcid_id:
            description = f'scientist (ORCID iD {orcid_id})'
        elif zbmath_id:
            description = f'scientist (zbMath ID {orcid_id})'
        else:
            description = 'scientist'

    return cls(
        id = identifier,
        label = label,
        description = description,
        orcid_id = orcid_id,
        zbmath_id = zbmath_id,
        wikidata_id = wikidata_id
    )

from_zbmath(raw) classmethod

Build an Author instance from a single zbMath author dict.

Parameters:

Name Type Description Default
raw str

Dict-like object from the zbMath contributors.authors list with optional keys name and codes.

required

Returns:

Type Description
Author

Author instance; id is 'no author found' when a name is present

Author

but no MaRDI Portal entry exists yet.

Source code in MaRDMO/publication/models.py
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
@classmethod
def from_zbmath(cls, raw: str) -> 'Author':
    '''Build an Author instance from a single zbMath author dict.

    Args:
        raw: Dict-like object from the zbMath ``contributors.authors`` list
             with optional keys ``name`` and ``codes``.

    Returns:
        Author instance; ``id`` is ``'no author found'`` when a name is present
        but no MaRDI Portal entry exists yet.
    '''
    # Get Label
    label = None
    if raw.get('name'):
        label = " ".join(reversed(raw['name'].split(", ")))

    # Get MaRDI Portal ID
    identifier = None
    if label:
        identifier = 'no author found'

    # Get ORCID ID
    orcid_id = None

    # Get zbMath ID
    zbmath_id = None
    if raw.get('codes'):
        zbmath_id = raw['codes'][0]

    # Get Wikidata ID
    wikidata_id = None

    # Get Description
    description = None
    if label:
        if zbmath_id:
            description = f'scientist (zbMath ID {zbmath_id})'
        else:
            description = 'scientist'

    return cls(
        id = identifier,
        label = label,
        description = description,
        orcid_id = orcid_id,
        zbmath_id = zbmath_id,
        wikidata_id = wikidata_id
    )

Journal dataclass

Data Class For Journals

Source code in MaRDMO/publication/models.py
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
@dataclass
class Journal:
    '''Data Class For Journals'''
    id: str
    issn: str
    label: str
    description: str

    @classmethod
    def from_query(cls, raw: str) -> 'Journal':
        '''Parse a ``<|>``-delimited SPARQL result string into a Journal instance.

        Args:
            raw: Space-and-pipe-delimited string with three or four fields:
                 identifier, label, description, and optionally issn.

        Returns:
            Journal instance populated from the parsed fields.
        '''
        raw_splitted = raw.split(" <|> ")

        identifier = raw_splitted[0]
        label = raw_splitted[1]
        description = raw_splitted[2]
        if len(raw_splitted) == 4:
            issn = raw_splitted[3]
        else:
            issn = None

        if not description:
            if issn:
                description = f'scientific journal (ISSN {issn})'
            else:
                description = 'scientific journal'

        return cls(
            id = identifier,
            issn = issn,
            label = label,
            description = description
        )

    @classmethod
    def from_crossref(cls, ids: list, item: list) -> 'Journal':
        '''Build a Journal instance from Crossref ISSN and title data.

        Args:
            ids:  List of ISSN strings from the Crossref response (may be empty).
            item: List of container-title strings from the Crossref response (may be empty).

        Returns:
            Journal instance; ``id`` is ``'no journal found'`` when a title is present
            but no MaRDI Portal entry exists yet.
        '''
        # Get Label
        label = None
        if item:
            label = item[0]

        # Get ISSN
        issn = None
        if ids:
            issn = ids[0]

        # Get MaRDI Portal ID
        identifier = None
        if label:
            identifier = 'no journal found'

        # Get Description
        description = None
        if label:
            if issn:
                description = f'scientific journal (ISSN {issn})'
            else:
                description = 'scientific journal'

        return cls(
            id = identifier,
            issn = issn,
            label = label,
            description = description,
        )

    @classmethod
    def from_datacite(cls, ids: list, item: list) -> 'Journal':
        '''Build a Journal instance from DataCite related-identifier and related-item data.

        Args:
            ids:  List of relatedIdentifier dicts from the DataCite response (may be empty).
            item: List of relatedItem dicts from the DataCite response (may be empty).

        Returns:
            Journal instance; ``id`` is ``'no journal found'`` when a title is present
            but no MaRDI Portal entry exists yet.
        '''
        # Get Label
        label = None
        if item:
            label = (item[0].get('titles') or [{}])[0].get('title')

        # Get ISSN
        issn = None
        if ids:
            if ids[0].get('relatedIdentifierType') == 'ISSN':
                issn = ids[0].get('relatedIdentifier')

        # Get MaRDI Portal ID
        identifier = None
        if label:
            identifier = 'no journal found'

        # Get Description
        description = None
        if label:
            if issn:
                description = f'scientific journal (ISSN {issn})'
            else:
                description = 'scientific journal'

        return cls(
            id = identifier,
            issn = issn,
            label = label,
            description = description,
        )

    @classmethod
    def from_doi(cls, ids: list, item: str) -> 'Journal':
        '''Build a Journal instance from DOI API ISSN and container-title data.

        Args:
            ids:  List of ISSN strings from the DOI API response (may be empty).
            item: Container-title string from the DOI API response (may be None).

        Returns:
            Journal instance; ``id`` is ``'no journal found'`` when a title is present
            but no MaRDI Portal entry exists yet.
        '''
        # Get Label
        label = None
        if item:
            label = item

        # Get ISSN
        issn = None
        if ids:
            issn = ids[0]

        # Get MaRDI Portal ID
        identifier = None
        if label:
            identifier = 'no journal found'

        # Get Description
        description = None
        if label:
            if issn:
                description = f'scientific journal (ISSN {issn})'
            else:
                description = 'scientific journal'

        return cls(
            id = identifier,
            issn = issn,
            label = label,
            description = description,
        )

    @classmethod
    def from_zbmath(cls, raw: dict) -> 'Journal':
        '''Build a Journal instance from a zbMath source dict.

        Args:
            raw: The ``source`` dict from a zbMath result entry, expected to contain
                 an optional ``series`` list with ``title`` and ``issn`` entries.

        Returns:
            Journal instance; ``id`` is ``'no journal found'`` when a title is present
            but no MaRDI Portal entry exists yet.
        '''
        series = (raw.get('series') or [{}])[0]

        # Get Label
        label = None
        if series.get('title'):
            label = series['title']

        # Get ISSN
        issn = None
        if (series.get('issn') or [{}])[0].get('number'):
            issn = series['issn'][0]['number']

        # Get MaRDI Portal ID
        identifier = None
        if label:
            identifier = 'no journal found'

        # Get Description
        description = None
        if label:
            if issn:
                description = f'scientific journal (ISSN {issn})'
            else:
                description = 'scientific journal'

        return cls(
            id = identifier,
            issn = issn,
            label = label,
            description = description,
        )

from_crossref(ids, item) classmethod

Build a Journal instance from Crossref ISSN and title data.

Parameters:

Name Type Description Default
ids list

List of ISSN strings from the Crossref response (may be empty).

required
item list

List of container-title strings from the Crossref response (may be empty).

required

Returns:

Type Description
Journal

Journal instance; id is 'no journal found' when a title is present

Journal

but no MaRDI Portal entry exists yet.

Source code in MaRDMO/publication/models.py
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
@classmethod
def from_crossref(cls, ids: list, item: list) -> 'Journal':
    '''Build a Journal instance from Crossref ISSN and title data.

    Args:
        ids:  List of ISSN strings from the Crossref response (may be empty).
        item: List of container-title strings from the Crossref response (may be empty).

    Returns:
        Journal instance; ``id`` is ``'no journal found'`` when a title is present
        but no MaRDI Portal entry exists yet.
    '''
    # Get Label
    label = None
    if item:
        label = item[0]

    # Get ISSN
    issn = None
    if ids:
        issn = ids[0]

    # Get MaRDI Portal ID
    identifier = None
    if label:
        identifier = 'no journal found'

    # Get Description
    description = None
    if label:
        if issn:
            description = f'scientific journal (ISSN {issn})'
        else:
            description = 'scientific journal'

    return cls(
        id = identifier,
        issn = issn,
        label = label,
        description = description,
    )

from_datacite(ids, item) classmethod

Build a Journal instance from DataCite related-identifier and related-item data.

Parameters:

Name Type Description Default
ids list

List of relatedIdentifier dicts from the DataCite response (may be empty).

required
item list

List of relatedItem dicts from the DataCite response (may be empty).

required

Returns:

Type Description
Journal

Journal instance; id is 'no journal found' when a title is present

Journal

but no MaRDI Portal entry exists yet.

Source code in MaRDMO/publication/models.py
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
@classmethod
def from_datacite(cls, ids: list, item: list) -> 'Journal':
    '''Build a Journal instance from DataCite related-identifier and related-item data.

    Args:
        ids:  List of relatedIdentifier dicts from the DataCite response (may be empty).
        item: List of relatedItem dicts from the DataCite response (may be empty).

    Returns:
        Journal instance; ``id`` is ``'no journal found'`` when a title is present
        but no MaRDI Portal entry exists yet.
    '''
    # Get Label
    label = None
    if item:
        label = (item[0].get('titles') or [{}])[0].get('title')

    # Get ISSN
    issn = None
    if ids:
        if ids[0].get('relatedIdentifierType') == 'ISSN':
            issn = ids[0].get('relatedIdentifier')

    # Get MaRDI Portal ID
    identifier = None
    if label:
        identifier = 'no journal found'

    # Get Description
    description = None
    if label:
        if issn:
            description = f'scientific journal (ISSN {issn})'
        else:
            description = 'scientific journal'

    return cls(
        id = identifier,
        issn = issn,
        label = label,
        description = description,
    )

from_doi(ids, item) classmethod

Build a Journal instance from DOI API ISSN and container-title data.

Parameters:

Name Type Description Default
ids list

List of ISSN strings from the DOI API response (may be empty).

required
item str

Container-title string from the DOI API response (may be None).

required

Returns:

Type Description
Journal

Journal instance; id is 'no journal found' when a title is present

Journal

but no MaRDI Portal entry exists yet.

Source code in MaRDMO/publication/models.py
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
@classmethod
def from_doi(cls, ids: list, item: str) -> 'Journal':
    '''Build a Journal instance from DOI API ISSN and container-title data.

    Args:
        ids:  List of ISSN strings from the DOI API response (may be empty).
        item: Container-title string from the DOI API response (may be None).

    Returns:
        Journal instance; ``id`` is ``'no journal found'`` when a title is present
        but no MaRDI Portal entry exists yet.
    '''
    # Get Label
    label = None
    if item:
        label = item

    # Get ISSN
    issn = None
    if ids:
        issn = ids[0]

    # Get MaRDI Portal ID
    identifier = None
    if label:
        identifier = 'no journal found'

    # Get Description
    description = None
    if label:
        if issn:
            description = f'scientific journal (ISSN {issn})'
        else:
            description = 'scientific journal'

    return cls(
        id = identifier,
        issn = issn,
        label = label,
        description = description,
    )

from_query(raw) classmethod

Parse a <|>-delimited SPARQL result string into a Journal instance.

Parameters:

Name Type Description Default
raw str

Space-and-pipe-delimited string with three or four fields: identifier, label, description, and optionally issn.

required

Returns:

Type Description
Journal

Journal instance populated from the parsed fields.

Source code in MaRDMO/publication/models.py
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
@classmethod
def from_query(cls, raw: str) -> 'Journal':
    '''Parse a ``<|>``-delimited SPARQL result string into a Journal instance.

    Args:
        raw: Space-and-pipe-delimited string with three or four fields:
             identifier, label, description, and optionally issn.

    Returns:
        Journal instance populated from the parsed fields.
    '''
    raw_splitted = raw.split(" <|> ")

    identifier = raw_splitted[0]
    label = raw_splitted[1]
    description = raw_splitted[2]
    if len(raw_splitted) == 4:
        issn = raw_splitted[3]
    else:
        issn = None

    if not description:
        if issn:
            description = f'scientific journal (ISSN {issn})'
        else:
            description = 'scientific journal'

    return cls(
        id = identifier,
        issn = issn,
        label = label,
        description = description
    )

from_zbmath(raw) classmethod

Build a Journal instance from a zbMath source dict.

Parameters:

Name Type Description Default
raw dict

The source dict from a zbMath result entry, expected to contain an optional series list with title and issn entries.

required

Returns:

Type Description
Journal

Journal instance; id is 'no journal found' when a title is present

Journal

but no MaRDI Portal entry exists yet.

Source code in MaRDMO/publication/models.py
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
@classmethod
def from_zbmath(cls, raw: dict) -> 'Journal':
    '''Build a Journal instance from a zbMath source dict.

    Args:
        raw: The ``source`` dict from a zbMath result entry, expected to contain
             an optional ``series`` list with ``title`` and ``issn`` entries.

    Returns:
        Journal instance; ``id`` is ``'no journal found'`` when a title is present
        but no MaRDI Portal entry exists yet.
    '''
    series = (raw.get('series') or [{}])[0]

    # Get Label
    label = None
    if series.get('title'):
        label = series['title']

    # Get ISSN
    issn = None
    if (series.get('issn') or [{}])[0].get('number'):
        issn = series['issn'][0]['number']

    # Get MaRDI Portal ID
    identifier = None
    if label:
        identifier = 'no journal found'

    # Get Description
    description = None
    if label:
        if issn:
            description = f'scientific journal (ISSN {issn})'
        else:
            description = 'scientific journal'

    return cls(
        id = identifier,
        issn = issn,
        label = label,
        description = description,
    )

Publication dataclass

Data Class For Publications

Source code in MaRDMO/publication/models.py
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
@dataclass
class Publication:
    '''Data Class For Publications'''
    id: Optional[str] = None
    label: Optional[str] = None
    description: Optional[str] = None
    entrytype: Optional[str] = None
    title: Optional[str] = None
    date: Optional[str] = None
    volume: Optional[str] = None
    issue: Optional[str] = None
    page: Optional[str] = None
    reference: Optional[str] = None
    journal: list[Journal] = field(default_factory=list)
    authors: list[Author] = field(default_factory=list)
    language: list[Relatant] = field(default_factory=list)
    applies: list[RelatantWithClass] = field(default_factory=list)
    analyzes: list[RelatantWithClass] = field(default_factory=list)
    documents: list[RelatantWithClass] = field(default_factory=list)
    invents: list[RelatantWithClass] = field(default_factory=list)
    studies: list[RelatantWithClass] = field(default_factory=list)
    surveys: list[RelatantWithClass] = field(default_factory=list)
    uses: list[RelatantWithClass] = field(default_factory=list)

    options: ClassVar[Optional[dict]] = None

    @classmethod
    def get_options(cls) -> dict:
        '''Return the cached option-label mapping used by all class generators.

        Returns:
            Dict mapping option keys (e.g. ``'DOI'``) to their display labels,
            loaded once and cached as a class variable.
        '''
        if cls.options is None:
            cls.options = get_options()
        return cls.options

    @classmethod
    def from_query(cls, raw_data: dict) -> 'Publication':
        '''Build a Publication instance from a SPARQL query result list.

        Args:
            raw_data: List of SPARQL result row dicts; the first element is used.
                      Expected keys include ``id``, ``label``, ``description``,
                      ``entrytypelabel``, ``title``, ``date``, ``doi``,
                      ``journalInfo``, ``authorInfos``, and relation keys such as
                      ``applies``, ``analyzes``, ``documents``, etc.

        Returns:
            Publication instance populated from the SPARQL result.
        '''
        options = cls.get_options()
        items = get_items()

        data = raw_data[0]

        publication = {
            # Get ID
            'id': data.get('id', {}).get('value'),
            # Get Label
            'label': data.get('label', {}).get('value'),
            # Get Description
            'description': data.get('description', {}).get('value'),
            # Get Entrytype
            'entrytype': data.get('entrytypelabel', {}).get('value'),
            # Get Language
            'language': (
                [
                    Relatant.from_triple(
                        f"mardi:{items['english']}",
                        "English",
                        "West Germanic language"
                    )
                ]
                if data.get('languagelabel', {}).get('value', '').lower()
                    in {"en", "eng", "english"}
                else []
            ),
            # Get Title
            'title': data.get('title', {}).get('value'),
            # Get Date
            'date': data.get('date', {}).get('value', ''),
            # Get Volume
            'volume': data.get('volume', {}).get('value'),
            # Get Issue
            'issue': data.get('issue', {}).get('value'),
            # Get Page
            'page': data.get('page', {}).get('value'),
            # Get Reference
            'reference': {
                idx: [options['DOI'], data[prop]['value']]
                for idx, prop in enumerate(['doi'])
                if data.get(prop, {}).get('value')
            },
            # Get Journal
            'journal': 
                [
                    Journal.from_query(
                        data.get('journalInfo', {}).get('value')
                    )
                ]
                if 'journalInfo' in data else [],
            # Get Authors
            'authors': 
                [
                    Author.from_query(
                        author
                    )
                    for author in data.get('authorInfos', {}).get('value', '').split(" || ")
                    if author
                ]
                if 'authorInfos' in data else [],
            # Get Applies Relation(s)
            'applies': split_value(
                data = data,
                key = 'applies',
                transform = RelatantWithClass.from_query
            ),
            # Get Analyzes Relation(s)
            'analyzes': split_value(
                data = data,
                key = 'analyzes',
                transform = RelatantWithClass.from_query
            ),
            # Get Documents Relation(s)
            'documents': split_value(
                data = data,
                key = 'documents',
                transform = RelatantWithClass.from_query
            ),
            # Get Invents Relation(s)
            'invents': split_value(
                data = data,
                key = 'invents',
                transform = RelatantWithClass.from_query
            ),
            # Get Studies Relation(s)
            'studies': split_value(
                data = data,
                key = 'studies',
                transform = RelatantWithClass.from_query
            ),
            # Get Surveys Relation(s)
            'surveys': split_value(
                data = data,
                key = 'surveys',
                transform = RelatantWithClass.from_query
            ),
            # Get Uses Relation(s)
            'uses': split_value(
                data = data,
                key = 'uses',
                transform = RelatantWithClass.from_query
            ),
        }

        return cls(
            **publication
            )

    @classmethod
    def from_crossref(cls, raw_data: dict) -> 'Publication':
        '''Build a Publication instance from a Crossref API response.

        Args:
            raw_data: Response object whose ``.json()['message']`` contains
                      Crossref metadata fields such as ``title``, ``author``,
                      ``DOI``, ``ISSN``, ``volume``, ``issue``, ``page``,
                      ``published``, and ``container-title``.

        Returns:
            Publication instance populated from the Crossref metadata.
        '''
        options = cls.get_options()
        items = get_items()

        data = raw_data.json().get('message', {})

        publication = {
            # Get ID
            'id': None,
            # Get Label
            'label': data.get('title', [''])[0],
            # Get Description
            'description': 
                f'scientific article (doi: {data["DOI"]})'
                if data.get("DOI") else 'scientific article',
            # Get Entrytype
            'entrytype':
                'scholarly article'
                if data.get('type') == 'journal-article' else 'publication',
            # Get Language
            'language': (
                [
                    Relatant.from_triple(
                        f"mardi:{items['english']}",
                        "English",
                        "West Germanic language"
                    )
                ]
                if (data.get('language') or '').lower()
                    in {"en", "eng", "english"}
                else []
            ),
            # Get Title
            'title': data.get('title', [''])[0],
            # Get Date
            'date': date_format(
                data.get('published', {}).get('date-parts', [[]])[0]
            ),
            # Get Volume
            'volume': data.get('volume'),
            # Get Issue
            'issue': data.get('issue'),
            # Get Page
            'page': data.get('page'),
            # Get Reference
            'reference': {
                idx: [options['DOI'], data[prop]]
                for idx, prop in enumerate(['DOI'])
                if data.get(prop, {})
            },
            # Get Journal
            'journal':
                [
                    Journal.from_crossref(
                        data.get('ISSN', []),
                        data.get('container-title', [])
                    )
                ]
                if 'ISSN' in data or 'container-title' in data else [],
            # Get Authors
            'authors': 
                [
                    Author.from_crossref(
                        author
                    )
                    for author in data.get('author', [])
                    if author
                ]
                if 'author' in data else [],
        }

        return cls(
            **publication
        )

    @classmethod
    def from_datacite(cls, raw_data: dict) -> 'Publication':
        '''Build a Publication instance from a DataCite API response.

        Args:
            raw_data: Response object whose ``.json()['data']['attributes']`` contains
                      DataCite metadata fields such as ``titles``, ``creators``,
                      ``doi``, ``dates``, ``relatedItems``, and ``relatedIdentifiers``.

        Returns:
            Publication instance populated from the DataCite metadata.
        '''
        options = cls.get_options()
        items = get_items()

        data = raw_data.json().get('data', {}).get('attributes', {})

        publication = {
            # Get ID
            'id': None,
            # Get Label
            'label': (data.get('titles') or [{}])[0].get('title'),
            # Get Description
            'description': 
                f'scientific article (doi: {data["doi"]})'
                if data.get("doi") else 'scientific article',
            # Get Entrytype
            'entrytype':
                'scholarly article'
                if data.get('types', {}).get('bibtex') == 'article' else 'publication',
            # Get Language
            'language': (
                [
                    Relatant.from_triple(
                        f"mardi:{items['english']}",
                        "English",
                        "West Germanic language"
                    )
                ]
                if (data.get('language') or '').lower()
                    in {"en", "eng", "english"}
                else []
            ),
            # Get Title
            'title': (data.get('titles') or [{}])[0].get('title'),
            # Get Date
            'date': 
                next(
                    (
                        date_format(date_part['date'].split('-'))
                        for date_part in data.get('dates', [])
                        if date_part.get('dateType') == 'Issued' and date_part.get('date')
                    ),
                    None,
                ),
            # Get Volume
            'volume': (data.get('relatedItems') or [{}])[0].get('volume'),
            # Get Issue
            'issue': (data.get('relatedItems') or [{}])[0].get('issue'),
            # Get Page
            'page':
                (
                    f"{data['relatedItems'][0].get('firstPage', '')}-"
                    f"{data['relatedItems'][0].get('lastPage', '')}"
                )
                if data.get('relatedItems') else None,
            # Get Reference
            'reference': {
                idx: [options['DOI'], data[prop]]
                for idx, prop in enumerate(['doi'])
                if data.get(prop, {})
            },
            # Get Journal
            'journal': 
                [
                    Journal.from_datacite(
                        data.get('relatedIdentifiers') or [{}],
                        data.get('relatedItems') or [{}],
                    )
                ]
                if 'relatedIdentifiers' in data or 'relatedItems' in data else [],
            # Get Authors
            'authors':
                [
                    Author.from_datacite(author)
                    for author in data.get('creators', [])
                    if author
                ]
                if 'creators' in data else [],
        }

        return cls(
            **publication
        )

    @classmethod
    def from_doi(cls, raw_data: dict) -> 'Publication':
        '''Build a Publication instance from a DOI resolution API response.

        Args:
            raw_data: Response object whose ``.json()`` contains DOI metadata
                      fields such as ``title``, ``author``, ``DOI``, ``ISSN``,
                      ``volume``, ``issue``, ``page``, ``published``, and
                      ``container-title``.

        Returns:
            Publication instance populated from the DOI metadata.
        '''
        options = cls.get_options()
        items = get_items()

        data = raw_data.json()

        publication = {
            # Get ID
            'id': None,
            # Get Label
            'label': data.get('title'),
            # Get Description
            'description': 
                f'scientific article (doi: {data["DOI"]})'
                if data.get("DOI") else 'scientific article',
            # Get Entrytype
            'entrytype': (
                'scholarly article'
                if data.get('type') in ('journal-article', 'article-journal')
                else 'publication'
            ),
            # Get Language
            'language': (
                [
                    Relatant.from_triple(
                        f"mardi:{items['english']}",
                        "English",
                        "West Germanic language"
                    )
                ]
                if (data.get('language') or '').lower()
                    in {"en", "eng", "english"}
                else []
            ),
            # Get Title
            'title': data.get('title'),
            # Get Date
            'date': date_format(
                data.get('published', {}).get('date-parts', [[]])[0]
            ),
            # Get Volume
            'volume': data.get('volume'),
            # Get Issue
            'issue': data.get('issue'),
            # Get Page
            'page': data.get('page'),
            # Get Reference
            'reference': {
                idx: [options['DOI'], data[prop]]
                for idx, prop in enumerate(['DOI'])
                if data.get(prop, {})
            },
            # Get Journal
            'journal':
                [
                    Journal.from_doi(
                        data.get('ISSN', []),
                        data.get('container-title', [])
                    )
                ]
                if 'ISSN' in data or 'container-title' in data else [],
            # Get Authors
            'authors':
                [
                    Author.from_doi(author)
                    for author in data.get('author', [])
                    if author
                ]
                if 'author' in data else [],
        }

        return cls(
            **publication
        )

    @classmethod
    def from_zbmath(cls, raw_data: dict) -> 'Publication':
        '''Build a Publication instance from a zbMath API response.

        Args:
            raw_data: Response object whose ``.json()['result'][0]`` contains
                      zbMath metadata fields such as ``title``, ``year``,
                      ``document_type``, ``language``, ``source``, ``links``,
                      and ``contributors``.

        Returns:
            Publication instance populated from the zbMath metadata.
        '''
        options = cls.get_options()
        items = get_items()

        data = raw_data.json().get('result', [''])[0]

        publication = {
            # Get ID
            'id': None,
            # Get Label
            'label': data.get('title', {}).get('title'),
            # Get Description
            'description': (
                f"scientific article (doi: {doi})"
                if (doi := next(
                    (
                        link.get("identifier")
                        for link in data.get("links", [])
                        if link.get("type") == "doi"
                    ),
                    None,
                ))
                else "scientific article"
            ),
            # Get Entrytype
            'entrytype': (
                'scholarly article'
                if data.get('document_type', {}).get('description') == 'journal article'
                else 'publication'
            ),
            # Get Language
            'language':(
                [
                    Relatant.from_triple(
                        f"mardi:{items['english']}",
                        "English",
                        "West Germanic language"
                    )
                ]
                if any(
                    code.lower() in {"en", "eng", "english"}
                    for code in (data.get("language", {}).get("languages") or [""])
                )
                else []
            ),
            # Get Date
            'date':
                f"{int(data['year']):04d}-00-00T00:00:00Z"
                if data.get('year') else None,
            # Get Title
            'title': data.get('title', {}).get('title'),
            # Get Volume
            'volume': (data.get('source', {}).get('series') or [''])[0].get('volume'),
            # Get Issue
            'issue': (data.get('source', {}).get('series') or [''])[0].get('issue'),
            # Get Page
            'page': data.get('source', {}).get('page'),
            # Get Reference
            'reference': (
                {0: [options['DOI'], doi]}
                if (
                    doi := next(
                    (
                        link.get('identifier')
                        for link in data.get('links', [])
                        if link.get('type') == 'doi'
                    ),
                    None
                    )
                )
                else {}
            ),
            # Get Journal
            'journal': 
                [
                    Journal.from_zbmath(
                        data.get('source', {})
                    )
                ]
                if 'source' in data else [],
            # Get Authors
            'authors':
                [
                    Author.from_zbmath(author)
                    for author in data.get('contributors', {}).get('authors', [])
                    if author
                ]
                if 'contributors' in data else [],
        }

        return cls(
            **publication
        )

from_crossref(raw_data) classmethod

Build a Publication instance from a Crossref API response.

Parameters:

Name Type Description Default
raw_data dict

Response object whose .json()['message'] contains Crossref metadata fields such as title, author, DOI, ISSN, volume, issue, page, published, and container-title.

required

Returns:

Type Description
Publication

Publication instance populated from the Crossref metadata.

Source code in MaRDMO/publication/models.py
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
@classmethod
def from_crossref(cls, raw_data: dict) -> 'Publication':
    '''Build a Publication instance from a Crossref API response.

    Args:
        raw_data: Response object whose ``.json()['message']`` contains
                  Crossref metadata fields such as ``title``, ``author``,
                  ``DOI``, ``ISSN``, ``volume``, ``issue``, ``page``,
                  ``published``, and ``container-title``.

    Returns:
        Publication instance populated from the Crossref metadata.
    '''
    options = cls.get_options()
    items = get_items()

    data = raw_data.json().get('message', {})

    publication = {
        # Get ID
        'id': None,
        # Get Label
        'label': data.get('title', [''])[0],
        # Get Description
        'description': 
            f'scientific article (doi: {data["DOI"]})'
            if data.get("DOI") else 'scientific article',
        # Get Entrytype
        'entrytype':
            'scholarly article'
            if data.get('type') == 'journal-article' else 'publication',
        # Get Language
        'language': (
            [
                Relatant.from_triple(
                    f"mardi:{items['english']}",
                    "English",
                    "West Germanic language"
                )
            ]
            if (data.get('language') or '').lower()
                in {"en", "eng", "english"}
            else []
        ),
        # Get Title
        'title': data.get('title', [''])[0],
        # Get Date
        'date': date_format(
            data.get('published', {}).get('date-parts', [[]])[0]
        ),
        # Get Volume
        'volume': data.get('volume'),
        # Get Issue
        'issue': data.get('issue'),
        # Get Page
        'page': data.get('page'),
        # Get Reference
        'reference': {
            idx: [options['DOI'], data[prop]]
            for idx, prop in enumerate(['DOI'])
            if data.get(prop, {})
        },
        # Get Journal
        'journal':
            [
                Journal.from_crossref(
                    data.get('ISSN', []),
                    data.get('container-title', [])
                )
            ]
            if 'ISSN' in data or 'container-title' in data else [],
        # Get Authors
        'authors': 
            [
                Author.from_crossref(
                    author
                )
                for author in data.get('author', [])
                if author
            ]
            if 'author' in data else [],
    }

    return cls(
        **publication
    )

from_datacite(raw_data) classmethod

Build a Publication instance from a DataCite API response.

Parameters:

Name Type Description Default
raw_data dict

Response object whose .json()['data']['attributes'] contains DataCite metadata fields such as titles, creators, doi, dates, relatedItems, and relatedIdentifiers.

required

Returns:

Type Description
Publication

Publication instance populated from the DataCite metadata.

Source code in MaRDMO/publication/models.py
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
@classmethod
def from_datacite(cls, raw_data: dict) -> 'Publication':
    '''Build a Publication instance from a DataCite API response.

    Args:
        raw_data: Response object whose ``.json()['data']['attributes']`` contains
                  DataCite metadata fields such as ``titles``, ``creators``,
                  ``doi``, ``dates``, ``relatedItems``, and ``relatedIdentifiers``.

    Returns:
        Publication instance populated from the DataCite metadata.
    '''
    options = cls.get_options()
    items = get_items()

    data = raw_data.json().get('data', {}).get('attributes', {})

    publication = {
        # Get ID
        'id': None,
        # Get Label
        'label': (data.get('titles') or [{}])[0].get('title'),
        # Get Description
        'description': 
            f'scientific article (doi: {data["doi"]})'
            if data.get("doi") else 'scientific article',
        # Get Entrytype
        'entrytype':
            'scholarly article'
            if data.get('types', {}).get('bibtex') == 'article' else 'publication',
        # Get Language
        'language': (
            [
                Relatant.from_triple(
                    f"mardi:{items['english']}",
                    "English",
                    "West Germanic language"
                )
            ]
            if (data.get('language') or '').lower()
                in {"en", "eng", "english"}
            else []
        ),
        # Get Title
        'title': (data.get('titles') or [{}])[0].get('title'),
        # Get Date
        'date': 
            next(
                (
                    date_format(date_part['date'].split('-'))
                    for date_part in data.get('dates', [])
                    if date_part.get('dateType') == 'Issued' and date_part.get('date')
                ),
                None,
            ),
        # Get Volume
        'volume': (data.get('relatedItems') or [{}])[0].get('volume'),
        # Get Issue
        'issue': (data.get('relatedItems') or [{}])[0].get('issue'),
        # Get Page
        'page':
            (
                f"{data['relatedItems'][0].get('firstPage', '')}-"
                f"{data['relatedItems'][0].get('lastPage', '')}"
            )
            if data.get('relatedItems') else None,
        # Get Reference
        'reference': {
            idx: [options['DOI'], data[prop]]
            for idx, prop in enumerate(['doi'])
            if data.get(prop, {})
        },
        # Get Journal
        'journal': 
            [
                Journal.from_datacite(
                    data.get('relatedIdentifiers') or [{}],
                    data.get('relatedItems') or [{}],
                )
            ]
            if 'relatedIdentifiers' in data or 'relatedItems' in data else [],
        # Get Authors
        'authors':
            [
                Author.from_datacite(author)
                for author in data.get('creators', [])
                if author
            ]
            if 'creators' in data else [],
    }

    return cls(
        **publication
    )

from_doi(raw_data) classmethod

Build a Publication instance from a DOI resolution API response.

Parameters:

Name Type Description Default
raw_data dict

Response object whose .json() contains DOI metadata fields such as title, author, DOI, ISSN, volume, issue, page, published, and container-title.

required

Returns:

Type Description
Publication

Publication instance populated from the DOI metadata.

Source code in MaRDMO/publication/models.py
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
@classmethod
def from_doi(cls, raw_data: dict) -> 'Publication':
    '''Build a Publication instance from a DOI resolution API response.

    Args:
        raw_data: Response object whose ``.json()`` contains DOI metadata
                  fields such as ``title``, ``author``, ``DOI``, ``ISSN``,
                  ``volume``, ``issue``, ``page``, ``published``, and
                  ``container-title``.

    Returns:
        Publication instance populated from the DOI metadata.
    '''
    options = cls.get_options()
    items = get_items()

    data = raw_data.json()

    publication = {
        # Get ID
        'id': None,
        # Get Label
        'label': data.get('title'),
        # Get Description
        'description': 
            f'scientific article (doi: {data["DOI"]})'
            if data.get("DOI") else 'scientific article',
        # Get Entrytype
        'entrytype': (
            'scholarly article'
            if data.get('type') in ('journal-article', 'article-journal')
            else 'publication'
        ),
        # Get Language
        'language': (
            [
                Relatant.from_triple(
                    f"mardi:{items['english']}",
                    "English",
                    "West Germanic language"
                )
            ]
            if (data.get('language') or '').lower()
                in {"en", "eng", "english"}
            else []
        ),
        # Get Title
        'title': data.get('title'),
        # Get Date
        'date': date_format(
            data.get('published', {}).get('date-parts', [[]])[0]
        ),
        # Get Volume
        'volume': data.get('volume'),
        # Get Issue
        'issue': data.get('issue'),
        # Get Page
        'page': data.get('page'),
        # Get Reference
        'reference': {
            idx: [options['DOI'], data[prop]]
            for idx, prop in enumerate(['DOI'])
            if data.get(prop, {})
        },
        # Get Journal
        'journal':
            [
                Journal.from_doi(
                    data.get('ISSN', []),
                    data.get('container-title', [])
                )
            ]
            if 'ISSN' in data or 'container-title' in data else [],
        # Get Authors
        'authors':
            [
                Author.from_doi(author)
                for author in data.get('author', [])
                if author
            ]
            if 'author' in data else [],
    }

    return cls(
        **publication
    )

from_query(raw_data) classmethod

Build a Publication instance from a SPARQL query result list.

Parameters:

Name Type Description Default
raw_data dict

List of SPARQL result row dicts; the first element is used. Expected keys include id, label, description, entrytypelabel, title, date, doi, journalInfo, authorInfos, and relation keys such as applies, analyzes, documents, etc.

required

Returns:

Type Description
Publication

Publication instance populated from the SPARQL result.

Source code in MaRDMO/publication/models.py
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
@classmethod
def from_query(cls, raw_data: dict) -> 'Publication':
    '''Build a Publication instance from a SPARQL query result list.

    Args:
        raw_data: List of SPARQL result row dicts; the first element is used.
                  Expected keys include ``id``, ``label``, ``description``,
                  ``entrytypelabel``, ``title``, ``date``, ``doi``,
                  ``journalInfo``, ``authorInfos``, and relation keys such as
                  ``applies``, ``analyzes``, ``documents``, etc.

    Returns:
        Publication instance populated from the SPARQL result.
    '''
    options = cls.get_options()
    items = get_items()

    data = raw_data[0]

    publication = {
        # Get ID
        'id': data.get('id', {}).get('value'),
        # Get Label
        'label': data.get('label', {}).get('value'),
        # Get Description
        'description': data.get('description', {}).get('value'),
        # Get Entrytype
        'entrytype': data.get('entrytypelabel', {}).get('value'),
        # Get Language
        'language': (
            [
                Relatant.from_triple(
                    f"mardi:{items['english']}",
                    "English",
                    "West Germanic language"
                )
            ]
            if data.get('languagelabel', {}).get('value', '').lower()
                in {"en", "eng", "english"}
            else []
        ),
        # Get Title
        'title': data.get('title', {}).get('value'),
        # Get Date
        'date': data.get('date', {}).get('value', ''),
        # Get Volume
        'volume': data.get('volume', {}).get('value'),
        # Get Issue
        'issue': data.get('issue', {}).get('value'),
        # Get Page
        'page': data.get('page', {}).get('value'),
        # Get Reference
        'reference': {
            idx: [options['DOI'], data[prop]['value']]
            for idx, prop in enumerate(['doi'])
            if data.get(prop, {}).get('value')
        },
        # Get Journal
        'journal': 
            [
                Journal.from_query(
                    data.get('journalInfo', {}).get('value')
                )
            ]
            if 'journalInfo' in data else [],
        # Get Authors
        'authors': 
            [
                Author.from_query(
                    author
                )
                for author in data.get('authorInfos', {}).get('value', '').split(" || ")
                if author
            ]
            if 'authorInfos' in data else [],
        # Get Applies Relation(s)
        'applies': split_value(
            data = data,
            key = 'applies',
            transform = RelatantWithClass.from_query
        ),
        # Get Analyzes Relation(s)
        'analyzes': split_value(
            data = data,
            key = 'analyzes',
            transform = RelatantWithClass.from_query
        ),
        # Get Documents Relation(s)
        'documents': split_value(
            data = data,
            key = 'documents',
            transform = RelatantWithClass.from_query
        ),
        # Get Invents Relation(s)
        'invents': split_value(
            data = data,
            key = 'invents',
            transform = RelatantWithClass.from_query
        ),
        # Get Studies Relation(s)
        'studies': split_value(
            data = data,
            key = 'studies',
            transform = RelatantWithClass.from_query
        ),
        # Get Surveys Relation(s)
        'surveys': split_value(
            data = data,
            key = 'surveys',
            transform = RelatantWithClass.from_query
        ),
        # Get Uses Relation(s)
        'uses': split_value(
            data = data,
            key = 'uses',
            transform = RelatantWithClass.from_query
        ),
    }

    return cls(
        **publication
        )

from_zbmath(raw_data) classmethod

Build a Publication instance from a zbMath API response.

Parameters:

Name Type Description Default
raw_data dict

Response object whose .json()['result'][0] contains zbMath metadata fields such as title, year, document_type, language, source, links, and contributors.

required

Returns:

Type Description
Publication

Publication instance populated from the zbMath metadata.

Source code in MaRDMO/publication/models.py
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
@classmethod
def from_zbmath(cls, raw_data: dict) -> 'Publication':
    '''Build a Publication instance from a zbMath API response.

    Args:
        raw_data: Response object whose ``.json()['result'][0]`` contains
                  zbMath metadata fields such as ``title``, ``year``,
                  ``document_type``, ``language``, ``source``, ``links``,
                  and ``contributors``.

    Returns:
        Publication instance populated from the zbMath metadata.
    '''
    options = cls.get_options()
    items = get_items()

    data = raw_data.json().get('result', [''])[0]

    publication = {
        # Get ID
        'id': None,
        # Get Label
        'label': data.get('title', {}).get('title'),
        # Get Description
        'description': (
            f"scientific article (doi: {doi})"
            if (doi := next(
                (
                    link.get("identifier")
                    for link in data.get("links", [])
                    if link.get("type") == "doi"
                ),
                None,
            ))
            else "scientific article"
        ),
        # Get Entrytype
        'entrytype': (
            'scholarly article'
            if data.get('document_type', {}).get('description') == 'journal article'
            else 'publication'
        ),
        # Get Language
        'language':(
            [
                Relatant.from_triple(
                    f"mardi:{items['english']}",
                    "English",
                    "West Germanic language"
                )
            ]
            if any(
                code.lower() in {"en", "eng", "english"}
                for code in (data.get("language", {}).get("languages") or [""])
            )
            else []
        ),
        # Get Date
        'date':
            f"{int(data['year']):04d}-00-00T00:00:00Z"
            if data.get('year') else None,
        # Get Title
        'title': data.get('title', {}).get('title'),
        # Get Volume
        'volume': (data.get('source', {}).get('series') or [''])[0].get('volume'),
        # Get Issue
        'issue': (data.get('source', {}).get('series') or [''])[0].get('issue'),
        # Get Page
        'page': data.get('source', {}).get('page'),
        # Get Reference
        'reference': (
            {0: [options['DOI'], doi]}
            if (
                doi := next(
                (
                    link.get('identifier')
                    for link in data.get('links', [])
                    if link.get('type') == 'doi'
                ),
                None
                )
            )
            else {}
        ),
        # Get Journal
        'journal': 
            [
                Journal.from_zbmath(
                    data.get('source', {})
                )
            ]
            if 'source' in data else [],
        # Get Authors
        'authors':
            [
                Author.from_zbmath(author)
                for author in data.get('contributors', {}).get('authors', [])
                if author
            ]
            if 'contributors' in data else [],
    }

    return cls(
        **publication
    )

get_options() classmethod

Return the cached option-label mapping used by all class generators.

Returns:

Type Description
dict

Dict mapping option keys (e.g. 'DOI') to their display labels,

dict

loaded once and cached as a class variable.

Source code in MaRDMO/publication/models.py
559
560
561
562
563
564
565
566
567
568
569
@classmethod
def get_options(cls) -> dict:
    '''Return the cached option-label mapping used by all class generators.

    Returns:
        Dict mapping option keys (e.g. ``'DOI'``) to their display labels,
        loaded once and cached as a class variable.
    '''
    if cls.options is None:
        cls.options = get_options()
    return cls.options

Providers

RDMO optionset providers for the Publication documentation catalog.

Implements the provider that searches MaRDI Portal and Wikidata for publication items to let users look up and attach existing publications to their project.

Provides:

  • :class:Publication — searches external sources for publication items; refreshes questionnaire fields upon selection, no creation

Publication

Bases: Provider

Publication Provider (MaRDI Portal / Wikidata), No User Creation, Refresh Upon Selection

Source code in MaRDMO/publication/providers.py
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
class Publication(Provider):
    '''Publication Provider (MaRDI Portal / Wikidata),
       No User Creation, Refresh Upon Selection
    '''

    search = True
    refresh = True

    def get_options(self, project, search=None, user=None, site=None):
        '''Query external knowledge-graph source(s) and return matching options.

        Args:
            project: RDMO project instance (unused).
            search:  Search string entered by the user; returns empty list when
                     fewer than 3 characters.
            user:    Requesting user (unused).
            site:    Current site (unused).

        Returns:
            List of ``{"id": …, "text": …}`` option dicts sorted by relevance.
        '''
        if not search or len(search) < 3:
            return []

        return query_sources(search)

get_options(project, search=None, user=None, site=None)

Query external knowledge-graph source(s) and return matching options.

Parameters:

Name Type Description Default
project

RDMO project instance (unused).

required
search

Search string entered by the user; returns empty list when fewer than 3 characters.

None
user

Requesting user (unused).

None
site

Current site (unused).

None

Returns:

Type Description

List of {"id": …, "text": …} option dicts sorted by relevance.

Source code in MaRDMO/publication/providers.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def get_options(self, project, search=None, user=None, site=None):
    '''Query external knowledge-graph source(s) and return matching options.

    Args:
        project: RDMO project instance (unused).
        search:  Search string entered by the user; returns empty list when
                 fewer than 3 characters.
        user:    Requesting user (unused).
        site:    Current site (unused).

    Returns:
        List of ``{"id": …, "text": …}`` option dicts sorted by relevance.
    '''
    if not search or len(search) < 3:
        return []

    return query_sources(search)

Workers

Background workers for collecting and exporting publication metadata.

Two worker classes handle different phases of publication documentation: retrieval (fetching bibliographic data from external APIs) and export (formatting and pushing data to the MaRDI Portal).

Provides:

  • :class:PublicationRetriever — fetches citation metadata for a DOI and writes the results into the questionnaire via the adders module
  • :class:PublicationExport — base class for catalog-specific export workers that package questionnaire answers for portal submission

PublicationExport

Base class providing author, journal, and publication export helpers.

Subclassed by :class:~MaRDMO.model.worker.PrepareModel and :class:~MaRDMO.algorithm.worker.PrepareAlgorithm. Holds the shared Wikibase vocabulary and the private _export_* helpers that build publication-related payload entries.

Source code in MaRDMO/publication/worker.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
class PublicationExport:
    '''Base class providing author, journal, and publication export helpers.

    Subclassed by :class:`~MaRDMO.model.worker.PrepareModel` and
    :class:`~MaRDMO.algorithm.worker.PrepareAlgorithm`.  Holds the shared
    Wikibase vocabulary and the private ``_export_*`` helpers that build
    publication-related payload entries.
    '''

    def __init__(self):
        '''Initialise with Wikibase properties and items from MaRDMOConfig.'''
        self.properties = get_properties()
        self.items = get_items()

    def _export_journals(self, payload, publications: dict):
        '''Add journal item entries (instance-of + ISSN) for all publications.

        Args:
            payload:      :class:`~MaRDMO.payload.GeneratePayload` instance.
            publications: Dict of publication answer dicts keyed by index.
        '''
        for publication in publications.values():
            for entry in publication.get('journal', {}).values():
                if not entry.get("ID") or entry.get("ID") == 'no journal found':
                    continue

                payload.get_item_key(
                value = entry
                )

                payload.add_answer(
                    verb = self.properties["instance of"],
                    object_and_type = [self.items["scientific journal"], "wikibase-item"],
                )

                if entry.get('issn'):
                    payload.add_answer(
                        verb = self.properties["ISSN"],
                        object_and_type = [entry["issn"], "external-id"],
                    )

    def _export_authors(self, payload, publications: dict):
        '''Add author item entries (instance-of, profile type, ORCID, zbMATH) for all publications.

        Args:
            payload:      :class:`~MaRDMO.payload.GeneratePayload` instance.
            publications: Dict of publication answer dicts keyed by index.
        '''
        for publication in publications.values():
            for entry in publication.get('author', {}).values():
                if not entry.get("ID") or entry.get("ID") == 'no author found':
                    continue

                payload.get_item_key(
                value = entry
                )

                payload.add_answer(
                    verb = self.properties["instance of"],
                    object_and_type = [self.items["human"], "wikibase-item"],
                )

                payload.add_answer(
                    verb = self.properties["MaRDI profile type"],
                    object_and_type = [self.items["Person"], "wikibase-item"],
                )

                if entry.get('orcid'):
                    payload.add_answer(
                        verb = self.properties["ORCID iD"],
                        object_and_type = [
                            entry['orcid'],
                            "external-id"
                        ],
                    )

                if entry.get('zbmath'):
                    payload.add_answer(
                        verb = self.properties["zbMATH author ID"],
                        object_and_type = [
                            entry['zbmath'],
                            "external-id"
                        ],
                    )

    def _export_publications(self, payload, publications: dict, relations: list):
        '''Add publication item entries and their relatant links to the payload.

        Args:
            payload:      :class:`~MaRDMO.payload.GeneratePayload` instance.
            publications: Dict of publication answer dicts.
            relations:    List of ``(relation_key, relatant_key)`` pairs that
                          link each publication to its parent entity.
        '''
        for entry in publications.values():
            if not entry.get("ID"):
                continue

            payload.get_item_key(
                value = entry
            )

            # Only add class, profile, and DOI for non-MaRDI items
            if "mardi" not in entry["ID"]:

                 # Set and add Publication Class
                if entry.get("entrytype") == "scholarly article":
                    pclass = self.items["scholarly article"]
                else:
                    pclass = self.items["publication"]

                payload.add_answer(
                    verb = self.properties["instance of"],
                    object_and_type = [pclass, "wikibase-item"],
                )

                # Add Publication Profile
                payload.add_answer(
                        verb = self.properties["MaRDI profile type"],
                        object_and_type = [
                            self.items["MaRDI publication profile"],
                            "wikibase-item"
                        ],
                    )

                # Add DOI
                if entry.get("reference", {}).get(0):
                    payload.add_answer(
                        verb = self.properties["DOI"],
                        object_and_type = [
                            entry["reference"][0][1].upper(),
                            "external-id"
                        ],
                    )

                # bibliographic data
                if entry.get("title"):
                    payload.add_answer(
                        verb = self.properties["title"],
                        object_and_type = [
                            {"text": entry["title"], "language": "en"},
                            "monolingualtext",
                        ],
                    )

                if entry.get("volume"):
                    payload.add_answer(
                        verb = self.properties["volume"],
                        object_and_type = [entry["volume"], "string"],
                    )

                if entry.get("issue"):
                    payload.add_answer(
                        verb = self.properties["issue"],
                        object_and_type = [entry["issue"], "string"],
                    )

                if entry.get("page"):
                    payload.add_answer(
                        verb = self.properties["page(s)"],
                        object_and_type = [entry["page"], "string"],
                    )

                if entry.get("date"):
                    payload.add_answer(
                        verb = self.properties["publication date"],
                        object_and_type = [
                            {
                                "time": f"+{entry['date']}",
                                "precision": date_precision(
                                    date_str = entry['date']
                                ),
                                "calendarmodel": (
                                    "http://www.wikidata.org/entity/Q1985727"
                                ),
                            },
                            "time",
                        ],
                    )

                # Add Language
                payload.add_single_relation(
                    statement = {
                        'relation': self.properties["language of work or name"],
                        'relatant': "language"
                    }
                )
                # Add Journal
                payload.add_single_relation(
                    statement = {
                        'relation': self.properties["published in"],
                        'relatant': "journal"
                    }
                )
                # Add Authors
                payload.add_single_relation(
                    statement = {
                        'relation': self.properties["author"],
                        'relatant': "author"
                    },
                    alt_statement = {
                        "relation": self.properties["author name string"],
                        "relatant": "Name",
                    },
                )

            # Add caller-supplied relations (P2E, P2A/P2BS, etc.)
            for relation, relatant in relations:
                payload.add_multiple_relation(
                    statement = {
                        'relation': relation,
                        'relatant': relatant
                    },
                    reverse = True,
                )

__init__()

Initialise with Wikibase properties and items from MaRDMOConfig.

Source code in MaRDMO/publication/worker.py
164
165
166
167
def __init__(self):
    '''Initialise with Wikibase properties and items from MaRDMOConfig.'''
    self.properties = get_properties()
    self.items = get_items()

PublicationRetriever

Retrieve Metadata from MaRDI Portal, Wikidata, and other sources like Crossref, DataCite, zbMath, DOI, and ORCid for Workflow, Model, and Algorithm documentation.

Source code in MaRDMO/publication/worker.py
 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
146
147
148
149
150
151
152
153
class PublicationRetriever:
    '''Retrieve Metadata from MaRDI Portal, Wikidata, and other
       sources like Crossref, DataCite, zbMath, DOI, and ORCid for Workflow,
       Model, and Algorithm documentation.'''

    # Get Publication-related Questions
    questions = get_questions('publication')

    def get_information(self, project, snapshot, answers, options):
        '''Fetch and store citation metadata for all publications in *answers*.

        Iterates over ``answers["publication"]``, skips entries without an ID
        or (for workflow catalogs) entries not flagged for export, then resolves
        each publication via :func:`~.utils.get_citation` and writes the
        resulting metadata fields (title, year, language, journal, authors,
        etc.) back into the questionnaire.

        Args:
            project:  RDMO project instance.
            snapshot: RDMO snapshot (``None`` for the current snapshot).
            answers:  Top-level answers dict (mutated in place with citation data).
            options:  Global RDMO options dict (used for the ``"Yes"`` option check).
        '''

        for key in answers.get('publication', {}):

            if str(project.catalog).endswith('mardmo-interdisciplinary-workflow-catalog'):
                # Ignore references for individual triple in workflow catalog
                if answers['publication'][key]['workflow'] != options['Yes']:
                    continue

            # If ID is missing (not answered or deleted)
            if not answers['publication'][key].get('ID'):
                continue

            #Clean potential old data...
            clean_background_data(
                key_dict = CITATIONINFOS | LANGUAGES | JOURNALS | AUTHORS,
                questions = self.questions["Publication"],
                project = project,
                snapshot = snapshot,
                set_index = key
            )

            #If User selected a Publication from Wikidata, MathAlgoDB or did not find it...
            if answers['publication'][key]['ID'].startswith(('wikidata','not found')):

                #...check if DOI is available.
                if not answers['publication'][key].get('reference', {}).get(0, ['',''])[1]:
                    continue

                #Get the Citation of several ressource.
                data_all = get_citation(answers['publication'][key]['reference'][0][1].upper())

                #If Publication available at MaRDI, Wikidata, Crossref, Datacite, zbMath or DOI...
                if any(
                    data_all.get(k)
                    for k in ("mardi", "wikidata", "crossref", "datacite", "zbmath", "doi")
                ):

                    data = (
                        data_all.get('mardi')
                        or data_all.get("wikidata")
                        or data_all.get("crossref")
                        or data_all.get("datacite")
                        or data_all.get("zbmath")
                        or data_all.get("doi")
                    )

                    #...add data to Questionnaire and...
                    if data_all.get('mardi') or data_all.get('wikidata'):
                        value_editor(
                            project = project,
                            uri = f'{BASE_URI}{self.questions["Publication"]["ID"]["uri"]}',
                            info = {
                                'text': 
                                    f"{data.label} ({data.description}) [{data.id.split(':')[0]}]",
                                'external_id': data.id,
                                'set_index': key
                            }
                        )

                    for uri, data_key in (ITEMINFOS|CITATIONINFOS).items():
                        value_editor(
                            project = project,
                            uri = f'{BASE_URI}{self.questions["Publication"][uri]["uri"]}',
                            info = {
                                'text': getattr(data, data_key), 
                                'set_index': key
                            }
                        )
                    for idx, language in enumerate(data.language):
                        for uri, data_key in LANGUAGES.items():
                            value_editor(
                                project = project,
                                uri = f'{BASE_URI}{self.questions["Publication"][uri]["uri"]}',
                                info = {
                                    'text': getattr(language, data_key), 
                                    'collection_index': idx, 
                                    'set_index': key
                                }
                            )
                    for idx, journal in enumerate(data.journal):
                        for uri, data_key in JOURNALS.items():
                            value_editor(
                                project = project,
                                uri = f'{BASE_URI}{self.questions["Publication"][uri]["uri"]}',
                                info = {
                                    'text': getattr(journal, data_key), 
                                    'collection_index': idx, 
                                    'set_index': key
                                }
                            )
                    for idx, author in enumerate(data.authors):
                        for uri, data_key in AUTHORS.items():
                            value_editor(
                                project = project,
                                uri = f'{BASE_URI}{self.questions["Publication"][uri]["uri"]}',
                                info = {
                                    'text': getattr(author, data_key), 
                                    'collection_index': idx, 
                                    'set_index': key
                                }
                            )

                    #...output dictionary.
                    if data_all.get('mardi') or data_all.get('wikidata'):
                        answers['publication'][key]['ID'] = data.id
                    answers['publication'][key]['Name'] = data.label
                    answers['publication'][key]['Description'] = data.description

        return answers

get_information(project, snapshot, answers, options)

Fetch and store citation metadata for all publications in answers.

Iterates over answers["publication"], skips entries without an ID or (for workflow catalogs) entries not flagged for export, then resolves each publication via :func:~.utils.get_citation and writes the resulting metadata fields (title, year, language, journal, authors, etc.) back into the questionnaire.

Parameters:

Name Type Description Default
project

RDMO project instance.

required
snapshot

RDMO snapshot (None for the current snapshot).

required
answers

Top-level answers dict (mutated in place with citation data).

required
options

Global RDMO options dict (used for the "Yes" option check).

required
Source code in MaRDMO/publication/worker.py
 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
146
147
148
149
150
151
152
153
def get_information(self, project, snapshot, answers, options):
    '''Fetch and store citation metadata for all publications in *answers*.

    Iterates over ``answers["publication"]``, skips entries without an ID
    or (for workflow catalogs) entries not flagged for export, then resolves
    each publication via :func:`~.utils.get_citation` and writes the
    resulting metadata fields (title, year, language, journal, authors,
    etc.) back into the questionnaire.

    Args:
        project:  RDMO project instance.
        snapshot: RDMO snapshot (``None`` for the current snapshot).
        answers:  Top-level answers dict (mutated in place with citation data).
        options:  Global RDMO options dict (used for the ``"Yes"`` option check).
    '''

    for key in answers.get('publication', {}):

        if str(project.catalog).endswith('mardmo-interdisciplinary-workflow-catalog'):
            # Ignore references for individual triple in workflow catalog
            if answers['publication'][key]['workflow'] != options['Yes']:
                continue

        # If ID is missing (not answered or deleted)
        if not answers['publication'][key].get('ID'):
            continue

        #Clean potential old data...
        clean_background_data(
            key_dict = CITATIONINFOS | LANGUAGES | JOURNALS | AUTHORS,
            questions = self.questions["Publication"],
            project = project,
            snapshot = snapshot,
            set_index = key
        )

        #If User selected a Publication from Wikidata, MathAlgoDB or did not find it...
        if answers['publication'][key]['ID'].startswith(('wikidata','not found')):

            #...check if DOI is available.
            if not answers['publication'][key].get('reference', {}).get(0, ['',''])[1]:
                continue

            #Get the Citation of several ressource.
            data_all = get_citation(answers['publication'][key]['reference'][0][1].upper())

            #If Publication available at MaRDI, Wikidata, Crossref, Datacite, zbMath or DOI...
            if any(
                data_all.get(k)
                for k in ("mardi", "wikidata", "crossref", "datacite", "zbmath", "doi")
            ):

                data = (
                    data_all.get('mardi')
                    or data_all.get("wikidata")
                    or data_all.get("crossref")
                    or data_all.get("datacite")
                    or data_all.get("zbmath")
                    or data_all.get("doi")
                )

                #...add data to Questionnaire and...
                if data_all.get('mardi') or data_all.get('wikidata'):
                    value_editor(
                        project = project,
                        uri = f'{BASE_URI}{self.questions["Publication"]["ID"]["uri"]}',
                        info = {
                            'text': 
                                f"{data.label} ({data.description}) [{data.id.split(':')[0]}]",
                            'external_id': data.id,
                            'set_index': key
                        }
                    )

                for uri, data_key in (ITEMINFOS|CITATIONINFOS).items():
                    value_editor(
                        project = project,
                        uri = f'{BASE_URI}{self.questions["Publication"][uri]["uri"]}',
                        info = {
                            'text': getattr(data, data_key), 
                            'set_index': key
                        }
                    )
                for idx, language in enumerate(data.language):
                    for uri, data_key in LANGUAGES.items():
                        value_editor(
                            project = project,
                            uri = f'{BASE_URI}{self.questions["Publication"][uri]["uri"]}',
                            info = {
                                'text': getattr(language, data_key), 
                                'collection_index': idx, 
                                'set_index': key
                            }
                        )
                for idx, journal in enumerate(data.journal):
                    for uri, data_key in JOURNALS.items():
                        value_editor(
                            project = project,
                            uri = f'{BASE_URI}{self.questions["Publication"][uri]["uri"]}',
                            info = {
                                'text': getattr(journal, data_key), 
                                'collection_index': idx, 
                                'set_index': key
                            }
                        )
                for idx, author in enumerate(data.authors):
                    for uri, data_key in AUTHORS.items():
                        value_editor(
                            project = project,
                            uri = f'{BASE_URI}{self.questions["Publication"][uri]["uri"]}',
                            info = {
                                'text': getattr(author, data_key), 
                                'collection_index': idx, 
                                'set_index': key
                            }
                        )

                #...output dictionary.
                if data_all.get('mardi') or data_all.get('wikidata'):
                    answers['publication'][key]['ID'] = data.id
                answers['publication'][key]['Name'] = data.label
                answers['publication'][key]['Description'] = data.description

    return answers

Constants

Compile-time constants for the Publication documentation sub-package.

Defines the property-to-predicate mappings (PROPS), item-info dicts (ITEMINFOS, CITATIONINFOS), and lookup tables for journals, authors, and languages that the publication worker and handlers consume.

These constants are imported directly; no factory functions are needed because the publication constants do not depend on the live RDMO database state.

Utils

Utility functions for fetching and normalizing publication metadata.

Provides helpers that query external bibliographic APIs (Crossref, DataCite, ZbMATH, ORCID), parse their responses, and map the results to the internal :class:~MaRDMO.publication.models.Author, :class:~MaRDMO.publication.models.Journal, and :class:~MaRDMO.publication.models.Publication dataclasses.

Provides:

  • get_citation — primary entry point: fetch full citation data for a DOI
  • get_crossref_data — query the Crossref REST API
  • get_datacite_data — query the DataCite REST API
  • get_doi_data — DOI resolver wrapper
  • get_zbmath_data — query the ZbMATH Open API
  • get_orcids — look up ORCID IDs for publication authors
  • get_author_by_orcid — fetch a single author record from the ORCID API
  • extract_authors — parse author list from API response
  • extract_journals — parse journal info from API response
  • assign_id — attach portal / ZbMATH IDs to entity dicts
  • assign_orcid — attach ORCID identifiers to author dicts
  • clean_background_data — remove stale pre-existing answers before filling new ones
  • additional_queries — run supplementary SPARQL queries for linked entities

additional_queries(publication, choice, key, parameter, function)

Run supplemental Wikidata and MaRDI Portal SPARQL queries for a sub-entity type.

Queries first Wikidata, then MaRDI Portal, using the results of the first to refine the second. Calls function to parse raw SPARQL results into model instances, then calls :func:assign_id to back-fill any missing IDs on publication[choice].<key>.

Parameters:

Name Type Description Default
publication

Dict mapping source keys to :class:~.models.Publication instances (mutated in place).

required
choice

Active source key (e.g. "crossref").

required
key

Sub-entity attribute name and query file stem ("authors" or "journal").

required
parameter

Dict with "wikidata" and "mardi" lists of format arguments for the SPARQL query template.

required
function

Callable that parses raw SPARQL results into a {index: entity} dict (e.g. :func:extract_authors).

required
Source code in MaRDMO/publication/utils.py
 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
def additional_queries(publication, choice, key, parameter, function):
    '''Run supplemental Wikidata and MaRDI Portal SPARQL queries for a sub-entity type.

    Queries first Wikidata, then MaRDI Portal, using the results of the first
    to refine the second.  Calls *function* to parse raw SPARQL results into
    model instances, then calls :func:`assign_id` to back-fill any missing IDs
    on ``publication[choice].<key>``.

    Args:
        publication: Dict mapping source keys to :class:`~.models.Publication`
                     instances (mutated in place).
        choice:      Active source key (e.g. ``"crossref"``).
        key:         Sub-entity attribute name and query file stem
                     (``"authors"`` or ``"journal"``).
        parameter:   Dict with ``"wikidata"`` and ``"mardi"`` lists of format
                     arguments for the SPARQL query template.
        function:    Callable that parses raw SPARQL results into a ``{index:
                     entity}`` dict (e.g. :func:`extract_authors`).
    '''

    # Get & Extract Information from  Wikidata
    wikidata_query = get_sparql_query(
        f"publication/queries/{key}.sparql"
    ).format(
        *parameter['wikidata']
    )
    wikidata_results = query_sparql(
        wikidata_query,
        get_url('wikidata', 'sparql')
    )
    wikidata_info = function(wikidata_results)

    # Get & Extract Information from MaRDI Portal
    wikidata_id = ' '.join(
        f'"{entity.id}"' if entity.id else '""'
        for entity in wikidata_info.values()
    )

    if wikidata_id:
        parameter['mardi'][-1] = wikidata_id

    mardi_query = get_sparql_query(
        f"publication/queries/{key}.sparql"
    ).format(
        *parameter['mardi']
    )
    mardi_results = query_sparql(
        mardi_query,
        get_url('mardi', 'sparql')
    )
    mardi_info = function(mardi_results)

    # Add (missing) MaRDI Portal / Wikidata IDs to authors
    if mardi_info:
        assign_id(
            getattr(
                publication[choice],
                key
            ),
            mardi_info,
            'mardi'
        )
    elif wikidata_info:
        assign_id(
            getattr(
                publication[choice],
                key
            ),
            wikidata_info,
            'wikidata'
        )

assign_id(entities, target, prefix)

Back-fill missing or placeholder IDs on entities from target.

Matches entities by label (case-insensitive) and overwrites the id, label, and description fields when the entity currently has no ID or a placeholder such as "not found".

Parameters:

Name Type Description Default
entities

Iterable of entity dataclass instances to update.

required
target

Dict of resolved entities (from SPARQL results) to match against.

required
prefix

Source prefix prepended to the resolved ID (e.g. "mardi").

required
Source code in MaRDMO/publication/utils.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def assign_id(entities, target, prefix):
    '''Back-fill missing or placeholder IDs on *entities* from *target*.

    Matches entities by label (case-insensitive) and overwrites the ``id``,
    ``label``, and ``description`` fields when the entity currently has no ID
    or a placeholder such as ``"not found"``.

    Args:
        entities: Iterable of entity dataclass instances to update.
        target:   Dict of resolved entities (from SPARQL results) to match against.
        prefix:   Source prefix prepended to the resolved ID (e.g. ``"mardi"``).
    '''
    for entity in entities:
        if (
            not entity.id
            or entity.id in ('not found', 'no author found', 'no journal found')
            or entity.id.startswith('wikidata')
        ):
            for id_entity in target.values():
                if entity.label.lower() == id_entity.label.lower():
                    entity.id = f"{prefix}:{id_entity.id}"
                    entity.label = id_entity.label
                    entity.description = id_entity.description

assign_orcid(publication, source, id_type='orcid')

Back-fill missing ORCID iDs on authors from the orcid lookup result.

Parameters:

Name Type Description Default
publication

Dict mapping source keys to :class:~.models.Publication instances (mutated in place).

required
source

Key of the publication entry whose authors are updated.

required
id_type

Key for the ORCID lookup result dict (default "orcid").

'orcid'
Source code in MaRDMO/publication/utils.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def assign_orcid(publication, source, id_type = 'orcid'):
    '''Back-fill missing ORCID iDs on authors from the *orcid* lookup result.

    Args:
        publication: Dict mapping source keys to :class:`~.models.Publication`
                     instances (mutated in place).
        source:      Key of the publication entry whose authors are updated.
        id_type:     Key for the ORCID lookup result dict (default ``"orcid"``).
    '''
    for author in publication[source].authors:
        if not author.orcid_id:
            for id_author in publication[id_type].values():
                if author.label == id_author.label:
                    author.orcid_id = id_author.orcid_id

clean_background_data(key_dict, questions, project, snapshot, set_index)

Delete questionnaire values that were temporarily saved during background processing.

Parameters:

Name Type Description Default
key_dict

Iterable of question-dict keys to delete.

required
questions

Questions dict mapping keys to attribute URI fragments.

required
project

RDMO project instance.

required
snapshot

RDMO snapshot (None for the current working snapshot).

required
set_index

Set-index of the entries to delete.

required
Source code in MaRDMO/publication/utils.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def clean_background_data(key_dict, questions, project, snapshot, set_index):
    '''Delete questionnaire values that were temporarily saved during background processing.

    Args:
        key_dict:  Iterable of question-dict keys to delete.
        questions: Questions dict mapping keys to attribute URI fragments.
        project:   RDMO project instance.
        snapshot:  RDMO snapshot (``None`` for the current working snapshot).
        set_index: Set-index of the entries to delete.
    '''
    for key in key_dict:
        Value.objects.filter(
            attribute_id = Attribute.objects.get(
                uri = f'{BASE_URI}{questions[key]["uri"]}'
            ),
            set_index = set_index,
            project = project,
            snapshot = snapshot
        ).delete()

extract_authors(data)

Parse SPARQL result rows into a dict of :class:~.models.Author instances.

Expects data[0]["author_info"]["value"] as a " || "-delimited string of author records.

Parameters:

Name Type Description Default
data

Raw SPARQL result list (may be empty).

required

Returns:

Type Description

Dict {index: Author} for each non-empty record; empty dict if

data is falsy.

Source code in MaRDMO/publication/utils.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def extract_authors(data):
    '''Parse SPARQL result rows into a dict of :class:`~.models.Author` instances.

    Expects ``data[0]["author_info"]["value"]`` as a ``" || "``-delimited
    string of author records.

    Args:
        data: Raw SPARQL result list (may be empty).

    Returns:
        Dict ``{index: Author}`` for each non-empty record; empty dict if
        *data* is falsy.
    '''
    authors = {}
    if data:
        for idx, entry in enumerate(data[0].get('author_info', {}).get('value', '').split(" || ")):
            if entry:
                authors[idx] = Author.from_query(entry)
    return authors

extract_journals(data)

Parse SPARQL result rows into a dict of :class:~.models.Journal instances.

Expects data[0]["journal_info"]["value"] as a " || "-delimited string of journal records.

Parameters:

Name Type Description Default
data

Raw SPARQL result list (may be empty).

required

Returns:

Type Description

Dict {index: Journal} for each non-empty record; empty dict if

data is falsy.

Source code in MaRDMO/publication/utils.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def extract_journals(data):
    '''Parse SPARQL result rows into a dict of :class:`~.models.Journal` instances.

    Expects ``data[0]["journal_info"]["value"]`` as a ``" || "``-delimited
    string of journal records.

    Args:
        data: Raw SPARQL result list (may be empty).

    Returns:
        Dict ``{index: Journal}`` for each non-empty record; empty dict if
        *data* is falsy.
    '''
    journals = {}
    if data:
        for idx, entry in enumerate(data[0].get('journal_info', {}).get('value', '').split(" || ")):
            if entry:
                journals[idx] = Journal.from_query(entry)
    return journals

get_author_by_orcid(orcid_id)

Fetch personal-details for a researcher from the ORCID public API.

Parameters:

Name Type Description Default
orcid_id

ORCID iD string (e.g. "0000-0002-1825-0097").

required

Returns:

Type Description

class:requests.Response (status 200) containing a JSON payload with

the researcher's personal details on success, or the caught

class:requests.exceptions.RequestException on failure.

Source code in MaRDMO/publication/utils.py
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
def get_author_by_orcid(orcid_id):
    '''Fetch personal-details for a researcher from the ORCID public API.

    Args:
        orcid_id: ORCID iD string (e.g. ``"0000-0002-1825-0097"``).

    Returns:
        :class:`requests.Response` (status 200) containing a JSON payload with
        the researcher's personal details on success, or the caught
        :class:`requests.exceptions.RequestException` on failure.
    '''
    try:
        request = requests.get(
            f"https://pub.orcid.org/v3.0/{orcid_id}/personal-details",
            headers = {'Accept': 'application/json'},
            timeout = 5
        )
        request.raise_for_status()
        return request
    except requests.exceptions.RequestException as error:
        return error

get_citation(doi)

Retrieve full citation metadata for a DOI from multiple sources.

Queries MaRDI Portal and Wikidata via SPARQL in parallel. If neither yields a result, falls back to CrossRef, DataCite, zbMath, and the DOI metadata service. Then enriches authors with ORCID iDs and runs supplemental author/journal queries against MaRDI Portal and Wikidata.

Parameters:

Name Type Description Default
doi

DOI string (e.g. "10.1000/xyz123").

required

Returns:

Type Description

Dict mapping source keys ("mardi", "wikidata", "crossref",

etc.) to :class:~.models.Publication instances or None; empty

dict if doi does not match the expected format.

Source code in MaRDMO/publication/utils.py
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def get_citation(doi):
    '''Retrieve full citation metadata for a DOI from multiple sources.

    Queries MaRDI Portal and Wikidata via SPARQL in parallel.  If neither
    yields a result, falls back to CrossRef, DataCite, zbMath, and the DOI
    metadata service.  Then enriches authors with ORCID iDs and runs
    supplemental author/journal queries against MaRDI Portal and Wikidata.

    Args:
        doi: DOI string (e.g. ``"10.1000/xyz123"``).

    Returns:
        Dict mapping source keys (``"mardi"``, ``"wikidata"``, ``"crossref"``,
        etc.) to :class:`~.models.Publication` instances or ``None``; empty
        dict if *doi* does not match the expected format.
    '''
    publication = {}

    if not re.match(r'10.\d{4,9}/[-._;()/:a-z0-9A-Z]+', doi):
        return publication

    choice = None

    # Define MaRDI Portal / Wikidata / MathAlgoDB SPARQL Queries
    mardi_query = get_sparql_query(
        'publication/queries/full_doi_mardi.sparql'
    ).format(
        doi,
        **get_items(),
        **get_properties()
    )
    wikidata_query = get_sparql_query(
        'publication/queries/full_doi_wikidata.sparql'
    ).format(
        doi
    )

    # Get Citation Data from MaRDI Portal / Wikidata / MathAlgoDB
    results = query_sparql_pool(
        {
            'wikidata': (wikidata_query, get_url('wikidata', 'sparql')),
            'mardi':(mardi_query, get_url('mardi', 'sparql')),
        }
    )

    # Structure Publication Information
    for key in ['mardi', 'wikidata']:
        try:
            publication[key] = Publication.from_query(results.get(key))
        except:
            publication[key] = None

    # Return if Publication found on MaRDI
    if publication['mardi']:
        return publication

    if not publication['wikidata']:
        # If no Citation Data in KGs get information from CrossRef, DataCite, DOI, zbMath
        pool = ThreadPool(processes=4)
        results = pool.map(
            lambda fn: fn(doi),
            [
                get_crossref_data,
                get_datacite_data,
                get_zbmath_data,
                get_doi_data
            ]
        )

        for idx, source in enumerate(['crossref', 'datacite', 'zbmath', 'doi']):
            if hasattr(results[idx], "status_code") and results[idx].status_code == 200:
                source_func_name = f"from_{source}"
                source_func = getattr(Publication, source_func_name)
                publication[source] = source_func(results[idx])
            else:
                publication[source] = None

    # Get Authors assigned to publication from ORCID
    publication['orcid'] = {}
    response = get_orcids(doi)
    if response.status_code == 200:
        orcids = response.json().get('result')
        if orcids:
            for idx, entry in enumerate(orcids):
                orcid_id = entry.get('orcid-identifier', {}).get('path', '')
                response = get_author_by_orcid(orcid_id)
                if response.status_code == 200:
                    orcid_author = response.json()
                    publication['orcid'][idx] = Author.from_orcid(orcid_author)

    # Add (missing) ORCID IDs to authors
    for choice in ['mardi', 'wikidata', 'crossref', 'datacite', 'zbmath', 'doi']:
        if publication.get(choice):
            assign_orcid(publication, choice)
            break
    else:
        choice = None

    # Additional Queries for chosen information source
    if choice:
        # Check if Authors already in MaRDI Portal or Wikidata
        orcid_id = ' '.join(
            f'"{author.orcid_id}"' if author.orcid_id else '""'
            for author in publication[choice].authors
        )
        zbmath_id = ' '.join(
            f'"{author.zbmath_id}"' if author.zbmath_id else '""'
            for author in publication[choice].authors
        )
        wikidata_id = ' '.join(
            f'"{author.wikidata_id}"' if author.wikidata_id else '""'
            for author in publication[choice].authors
        )

        properties = get_properties()
        if orcid_id and zbmath_id and wikidata_id:
            additional_queries(
                publication,
                choice,
                'authors', 
                {
                    'mardi': [
                        orcid_id,
                        zbmath_id,
                        properties['ORCID iD'],
                        properties['zbMATH author ID'],
                        properties['Wikidata QID'],
                        wikidata_id
                    ],
                    'wikidata': [
                        orcid_id,
                        zbmath_id,
                        'P496',
                        'P1556',
                        '',
                        wikidata_id
                    ],
                },
                extract_authors
            )

        # Check if Journal already in MaRDI Portal or Wikidata
        journal_id = wikidata_id = ""
        if publication[choice].journal:
            if publication[choice].journal[0].issn:
                journal_id = f'"{publication[choice].journal[0].issn}"'
            if (
                publication[choice].journal[0].id
                and 'wikidata' in publication[choice].journal[0].id
            ):
                wikidata_id = f'"{publication[choice].journal[0].id.split(":")[1]}"'

        if journal_id or wikidata_id:
            additional_queries(
                publication,
                choice,
                'journal',
                {
                    'mardi': [
                        journal_id,
                        properties['ISSN'],
                        properties['Wikidata QID'],
                        wikidata_id
                    ],
                    'wikidata': [
                        journal_id,
                        'P236',
                        '',
                        wikidata_id
                    ],
                },
                extract_journals
            )

    return publication

get_crossref_data(doi)

Fetch citation metadata for doi from the CrossRef REST API.

Parameters:

Name Type Description Default
doi

DOI string.

required

Returns:

Type Description

class:requests.Response (status 200) on success, or the caught

class:requests.exceptions.RequestException on failure.

Source code in MaRDMO/publication/utils.py
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
def get_crossref_data(doi):
    '''Fetch citation metadata for *doi* from the CrossRef REST API.

    Args:
        doi: DOI string.

    Returns:
        :class:`requests.Response` (status 200) on success, or the caught
        :class:`requests.exceptions.RequestException` on failure.
    '''
    try:
        request = requests.get(
            f"https://api.crossref.org/works/{doi}",
            timeout = 5
        )
        request.raise_for_status()
        return request
    except requests.exceptions.RequestException as error:
        return error

get_datacite_data(doi)

Fetch citation metadata for doi from the DataCite REST API.

Parameters:

Name Type Description Default
doi

DOI string.

required

Returns:

Type Description

class:requests.Response (status 200) on success, or the caught

class:requests.exceptions.RequestException on failure.

Source code in MaRDMO/publication/utils.py
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
def get_datacite_data(doi):
    '''Fetch citation metadata for *doi* from the DataCite REST API.

    Args:
        doi: DOI string.

    Returns:
        :class:`requests.Response` (status 200) on success, or the caught
        :class:`requests.exceptions.RequestException` on failure.
    '''
    try:
        request = requests.get(
            f"https://api.datacite.org/dois/{doi}",
            timeout = 5
        )
        request.raise_for_status()
        return request
    except requests.exceptions.RequestException as error:
        return error

get_doi_data(doi)

Fetch citation metadata for doi from the DOI metadata service.

Parameters:

Name Type Description Default
doi

DOI string.

required

Returns:

Type Description

class:requests.Response (status 200) on success, or the caught

class:requests.exceptions.RequestException on failure.

Source code in MaRDMO/publication/utils.py
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
def get_doi_data(doi):
    '''Fetch citation metadata for *doi* from the DOI metadata service.

    Args:
        doi: DOI string.

    Returns:
        :class:`requests.Response` (status 200) on success, or the caught
        :class:`requests.exceptions.RequestException` on failure.
    '''
    try:
        request = requests.get(
            f"https://citation.doi.org/metadata?doi={doi}",
            headers = {"accept": "application/json"},
            timeout = 0.0000001
        )
        request.raise_for_status()
        return request
    except requests.exceptions.RequestException as error:
        return error

get_orcids(doi)

Query the ORCID public API for researchers associated with doi.

Parameters:

Name Type Description Default
doi

DOI string used as the doi-self search criterion.

required

Returns:

Type Description

class:requests.Response (status 200) containing a JSON payload with

an "result" list of ORCID records on success, or the caught

class:requests.exceptions.RequestException on failure.

Source code in MaRDMO/publication/utils.py
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
def get_orcids(doi):
    '''Query the ORCID public API for researchers associated with *doi*.

    Args:
        doi: DOI string used as the ``doi-self`` search criterion.

    Returns:
        :class:`requests.Response` (status 200) containing a JSON payload with
        an ``"result"`` list of ORCID records on success, or the caught
        :class:`requests.exceptions.RequestException` on failure.
    '''
    try:
        request = requests.get(
            f'https://pub.orcid.org/v3.0/search/?q=doi-self:"{doi}"',
            headers = {'Accept': 'application/json'},
            timeout = 5
        )
        request.raise_for_status()
        return request
    except requests.exceptions.RequestException as error:
        return error

get_zbmath_data(doi)

Fetch citation metadata for doi from the zbMath Open API.

Parameters:

Name Type Description Default
doi

DOI string.

required

Returns:

Type Description

class:requests.Response (status 200) on success, or the caught

class:requests.exceptions.RequestException on failure.

Source code in MaRDMO/publication/utils.py
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
def get_zbmath_data(doi):
    '''Fetch citation metadata for *doi* from the zbMath Open API.

    Args:
        doi: DOI string.

    Returns:
        :class:`requests.Response` (status 200) on success, or the caught
        :class:`requests.exceptions.RequestException` on failure.
    '''
    try:
        request = requests.get(
            f"https://api.zbmath.org/v1/document/_structured_search?page=0&results_per_page=100&DOI={doi}",
            timeout = 5
        )
        request.raise_for_status()
        return request
    except requests.exceptions.RequestException as error:
        return error