Coverage for rdflib_ocdm / storer.py: 96%
77 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-30 21:23 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-30 21:23 +0000
1#!/usr/bin/python
3# SPDX-FileCopyrightText: 2023-2026 Arcangelo Massari <arcangelo.massari@unibo.it>
4#
5# SPDX-License-Identifier: ISC
7from __future__ import annotations
9import os
10from datetime import datetime
11from typing import TYPE_CHECKING
13from oc_ocdm.support.reporter import Reporter
14from SPARQLWrapper import SPARQLWrapper
16from rdflib_ocdm.ocdm_graph import OCDMDataset, OCDMGraph, OCDMGraphCommons
17from rdflib_ocdm.query_utils import get_update_query
18from rdflib_ocdm.retry_utils import execute_with_retry
20if TYPE_CHECKING:
21 from typing import Set
23 from rdflib import Graph
26class Storer:
27 def __init__(
28 self,
29 abstract_set: OCDMGraphCommons | Graph,
30 repok: Reporter | None = None,
31 reperr: Reporter | None = None,
32 output_format: str = "json-ld",
33 zip_output: bool = False,
34 ) -> None:
35 self.a_set = abstract_set
36 supported_formats: Set[str] = {
37 "application/n-triples",
38 "ntriples",
39 "nt",
40 "nt11",
41 "application/n-quads",
42 "nquads",
43 "json-ld",
44 }
45 if output_format not in supported_formats:
46 raise ValueError(
47 f"Given output_format '{output_format}' is not supported."
48 f" Available formats: {supported_formats}."
49 )
50 else:
51 self.output_format: str = output_format
52 self.zip_output = zip_output
53 if repok is None:
54 self.repok: Reporter = Reporter(prefix="[Storer: INFO] ")
55 else:
56 self.repok: Reporter = repok
58 if reperr is None:
59 self.reperr: Reporter = Reporter(prefix="[Storer: ERROR] ")
60 else:
61 self.reperr: Reporter = reperr
63 def _query(
64 self,
65 query_string: str,
66 triplestore_url: str,
67 base_dir: str | None = None,
68 added_statements: int = 0,
69 removed_statements: int = 0,
70 max_retries: int = 5,
71 ) -> bool:
72 if query_string != "": 72 ↛ 112line 72 didn't jump to line 112 because the condition on line 72 was always true
73 try:
74 # Use the retry utility function with custom error handling
75 def execute_query():
76 sparql: SPARQLWrapper = SPARQLWrapper(triplestore_url)
77 sparql.setQuery(query_string)
78 sparql.setMethod("POST")
79 sparql.query()
80 return True
82 execute_with_retry(
83 execute_query, max_retries=max_retries, reporter=self.repok
84 )
86 self.repok.add_sentence(
87 f"Triplestore updated with {added_statements} added statements and "
88 f"with {removed_statements} removed statements."
89 )
91 return True
92 except ValueError as e:
93 # Handle the case when all retries failed
94 self.reperr.add_sentence(
95 "[3] Graph was not loaded into the triplestore"
96 f" due to communication problems: {e}"
97 )
98 if base_dir is not None:
99 tp_err_dir: str = base_dir + os.sep + "tp_err"
100 if not os.path.exists(tp_err_dir): 100 ↛ 102line 100 didn't jump to line 102 because the condition on line 100 was always true
101 os.makedirs(tp_err_dir)
102 cur_file_err: str = (
103 tp_err_dir
104 + os.sep
105 + datetime.now().strftime(
106 "%Y-%m-%d-%H-%M-%S-%f_not_uploaded.txt"
107 )
108 )
109 with open(cur_file_err, "wt", encoding="utf-8") as f:
110 f.write(query_string)
111 return False
112 return False
114 def upload_all(
115 self, triplestore_url: str, base_dir: str | None = None, batch_size: int = 10
116 ) -> bool:
117 self.repok.new_article()
118 self.reperr.new_article()
119 if batch_size <= 0:
120 batch_size = 10
121 query_string: str = ""
122 added_statements: int = 0
123 removed_statements: int = 0
124 skipped_queries: int = 0
125 result: bool = True
126 entity_type = (
127 "graph" if isinstance(self.a_set, (OCDMGraph, OCDMDataset)) else "prov"
128 )
129 for idx, entity in enumerate(list(self.a_set.all_entities)): # type: ignore[union-attr]
130 update_query, n_added, n_removed = get_update_query(
131 self.a_set, entity, entity_type
132 )
133 if update_query == "":
134 skipped_queries += 1
135 else:
136 index = idx - skipped_queries
137 if index == 0:
138 # First query
139 query_string = update_query
140 added_statements = n_added
141 removed_statements = n_removed
142 elif index % batch_size == 0:
143 # batch_size-multiple query
144 result &= self._query(
145 query_string,
146 triplestore_url,
147 base_dir,
148 added_statements,
149 removed_statements,
150 )
151 query_string = update_query
152 added_statements = n_added
153 removed_statements = n_removed
154 else:
155 # Accumulated query
156 query_string += " ; " + update_query
157 added_statements += n_added
158 removed_statements += n_removed
159 if query_string != "": 159 ↛ 167line 159 didn't jump to line 167 because the condition on line 159 was always true
160 result &= self._query(
161 query_string,
162 triplestore_url,
163 base_dir,
164 added_statements,
165 removed_statements,
166 )
167 return result