Coverage for oc_ocdm/graph/entities/identifier.py: 93%
89 statements
« prev ^ index » next coverage.py v6.5.0, created at 2025-05-30 22:05 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2025-05-30 22:05 +0000
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3# Copyright (c) 2016, Silvio Peroni <essepuntato@gmail.com>
4#
5# Permission to use, copy, modify, and/or distribute this software for any purpose
6# with or without fee is hereby granted, provided that the above copyright notice
7# and this permission notice appear in all copies.
8#
9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
11# FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
12# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
13# DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
14# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15# SOFTWARE.
16from __future__ import annotations
18import re
19from typing import TYPE_CHECKING
21if TYPE_CHECKING:
22 from typing import Optional
23 from rdflib import URIRef
25from oc_ocdm.decorators import accepts_only
26from oc_ocdm.graph.graph_entity import GraphEntity
27from oc_ocdm.support.support import encode_url, is_string_empty
30class Identifier(GraphEntity):
31 """Identifier (short: id): an external identifier (e.g. DOI, ORCID, PubMedID, Open
32 Citation Identifier) associated with the bibliographic entity. Members of this class of
33 metadata are themselves given unique corpus identifiers e.g. 'id/0420129'."""
35 @accepts_only('id')
36 def merge(self, other: Identifier):
37 """
38 The merge operation allows combining two ``Identifier`` entities into a single one,
39 by marking the second entity as to be deleted while also copying its data into the current
40 ``Identifier``. Moreover, every triple from the containing ``GraphSet`` referring to the second
41 entity gets "redirected" to the current entity: **every other reference contained inside a
42 different source (e.g. a triplestore) must be manually handled by the user!**
44 In case of functional properties, values from the current entity get overwritten
45 by those coming from the second entity while, in all other cases, values from the
46 second entity are simply appended to those of the current entity. In this context,
47 ``rdfs:label`` is considered as a functional property, while ``rdf:type`` is not.
49 :param other: The entity which will be marked as to be deleted and whose properties will
50 be merged into the current entity.
51 :type other: Identifier
52 :raises TypeError: if the parameter is of the wrong type
53 :return: None
54 """
55 super(Identifier, self).merge(other)
57 literal_value: Optional[str] = other.get_literal_value()
58 scheme: Optional[URIRef] = other.get_scheme()
59 if literal_value is not None and scheme is not None:
60 self._associate_identifier_with_scheme(literal_value, scheme)
62 # HAS LITERAL VALUE and HAS SCHEME
63 def get_literal_value(self) -> Optional[str]:
64 """
65 Getter method corresponding to the ``literal:hasLiteralValue`` RDF predicate.
67 :return: The requested value if found, None otherwise
68 """
69 return self._get_literal(GraphEntity.iri_has_literal_value)
71 def get_scheme(self) -> Optional[URIRef]:
72 """
73 Getter method corresponding to the ``datacite:usesIdentifierScheme`` RDF predicate.
75 :return: The requested value if found, None otherwise
76 """
77 uri: Optional[URIRef] = self._get_uri_reference(GraphEntity.iri_uses_identifier_scheme)
78 return uri
80 @accepts_only('literal')
81 def create_oci(self, string: str) -> None:
82 """
83 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
84 ``datacite:usesIdentifierScheme`` RDF predicate.
85 It implicitly sets the object value ``datacite:oci`` for the
86 ``datacite:usesIdentifierScheme`` RDF predicate.
88 **WARNING: this is a functional property, hence any existing value will be overwritten!**
90 :param string: The value that will be set as the object of the property related to this method
91 :type string: str
92 :raises TypeError: if the parameter is of the wrong type
93 :return: None
94 """
95 self._associate_identifier_with_scheme(string, GraphEntity.iri_oci)
97 @accepts_only('literal')
98 def create_orcid(self, string: str) -> None:
99 """
100 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
101 ``datacite:usesIdentifierScheme`` RDF predicate.
102 It implicitly sets the object value ``datacite:orcid`` for the
103 ``datacite:usesIdentifierScheme`` RDF predicate.
105 **WARNING: this is a functional property, hence any existing value will be overwritten!**
107 :param string: The value that will be set as the object of the property related to this method
108 :type string: str
109 :raises TypeError: if the parameter is of the wrong type
110 :return: None
111 """
112 self._associate_identifier_with_scheme(string, GraphEntity.iri_orcid)
114 @accepts_only('literal')
115 def create_openalex(self, string: str) -> None:
116 """
117 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
118 ``datacite:usesIdentifierScheme`` RDF predicate.
119 It implicitly sets the object value ``datacite:openalex`` for the
120 ``datacite:usesIdentifierScheme`` RDF predicate.
122 **WARNING: this is a functional property, hence any existing value will be overwritten!**
124 :param string: The value that will be set as the object of the property related to this method
125 :type string: str
126 :raises TypeError: if the parameter is of the wrong type
127 :return: None
128 """
129 self._associate_identifier_with_scheme(string, GraphEntity.iri_openalex)
131 @accepts_only('literal')
132 def create_doi(self, string: str) -> None:
133 """
134 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
135 ``datacite:usesIdentifierScheme`` RDF predicate.
136 It implicitly sets the object value ``datacite:doi`` for the
137 ``datacite:usesIdentifierScheme`` RDF predicate.
139 The string gets internally preprocessed by converting it to lowercase
140 (e.g. 'DOI:10.1111/HEX.12487' becomes 'doi:10.1111/hex.12487').
142 **WARNING: this is a functional property, hence any existing value will be overwritten!**
144 :param string: The value that will be set as the object of the property related to this method
145 :type string: str
146 :raises TypeError: if the parameter is of the wrong type
147 :return: None
148 """
149 self._associate_identifier_with_scheme(string.lower(), GraphEntity.iri_doi)
151 @accepts_only('literal')
152 def create_jid(self, string: str) -> None:
153 """
154 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
155 ``datacite:usesIdentifierScheme`` RDF predicate.
156 It implicitly sets the object value ``datacite:jid`` for the
157 ``datacite:usesIdentifierScheme`` RDF predicate.
159 **WARNING: this is a functional property, hence any existing value will be overwritten!**
161 :param string: The value that will be set as the object of the property related to this method
162 :type string: str
163 :raises TypeError: if the parameter is of the wrong type
164 :return: None
165 """
166 self._associate_identifier_with_scheme(string, GraphEntity.iri_jid)
168 @accepts_only('literal')
169 def create_pmid(self, string: str) -> None:
170 """
171 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
172 ``datacite:usesIdentifierScheme`` RDF predicate.
173 It implicitly sets the object value ``datacite:pmid`` for the
174 ``datacite:usesIdentifierScheme`` RDF predicate.
176 **WARNING: this is a functional property, hence any existing value will be overwritten!**
178 :param string: The value that will be set as the object of the property related to this method
179 :type string: str
180 :raises TypeError: if the parameter is of the wrong type
181 :return: None
182 """
183 self._associate_identifier_with_scheme(string, GraphEntity.iri_pmid)
185 @accepts_only('literal')
186 def create_pmcid(self, string: str) -> None:
187 """
188 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
189 ``datacite:usesIdentifierScheme`` RDF predicate.
190 It implicitly sets the object value ``datacite:pmcid`` for the
191 ``datacite:usesIdentifierScheme`` RDF predicate.
193 **WARNING: this is a functional property, hence any existing value will be overwritten!**
195 :param string: The value that will be set as the object of the property related to this method
196 :type string: str
197 :raises TypeError: if the parameter is of the wrong type
198 :return: None
199 """
200 self._associate_identifier_with_scheme(string, GraphEntity.iri_pmcid)
202 @accepts_only('literal')
203 def create_issn(self, string: str) -> None:
204 """
205 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
206 ``datacite:usesIdentifierScheme`` RDF predicate.
207 It implicitly sets the object value ``datacite:issn`` for the
208 ``datacite:usesIdentifierScheme`` RDF predicate.
210 The string gets internally preprocessed by eventually replacing long dashes with short ones
211 (e.g. '1522–4501' becomes '1522-4501').
213 **WARNING: this is a functional property, hence any existing value will be overwritten!**
215 :param string: The value that will be set as the object of the property related to this method. **It
216 must be a string different from '0000-0000'.**
217 :type string: str
218 :raises TypeError: if the parameter is of the wrong type
219 :return: None
220 """
221 cur_string = re.sub("–", "-", string)
222 if cur_string != "0000-0000":
223 self._associate_identifier_with_scheme(string, GraphEntity.iri_issn)
225 @accepts_only('literal')
226 def create_isbn(self, string: str) -> None:
227 """
228 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
229 ``datacite:usesIdentifierScheme`` RDF predicate.
230 It implicitly sets the object value ``datacite:isbn`` for the
231 ``datacite:usesIdentifierScheme`` RDF predicate.
233 The string gets internally preprocessed by eventually replacing long dashes with short ones
234 (e.g. '817525766–0' becomes '817525766-0').
236 **WARNING: this is a functional property, hence any existing value will be overwritten!**
238 :param string: The value that will be set as the object of the property related to this method
239 :type string: str
240 :raises TypeError: if the parameter is of the wrong type
241 :return: None
242 """
243 self._associate_identifier_with_scheme(re.sub("–", "-", string), GraphEntity.iri_isbn)
245 @accepts_only('literal')
246 def create_url(self, string: str) -> None:
247 """
248 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
249 ``datacite:usesIdentifierScheme`` RDF predicate.
250 It implicitly sets the object value ``datacite:url`` for the
251 ``datacite:usesIdentifierScheme`` RDF predicate.
253 The string gets internally preprocessed both by converting it to lowercase
254 (e.g. 'https://OPENCITATIONS.NET/' becomes 'https://opencitations.net/') and by
255 applying `URL encoding` on it (e.g. 'https://opencitations.net/file name.txt'
256 becomes 'https://opencitations.net/file%20name.txt').
258 **WARNING: this is a functional property, hence any existing value will be overwritten!**
260 :param string: The value that will be set as the object of the property related to this method
261 :type string: str
262 :raises TypeError: if the parameter is of the wrong type
263 :return: None
264 """
265 self._associate_identifier_with_scheme(encode_url(string.lower()), GraphEntity.iri_url)
267 @accepts_only('literal')
268 def create_xpath(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:local-resource-identifier-scheme` 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_xpath)
284 @accepts_only('literal')
285 def create_intrepid(self, string: str) -> None:
286 """
287 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
288 ``datacite:usesIdentifierScheme`` RDF predicate.
289 It implicitly sets the object value ``datacite:intrepid`` for the
290 ``datacite:usesIdentifierScheme`` RDF predicate.
292 **WARNING: this is a functional property, hence any existing value will be overwritten!**
294 :param string: The value that will be set as the object of the property related to this method
295 :type string: str
296 :raises TypeError: if the parameter is of the wrong type
297 :return: None
298 """
299 self._associate_identifier_with_scheme(string, GraphEntity.iri_intrepid)
301 @accepts_only('literal')
302 def create_xmlid(self, string: str) -> None:
303 """
304 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
305 ``datacite:usesIdentifierScheme`` RDF predicate.
306 It implicitly sets the object value `datacite:local-resource-identifier-scheme` for the
307 ``datacite:usesIdentifierScheme`` RDF predicate.
309 **WARNING: this is a functional property, hence any existing value will be overwritten!**
311 :param string: The value that will be set as the object of the property related to this method
312 :type string: str
313 :raises TypeError: if the parameter is of the wrong type
314 :return: None
315 """
316 self._associate_identifier_with_scheme(string, GraphEntity.iri_xmlid)
318 @accepts_only('literal')
319 def create_wikidata(self, string: str) -> None:
320 """
321 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
322 ``datacite:usesIdentifierScheme`` RDF predicate.
323 It implicitly sets the object value ``datacite:wikidata`` for the
324 ``datacite:usesIdentifierScheme`` RDF predicate.
326 **WARNING: this is a functional property, hence any existing value will be overwritten!**
328 :param string: The value that will be set as the object of the property related to this method
329 :type string: str
330 :raises TypeError: if the parameter is of the wrong type
331 :return: None
332 """
333 self._associate_identifier_with_scheme(string, GraphEntity.iri_wikidata)
335 @accepts_only('literal')
336 def create_wikipedia(self, string: str) -> None:
337 """
338 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
339 ``datacite:usesIdentifierScheme`` RDF predicate.
340 It implicitly sets the object value ``datacite:wikipedia`` for the
341 ``datacite:usesIdentifierScheme`` RDF predicate.
343 **WARNING: this is a functional property, hence any existing value will be overwritten!**
345 :param string: The value that will be set as the object of the property related to this method
346 :type string: str
347 :raises TypeError: if the parameter is of the wrong type
348 :return: None
349 """
350 self._associate_identifier_with_scheme(string, GraphEntity.iri_wikipedia)
352 @accepts_only('literal')
353 def create_arxiv(self, string: str) -> None:
354 """
355 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
356 ``datacite:usesIdentifierScheme`` RDF predicate.
357 It implicitly sets the object value ``datacite:crossref`` for the
358 ``datacite:usesIdentifierScheme`` RDF predicate.
360 **WARNING: this is a functional property, hence any existing value will be overwritten!**
362 :param string: The value that will be set as the object of the property related to this method
363 :type string: str
364 :raises TypeError: if the parameter is of the wrong type
365 :return: None
366 """
367 self._associate_identifier_with_scheme(string, GraphEntity.iri_arxiv)
369 @accepts_only('literal')
370 def create_crossref(self, string: str) -> None:
371 """
372 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
373 ``datacite:usesIdentifierScheme`` RDF predicate.
374 It implicitly sets the object value ``datacite:crossref`` for the
375 ``datacite:usesIdentifierScheme`` RDF predicate.
377 **WARNING: this is a functional property, hence any existing value will be overwritten!**
379 :param string: The value that will be set as the object of the property related to this method
380 :type string: str
381 :raises TypeError: if the parameter is of the wrong type
382 :return: None
383 """
384 self._associate_identifier_with_scheme(string, GraphEntity.iri_crossref)
386 @accepts_only('literal')
387 def create_datacite(self, string: str) -> None:
388 """
389 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
390 ``datacite:usesIdentifierScheme`` RDF predicate.
391 It implicitly sets the object value ``datacite:datacite`` for the
392 ``datacite:usesIdentifierScheme`` RDF predicate.
394 **WARNING: this is a functional property, hence any existing value will be overwritten!**
396 :param string: The value that will be set as the object of the property related to this method
397 :type string: str
398 :raises TypeError: if the parameter is of the wrong type
399 :return: None
400 """
401 self._associate_identifier_with_scheme(string, GraphEntity.iri_datacite)
403 @accepts_only('literal')
404 def create_viaf(self, string: str) -> None:
405 """
406 Setter method corresponding to both the ``literal:hasLiteralValue`` and the
407 ``datacite:usesIdentifierScheme`` RDF predicate.
408 It implicitly sets the object value ``datacite:viaf`` for the
409 ``datacite:usesIdentifierScheme`` RDF predicate.
411 **WARNING: this is a functional property, hence any existing value will be overwritten!**
413 :param string: The value that will be set as the object of the property related to this method
414 :type string: str
415 :raises TypeError: if the parameter is of the wrong type
416 :return: None
417 """
418 self._associate_identifier_with_scheme(string, GraphEntity.iri_viaf)
420 def _associate_identifier_with_scheme(self, string: str, id_type: URIRef) -> None:
421 if not is_string_empty(string):
422 self.remove_identifier_with_scheme()
423 self._create_literal(GraphEntity.iri_has_literal_value, string)
424 self.g.add((self.res, GraphEntity.iri_uses_identifier_scheme, id_type))
426 def remove_identifier_with_scheme(self) -> None:
427 """
428 Remover method corresponding to both the ``literal:hasLiteralValue`` and the
429 ``datacite:usesIdentifierScheme`` RDF predicate.
431 :return: None
432 """
433 self.g.remove((self.res, GraphEntity.iri_has_literal_value, None))
434 self.g.remove((self.res, GraphEntity.iri_uses_identifier_scheme, None))