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

128 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-05-08 20:23 +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((self.res, MetadataEntity.iri_keyword, RDFTerm("literal", string, "http://www.w3.org/2001/XMLSchema#string"))) 

261 else: 

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

263 

264 # HAS SUBJECT 

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

266 """ 

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

268 

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

270 """ 

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

272 return uri_list 

273 

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

275 """ 

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

277 

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

279 

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

281 :type thing_res: URIRef 

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

283 :return: None 

284 """ 

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

286 

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

288 """ 

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

290 

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

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

293 

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

295 related to this method (defaults to None) 

296 :type thing_res: URIRef 

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

298 :return: None 

299 """ 

300 if thing_res is not None: 

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

302 else: 

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

304 

305 # HAS LANDING PAGE 

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

307 """ 

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

309 

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

311 """ 

312 return self._get_uri_reference(MetadataEntity.iri_landing_page) 

313 

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

315 """ 

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

317 

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

319 

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

321 

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

323 :type thing_res: URIRef 

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

325 :return: None 

326 """ 

327 self.remove_landing_page() 

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

329 

330 def remove_landing_page(self) -> None: 

331 """ 

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

333 

334 :return: None 

335 """ 

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

337 

338 # HAS SUB-DATASET 

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

340 """ 

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

342 

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

344 """ 

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

346 result: List[Dataset] = [] 

347 for uri in uri_list: 

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

349 return result 

350 

351 @accepts_only('_dataset_') 

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

353 """ 

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

355 

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

357 

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

359 :type obj: Dataset 

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

361 :return: None 

362 """ 

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

364 

365 @accepts_only('_dataset_') 

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

367 """ 

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

369 

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

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

372 

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

374 related to this method (defaults to None) 

375 :type dataset_res: Dataset 

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

377 :return: None 

378 """ 

379 if dataset_res is not None: 

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

381 else: 

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

383 

384 # HAS SPARQL ENDPOINT 

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

386 """ 

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

388 

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

390 """ 

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

392 return uri 

393 

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

395 """ 

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

397 

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

399 

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

401 

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

403 :type thing_res: URIRef 

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

405 :return: None 

406 """ 

407 self.remove_sparql_endpoint() 

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

409 

410 def remove_sparql_endpoint(self) -> None: 

411 """ 

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

413 

414 :return: None 

415 """ 

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

417 

418 # HAS DISTRIBUTION (Distribution) 

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

420 """ 

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

422 

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

424 """ 

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

426 result: List[Distribution] = [] 

427 for uri in uri_list: 

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

429 return result 

430 

431 @accepts_only('di') 

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

433 """ 

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

435 

436 `A distribution of the dataset.` 

437 

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

439 :type obj: Distribution 

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

441 :return: None 

442 """ 

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

444 

445 @accepts_only('di') 

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

447 """ 

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

449 

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

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

452 

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

454 related to this method (defaults to None) 

455 :type di_res: Distribution 

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

457 :return: None 

458 """ 

459 if di_res is not None: 

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

461 else: 

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