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

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 

17from triplelite import RDFTerm 

18 

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

41 

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) 

50 

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) 

56 

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. 

61 

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

63 """ 

64 return self._get_literal(GraphEntity.iri_has_literal_value) 

65 

66 def get_scheme(self) -> Optional[str]: 

67 """ 

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

69 

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 

74 

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. 

81 

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

83 

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) 

90 

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

113 

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

115 

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) 

122 

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. 

129 

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

132 

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

134 

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) 

141 

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. 

148 

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

150 

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) 

157 

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. 

164 

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

166 

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) 

173 

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. 

180 

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

182 

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) 

189 

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. 

196 

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

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

199 

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

201 

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) 

211 

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. 

218 

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

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

221 

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

223 

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) 

230 

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. 

237 

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

242 

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

244 

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) 

251 

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. 

258 

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

260 

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) 

267 

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. 

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

283 

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. 

290 

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

292 

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) 

299 

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. 

306 

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

308 

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) 

315 

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. 

322 

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

324 

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) 

331 

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. 

338 

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

340 

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) 

347 

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. 

354 

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

356 

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) 

363 

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. 

370 

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

372 

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) 

379 

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. 

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

395 

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

401 

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. 

406 

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