Coverage for oc_ocdm/graph/entities/bibliographic/discourse_element.py: 54%

147 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 

18from typing import TYPE_CHECKING 

19 

20from oc_ocdm.decorators import accepts_only 

21 

22if TYPE_CHECKING: 

23 from typing import Optional, List 

24 from rdflib import URIRef 

25 from oc_ocdm.graph.entities.bibliographic.reference_pointer import ReferencePointer 

26 from oc_ocdm.graph.entities.bibliographic.pointer_list import PointerList 

27from oc_ocdm.graph.graph_entity import GraphEntity 

28from oc_ocdm.graph.entities.bibliographic_entity import BibliographicEntity 

29from oc_ocdm.support.support import create_type 

30 

31 

32class DiscourseElement(BibliographicEntity): 

33 """Discourse element (short: de): a document component, either structural (e.g. 

34 paragraph, section, chapter, table, caption, footnote, title) or rhetorical (e.g. 

35 introduction, discussion, acknowledgements, reference list, figure, appendix), in which 

36 the content of a bibliographic resource can be organized.""" 

37 

38 @accepts_only('de') 

39 def merge(self, other: DiscourseElement) -> None: 

40 """ 

41 The merge operation allows combining two ``DiscourseElement`` entities into a single one, 

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

43 ``DiscourseElement``. Moreover, every triple from the containing ``GraphSet`` referring to the second 

44 entity gets "redirected" to the current entity: **every other reference contained inside a 

45 different source (e.g. a triplestore) must be manually handled by the user!** 

46 

47 In case of functional properties, values from the current entity get overwritten 

48 by those coming from the second entity while, in all other cases, values from the 

49 second entity are simply appended to those of the current entity. In this context, 

50 ``rdfs:label`` is considered as a functional property, while ``rdf:type`` is not. 

51 

52 :param other: The entity which will be marked as to be deleted and whose properties will 

53 be merged into the current entity. 

54 :type other: DiscourseElement 

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

56 :return: None 

57 """ 

58 super(DiscourseElement, self).merge(other) 

59 

60 title: Optional[str] = other.get_title() 

61 if title is not None: 

62 self.has_title(title) 

63 

64 de_list: List[DiscourseElement] = other.get_contained_discourse_elements() 

65 for cur_de in de_list: 

66 self.contains_discourse_element(cur_de) 

67 

68 next_de: Optional[DiscourseElement] = other.get_next_de() 

69 if next_de is not None: 

70 self.has_next_de(next_de) 

71 

72 rp_list: List[ReferencePointer] = other.get_is_context_of_rp() 

73 for cur_rp in rp_list: 

74 self.is_context_of_rp(cur_rp) 

75 

76 pl_list: List[PointerList] = other.get_is_context_of_pl() 

77 for cur_pl in pl_list: 

78 self.is_context_of_pl(cur_pl) 

79 

80 content: Optional[str] = other.get_content() 

81 if content is not None: 

82 self.has_content(content) 

83 

84 number: Optional[str] = other.get_number() 

85 if number is not None: 

86 self.has_number(number) 

87 

88 # HAS TITLE 

89 def get_title(self) -> Optional[str]: 

90 """ 

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

92 

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

94 """ 

95 return self._get_literal(GraphEntity.iri_title) 

96 

97 @accepts_only('literal') 

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

99 """ 

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

101 

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

103 

104 `The title of the discourse element, such as the title of a figure or a section in an article.` 

105 

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

107 :type string: str 

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

109 :return: None 

110 """ 

111 self.remove_title() 

112 self._create_literal(GraphEntity.iri_title, string) 

113 

114 def remove_title(self) -> None: 

115 """ 

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

117 

118 :return: None 

119 """ 

120 self.g.remove((self.res, GraphEntity.iri_title, None)) 

121 

122 # HAS PART (DiscourseElement) 

123 def get_contained_discourse_elements(self) -> List[DiscourseElement]: 

124 """ 

125 Getter method corresponding to the ``frbr:part`` RDF predicate. 

126 

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

128 """ 

129 uri_list: List[URIRef] = self._get_multiple_uri_references(GraphEntity.iri_contains_de, 'de') 

130 result: List[DiscourseElement] = [] 

131 for uri in uri_list: 

132 result.append(self.g_set.add_de(self.resp_agent, self.source, uri)) 

133 return result 

134 

135 @accepts_only('de') 

136 def contains_discourse_element(self, de_res: DiscourseElement) -> None: 

137 """ 

138 Setter method corresponding to the ``frbr:part`` RDF predicate. 

139 

140 `The discourse element hierarchically nested within the parent element, such as a 

141 sentence within a paragraph, or a paragraph within a section.` 

142 

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

144 :type de_res: DiscourseElement 

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

146 :return: None 

147 """ 

148 self.g.add((self.res, GraphEntity.iri_contains_de, de_res.res)) 

149 

150 @accepts_only('de') 

151 def remove_contained_discourse_element(self, de_res: DiscourseElement = None) -> None: 

152 """ 

153 Remover method corresponding to the ``frbr:part`` RDF predicate. 

154 

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

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

157 

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

159 related to this method (defaults to None) 

160 :type de_res: DiscourseElement 

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

162 :return: None 

163 """ 

164 if de_res is not None: 

165 self.g.remove((self.res, GraphEntity.iri_contains_de, de_res.res)) 

166 else: 

167 self.g.remove((self.res, GraphEntity.iri_contains_de, None)) 

168 

169 # HAS NEXT (DiscourseElement) 

170 def get_next_de(self) -> Optional[DiscourseElement]: 

171 """ 

172 Getter method corresponding to the ``oco:hasNext`` RDF predicate. 

173 

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

175 """ 

176 uri: Optional[URIRef] = self._get_uri_reference(GraphEntity.iri_has_next, 'de') 

177 if uri is not None: 

178 return self.g_set.add_de(self.resp_agent, self.source, uri) 

179 

180 @accepts_only('de') 

181 def has_next_de(self, de_res: DiscourseElement) -> None: 

182 """ 

183 Setter method corresponding to the ``oco:hasNext`` RDF predicate. 

184 

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

186 

187 `The following discourse element that includes at least one in-text reference pointer.` 

188 

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

190 :type de_res: DiscourseElement 

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

192 :return: None 

193 """ 

194 self.remove_next_de() 

195 self.g.add((self.res, GraphEntity.iri_has_next, de_res.res)) 

196 

197 def remove_next_de(self) -> None: 

198 """ 

199 Remover method corresponding to the ``oco:hasNext`` RDF predicate. 

200 

201 :return: None 

202 """ 

203 self.g.remove((self.res, GraphEntity.iri_has_next, None)) 

204 

205 # IS CONTEXT OF (ReferencePointer) 

206 def get_is_context_of_rp(self) -> List[ReferencePointer]: 

207 """ 

208 Getter method corresponding to the ``c4o:isContextOf`` RDF predicate. 

209 

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

211 """ 

212 uri_list: List[URIRef] = self._get_multiple_uri_references(GraphEntity.iri_is_context_of, 'rp') 

213 result: List[ReferencePointer] = [] 

214 for uri in uri_list: 

215 result.append(self.g_set.add_rp(self.resp_agent, self.source, uri)) 

216 return result 

217 

218 @accepts_only('rp') 

219 def is_context_of_rp(self, rp_res: ReferencePointer) -> None: 

220 """ 

221 Setter method corresponding to the ``c4o:isContextOf`` RDF predicate. 

222 

223 `Provides the textual and semantic context of the in-text reference pointer 

224 that appears within the discourse element.` 

225 

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

227 :type rp_res: ReferencePointer 

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

229 :return: None 

230 """ 

231 self.g.add((self.res, GraphEntity.iri_is_context_of, rp_res.res)) 

232 

233 @accepts_only('rp') 

234 def remove_is_context_of_rp(self, rp_res: ReferencePointer = None) -> None: 

235 """ 

236 Remover method corresponding to the ``c4o:isContextOf`` RDF predicate. 

237 

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

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

240 

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

242 related to this method (defaults to None) 

243 :type rp_res: ReferencePointer 

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

245 :return: None 

246 """ 

247 if rp_res is not None: 

248 self.g.remove((self.res, GraphEntity.iri_is_context_of, rp_res.res)) 

249 else: 

250 self.g.remove((self.res, GraphEntity.iri_is_context_of, None)) 

251 

252 # IS CONTEXT OF (PointerList) 

253 def get_is_context_of_pl(self) -> List[PointerList]: 

254 """ 

255 Getter method corresponding to the ``c4o:isContextOf`` RDF predicate. 

256 

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

258 """ 

259 uri_list: List[URIRef] = self._get_multiple_uri_references(GraphEntity.iri_is_context_of, 'pl') 

260 result: List[PointerList] = [] 

261 for uri in uri_list: 

262 result.append(self.g_set.add_pl(self.resp_agent, self.source, uri)) 

263 return result 

264 

265 @accepts_only('pl') 

266 def is_context_of_pl(self, pl_res: PointerList) -> None: 

267 """ 

268 Setter method corresponding to the ``c4o:isContextOf`` RDF predicate. 

269 

270 `Provides the textual and semantic context of the list of 

271 in-text reference pointers that appears within the discourse element.` 

272 

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

274 :type pl_res: PointerList 

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

276 :return: None 

277 """ 

278 self.g.add((self.res, GraphEntity.iri_is_context_of, pl_res.res)) 

279 

280 @accepts_only('pl') 

281 def remove_is_context_of_pl(self, pl_res: PointerList = None) -> None: 

282 """ 

283 Remover method corresponding to the ``c4o:isContextOf`` RDF predicate. 

284 

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

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

287 

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

289 related to this method (defaults to None) 

290 :type pl_res: PointerList 

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

292 :return: None 

293 """ 

294 if pl_res is not None: 

295 self.g.remove((self.res, GraphEntity.iri_is_context_of, pl_res.res)) 

296 else: 

297 self.g.remove((self.res, GraphEntity.iri_is_context_of, None)) 

298 

299 # HAS CONTENT 

300 def get_content(self) -> Optional[str]: 

301 """ 

302 Getter method corresponding to the ``c4o:hasContent`` RDF predicate. 

303 

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

305 """ 

306 return self._get_literal(GraphEntity.iri_has_content) 

307 

308 @accepts_only('literal') 

309 def has_content(self, string: str) -> None: 

310 """ 

311 Setter method corresponding to the ``c4o:hasContent`` RDF predicate. 

312 

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

314 

315 `The literal document text contained by the discourse element.` 

316 

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

318 :type string: str 

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

320 :return: None 

321 """ 

322 self.remove_content() 

323 self._create_literal(GraphEntity.iri_has_content, string) 

324 

325 def remove_content(self) -> None: 

326 """ 

327 Remover method corresponding to the ``c4o:hasContent`` RDF predicate. 

328 

329 :return: None 

330 """ 

331 self.g.remove((self.res, GraphEntity.iri_has_content, None)) 

332 

333 # HAS NUMBER 

334 def get_number(self) -> Optional[str]: 

335 """ 

336 Getter method corresponding to the ``fabio:hasSequenceIdentifier`` RDF predicate. 

337 

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

339 """ 

340 return self._get_literal(GraphEntity.iri_has_sequence_identifier) 

341 

342 @accepts_only('literal') 

343 def has_number(self, string: str) -> None: 

344 """ 

345 Setter method corresponding to the ``fabio:hasSequenceIdentifier`` RDF predicate. 

346 

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

348 

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

350 :type string: str 

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

352 :return: None 

353 """ 

354 self.remove_number() 

355 self._create_literal(GraphEntity.iri_has_sequence_identifier, string) 

356 

357 def remove_number(self) -> None: 

358 """ 

359 Remover method corresponding to the ``fabio:hasSequenceIdentifier`` RDF predicate. 

360 

361 :return: None 

362 """ 

363 self.g.remove((self.res, GraphEntity.iri_has_sequence_identifier, None)) 

364 

365 # HAS TYPE 

366 @accepts_only('thing') 

367 def create_discourse_element(self, de_class: URIRef = None) -> None: 

368 """ 

369 Setter method corresponding to the ``rdf:type`` RDF predicate. 

370 If parameter is None, it implicitly sets the object value ``deo:DiscourseElement``. 

371 

372 **WARNING: the OCDM specification admits at most two types for an entity. 

373 The main type cannot be edited or removed. Any existing secondary type 

374 will be overwritten!** 

375 

376 `The type of discourse element – such as “paragraph”, “section”, “sentence”, 

377 “acknowledgements”, “reference list” or “figure”.` 

378 

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

380 :type de_class: URIRef 

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

382 :return: None 

383 """ 

384 if de_class is not None: 

385 self._create_type(de_class) 

386 else: 

387 self._create_type(GraphEntity.iri_discourse_element) 

388 

389 def create_section(self) -> None: 

390 """ 

391 Setter method corresponding to the ``rdf:type`` RDF predicate. 

392 It implicitly sets the object value ``doco:Section``. 

393 

394 **WARNING: the OCDM specification admits at most two types for an entity. 

395 The main type cannot be edited or removed. Any existing secondary type 

396 will be overwritten!** 

397 

398 `The type of discourse element – such as “paragraph”, “section”, “sentence”, 

399 “acknowledgements”, “reference list” or “figure”.` 

400 

401 :return: None 

402 """ 

403 self._create_type(GraphEntity.iri_section) 

404 

405 def create_section_title(self) -> None: 

406 """ 

407 Setter method corresponding to the ``rdf:type`` RDF predicate. 

408 It implicitly sets the object value ``doco:SectionTitle``. 

409 

410 **WARNING: the OCDM specification admits at most two types for an entity. 

411 The main type cannot be edited or removed. Any existing secondary type 

412 will be overwritten!** 

413 

414 `The type of discourse element – such as “paragraph”, “section”, “sentence”, 

415 “acknowledgements”, “reference list” or “figure”.` 

416 

417 :return: None 

418 """ 

419 self._create_type(GraphEntity.iri_section_title) 

420 

421 def create_paragraph(self) -> None: 

422 """ 

423 Setter method corresponding to the ``rdf:type`` RDF predicate. 

424 It implicitly sets the object value ``doco:Paragraph``. 

425 

426 **WARNING: the OCDM specification admits at most two types for an entity. 

427 The main type cannot be edited or removed. Any existing secondary type 

428 will be overwritten!** 

429 

430 `The type of discourse element – such as “paragraph”, “section”, “sentence”, 

431 “acknowledgements”, “reference list” or “figure”.` 

432 

433 :return: None 

434 """ 

435 self._create_type(GraphEntity.iri_paragraph) 

436 

437 def create_sentence(self) -> None: 

438 """ 

439 Setter method corresponding to the ``rdf:type`` RDF predicate. 

440 It implicitly sets the object value ``doco:Sentence``. 

441 

442 **WARNING: the OCDM specification admits at most two types for an entity. 

443 The main type cannot be edited or removed. Any existing secondary type 

444 will be overwritten!** 

445 

446 `The type of discourse element – such as “paragraph”, “section”, “sentence”, 

447 “acknowledgements”, “reference list” or “figure”.` 

448 

449 :return: None 

450 """ 

451 self._create_type(GraphEntity.iri_sentence) 

452 

453 def create_text_chunk(self) -> None: 

454 """ 

455 Setter method corresponding to the ``rdf:type`` RDF predicate. 

456 It implicitly sets the object value ``doco:TextChunk``. 

457 

458 **WARNING: the OCDM specification admits at most two types for an entity. 

459 The main type cannot be edited or removed. Any existing secondary type 

460 will be overwritten!** 

461 

462 `The type of discourse element – such as “paragraph”, “section”, “sentence”, 

463 “acknowledgements”, “reference list” or “figure”.` 

464 

465 :return: None 

466 """ 

467 self._create_type(GraphEntity.iri_text_chunk) 

468 

469 def create_table(self) -> None: 

470 """ 

471 Setter method corresponding to the ``rdf:type`` RDF predicate. 

472 It implicitly sets the object value ``doco:Table``. 

473 

474 **WARNING: the OCDM specification admits at most two types for an entity. 

475 The main type cannot be edited or removed. Any existing secondary type 

476 will be overwritten!** 

477 

478 `The type of discourse element – such as “paragraph”, “section”, “sentence”, 

479 “acknowledgements”, “reference list” or “figure”.` 

480 

481 :return: None 

482 """ 

483 self._create_type(GraphEntity.iri_table) 

484 

485 def create_footnote(self) -> None: 

486 """ 

487 Setter method corresponding to the ``rdf:type`` RDF predicate. 

488 It implicitly sets the object value ``doco:Footnote``. 

489 

490 **WARNING: the OCDM specification admits at most two types for an entity. 

491 The main type cannot be edited or removed. Any existing secondary type 

492 will be overwritten!** 

493 

494 `The type of discourse element – such as “paragraph”, “section”, “sentence”, 

495 “acknowledgements”, “reference list” or “figure”.` 

496 

497 :return: None 

498 """ 

499 self._create_type(GraphEntity.iri_footnote) 

500 

501 def create_caption(self) -> None: 

502 """ 

503 Setter method corresponding to the ``rdf:type`` RDF predicate. 

504 It implicitly sets the object value ``deo:Caption``. 

505 

506 **WARNING: the OCDM specification admits at most two types for an entity. 

507 The main type cannot be edited or removed. Any existing secondary type 

508 will be overwritten!** 

509 

510 `The type of discourse element – such as “paragraph”, “section”, “sentence”, 

511 “acknowledgements”, “reference list” or “figure”.` 

512 

513 :return: None 

514 """ 

515 self._create_type(GraphEntity.iri_caption) 

516 

517 

518 def create_introduction(self) -> None: 

519 """ 

520 Setter method corresponding to the ``rdf:type`` RDF predicate. 

521 It implicitly sets the object value ``deo:Introduction``. 

522 

523 **WARNING: any existing rhetorical type WILL NOT be overwritten.** 

524 """ 

525 

526 create_type(self.g, self.res, GraphEntity.iri_introduction) 

527 

528 def create_methods(self) -> None: 

529 """ 

530 Setter method corresponding to the ``rdf:type`` RDF predicate. 

531 It implicitly sets the object value ``deo:Methods``. 

532 

533 **WARNING: any existing rhetorical type WILL NOT be overwritten.** 

534 """ 

535 create_type(self.g, self.res, GraphEntity.iri_methods) 

536 

537 def create_materials(self) -> None: 

538 """ 

539 Setter method corresponding to the ``rdf:type`` RDF predicate. 

540 It implicitly sets the object value ``deo:Materials``. 

541 

542 **WARNING: any existing rhetorical type WILL NOT be overwritten.** 

543 """ 

544 create_type(self.g, self.res, GraphEntity.iri_materials) 

545 

546 def create_related_work(self) -> None: 

547 """ 

548 Setter method corresponding to the ``rdf:type`` RDF predicate. 

549 It implicitly sets the object value ``deo:RelatedWork``. 

550 

551 **WARNING: any existing rhetorical type WILL NOT be overwritten.** 

552 """ 

553 create_type(self.g, self.res, GraphEntity.iri_related_work) 

554 

555 def create_results(self) -> None: 

556 """ 

557 Setter method corresponding to the ``rdf:type`` RDF predicate. 

558 It implicitly sets the object value ``deo:Results``. 

559 

560 **WARNING: any existing rhetorical type WILL NOT be overwritten.** 

561 """ 

562 create_type(self.g, self.res, GraphEntity.iri_results) 

563 

564 def create_discussion(self) -> None: 

565 """ 

566 Setter method corresponding to the ``rdf:type`` RDF predicate. 

567 It implicitly sets the object value ``deo:Discussion``. 

568 

569 **WARNING: any existing rhetorical type WILL NOT be overwritten.** 

570 """ 

571 create_type(self.g, self.res, GraphEntity.iri_discussion) 

572 

573 def create_conclusion(self) -> None: 

574 """ 

575 Setter method corresponding to the ``rdf:type`` RDF predicate. 

576 It implicitly sets the object value ``deo:Conclusion``. 

577 

578 **WARNING: any existing rhetorical type WILL NOT be overwritten.** 

579 """ 

580 create_type(self.g, self.res, GraphEntity.iri_conclusion)