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

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 

17 

18import re 

19from typing import TYPE_CHECKING 

20 

21if TYPE_CHECKING: 

22 from typing import Optional 

23 from rdflib import URIRef 

24 

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 

28 

29 

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'.""" 

34 

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!** 

43 

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. 

48 

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) 

56 

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) 

61 

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. 

66 

67 :return: The requested value if found, None otherwise 

68 """ 

69 return self._get_literal(GraphEntity.iri_has_literal_value) 

70 

71 def get_scheme(self) -> Optional[URIRef]: 

72 """ 

73 Getter method corresponding to the ``datacite:usesIdentifierScheme`` RDF predicate. 

74 

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 

79 

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. 

87 

88 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

89 

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) 

96 

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. 

104 

105 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

106 

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) 

113 

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. 

121 

122 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

123 

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) 

130 

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. 

138 

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'). 

141 

142 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

143 

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) 

150 

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. 

158 

159 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

160 

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) 

167 

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. 

175 

176 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

177 

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) 

184 

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. 

192 

193 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

194 

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) 

201 

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. 

209 

210 The string gets internally preprocessed by eventually replacing long dashes with short ones 

211 (e.g. '1522–4501' becomes '1522-4501'). 

212 

213 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

214 

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) 

224 

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. 

232 

233 The string gets internally preprocessed by eventually replacing long dashes with short ones 

234 (e.g. '817525766–0' becomes '817525766-0'). 

235 

236 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

237 

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) 

244 

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. 

252 

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'). 

257 

258 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

259 

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) 

266 

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. 

274 

275 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

276 

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) 

283 

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. 

291 

292 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

293 

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) 

300 

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. 

308 

309 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

310 

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) 

317 

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. 

325 

326 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

327 

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) 

334 

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. 

342 

343 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

344 

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) 

351 

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. 

359 

360 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

361 

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) 

368 

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. 

376 

377 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

378 

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) 

385 

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. 

393 

394 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

395 

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) 

402 

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. 

410 

411 **WARNING: this is a functional property, hence any existing value will be overwritten!** 

412 

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) 

419 

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)) 

425 

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. 

430 

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))