Coverage for oc_ocdm / graph / entities / bibliographic / discourse_element.py: 82%
142 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-28 18:52 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-28 18:52 +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 oc_ocdm.decorators import accepts_only
15if TYPE_CHECKING:
16 from typing import Optional, List
17 from rdflib import URIRef
18 from oc_ocdm.graph.entities.bibliographic.reference_pointer import ReferencePointer
19 from oc_ocdm.graph.entities.bibliographic.pointer_list import PointerList
20from oc_ocdm.graph.graph_entity import GraphEntity
21from oc_ocdm.graph.entities.bibliographic_entity import BibliographicEntity
22from oc_ocdm.support.support import create_type
25class DiscourseElement(BibliographicEntity):
26 """Discourse element (short: de): a document component, either structural (e.g.
27 paragraph, section, chapter, table, caption, footnote, title) or rhetorical (e.g.
28 introduction, discussion, acknowledgements, reference list, figure, appendix), in which
29 the content of a bibliographic resource can be organized."""
31 def _merge_properties(self, other: GraphEntity, prefer_self: bool) -> None:
32 """
33 The merge operation allows combining two ``DiscourseElement`` entities into a single one,
34 by marking the second entity as to be deleted while also copying its data into the current
35 ``DiscourseElement``. Moreover, every triple from the containing ``GraphSet`` referring to the second
36 entity gets "redirected" to the current entity: **every other reference contained inside a
37 different source (e.g. a triplestore) must be manually handled by the user!**
39 In case of functional properties, values from the current entity get overwritten
40 by those coming from the second entity while, in all other cases, values from the
41 second entity are simply appended to those of the current entity. In this context,
42 ``rdfs:label`` is considered as a functional property, while ``rdf:type`` is not.
44 :param other: The entity which will be marked as to be deleted and whose properties will
45 be merged into the current entity.
46 :type other: DiscourseElement
47 :raises TypeError: if the parameter is of the wrong type
48 :return: None
49 """
50 super()._merge_properties(other, prefer_self)
51 assert isinstance(other, DiscourseElement)
53 title: Optional[str] = other.get_title()
54 if title is not None:
55 self.has_title(title)
57 de_list: List[DiscourseElement] = other.get_contained_discourse_elements()
58 for cur_de in de_list:
59 self.contains_discourse_element(cur_de)
61 next_de: Optional[DiscourseElement] = other.get_next_de()
62 if next_de is not None:
63 self.has_next_de(next_de)
65 rp_list: List[ReferencePointer] = other.get_is_context_of_rp()
66 for cur_rp in rp_list:
67 self.is_context_of_rp(cur_rp)
69 pl_list: List[PointerList] = other.get_is_context_of_pl()
70 for cur_pl in pl_list:
71 self.is_context_of_pl(cur_pl)
73 content: Optional[str] = other.get_content()
74 if content is not None:
75 self.has_content(content)
77 number: Optional[str] = other.get_number()
78 if number is not None:
79 self.has_number(number)
81 # HAS TITLE
82 def get_title(self) -> Optional[str]:
83 """
84 Getter method corresponding to the ``dcterms:title`` RDF predicate.
86 :return: The requested value if found, None otherwise
87 """
88 return self._get_literal(GraphEntity.iri_title)
90 @accepts_only('literal')
91 def has_title(self, string: str) -> None:
92 """
93 Setter method corresponding to the ``dcterms:title`` RDF predicate.
95 **WARNING: this is a functional property, hence any existing value will be overwritten!**
97 `The title of the discourse element, such as the title of a figure or a section in an article.`
99 :param string: The value that will be set as the object of the property related to this method
100 :type string: str
101 :raises TypeError: if the parameter is of the wrong type
102 :return: None
103 """
104 self.remove_title()
105 self._create_literal(GraphEntity.iri_title, string)
107 def remove_title(self) -> None:
108 """
109 Remover method corresponding to the ``dcterms:title`` RDF predicate.
111 :return: None
112 """
113 self.g.remove((self.res, GraphEntity.iri_title, None))
115 # HAS PART (DiscourseElement)
116 def get_contained_discourse_elements(self) -> List[DiscourseElement]:
117 """
118 Getter method corresponding to the ``frbr:part`` RDF predicate.
120 :return: A list containing the requested values if found, None otherwise
121 """
122 uri_list: List[URIRef] = self._get_multiple_uri_references(GraphEntity.iri_contains_de, 'de')
123 result: List[DiscourseElement] = []
124 for uri in uri_list:
125 result.append(self.g_set.add_de(self.resp_agent, self.source, uri))
126 return result
128 @accepts_only('de')
129 def contains_discourse_element(self, de_res: DiscourseElement) -> None:
130 """
131 Setter method corresponding to the ``frbr:part`` RDF predicate.
133 `The discourse element hierarchically nested within the parent element, such as a
134 sentence within a paragraph, or a paragraph within a section.`
136 :param de_res: The value that will be set as the object of the property related to this method
137 :type de_res: DiscourseElement
138 :raises TypeError: if the parameter is of the wrong type
139 :return: None
140 """
141 self.g.add((self.res, GraphEntity.iri_contains_de, de_res.res))
143 @accepts_only('de')
144 def remove_contained_discourse_element(self, de_res: DiscourseElement | None = None) -> None:
145 """
146 Remover method corresponding to the ``frbr:part`` RDF predicate.
148 **WARNING: this is a non-functional property, hence, if the parameter
149 is None, any existing value will be removed!**
151 :param de_res: If not None, the specific object value that will be removed from the property
152 related to this method (defaults to None)
153 :type de_res: DiscourseElement
154 :raises TypeError: if the parameter is of the wrong type
155 :return: None
156 """
157 if de_res is not None:
158 self.g.remove((self.res, GraphEntity.iri_contains_de, de_res.res))
159 else:
160 self.g.remove((self.res, GraphEntity.iri_contains_de, None))
162 # HAS NEXT (DiscourseElement)
163 def get_next_de(self) -> Optional[DiscourseElement]:
164 """
165 Getter method corresponding to the ``oco:hasNext`` RDF predicate.
167 :return: The requested value if found, None otherwise
168 """
169 uri: Optional[URIRef] = self._get_uri_reference(GraphEntity.iri_has_next, 'de')
170 if uri is not None:
171 return self.g_set.add_de(self.resp_agent, self.source, uri)
173 @accepts_only('de')
174 def has_next_de(self, de_res: DiscourseElement) -> None:
175 """
176 Setter method corresponding to the ``oco:hasNext`` RDF predicate.
178 **WARNING: this is a functional property, hence any existing value will be overwritten!**
180 `The following discourse element that includes at least one in-text reference pointer.`
182 :param de_res: The value that will be set as the object of the property related to this method
183 :type de_res: DiscourseElement
184 :raises TypeError: if the parameter is of the wrong type
185 :return: None
186 """
187 self.remove_next_de()
188 self.g.add((self.res, GraphEntity.iri_has_next, de_res.res))
190 def remove_next_de(self) -> None:
191 """
192 Remover method corresponding to the ``oco:hasNext`` RDF predicate.
194 :return: None
195 """
196 self.g.remove((self.res, GraphEntity.iri_has_next, None))
198 # IS CONTEXT OF (ReferencePointer)
199 def get_is_context_of_rp(self) -> List[ReferencePointer]:
200 """
201 Getter method corresponding to the ``c4o:isContextOf`` RDF predicate.
203 :return: A list containing the requested values if found, None otherwise
204 """
205 uri_list: List[URIRef] = self._get_multiple_uri_references(GraphEntity.iri_is_context_of, 'rp')
206 result: List[ReferencePointer] = []
207 for uri in uri_list:
208 result.append(self.g_set.add_rp(self.resp_agent, self.source, uri))
209 return result
211 @accepts_only('rp')
212 def is_context_of_rp(self, rp_res: ReferencePointer) -> None:
213 """
214 Setter method corresponding to the ``c4o:isContextOf`` RDF predicate.
216 `Provides the textual and semantic context of the in-text reference pointer
217 that appears within the discourse element.`
219 :param rp_res: The value that will be set as the object of the property related to this method
220 :type rp_res: ReferencePointer
221 :raises TypeError: if the parameter is of the wrong type
222 :return: None
223 """
224 self.g.add((self.res, GraphEntity.iri_is_context_of, rp_res.res))
226 @accepts_only('rp')
227 def remove_is_context_of_rp(self, rp_res: ReferencePointer | None = None) -> None:
228 """
229 Remover method corresponding to the ``c4o:isContextOf`` RDF predicate.
231 **WARNING: this is a non-functional property, hence, if the parameter
232 is None, any existing value will be removed!**
234 :param rp_res: If not None, the specific object value that will be removed from the property
235 related to this method (defaults to None)
236 :type rp_res: ReferencePointer
237 :raises TypeError: if the parameter is of the wrong type
238 :return: None
239 """
240 if rp_res is not None:
241 self.g.remove((self.res, GraphEntity.iri_is_context_of, rp_res.res))
242 else:
243 self.g.remove((self.res, GraphEntity.iri_is_context_of, None))
245 # IS CONTEXT OF (PointerList)
246 def get_is_context_of_pl(self) -> List[PointerList]:
247 """
248 Getter method corresponding to the ``c4o:isContextOf`` RDF predicate.
250 :return: A list containing the requested values if found, None otherwise
251 """
252 uri_list: List[URIRef] = self._get_multiple_uri_references(GraphEntity.iri_is_context_of, 'pl')
253 result: List[PointerList] = []
254 for uri in uri_list:
255 result.append(self.g_set.add_pl(self.resp_agent, self.source, uri))
256 return result
258 @accepts_only('pl')
259 def is_context_of_pl(self, pl_res: PointerList) -> None:
260 """
261 Setter method corresponding to the ``c4o:isContextOf`` RDF predicate.
263 `Provides the textual and semantic context of the list of
264 in-text reference pointers that appears within the discourse element.`
266 :param pl_res: The value that will be set as the object of the property related to this method
267 :type pl_res: PointerList
268 :raises TypeError: if the parameter is of the wrong type
269 :return: None
270 """
271 self.g.add((self.res, GraphEntity.iri_is_context_of, pl_res.res))
273 @accepts_only('pl')
274 def remove_is_context_of_pl(self, pl_res: PointerList | None = None) -> None:
275 """
276 Remover method corresponding to the ``c4o:isContextOf`` RDF predicate.
278 **WARNING: this is a non-functional property, hence, if the parameter
279 is None, any existing value will be removed!**
281 :param pl_res: If not None, the specific object value that will be removed from the property
282 related to this method (defaults to None)
283 :type pl_res: PointerList
284 :raises TypeError: if the parameter is of the wrong type
285 :return: None
286 """
287 if pl_res is not None:
288 self.g.remove((self.res, GraphEntity.iri_is_context_of, pl_res.res))
289 else:
290 self.g.remove((self.res, GraphEntity.iri_is_context_of, None))
292 # HAS CONTENT
293 def get_content(self) -> Optional[str]:
294 """
295 Getter method corresponding to the ``c4o:hasContent`` RDF predicate.
297 :return: The requested value if found, None otherwise
298 """
299 return self._get_literal(GraphEntity.iri_has_content)
301 @accepts_only('literal')
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 @accepts_only('literal')
336 def has_number(self, string: str) -> None:
337 """
338 Setter method corresponding to the ``fabio:hasSequenceIdentifier`` RDF predicate.
340 **WARNING: this is a functional property, hence any existing value will be overwritten!**
342 :param string: The value that will be set as the object of the property related to this method
343 :type string: str
344 :raises TypeError: if the parameter is of the wrong type
345 :return: None
346 """
347 self.remove_number()
348 self._create_literal(GraphEntity.iri_has_sequence_identifier, string)
350 def remove_number(self) -> None:
351 """
352 Remover method corresponding to the ``fabio:hasSequenceIdentifier`` RDF predicate.
354 :return: None
355 """
356 self.g.remove((self.res, GraphEntity.iri_has_sequence_identifier, None))
358 # HAS TYPE
359 @accepts_only('thing')
360 def create_discourse_element(self, de_class: URIRef | None = None) -> None:
361 """
362 Setter method corresponding to the ``rdf:type`` RDF predicate.
363 If parameter is None, it implicitly sets the object value ``deo:DiscourseElement``.
365 **WARNING: the OCDM specification admits at most two types for an entity.
366 The main type cannot be edited or removed. Any existing secondary type
367 will be overwritten!**
369 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
370 “acknowledgements”, “reference list” or “figure”.`
372 :param de_class: The value that will be set as the object of the property related to this method
373 :type de_class: URIRef
374 :raises TypeError: if the parameter is of the wrong type
375 :return: None
376 """
377 if de_class is not None:
378 self._create_type(de_class)
379 else:
380 self._create_type(GraphEntity.iri_discourse_element)
382 def create_section(self) -> None:
383 """
384 Setter method corresponding to the ``rdf:type`` RDF predicate.
385 It implicitly sets the object value ``doco:Section``.
387 **WARNING: the OCDM specification admits at most two types for an entity.
388 The main type cannot be edited or removed. Any existing secondary type
389 will be overwritten!**
391 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
392 “acknowledgements”, “reference list” or “figure”.`
394 :return: None
395 """
396 self._create_type(GraphEntity.iri_section)
398 def create_section_title(self) -> None:
399 """
400 Setter method corresponding to the ``rdf:type`` RDF predicate.
401 It implicitly sets the object value ``doco:SectionTitle``.
403 **WARNING: the OCDM specification admits at most two types for an entity.
404 The main type cannot be edited or removed. Any existing secondary type
405 will be overwritten!**
407 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
408 “acknowledgements”, “reference list” or “figure”.`
410 :return: None
411 """
412 self._create_type(GraphEntity.iri_section_title)
414 def create_paragraph(self) -> None:
415 """
416 Setter method corresponding to the ``rdf:type`` RDF predicate.
417 It implicitly sets the object value ``doco:Paragraph``.
419 **WARNING: the OCDM specification admits at most two types for an entity.
420 The main type cannot be edited or removed. Any existing secondary type
421 will be overwritten!**
423 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
424 “acknowledgements”, “reference list” or “figure”.`
426 :return: None
427 """
428 self._create_type(GraphEntity.iri_paragraph)
430 def create_sentence(self) -> None:
431 """
432 Setter method corresponding to the ``rdf:type`` RDF predicate.
433 It implicitly sets the object value ``doco:Sentence``.
435 **WARNING: the OCDM specification admits at most two types for an entity.
436 The main type cannot be edited or removed. Any existing secondary type
437 will be overwritten!**
439 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
440 “acknowledgements”, “reference list” or “figure”.`
442 :return: None
443 """
444 self._create_type(GraphEntity.iri_sentence)
446 def create_text_chunk(self) -> None:
447 """
448 Setter method corresponding to the ``rdf:type`` RDF predicate.
449 It implicitly sets the object value ``doco:TextChunk``.
451 **WARNING: the OCDM specification admits at most two types for an entity.
452 The main type cannot be edited or removed. Any existing secondary type
453 will be overwritten!**
455 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
456 “acknowledgements”, “reference list” or “figure”.`
458 :return: None
459 """
460 self._create_type(GraphEntity.iri_text_chunk)
462 def create_table(self) -> None:
463 """
464 Setter method corresponding to the ``rdf:type`` RDF predicate.
465 It implicitly sets the object value ``doco:Table``.
467 **WARNING: the OCDM specification admits at most two types for an entity.
468 The main type cannot be edited or removed. Any existing secondary type
469 will be overwritten!**
471 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
472 “acknowledgements”, “reference list” or “figure”.`
474 :return: None
475 """
476 self._create_type(GraphEntity.iri_table)
478 def create_footnote(self) -> None:
479 """
480 Setter method corresponding to the ``rdf:type`` RDF predicate.
481 It implicitly sets the object value ``doco:Footnote``.
483 **WARNING: the OCDM specification admits at most two types for an entity.
484 The main type cannot be edited or removed. Any existing secondary type
485 will be overwritten!**
487 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
488 “acknowledgements”, “reference list” or “figure”.`
490 :return: None
491 """
492 self._create_type(GraphEntity.iri_footnote)
494 def create_caption(self) -> None:
495 """
496 Setter method corresponding to the ``rdf:type`` RDF predicate.
497 It implicitly sets the object value ``deo:Caption``.
499 **WARNING: the OCDM specification admits at most two types for an entity.
500 The main type cannot be edited or removed. Any existing secondary type
501 will be overwritten!**
503 `The type of discourse element – such as “paragraph”, “section”, “sentence”,
504 “acknowledgements”, “reference list” or “figure”.`
506 :return: None
507 """
508 self._create_type(GraphEntity.iri_caption)
511 def create_introduction(self) -> None:
512 """
513 Setter method corresponding to the ``rdf:type`` RDF predicate.
514 It implicitly sets the object value ``deo:Introduction``.
516 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
517 """
519 create_type(self.g, self.res, GraphEntity.iri_introduction)
521 def create_methods(self) -> None:
522 """
523 Setter method corresponding to the ``rdf:type`` RDF predicate.
524 It implicitly sets the object value ``deo:Methods``.
526 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
527 """
528 create_type(self.g, self.res, GraphEntity.iri_methods)
530 def create_materials(self) -> None:
531 """
532 Setter method corresponding to the ``rdf:type`` RDF predicate.
533 It implicitly sets the object value ``deo:Materials``.
535 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
536 """
537 create_type(self.g, self.res, GraphEntity.iri_materials)
539 def create_related_work(self) -> None:
540 """
541 Setter method corresponding to the ``rdf:type`` RDF predicate.
542 It implicitly sets the object value ``deo:RelatedWork``.
544 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
545 """
546 create_type(self.g, self.res, GraphEntity.iri_related_work)
548 def create_results(self) -> None:
549 """
550 Setter method corresponding to the ``rdf:type`` RDF predicate.
551 It implicitly sets the object value ``deo:Results``.
553 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
554 """
555 create_type(self.g, self.res, GraphEntity.iri_results)
557 def create_discussion(self) -> None:
558 """
559 Setter method corresponding to the ``rdf:type`` RDF predicate.
560 It implicitly sets the object value ``deo:Discussion``.
562 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
563 """
564 create_type(self.g, self.res, GraphEntity.iri_discussion)
566 def create_conclusion(self) -> None:
567 """
568 Setter method corresponding to the ``rdf:type`` RDF predicate.
569 It implicitly sets the object value ``deo:Conclusion``.
571 **WARNING: any existing rhetorical type WILL NOT be overwritten.**
572 """
573 create_type(self.g, self.res, GraphEntity.iri_conclusion)