Coverage for oc_ocdm / graph / entities / bibliographic / discourse_element.py: 81%
139 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-05-08 20:23 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-05-08 20:23 +0000
1#!/usr/bin/python
3# SPDX-FileCopyrightText: 2020-2022 Simone Persiani <iosonopersia@gmail.com>
4# SPDX-FileCopyrightText: 2024 martasoricetti <marta.soricetti@studio.unibo.it>
5#
6# SPDX-License-Identifier: ISC
8# -*- coding: utf-8 -*-
9from __future__ import annotations
11from typing import TYPE_CHECKING
13from triplelite import RDFTerm
15from oc_ocdm.decorators import accepts_only
17if TYPE_CHECKING:
18 from typing import List, Optional
20 from oc_ocdm.graph.entities.bibliographic.pointer_list import PointerList
21 from oc_ocdm.graph.entities.bibliographic.reference_pointer import ReferencePointer
22from oc_ocdm.graph.entities.bibliographic_entity import BibliographicEntity
23from oc_ocdm.graph.graph_entity import GraphEntity
24from oc_ocdm.support.support import create_type
27class DiscourseElement(BibliographicEntity):
28 """Discourse element (short: de): a document component, either structural (e.g.
29 paragraph, section, chapter, table, caption, footnote, title) or rhetorical (e.g.
30 introduction, discussion, acknowledgements, reference list, figure, appendix), in which
31 the content of a bibliographic resource can be organized."""
33 def _merge_properties(self, other: GraphEntity, prefer_self: bool) -> None:
34 """
35 The merge operation allows combining two ``DiscourseElement`` entities into a single one,
36 by marking the second entity as to be deleted while also copying its data into the current
37 ``DiscourseElement``. Moreover, every triple from the containing ``GraphSet`` referring to the second
38 entity gets "redirected" to the current entity: **every other reference contained inside a
39 different source (e.g. a triplestore) must be manually handled by the user!**
41 In case of functional properties, values from the current entity get overwritten
42 by those coming from the second entity while, in all other cases, values from the
43 second entity are simply appended to those of the current entity. In this context,
44 ``rdfs:label`` is considered as a functional property, while ``rdf:type`` is not.
46 :param other: The entity which will be marked as to be deleted and whose properties will
47 be merged into the current entity.
48 :type other: DiscourseElement
49 :raises TypeError: if the parameter is of the wrong type
50 :return: None
51 """
52 super()._merge_properties(other, prefer_self)
53 assert isinstance(other, DiscourseElement)
55 title: Optional[str] = other.get_title()
56 if title is not None:
57 self.has_title(title)
59 de_list: List[DiscourseElement] = other.get_contained_discourse_elements()
60 for cur_de in de_list:
61 self.contains_discourse_element(cur_de)
63 next_de: Optional[DiscourseElement] = other.get_next_de()
64 if next_de is not None:
65 self.has_next_de(next_de)
67 rp_list: List[ReferencePointer] = other.get_is_context_of_rp()
68 for cur_rp in rp_list:
69 self.is_context_of_rp(cur_rp)
71 pl_list: List[PointerList] = other.get_is_context_of_pl()
72 for cur_pl in pl_list:
73 self.is_context_of_pl(cur_pl)
75 content: Optional[str] = other.get_content()
76 if content is not None:
77 self.has_content(content)
79 number: Optional[str] = other.get_number()
80 if number is not None:
81 self.has_number(number)
83 # HAS TITLE
84 def get_title(self) -> Optional[str]:
85 """
86 Getter method corresponding to the ``dcterms:title`` RDF predicate.
88 :return: The requested value if found, None otherwise
89 """
90 return self._get_literal(GraphEntity.iri_title)
92 def has_title(self, string: str) -> None:
93 """
94 Setter method corresponding to the ``dcterms:title`` RDF predicate.
96 **WARNING: this is a functional property, hence any existing value will be overwritten!**
98 `The title of the discourse element, such as the title of a figure or a section in an article.`
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.remove_title()
106 self._create_literal(GraphEntity.iri_title, string)
108 def remove_title(self) -> None:
109 """
110 Remover method corresponding to the ``dcterms:title`` RDF predicate.
112 :return: None
113 """
114 self.g.remove((self.res, GraphEntity.iri_title, None))
116 # HAS PART (DiscourseElement)
117 def get_contained_discourse_elements(self) -> List[DiscourseElement]:
118 """
119 Getter method corresponding to the ``frbr:part`` RDF predicate.
121 :return: A list containing the requested values if found, None otherwise
122 """
123 uri_list: List[str] = self._get_multiple_uri_references(GraphEntity.iri_contains_de, 'de')
124 result: List[DiscourseElement] = []
125 for uri in uri_list:
126 result.append(self.g_set.add_de(self.resp_agent, self.source, uri))
127 return result
129 @accepts_only('de')
130 def contains_discourse_element(self, de_res: DiscourseElement) -> None:
131 """
132 Setter method corresponding to the ``frbr:part`` RDF predicate.
134 `The discourse element hierarchically nested within the parent element, such as a
135 sentence within a paragraph, or a paragraph within a section.`
137 :param de_res: The value that will be set as the object of the property related to this method
138 :type de_res: DiscourseElement
139 :raises TypeError: if the parameter is of the wrong type
140 :return: None
141 """
142 self.g.add((self.res, GraphEntity.iri_contains_de, RDFTerm("uri", str(de_res.res))))
144 @accepts_only('de')
145 def remove_contained_discourse_element(self, de_res: DiscourseElement | None = None) -> None:
146 """
147 Remover method corresponding to the ``frbr:part`` RDF predicate.
149 **WARNING: this is a non-functional property, hence, if the parameter
150 is None, any existing value will be removed!**
152 :param de_res: If not None, the specific object value that will be removed from the property
153 related to this method (defaults to None)
154 :type de_res: DiscourseElement
155 :raises TypeError: if the parameter is of the wrong type
156 :return: None
157 """
158 if de_res is not None:
159 self.g.remove((self.res, GraphEntity.iri_contains_de, RDFTerm("uri", str(de_res.res))))
160 else:
161 self.g.remove((self.res, GraphEntity.iri_contains_de, None))
163 # HAS NEXT (DiscourseElement)
164 def get_next_de(self) -> Optional[DiscourseElement]:
165 """
166 Getter method corresponding to the ``oco:hasNext`` RDF predicate.
168 :return: The requested value if found, None otherwise
169 """
170 uri: Optional[str] = self._get_uri_reference(GraphEntity.iri_has_next, 'de')
171 if uri is not None:
172 return self.g_set.add_de(self.resp_agent, self.source, uri)
174 @accepts_only('de')
175 def has_next_de(self, de_res: DiscourseElement) -> None:
176 """
177 Setter method corresponding to the ``oco:hasNext`` RDF predicate.
179 **WARNING: this is a functional property, hence any existing value will be overwritten!**
181 `The following discourse element that includes at least one in-text reference pointer.`
183 :param de_res: The value that will be set as the object of the property related to this method
184 :type de_res: DiscourseElement
185 :raises TypeError: if the parameter is of the wrong type
186 :return: None
187 """
188 self.remove_next_de()
189 self.g.add((self.res, GraphEntity.iri_has_next, RDFTerm("uri", str(de_res.res))))
191 def remove_next_de(self) -> None:
192 """
193 Remover method corresponding to the ``oco:hasNext`` RDF predicate.
195 :return: None
196 """
197 self.g.remove((self.res, GraphEntity.iri_has_next, None))
199 # IS CONTEXT OF (ReferencePointer)
200 def get_is_context_of_rp(self) -> List[ReferencePointer]:
201 """
202 Getter method corresponding to the ``c4o:isContextOf`` RDF predicate.
204 :return: A list containing the requested values if found, None otherwise
205 """
206 uri_list: List[str] = self._get_multiple_uri_references(GraphEntity.iri_is_context_of, 'rp')
207 result: List[ReferencePointer] = []
208 for uri in uri_list:
209 result.append(self.g_set.add_rp(self.resp_agent, self.source, uri))
210 return result
212 @accepts_only('rp')
213 def is_context_of_rp(self, rp_res: ReferencePointer) -> None:
214 """
215 Setter method corresponding to the ``c4o:isContextOf`` RDF predicate.
217 `Provides the textual and semantic context of the in-text reference pointer
218 that appears within the discourse element.`
220 :param rp_res: The value that will be set as the object of the property related to this method
221 :type rp_res: ReferencePointer
222 :raises TypeError: if the parameter is of the wrong type
223 :return: None
224 """
225 self.g.add((self.res, GraphEntity.iri_is_context_of, RDFTerm("uri", str(rp_res.res))))
227 @accepts_only('rp')
228 def remove_is_context_of_rp(self, rp_res: ReferencePointer | None = None) -> None:
229 """
230 Remover method corresponding to the ``c4o:isContextOf`` RDF predicate.
232 **WARNING: this is a non-functional property, hence, if the parameter
233 is None, any existing value will be removed!**
235 :param rp_res: If not None, the specific object value that will be removed from the property
236 related to this method (defaults to None)
237 :type rp_res: ReferencePointer
238 :raises TypeError: if the parameter is of the wrong type
239 :return: None
240 """
241 if rp_res is not None:
242 self.g.remove((self.res, GraphEntity.iri_is_context_of, RDFTerm("uri", str(rp_res.res))))
243 else:
244 self.g.remove((self.res, GraphEntity.iri_is_context_of, None))
246 # IS CONTEXT OF (PointerList)
247 def get_is_context_of_pl(self) -> List[PointerList]:
248 """
249 Getter method corresponding to the ``c4o:isContextOf`` RDF predicate.
251 :return: A list containing the requested values if found, None otherwise
252 """
253 uri_list: List[str] = self._get_multiple_uri_references(GraphEntity.iri_is_context_of, 'pl')
254 result: List[PointerList] = []
255 for uri in uri_list:
256 result.append(self.g_set.add_pl(self.resp_agent, self.source, uri))
257 return result
259 @accepts_only('pl')
260 def is_context_of_pl(self, pl_res: PointerList) -> None:
261 """
262 Setter method corresponding to the ``c4o:isContextOf`` RDF predicate.
264 `Provides the textual and semantic context of the list of
265 in-text reference pointers that appears within the discourse element.`
267 :param pl_res: The value that will be set as the object of the property related to this method
268 :type pl_res: PointerList
269 :raises TypeError: if the parameter is of the wrong type
270 :return: None
271 """
272 self.g.add((self.res, GraphEntity.iri_is_context_of, RDFTerm("uri", str(pl_res.res))))
274 @accepts_only('pl')
275 def remove_is_context_of_pl(self, pl_res: PointerList | None = None) -> None:
276 """
277 Remover method corresponding to the ``c4o:isContextOf`` RDF predicate.
279 **WARNING: this is a non-functional property, hence, if the parameter
280 is None, any existing value will be removed!**
282 :param pl_res: If not None, the specific object value that will be removed from the property
283 related to this method (defaults to None)
284 :type pl_res: PointerList
285 :raises TypeError: if the parameter is of the wrong type
286 :return: None
287 """
288 if pl_res is not None:
289 self.g.remove((self.res, GraphEntity.iri_is_context_of, RDFTerm("uri", str(pl_res.res))))
290 else:
291 self.g.remove((self.res, GraphEntity.iri_is_context_of, None))
293 # HAS CONTENT
294 def get_content(self) -> Optional[str]:
295 """
296 Getter method corresponding to the ``c4o:hasContent`` RDF predicate.
298 :return: The requested value if found, None otherwise
299 """
300 return self._get_literal(GraphEntity.iri_has_content)
302 def has_content(self, string: str) -> None:
303 """
304 Setter method corresponding to the ``c4o:hasContent`` RDF predicate.
306 **WARNING: this is a functional property, hence any existing value will be overwritten!**
308 `The literal document text contained by the discourse element.`
310 :param string: The value that will be set as the object of the property related to this method
311 :type string: str
312 :raises TypeError: if the parameter is of the wrong type
313 :return: None
314 """
315 self.remove_content()
316 self._create_literal(GraphEntity.iri_has_content, string)
318 def remove_content(self) -> None:
319 """
320 Remover method corresponding to the ``c4o:hasContent`` RDF predicate.
322 :return: None
323 """
324 self.g.remove((self.res, GraphEntity.iri_has_content, None))
326 # HAS NUMBER
327 def get_number(self) -> Optional[str]:
328 """
329 Getter method corresponding to the ``fabio:hasSequenceIdentifier`` RDF predicate.
331 :return: The requested value if found, None otherwise
332 """
333 return self._get_literal(GraphEntity.iri_has_sequence_identifier)
335 def has_number(self, string: str) -> None:
336 """
337 Setter method corresponding to the ``fabio:hasSequenceIdentifier`` RDF predicate.
339 **WARNING: this is a functional property, hence any existing value will be overwritten!**
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.remove_number()
347 self._create_literal(GraphEntity.iri_has_sequence_identifier, string)
349 def remove_number(self) -> None:
350 """
351 Remover method corresponding to the ``fabio:hasSequenceIdentifier`` RDF predicate.
353 :return: None
354 """
355 self.g.remove((self.res, GraphEntity.iri_has_sequence_identifier, None))
357 # HAS TYPE
358 def create_discourse_element(self, de_class: str | None = None) -> None:
359 """
360 Setter method corresponding to the ``rdf:type`` RDF predicate.
361 If parameter is None, it implicitly sets the object value ``deo:DiscourseElement``.
363 **WARNING: the OCDM specification admits at most two types for an entity.
364 The main type cannot be edited or removed. Any existing secondary type
365 will be overwritten!**
367 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
368 “acknowledgements”, “reference list” or “figure”.`
370 :param de_class: The value that will be set as the object of the property related to this method
371 :type de_class: URIRef
372 :raises TypeError: if the parameter is of the wrong type
373 :return: None
374 """
375 if de_class is not None:
376 self._create_type(de_class)
377 else:
378 self._create_type(GraphEntity.iri_discourse_element)
380 def create_section(self) -> None:
381 """
382 Setter method corresponding to the ``rdf:type`` RDF predicate.
383 It implicitly sets the object value ``doco:Section``.
385 **WARNING: the OCDM specification admits at most two types for an entity.
386 The main type cannot be edited or removed. Any existing secondary type
387 will be overwritten!**
389 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
390 “acknowledgements”, “reference list” or “figure”.`
392 :return: None
393 """
394 self._create_type(GraphEntity.iri_section)
396 def create_section_title(self) -> None:
397 """
398 Setter method corresponding to the ``rdf:type`` RDF predicate.
399 It implicitly sets the object value ``doco:SectionTitle``.
401 **WARNING: the OCDM specification admits at most two types for an entity.
402 The main type cannot be edited or removed. Any existing secondary type
403 will be overwritten!**
405 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
406 “acknowledgements”, “reference list” or “figure”.`
408 :return: None
409 """
410 self._create_type(GraphEntity.iri_section_title)
412 def create_paragraph(self) -> None:
413 """
414 Setter method corresponding to the ``rdf:type`` RDF predicate.
415 It implicitly sets the object value ``doco:Paragraph``.
417 **WARNING: the OCDM specification admits at most two types for an entity.
418 The main type cannot be edited or removed. Any existing secondary type
419 will be overwritten!**
421 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
422 “acknowledgements”, “reference list” or “figure”.`
424 :return: None
425 """
426 self._create_type(GraphEntity.iri_paragraph)
428 def create_sentence(self) -> None:
429 """
430 Setter method corresponding to the ``rdf:type`` RDF predicate.
431 It implicitly sets the object value ``doco:Sentence``.
433 **WARNING: the OCDM specification admits at most two types for an entity.
434 The main type cannot be edited or removed. Any existing secondary type
435 will be overwritten!**
437 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
438 “acknowledgements”, “reference list” or “figure”.`
440 :return: None
441 """
442 self._create_type(GraphEntity.iri_sentence)
444 def create_text_chunk(self) -> None:
445 """
446 Setter method corresponding to the ``rdf:type`` RDF predicate.
447 It implicitly sets the object value ``doco:TextChunk``.
449 **WARNING: the OCDM specification admits at most two types for an entity.
450 The main type cannot be edited or removed. Any existing secondary type
451 will be overwritten!**
453 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
454 “acknowledgements”, “reference list” or “figure”.`
456 :return: None
457 """
458 self._create_type(GraphEntity.iri_text_chunk)
460 def create_table(self) -> None:
461 """
462 Setter method corresponding to the ``rdf:type`` RDF predicate.
463 It implicitly sets the object value ``doco:Table``.
465 **WARNING: the OCDM specification admits at most two types for an entity.
466 The main type cannot be edited or removed. Any existing secondary type
467 will be overwritten!**
469 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
470 “acknowledgements”, “reference list” or “figure”.`
472 :return: None
473 """
474 self._create_type(GraphEntity.iri_table)
476 def create_footnote(self) -> None:
477 """
478 Setter method corresponding to the ``rdf:type`` RDF predicate.
479 It implicitly sets the object value ``doco:Footnote``.
481 **WARNING: the OCDM specification admits at most two types for an entity.
482 The main type cannot be edited or removed. Any existing secondary type
483 will be overwritten!**
485 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
486 “acknowledgements”, “reference list” or “figure”.`
488 :return: None
489 """
490 self._create_type(GraphEntity.iri_footnote)
492 def create_caption(self) -> None:
493 """
494 Setter method corresponding to the ``rdf:type`` RDF predicate.
495 It implicitly sets the object value ``deo:Caption``.
497 **WARNING: the OCDM specification admits at most two types for an entity.
498 The main type cannot be edited or removed. Any existing secondary type
499 will be overwritten!**
501 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
502 “acknowledgements”, “reference list” or “figure”.`
504 :return: None
505 """
506 self._create_type(GraphEntity.iri_caption)
509 def create_introduction(self) -> None:
510 """
511 Setter method corresponding to the ``rdf:type`` RDF predicate.
512 It implicitly sets the object value ``deo:Introduction``.
514 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
515 """
517 create_type(self.g, self.res, GraphEntity.iri_introduction)
519 def create_methods(self) -> None:
520 """
521 Setter method corresponding to the ``rdf:type`` RDF predicate.
522 It implicitly sets the object value ``deo:Methods``.
524 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
525 """
526 create_type(self.g, self.res, GraphEntity.iri_methods)
528 def create_materials(self) -> None:
529 """
530 Setter method corresponding to the ``rdf:type`` RDF predicate.
531 It implicitly sets the object value ``deo:Materials``.
533 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
534 """
535 create_type(self.g, self.res, GraphEntity.iri_materials)
537 def create_related_work(self) -> None:
538 """
539 Setter method corresponding to the ``rdf:type`` RDF predicate.
540 It implicitly sets the object value ``deo:RelatedWork``.
542 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
543 """
544 create_type(self.g, self.res, GraphEntity.iri_related_work)
546 def create_results(self) -> None:
547 """
548 Setter method corresponding to the ``rdf:type`` RDF predicate.
549 It implicitly sets the object value ``deo:Results``.
551 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
552 """
553 create_type(self.g, self.res, GraphEntity.iri_results)
555 def create_discussion(self) -> None:
556 """
557 Setter method corresponding to the ``rdf:type`` RDF predicate.
558 It implicitly sets the object value ``deo:Discussion``.
560 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
561 """
562 create_type(self.g, self.res, GraphEntity.iri_discussion)
564 def create_conclusion(self) -> None:
565 """
566 Setter method corresponding to the ``rdf:type`` RDF predicate.
567 It implicitly sets the object value ``deo:Conclusion``.
569 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
570 """
571 create_type(self.g, self.res, GraphEntity.iri_conclusion)