Coverage for oc_ocdm / metadata / entities / dataset.py: 93%

128 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# 

5# SPDX-License-Identifier: ISC 

6 

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

8from __future__ import annotations 

9 

10from typing import TYPE_CHECKING 

11 

12from triplelite import RDFTerm 

13 

14from oc_ocdm.constants import XSD_DATETIME 

15from oc_ocdm.decorators import accepts_only 

16from oc_ocdm.metadata.metadata_entity import MetadataEntity 

17 

18if TYPE_CHECKING: 

19 from typing import List 

20 

21 from oc_ocdm.metadata.entities.distribution import Distribution 

22 

23 

24class Dataset(MetadataEntity): 

25 """Dataset (short: not applicable and strictly dependent on the implementation of the 

26 dataset infrastructure): a set of collected information about something.""" 

27 

28 def _merge_properties(self, other: MetadataEntity) -> None: 

29 """ 

30 The merge operation allows combining two ``Dataset`` entities into a single one, 

31 by marking the second entity as to be deleted while also copying its data into the current 

32 ``Dataset``. Moreover, every triple from the containing ``MetadataSet`` 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: Dataset 

44 :raises TypeError: if the parameter is of the wrong type 

45 :return: None 

46 """ 

47 super()._merge_properties(other) 

48 assert isinstance(other, Dataset) 

49 

50 title: str | None = other.get_title() 

51 if title is not None: 

52 self.has_title(title) 

53 

54 description: str | None = other.get_description() 

55 if description is not None: 

56 self.has_description(description) 

57 

58 pub_date: str | None = other.get_publication_date() 

59 if pub_date is not None: 

60 self.has_publication_date(pub_date) 

61 

62 mod_date: str | None = other.get_modification_date() 

63 if mod_date is not None: 

64 self.has_modification_date(mod_date) 

65 

66 keywords_list: List[str] = other.get_keywords() 

67 for cur_keyword in keywords_list: 

68 self.has_keyword(cur_keyword) 

69 

70 subjects_list: List[str] = other.get_subjects() 

71 for cur_subject in subjects_list: 

72 self.has_subject(cur_subject) 

73 

74 landing_page: str | None = other.get_landing_page() 

75 if landing_page is not None: 

76 self.has_landing_page(landing_page) 

77 

78 sub_datasets_list: List[Dataset] = other.get_sub_datasets() 

79 for cur_sub_dataset in sub_datasets_list: 

80 self.has_sub_dataset(cur_sub_dataset) 

81 

82 sparql_endpoint: str | None = other.get_sparql_endpoint() 

83 if sparql_endpoint is not None: 

84 self.has_sparql_endpoint(sparql_endpoint) 

85 

86 distributions_list: List[Distribution] = other.get_distributions() 

87 for cur_distribution in distributions_list: 

88 self.has_distribution(cur_distribution) 

89 

90 # HAS TITLE 

91 def get_title(self) -> str | None: 

92 """ 

93 Getter method corresponding to the ``dcterms:title`` RDF predicate. 

94 

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

96 """ 

97 return self._get_literal(MetadataEntity.iri_title) 

98 

99 def has_title(self, string: str) -> None: 

100 """ 

101 Setter method corresponding to the ``dcterms:title`` RDF predicate. 

102 

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

104 

105 `The title of the dataset.` 

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.remove_title() 

113 self._create_literal(MetadataEntity.iri_title, string) 

114 

115 def remove_title(self) -> None: 

116 """ 

117 Remover method corresponding to the ``dcterms:title`` RDF predicate. 

118 

119 :return: None 

120 """ 

121 self.g.remove((self.res, MetadataEntity.iri_title, None)) 

122 

123 # HAS DESCRIPTION 

124 def get_description(self) -> str | None: 

125 """ 

126 Getter method corresponding to the ``dcterms:description`` RDF predicate. 

127 

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

129 """ 

130 return self._get_literal(MetadataEntity.iri_description) 

131 

132 def has_description(self, string: str) -> None: 

133 """ 

134 Setter method corresponding to the ``dcterms:description`` RDF predicate. 

135 

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

137 

138 `A short textual description of the content of the dataset.` 

139 

140 :param string: The value that will be set as the object of the property related to this method 

141 :type string: str 

142 :raises TypeError: if the parameter is of the wrong type 

143 :return: None 

144 """ 

145 self.remove_description() 

146 self._create_literal(MetadataEntity.iri_description, string) 

147 

148 def remove_description(self) -> None: 

149 """ 

150 Remover method corresponding to the ``dcterms:description`` RDF predicate. 

151 

152 :return: None 

153 """ 

154 self.g.remove((self.res, MetadataEntity.iri_description, None)) 

155 

156 # HAS PUBLICATION DATE 

157 def get_publication_date(self) -> str | None: 

158 """ 

159 Getter method corresponding to the ``dcterms:issued`` RDF predicate. 

160 

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

162 """ 

163 return self._get_literal(MetadataEntity.iri_issued) 

164 

165 def has_publication_date(self, string: str) -> None: 

166 """ 

167 Setter method corresponding to the ``dcterms:issued`` RDF predicate. 

168 

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

170 

171 `The date of first publication of the dataset.` 

172 

173 :param string: The value that will be set as the object of the property related to this method. **It must 

174 be a string compliant with the** ``xsd:dateTime`` **datatype.** 

175 :type string: str 

176 :raises TypeError: if the parameter is of the wrong type 

177 :return: None 

178 """ 

179 self.remove_publication_date() 

180 self._create_literal(MetadataEntity.iri_issued, string, XSD_DATETIME, False) 

181 

182 def remove_publication_date(self) -> None: 

183 """ 

184 Remover method corresponding to the ``dcterms:issued`` RDF predicate. 

185 

186 :return: None 

187 """ 

188 self.g.remove((self.res, MetadataEntity.iri_issued, None)) 

189 

190 # HAS MODIFICATION DATE 

191 def get_modification_date(self) -> str | None: 

192 """ 

193 Getter method corresponding to the ``dcterms:modified`` RDF predicate. 

194 

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

196 """ 

197 return self._get_literal(MetadataEntity.iri_modified) 

198 

199 def has_modification_date(self, string: str) -> None: 

200 """ 

201 Setter method corresponding to the ``dcterms:modified`` RDF predicate. 

202 

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

204 

205 `The date on which the dataset has been modified.` 

206 

207 :param string: The value that will be set as the object of the property related to this method. **It must 

208 be a string compliant with the** ``xsd:dateTime`` **datatype.** 

209 :type string: str 

210 :raises TypeError: if the parameter is of the wrong type 

211 :return: None 

212 """ 

213 self.remove_modification_date() 

214 self._create_literal(MetadataEntity.iri_modified, string, XSD_DATETIME, False) 

215 

216 def remove_modification_date(self) -> None: 

217 """ 

218 Remover method corresponding to the ``dcterms:modified`` RDF predicate. 

219 

220 :return: None 

221 """ 

222 self.g.remove((self.res, MetadataEntity.iri_modified, None)) 

223 

224 # HAS KEYWORD 

225 def get_keywords(self) -> List[str]: 

226 """ 

227 Getter method corresponding to the ``dcat:keyword`` RDF predicate. 

228 

229 :return: A list containing the requested values if found, None otherwise 

230 """ 

231 return self._get_multiple_literals(MetadataEntity.iri_keyword) 

232 

233 def has_keyword(self, string: str) -> None: 

234 """ 

235 Setter method corresponding to the ``dcat:keyword`` RDF predicate. 

236 

237 `A keyword or phrase describing the content of the dataset.` 

238 

239 :param string: The value that will be set as the object of the property related to this method 

240 :type string: str 

241 :raises TypeError: if the parameter is of the wrong type 

242 :return: None 

243 """ 

244 self._create_literal(MetadataEntity.iri_keyword, string) 

245 

246 def remove_keyword(self, string: str | None = None) -> None: 

247 """ 

248 Remover method corresponding to the ``dcat:keyword`` RDF predicate. 

249 

250 **WARNING: this is a non-functional property, hence, if the parameter 

251 is None, any existing value will be removed!** 

252 

253 :param string: If not None, the specific object value that will be removed from the property 

254 related to this method (defaults to None) 

255 :type string: str 

256 :raises TypeError: if the parameter is of the wrong type 

257 :return: None 

258 """ 

259 if string is not None: 

260 self.g.remove( 

261 ( 

262 self.res, 

263 MetadataEntity.iri_keyword, 

264 RDFTerm("literal", string, "http://www.w3.org/2001/XMLSchema#string"), 

265 ) 

266 ) 

267 else: 

268 self.g.remove((self.res, MetadataEntity.iri_keyword, None)) 

269 

270 # HAS SUBJECT 

271 def get_subjects(self) -> List[str]: 

272 """ 

273 Getter method corresponding to the ``dcat:theme`` RDF predicate. 

274 

275 :return: A list containing the requested values if found, None otherwise 

276 """ 

277 uri_list: List[str] = self._get_multiple_uri_references(MetadataEntity.iri_subject) 

278 return uri_list 

279 

280 def has_subject(self, thing_res: str) -> None: 

281 """ 

282 Setter method corresponding to the ``dcat:theme`` RDF predicate. 

283 

284 `A concept describing the primary subject of the dataset.` 

285 

286 :param thing_res: The value that will be set as the object of the property related to this method 

287 :type thing_res: URIRef 

288 :raises TypeError: if the parameter is of the wrong type 

289 :return: None 

290 """ 

291 self.g.add((self.res, MetadataEntity.iri_subject, RDFTerm("uri", str(thing_res)))) 

292 

293 def remove_subject(self, thing_res: str | None = None) -> None: 

294 """ 

295 Remover method corresponding to the ``dcat:theme`` RDF predicate. 

296 

297 **WARNING: this is a non-functional property, hence, if the parameter 

298 is None, any existing value will be removed!** 

299 

300 :param thing_res: If not None, the specific object value that will be removed from the property 

301 related to this method (defaults to None) 

302 :type thing_res: URIRef 

303 :raises TypeError: if the parameter is of the wrong type 

304 :return: None 

305 """ 

306 if thing_res is not None: 

307 self.g.remove((self.res, MetadataEntity.iri_subject, RDFTerm("uri", str(thing_res)))) 

308 else: 

309 self.g.remove((self.res, MetadataEntity.iri_subject, None)) 

310 

311 # HAS LANDING PAGE 

312 def get_landing_page(self) -> str | None: 

313 """ 

314 Getter method corresponding to the ``dcat:landingPage`` RDF predicate. 

315 

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

317 """ 

318 return self._get_uri_reference(MetadataEntity.iri_landing_page) 

319 

320 def has_landing_page(self, thing_res: str) -> None: 

321 """ 

322 Setter method corresponding to the ``dcat:landingPage`` RDF predicate. 

323 

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

325 

326 `An HTML page (indicated by its URL) representing a browsable page for the dataset.` 

327 

328 :param thing_res: The value that will be set as the object of the property related to this method 

329 :type thing_res: URIRef 

330 :raises TypeError: if the parameter is of the wrong type 

331 :return: None 

332 """ 

333 self.remove_landing_page() 

334 self.g.add((self.res, MetadataEntity.iri_landing_page, RDFTerm("uri", str(thing_res)))) 

335 

336 def remove_landing_page(self) -> None: 

337 """ 

338 Remover method corresponding to the ``dcat:landingPage`` RDF predicate. 

339 

340 :return: None 

341 """ 

342 self.g.remove((self.res, MetadataEntity.iri_landing_page, None)) 

343 

344 # HAS SUB-DATASET 

345 def get_sub_datasets(self) -> List[Dataset]: 

346 """ 

347 Getter method corresponding to the ``void:subset`` RDF predicate. 

348 

349 :return: A list containing the requested values if found, None otherwise 

350 """ 

351 uri_list: List[str] = self._get_multiple_uri_references(MetadataEntity.iri_subset, "_dataset_") 

352 result: List[Dataset] = [] 

353 for uri in uri_list: 

354 result.append(self.m_set.add_dataset(self.dataset_name, self.resp_agent or "", self.source, uri)) 

355 return result 

356 

357 @accepts_only("_dataset_") 

358 def has_sub_dataset(self, obj: Dataset) -> None: 

359 """ 

360 Setter method corresponding to the ``void:subset`` RDF predicate. 

361 

362 `A link to a subset of the present dataset.` 

363 

364 :param obj: The value that will be set as the object of the property related to this method 

365 :type obj: Dataset 

366 :raises TypeError: if the parameter is of the wrong type 

367 :return: None 

368 """ 

369 self.g.add((self.res, MetadataEntity.iri_subset, RDFTerm("uri", str(obj.res)))) 

370 

371 @accepts_only("_dataset_") 

372 def remove_sub_dataset(self, dataset_res: Dataset | None = None) -> None: 

373 """ 

374 Remover method corresponding to the ``void:subset`` RDF predicate. 

375 

376 **WARNING: this is a non-functional property, hence, if the parameter 

377 is None, any existing value will be removed!** 

378 

379 :param dataset_res: If not None, the specific object value that will be removed from the property 

380 related to this method (defaults to None) 

381 :type dataset_res: Dataset 

382 :raises TypeError: if the parameter is of the wrong type 

383 :return: None 

384 """ 

385 if dataset_res is not None: 

386 self.g.remove((self.res, MetadataEntity.iri_subset, RDFTerm("uri", str(dataset_res.res)))) 

387 else: 

388 self.g.remove((self.res, MetadataEntity.iri_subset, None)) 

389 

390 # HAS SPARQL ENDPOINT 

391 def get_sparql_endpoint(self) -> str | None: 

392 """ 

393 Getter method corresponding to the ``void:sparqlEndpoint`` RDF predicate. 

394 

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

396 """ 

397 uri: str | None = self._get_uri_reference(MetadataEntity.iri_sparql_endpoint) 

398 return uri 

399 

400 def has_sparql_endpoint(self, thing_res: str) -> None: 

401 """ 

402 Setter method corresponding to the ``void:sparqlEndpoint`` RDF predicate. 

403 

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

405 

406 `The link to the SPARQL endpoint for querying the dataset.` 

407 

408 :param thing_res: The value that will be set as the object of the property related to this method 

409 :type thing_res: URIRef 

410 :raises TypeError: if the parameter is of the wrong type 

411 :return: None 

412 """ 

413 self.remove_sparql_endpoint() 

414 self.g.add((self.res, MetadataEntity.iri_sparql_endpoint, RDFTerm("uri", str(thing_res)))) 

415 

416 def remove_sparql_endpoint(self) -> None: 

417 """ 

418 Remover method corresponding to the ``void:sparqlEndpoint`` RDF predicate. 

419 

420 :return: None 

421 """ 

422 self.g.remove((self.res, MetadataEntity.iri_sparql_endpoint, None)) 

423 

424 # HAS DISTRIBUTION (Distribution) 

425 def get_distributions(self) -> List[Distribution]: 

426 """ 

427 Getter method corresponding to the ``dcat:distribution`` RDF predicate. 

428 

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

430 """ 

431 uri_list: List[str] = self._get_multiple_uri_references(MetadataEntity.iri_distribution, "di") 

432 result: List[Distribution] = [] 

433 for uri in uri_list: 

434 result.append(self.m_set.add_di(self.dataset_name, self.resp_agent or "", self.source, uri)) 

435 return result 

436 

437 @accepts_only("di") 

438 def has_distribution(self, obj: Distribution) -> None: 

439 """ 

440 Setter method corresponding to the ``dcat:distribution`` RDF predicate. 

441 

442 `A distribution of the dataset.` 

443 

444 :param obj: The value that will be set as the object of the property related to this method 

445 :type obj: Distribution 

446 :raises TypeError: if the parameter is of the wrong type 

447 :return: None 

448 """ 

449 self.g.add((self.res, MetadataEntity.iri_distribution, RDFTerm("uri", str(obj.res)))) 

450 

451 @accepts_only("di") 

452 def remove_distribution(self, di_res: Distribution | None = None) -> None: 

453 """ 

454 Remover method corresponding to the ``dcat:distribution`` RDF predicate. 

455 

456 **WARNING: this is a non-functional property, hence, if the parameter 

457 is None, any existing value will be removed!** 

458 

459 :param di_res: If not None, the specific object value that will be removed from the property 

460 related to this method (defaults to None) 

461 :type di_res: Distribution 

462 :raises TypeError: if the parameter is of the wrong type 

463 :return: None 

464 """ 

465 if di_res is not None: 

466 self.g.remove((self.res, MetadataEntity.iri_distribution, RDFTerm("uri", str(di_res.res)))) 

467 else: 

468 self.g.remove((self.res, MetadataEntity.iri_distribution, None))