Coverage for oc_ocdm / graph / entities / identifier.py: 95%
86 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-28 18:52 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-28 18:52 +0000
1#!/usr/bin/python
3# SPDX-FileCopyrightText: 2020-2022 Simone Persiani <iosonopersia@gmail.com>
4# SPDX-FileCopyrightText: 2022-2023 Arcangelo Massari <arcangelo.massari@unibo.it>
5#
6# SPDX-License-Identifier: ISC
8# -*- coding: utf-8 -*-
9from __future__ import annotations
11import re
12from typing import TYPE_CHECKING
14if TYPE_CHECKING:
15 from typing import Optional
16 from rdflib import URIRef
18from oc_ocdm.decorators import accepts_only
19from oc_ocdm.graph.graph_entity import GraphEntity
20from oc_ocdm.support.support import encode_url, is_string_empty
23class Identifier(GraphEntity):
24 """Identifier (short: id): an external identifier (e.g. DOI, ORCID, PubMedID, Open
25 Citation Identifier) associated with the bibliographic entity. Members of this class of
26 metadata are themselves given unique corpus identifiers e.g. 'id/0420129'."""
28 def _merge_properties(self, other: GraphEntity, prefer_self: bool) -> None:
29 """
30 The merge operation allows combining two ``Identifier`` entities into a single one,
31 by marking the second entity as to be deleted while also copying its data into the current
32 ``Identifier``. Moreover, every triple from the containing ``GraphSet`` referring to the second
33 entity gets "redirected" to the current entity: **every other reference contained inside a
34 different source (e.g. a triplestore) must be manually handled by the user!**
36 In case of functional properties, values from the current entity get overwritten
37 by those coming from the second entity while, in all other cases, values from the
38 second entity are simply appended to those of the current entity. In this context,
39 ``rdfs:label`` is considered as a functional property, while ``rdf:type`` is not.
41 :param other: The entity which will be marked as to be deleted and whose properties will
42 be merged into the current entity.
43 :type other: Identifier
44 :raises TypeError: if the parameter is of the wrong type
45 :return: None
46 """
47 super()._merge_properties(other, prefer_self)
48 assert isinstance(other, Identifier)
50 literal_value: Optional[str] = other.get_literal_value()
51 scheme: Optional[URIRef] = other.get_scheme()
52 if literal_value is not None and scheme is not None:
53 self._associate_identifier_with_scheme(literal_value, scheme)
55 # HAS LITERAL VALUE and HAS SCHEME
56 def get_literal_value(self) -> Optional[str]:
57 """
58 Getter method corresponding to the ``literal:hasLiteralValue`` RDF predicate.
60 :return: The requested value if found, None otherwise
61 """
62 return self._get_literal(GraphEntity.iri_has_literal_value)
64 def get_scheme(self) -> Optional[URIRef]:
65 """
66 Getter method corresponding to the ``datacite:usesIdentifierScheme`` RDF predicate.
68 :return: The requested value if found, None otherwise
69 """
70 uri: Optional[URIRef] = self._get_uri_reference(GraphEntity.iri_uses_identifier_scheme)
71 return uri
73 @accepts_only('literal')
74 def create_oci(self, string: str) -> None:
75 """
76 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
77 ``datacite:usesIdentifierScheme`` RDF predicate.
78 It implicitly sets the object value ``datacite:oci`` for the
79 ``datacite:usesIdentifierScheme`` RDF predicate.
81 **WARNING: this is a functional property, hence any existing value will be overwritten!**
83 :param string: The value that will be set as the object of the property related to this method
84 :type string: str
85 :raises TypeError: if the parameter is of the wrong type
86 :return: None
87 """
88 self._associate_identifier_with_scheme(string, GraphEntity.iri_oci)
90 @accepts_only('literal')
91 def create_orcid(self, string: str) -> None:
92 """
93 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
94 ``datacite:usesIdentifierScheme`` RDF predicate.
95 It implicitly sets the object value ``datacite:orcid`` for the
96 ``datacite:usesIdentifierScheme`` RDF predicate.
98 **WARNING: this is a functional property, hence any existing value will be overwritten!**
100 :param string: The value that will be set as the object of the property related to this method
101 :type string: str
102 :raises TypeError: if the parameter is of the wrong type
103 :return: None
104 """
105 self._associate_identifier_with_scheme(string, GraphEntity.iri_orcid)
107 @accepts_only('literal')
108 def create_openalex(self, string: str) -> None:
109 """
110 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
111 ``datacite:usesIdentifierScheme`` RDF predicate.
112 It implicitly sets the object value ``datacite:openalex`` for the
113 ``datacite:usesIdentifierScheme`` RDF predicate.
115 **WARNING: this is a functional property, hence any existing value will be overwritten!**
117 :param string: The value that will be set as the object of the property related to this method
118 :type string: str
119 :raises TypeError: if the parameter is of the wrong type
120 :return: None
121 """
122 self._associate_identifier_with_scheme(string, GraphEntity.iri_openalex)
124 @accepts_only('literal')
125 def create_doi(self, string: str) -> None:
126 """
127 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
128 ``datacite:usesIdentifierScheme`` RDF predicate.
129 It implicitly sets the object value ``datacite:doi`` for the
130 ``datacite:usesIdentifierScheme`` RDF predicate.
132 The string gets internally preprocessed by converting it to lowercase
133 (e.g. 'DOI:10.1111/HEX.12487' becomes 'doi:10.1111/hex.12487').
135 **WARNING: this is a functional property, hence any existing value will be overwritten!**
137 :param string: The value that will be set as the object of the property related to this method
138 :type string: str
139 :raises TypeError: if the parameter is of the wrong type
140 :return: None
141 """
142 self._associate_identifier_with_scheme(string.lower(), GraphEntity.iri_doi)
144 @accepts_only('literal')
145 def create_jid(self, string: str) -> None:
146 """
147 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
148 ``datacite:usesIdentifierScheme`` RDF predicate.
149 It implicitly sets the object value ``datacite:jid`` for the
150 ``datacite:usesIdentifierScheme`` RDF predicate.
152 **WARNING: this is a functional property, hence any existing value will be overwritten!**
154 :param string: The value that will be set as the object of the property related to this method
155 :type string: str
156 :raises TypeError: if the parameter is of the wrong type
157 :return: None
158 """
159 self._associate_identifier_with_scheme(string, GraphEntity.iri_jid)
161 @accepts_only('literal')
162 def create_pmid(self, string: str) -> None:
163 """
164 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
165 ``datacite:usesIdentifierScheme`` RDF predicate.
166 It implicitly sets the object value ``datacite:pmid`` for the
167 ``datacite:usesIdentifierScheme`` RDF predicate.
169 **WARNING: this is a functional property, hence any existing value will be overwritten!**
171 :param string: The value that will be set as the object of the property related to this method
172 :type string: str
173 :raises TypeError: if the parameter is of the wrong type
174 :return: None
175 """
176 self._associate_identifier_with_scheme(string, GraphEntity.iri_pmid)
178 @accepts_only('literal')
179 def create_pmcid(self, string: str) -> None:
180 """
181 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
182 ``datacite:usesIdentifierScheme`` RDF predicate.
183 It implicitly sets the object value ``datacite:pmcid`` for the
184 ``datacite:usesIdentifierScheme`` RDF predicate.
186 **WARNING: this is a functional property, hence any existing value will be overwritten!**
188 :param string: The value that will be set as the object of the property related to this method
189 :type string: str
190 :raises TypeError: if the parameter is of the wrong type
191 :return: None
192 """
193 self._associate_identifier_with_scheme(string, GraphEntity.iri_pmcid)
195 @accepts_only('literal')
196 def create_issn(self, string: str) -> None:
197 """
198 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
199 ``datacite:usesIdentifierScheme`` RDF predicate.
200 It implicitly sets the object value ``datacite:issn`` for the
201 ``datacite:usesIdentifierScheme`` RDF predicate.
203 The string gets internally preprocessed by eventually replacing long dashes with short ones
204 (e.g. '1522–4501' becomes '1522-4501').
206 **WARNING: this is a functional property, hence any existing value will be overwritten!**
208 :param string: The value that will be set as the object of the property related to this method. **It
209 must be a string different from '0000-0000'.**
210 :type string: str
211 :raises TypeError: if the parameter is of the wrong type
212 :return: None
213 """
214 cur_string = re.sub("–", "-", string)
215 if cur_string != "0000-0000":
216 self._associate_identifier_with_scheme(string, GraphEntity.iri_issn)
218 @accepts_only('literal')
219 def create_isbn(self, string: str) -> None:
220 """
221 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
222 ``datacite:usesIdentifierScheme`` RDF predicate.
223 It implicitly sets the object value ``datacite:isbn`` for the
224 ``datacite:usesIdentifierScheme`` RDF predicate.
226 The string gets internally preprocessed by eventually replacing long dashes with short ones
227 (e.g. '817525766–0' becomes '817525766-0').
229 **WARNING: this is a functional property, hence any existing value will be overwritten!**
231 :param string: The value that will be set as the object of the property related to this method
232 :type string: str
233 :raises TypeError: if the parameter is of the wrong type
234 :return: None
235 """
236 self._associate_identifier_with_scheme(re.sub("–", "-", string), GraphEntity.iri_isbn)
238 @accepts_only('literal')
239 def create_url(self, string: str) -> None:
240 """
241 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
242 ``datacite:usesIdentifierScheme`` RDF predicate.
243 It implicitly sets the object value ``datacite:url`` for the
244 ``datacite:usesIdentifierScheme`` RDF predicate.
246 The string gets internally preprocessed both by converting it to lowercase
247 (e.g. 'https://OPENCITATIONS.NET/' becomes 'https://opencitations.net/') and by
248 applying `URL encoding` on it (e.g. 'https://opencitations.net/file name.txt'
249 becomes 'https://opencitations.net/file%20name.txt').
251 **WARNING: this is a functional property, hence any existing value will be overwritten!**
253 :param string: The value that will be set as the object of the property related to this method
254 :type string: str
255 :raises TypeError: if the parameter is of the wrong type
256 :return: None
257 """
258 self._associate_identifier_with_scheme(encode_url(string.lower()), GraphEntity.iri_url)
260 @accepts_only('literal')
261 def create_xpath(self, string: str) -> None:
262 """
263 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
264 ``datacite:usesIdentifierScheme`` RDF predicate.
265 It implicitly sets the object value `datacite:local-resource-identifier-scheme` for the
266 ``datacite:usesIdentifierScheme`` RDF predicate.
268 **WARNING: this is a functional property, hence any existing value will be overwritten!**
270 :param string: The value that will be set as the object of the property related to this method
271 :type string: str
272 :raises TypeError: if the parameter is of the wrong type
273 :return: None
274 """
275 self._associate_identifier_with_scheme(string, GraphEntity.iri_xpath)
277 @accepts_only('literal')
278 def create_intrepid(self, string: str) -> None:
279 """
280 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
281 ``datacite:usesIdentifierScheme`` RDF predicate.
282 It implicitly sets the object value ``datacite:intrepid`` for the
283 ``datacite:usesIdentifierScheme`` RDF predicate.
285 **WARNING: this is a functional property, hence any existing value will be overwritten!**
287 :param string: The value that will be set as the object of the property related to this method
288 :type string: str
289 :raises TypeError: if the parameter is of the wrong type
290 :return: None
291 """
292 self._associate_identifier_with_scheme(string, GraphEntity.iri_intrepid)
294 @accepts_only('literal')
295 def create_xmlid(self, string: str) -> None:
296 """
297 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
298 ``datacite:usesIdentifierScheme`` RDF predicate.
299 It implicitly sets the object value `datacite:local-resource-identifier-scheme` for the
300 ``datacite:usesIdentifierScheme`` RDF predicate.
302 **WARNING: this is a functional property, hence any existing value will be overwritten!**
304 :param string: The value that will be set as the object of the property related to this method
305 :type string: str
306 :raises TypeError: if the parameter is of the wrong type
307 :return: None
308 """
309 self._associate_identifier_with_scheme(string, GraphEntity.iri_xmlid)
311 @accepts_only('literal')
312 def create_wikidata(self, string: str) -> None:
313 """
314 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
315 ``datacite:usesIdentifierScheme`` RDF predicate.
316 It implicitly sets the object value ``datacite:wikidata`` for the
317 ``datacite:usesIdentifierScheme`` RDF predicate.
319 **WARNING: this is a functional property, hence any existing value will be overwritten!**
321 :param string: The value that will be set as the object of the property related to this method
322 :type string: str
323 :raises TypeError: if the parameter is of the wrong type
324 :return: None
325 """
326 self._associate_identifier_with_scheme(string, GraphEntity.iri_wikidata)
328 @accepts_only('literal')
329 def create_wikipedia(self, string: str) -> None:
330 """
331 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
332 ``datacite:usesIdentifierScheme`` RDF predicate.
333 It implicitly sets the object value ``datacite:wikipedia`` for the
334 ``datacite:usesIdentifierScheme`` RDF predicate.
336 **WARNING: this is a functional property, hence any existing value will be overwritten!**
338 :param string: The value that will be set as the object of the property related to this method
339 :type string: str
340 :raises TypeError: if the parameter is of the wrong type
341 :return: None
342 """
343 self._associate_identifier_with_scheme(string, GraphEntity.iri_wikipedia)
345 @accepts_only('literal')
346 def create_arxiv(self, string: str) -> None:
347 """
348 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
349 ``datacite:usesIdentifierScheme`` RDF predicate.
350 It implicitly sets the object value ``datacite:crossref`` for the
351 ``datacite:usesIdentifierScheme`` RDF predicate.
353 **WARNING: this is a functional property, hence any existing value will be overwritten!**
355 :param string: The value that will be set as the object of the property related to this method
356 :type string: str
357 :raises TypeError: if the parameter is of the wrong type
358 :return: None
359 """
360 self._associate_identifier_with_scheme(string, GraphEntity.iri_arxiv)
362 @accepts_only('literal')
363 def create_crossref(self, string: str) -> None:
364 """
365 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
366 ``datacite:usesIdentifierScheme`` RDF predicate.
367 It implicitly sets the object value ``datacite:crossref`` for the
368 ``datacite:usesIdentifierScheme`` RDF predicate.
370 **WARNING: this is a functional property, hence any existing value will be overwritten!**
372 :param string: The value that will be set as the object of the property related to this method
373 :type string: str
374 :raises TypeError: if the parameter is of the wrong type
375 :return: None
376 """
377 self._associate_identifier_with_scheme(string, GraphEntity.iri_crossref)
379 @accepts_only('literal')
380 def create_datacite(self, string: str) -> None:
381 """
382 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
383 ``datacite:usesIdentifierScheme`` RDF predicate.
384 It implicitly sets the object value ``datacite:datacite`` for the
385 ``datacite:usesIdentifierScheme`` RDF predicate.
387 **WARNING: this is a functional property, hence any existing value will be overwritten!**
389 :param string: The value that will be set as the object of the property related to this method
390 :type string: str
391 :raises TypeError: if the parameter is of the wrong type
392 :return: None
393 """
394 self._associate_identifier_with_scheme(string, GraphEntity.iri_datacite)
396 @accepts_only('literal')
397 def create_viaf(self, string: str) -> None:
398 """
399 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
400 ``datacite:usesIdentifierScheme`` RDF predicate.
401 It implicitly sets the object value ``datacite:viaf`` for the
402 ``datacite:usesIdentifierScheme`` RDF predicate.
404 **WARNING: this is a functional property, hence any existing value will be overwritten!**
406 :param string: The value that will be set as the object of the property related to this method
407 :type string: str
408 :raises TypeError: if the parameter is of the wrong type
409 :return: None
410 """
411 self._associate_identifier_with_scheme(string, GraphEntity.iri_viaf)
413 def _associate_identifier_with_scheme(self, string: str, id_type: URIRef) -> None:
414 if not is_string_empty(string):
415 self.remove_identifier_with_scheme()
416 self._create_literal(GraphEntity.iri_has_literal_value, string)
417 self.g.add((self.res, GraphEntity.iri_uses_identifier_scheme, id_type))
419 def remove_identifier_with_scheme(self) -> None:
420 """
421 Remover method corresponding to both the ``literal:hasLiteralValue`` and the
422 ``datacite:usesIdentifierScheme`` RDF predicate.
424 :return: None
425 """
426 self.g.remove((self.res, GraphEntity.iri_has_literal_value, None))
427 self.g.remove((self.res, GraphEntity.iri_uses_identifier_scheme, None))