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

1#!/usr/bin/python 

2 

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 

7 

8# -*- coding: utf-8 -*- 

9from __future__ import annotations 

10 

11import re 

12from typing import TYPE_CHECKING 

13 

14if TYPE_CHECKING: 

15 from typing import Optional 

16 from rdflib import URIRef 

17 

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 

21 

22 

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

27 

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

35 

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. 

40 

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) 

49 

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) 

54 

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. 

59 

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

61 """ 

62 return self._get_literal(GraphEntity.iri_has_literal_value) 

63 

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

65 """ 

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

67 

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 

72 

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. 

80 

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

82 

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) 

89 

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. 

97 

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

99 

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) 

106 

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. 

114 

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

116 

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) 

123 

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. 

131 

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

134 

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

136 

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) 

143 

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. 

151 

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

153 

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) 

160 

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. 

168 

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

170 

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) 

177 

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. 

185 

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

187 

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) 

194 

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. 

202 

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

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

205 

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

207 

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) 

217 

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. 

225 

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

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

228 

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

230 

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) 

237 

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. 

245 

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

250 

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

252 

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) 

259 

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. 

267 

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

269 

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) 

276 

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. 

284 

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

286 

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) 

293 

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. 

301 

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

303 

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) 

310 

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. 

318 

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

320 

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) 

327 

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. 

335 

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

337 

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) 

344 

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. 

352 

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

354 

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) 

361 

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. 

369 

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

371 

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) 

378 

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. 

386 

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

388 

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) 

395 

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. 

403 

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

405 

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) 

412 

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

418 

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. 

423 

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