Coverage for oc_ocdm / graph / entities / identifier.py: 94%
68 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-07-06 20:05 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-07-06 20:05 +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
17from triplelite import RDFTerm
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 By default, functional values from the current entity get overwritten by values
37 from the second entity. When ``prefer_self`` is True, existing functional values
38 from the current entity are kept. Non-functional values from the second entity
39 are appended to those of the current entity. In this context,
40 ``rdfs:label`` is considered as a functional property, while ``rdf:type`` is not.
42 :param other: The entity which will be marked as to be deleted and whose properties will
43 be merged into the current entity.
44 :type other: Identifier
45 :raises TypeError: if the parameter is of the wrong type
46 :return: None
47 """
48 super()._merge_properties(other, prefer_self)
49 assert isinstance(other, Identifier)
51 literal_value: Optional[str] = other.get_literal_value()
52 scheme: Optional[str] = other.get_scheme()
53 should_merge_identifier = not prefer_self or self.get_literal_value() is None or self.get_scheme() is None
54 if literal_value is not None and scheme is not None and should_merge_identifier:
55 self._associate_identifier_with_scheme(literal_value, scheme)
57 # HAS LITERAL VALUE and HAS SCHEME
58 def get_literal_value(self) -> Optional[str]:
59 """
60 Getter method corresponding to the ``literal:hasLiteralValue`` RDF predicate.
62 :return: The requested value if found, None otherwise
63 """
64 return self._get_literal(GraphEntity.iri_has_literal_value)
66 def get_scheme(self) -> Optional[str]:
67 """
68 Getter method corresponding to the ``datacite:usesIdentifierScheme`` RDF predicate.
70 :return: The requested value if found, None otherwise
71 """
72 uri: Optional[str] = self._get_uri_reference(GraphEntity.iri_uses_identifier_scheme)
73 return uri
75 def create_oci(self, string: str) -> None:
76 """
77 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
78 ``datacite:usesIdentifierScheme`` RDF predicate.
79 It implicitly sets the object value ``datacite:oci`` for the
80 ``datacite:usesIdentifierScheme`` RDF predicate.
82 **WARNING: this is a functional property, hence any existing value will be overwritten!**
84 :param string: The value that will be set as the object of the property related to this method
85 :type string: str
86 :raises TypeError: if the parameter is of the wrong type
87 :return: None
88 """
89 self._associate_identifier_with_scheme(string, GraphEntity.iri_oci)
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 def create_openalex(self, string: str) -> None:
108 """
109 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
110 ``datacite:usesIdentifierScheme`` RDF predicate.
111 It implicitly sets the object value ``datacite:openalex`` for the
112 ``datacite:usesIdentifierScheme`` RDF predicate.
114 **WARNING: this is a functional property, hence any existing value will be overwritten!**
116 :param string: The value that will be set as the object of the property related to this method
117 :type string: str
118 :raises TypeError: if the parameter is of the wrong type
119 :return: None
120 """
121 self._associate_identifier_with_scheme(string, GraphEntity.iri_openalex)
123 def create_doi(self, string: str) -> None:
124 """
125 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
126 ``datacite:usesIdentifierScheme`` RDF predicate.
127 It implicitly sets the object value ``datacite:doi`` for the
128 ``datacite:usesIdentifierScheme`` RDF predicate.
130 The string gets internally preprocessed by converting it to lowercase
131 (e.g. 'DOI:10.1111/HEX.12487' becomes 'doi:10.1111/hex.12487').
133 **WARNING: this is a functional property, hence any existing value will be overwritten!**
135 :param string: The value that will be set as the object of the property related to this method
136 :type string: str
137 :raises TypeError: if the parameter is of the wrong type
138 :return: None
139 """
140 self._associate_identifier_with_scheme(string.lower(), GraphEntity.iri_doi)
142 def create_jid(self, string: str) -> None:
143 """
144 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
145 ``datacite:usesIdentifierScheme`` RDF predicate.
146 It implicitly sets the object value ``datacite:jid`` for the
147 ``datacite:usesIdentifierScheme`` RDF predicate.
149 **WARNING: this is a functional property, hence any existing value will be overwritten!**
151 :param string: The value that will be set as the object of the property related to this method
152 :type string: str
153 :raises TypeError: if the parameter is of the wrong type
154 :return: None
155 """
156 self._associate_identifier_with_scheme(string, GraphEntity.iri_jid)
158 def create_pmid(self, string: str) -> None:
159 """
160 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
161 ``datacite:usesIdentifierScheme`` RDF predicate.
162 It implicitly sets the object value ``datacite:pmid`` for the
163 ``datacite:usesIdentifierScheme`` RDF predicate.
165 **WARNING: this is a functional property, hence any existing value will be overwritten!**
167 :param string: The value that will be set as the object of the property related to this method
168 :type string: str
169 :raises TypeError: if the parameter is of the wrong type
170 :return: None
171 """
172 self._associate_identifier_with_scheme(string, GraphEntity.iri_pmid)
174 def create_pmcid(self, string: str) -> None:
175 """
176 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
177 ``datacite:usesIdentifierScheme`` RDF predicate.
178 It implicitly sets the object value ``datacite:pmcid`` for the
179 ``datacite:usesIdentifierScheme`` RDF predicate.
181 **WARNING: this is a functional property, hence any existing value will be overwritten!**
183 :param string: The value that will be set as the object of the property related to this method
184 :type string: str
185 :raises TypeError: if the parameter is of the wrong type
186 :return: None
187 """
188 self._associate_identifier_with_scheme(string, GraphEntity.iri_pmcid)
190 def create_issn(self, string: str) -> None:
191 """
192 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
193 ``datacite:usesIdentifierScheme`` RDF predicate.
194 It implicitly sets the object value ``datacite:issn`` for the
195 ``datacite:usesIdentifierScheme`` RDF predicate.
197 The string gets internally preprocessed by eventually replacing long dashes with short ones
198 (e.g. '1522–4501' becomes '1522-4501').
200 **WARNING: this is a functional property, hence any existing value will be overwritten!**
202 :param string: The value that will be set as the object of the property related to this method. **It
203 must be a string different from '0000-0000'.**
204 :type string: str
205 :raises TypeError: if the parameter is of the wrong type
206 :return: None
207 """
208 cur_string = re.sub("–", "-", string)
209 if cur_string != "0000-0000":
210 self._associate_identifier_with_scheme(cur_string, GraphEntity.iri_issn)
212 def create_isbn(self, string: str) -> None:
213 """
214 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
215 ``datacite:usesIdentifierScheme`` RDF predicate.
216 It implicitly sets the object value ``datacite:isbn`` for the
217 ``datacite:usesIdentifierScheme`` RDF predicate.
219 The string gets internally preprocessed by eventually replacing long dashes with short ones
220 (e.g. '817525766–0' becomes '817525766-0').
222 **WARNING: this is a functional property, hence any existing value will be overwritten!**
224 :param string: The value that will be set as the object of the property related to this method
225 :type string: str
226 :raises TypeError: if the parameter is of the wrong type
227 :return: None
228 """
229 self._associate_identifier_with_scheme(re.sub("–", "-", string), GraphEntity.iri_isbn)
231 def create_url(self, string: str) -> None:
232 """
233 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
234 ``datacite:usesIdentifierScheme`` RDF predicate.
235 It implicitly sets the object value ``datacite:url`` for the
236 ``datacite:usesIdentifierScheme`` RDF predicate.
238 The string gets internally preprocessed both by converting it to lowercase
239 (e.g. 'https://OPENCITATIONS.NET/' becomes 'https://opencitations.net/') and by
240 applying `URL encoding` on it (e.g. 'https://opencitations.net/file name.txt'
241 becomes 'https://opencitations.net/file%20name.txt').
243 **WARNING: this is a functional property, hence any existing value will be overwritten!**
245 :param string: The value that will be set as the object of the property related to this method
246 :type string: str
247 :raises TypeError: if the parameter is of the wrong type
248 :return: None
249 """
250 self._associate_identifier_with_scheme(encode_url(string.lower()), GraphEntity.iri_url)
252 def create_xpath(self, string: str) -> None:
253 """
254 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
255 ``datacite:usesIdentifierScheme`` RDF predicate.
256 It implicitly sets the object value `datacite:local-resource-identifier-scheme` for the
257 ``datacite:usesIdentifierScheme`` RDF predicate.
259 **WARNING: this is a functional property, hence any existing value will be overwritten!**
261 :param string: The value that will be set as the object of the property related to this method
262 :type string: str
263 :raises TypeError: if the parameter is of the wrong type
264 :return: None
265 """
266 self._associate_identifier_with_scheme(string, GraphEntity.iri_xpath)
268 def create_intrepid(self, string: str) -> None:
269 """
270 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
271 ``datacite:usesIdentifierScheme`` RDF predicate.
272 It implicitly sets the object value ``datacite:intrepid`` for the
273 ``datacite:usesIdentifierScheme`` RDF predicate.
275 **WARNING: this is a functional property, hence any existing value will be overwritten!**
277 :param string: The value that will be set as the object of the property related to this method
278 :type string: str
279 :raises TypeError: if the parameter is of the wrong type
280 :return: None
281 """
282 self._associate_identifier_with_scheme(string, GraphEntity.iri_intrepid)
284 def create_xmlid(self, string: str) -> None:
285 """
286 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
287 ``datacite:usesIdentifierScheme`` RDF predicate.
288 It implicitly sets the object value `datacite:local-resource-identifier-scheme` for the
289 ``datacite:usesIdentifierScheme`` RDF predicate.
291 **WARNING: this is a functional property, hence any existing value will be overwritten!**
293 :param string: The value that will be set as the object of the property related to this method
294 :type string: str
295 :raises TypeError: if the parameter is of the wrong type
296 :return: None
297 """
298 self._associate_identifier_with_scheme(string, GraphEntity.iri_xmlid)
300 def create_wikidata(self, string: str) -> None:
301 """
302 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
303 ``datacite:usesIdentifierScheme`` RDF predicate.
304 It implicitly sets the object value ``datacite:wikidata`` for the
305 ``datacite:usesIdentifierScheme`` RDF predicate.
307 **WARNING: this is a functional property, hence any existing value will be overwritten!**
309 :param string: The value that will be set as the object of the property related to this method
310 :type string: str
311 :raises TypeError: if the parameter is of the wrong type
312 :return: None
313 """
314 self._associate_identifier_with_scheme(string, GraphEntity.iri_wikidata)
316 def create_wikipedia(self, string: str) -> None:
317 """
318 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
319 ``datacite:usesIdentifierScheme`` RDF predicate.
320 It implicitly sets the object value ``datacite:wikipedia`` for the
321 ``datacite:usesIdentifierScheme`` RDF predicate.
323 **WARNING: this is a functional property, hence any existing value will be overwritten!**
325 :param string: The value that will be set as the object of the property related to this method
326 :type string: str
327 :raises TypeError: if the parameter is of the wrong type
328 :return: None
329 """
330 self._associate_identifier_with_scheme(string, GraphEntity.iri_wikipedia)
332 def create_arxiv(self, string: str) -> None:
333 """
334 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
335 ``datacite:usesIdentifierScheme`` RDF predicate.
336 It implicitly sets the object value ``datacite:crossref`` for the
337 ``datacite:usesIdentifierScheme`` RDF predicate.
339 **WARNING: this is a functional property, hence any existing value will be overwritten!**
341 :param string: The value that will be set as the object of the property related to this method
342 :type string: str
343 :raises TypeError: if the parameter is of the wrong type
344 :return: None
345 """
346 self._associate_identifier_with_scheme(string, GraphEntity.iri_arxiv)
348 def create_crossref(self, string: str) -> None:
349 """
350 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
351 ``datacite:usesIdentifierScheme`` RDF predicate.
352 It implicitly sets the object value ``datacite:crossref`` for the
353 ``datacite:usesIdentifierScheme`` RDF predicate.
355 **WARNING: this is a functional property, hence any existing value will be overwritten!**
357 :param string: The value that will be set as the object of the property related to this method
358 :type string: str
359 :raises TypeError: if the parameter is of the wrong type
360 :return: None
361 """
362 self._associate_identifier_with_scheme(string, GraphEntity.iri_crossref)
364 def create_datacite(self, string: str) -> None:
365 """
366 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
367 ``datacite:usesIdentifierScheme`` RDF predicate.
368 It implicitly sets the object value ``datacite:datacite`` for the
369 ``datacite:usesIdentifierScheme`` RDF predicate.
371 **WARNING: this is a functional property, hence any existing value will be overwritten!**
373 :param string: The value that will be set as the object of the property related to this method
374 :type string: str
375 :raises TypeError: if the parameter is of the wrong type
376 :return: None
377 """
378 self._associate_identifier_with_scheme(string, GraphEntity.iri_datacite)
380 def create_viaf(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:viaf`` 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_viaf)
396 def _associate_identifier_with_scheme(self, string: str, id_type: str) -> None:
397 if not is_string_empty(string):
398 self.remove_identifier_with_scheme()
399 self._create_literal(GraphEntity.iri_has_literal_value, string)
400 self.g.add((self.res, GraphEntity.iri_uses_identifier_scheme, RDFTerm("uri", str(id_type))))
402 def remove_identifier_with_scheme(self) -> None:
403 """
404 Remover method corresponding to both the ``literal:hasLiteralValue`` and the
405 ``datacite:usesIdentifierScheme`` RDF predicate.
407 :return: None
408 """
409 self.g.remove((self.res, GraphEntity.iri_has_literal_value, None))
410 self.g.remove((self.res, GraphEntity.iri_uses_identifier_scheme, None))