Coverage for rdflib_ocdm/query_utils.py: 89%
72 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-11-01 22:02 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-11-01 22:02 +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.
17from __future__ import annotations
19from typing import TYPE_CHECKING
21if TYPE_CHECKING: 21 ↛ 22line 21 didn't jump to line 22 because the condition on line 21 was never true
22 from typing import Tuple
23 from rdflib.compare import IsomorphicGraph
24 from rdflib_ocdm.ocdm_graph import OCDMGraphCommons
26from rdflib import Dataset, Graph, URIRef
27from rdflib.compare import graph_diff, to_isomorphic
29from rdflib_ocdm.graph_utils import _extract_graph_iri
30from rdflib_ocdm.support import get_entity_subgraph
33def get_delete_query(data: Dataset|Graph, graph_iri: URIRef = None) -> Tuple[str, int]:
34 num_of_statements: int = len(data)
35 if num_of_statements <= 0:
36 return "", 0
37 else:
38 statements: str = data.serialize(format="nt11").replace('\n', '')
39 if graph_iri:
40 delete_string: str = f"DELETE DATA {{ GRAPH <{graph_iri}> {{ {statements} }} }}"
41 else:
42 delete_string: str = f"DELETE DATA {{ {statements} }}"
43 return delete_string, num_of_statements
45def get_insert_query(data: Dataset|Graph, graph_iri: URIRef = None) -> Tuple[str, int]:
46 num_of_statements: int = len(data)
47 if num_of_statements <= 0:
48 return "", 0
49 else:
50 statements: str = data.serialize(format="nt11").replace('\n', '')
51 if graph_iri:
52 insert_string: str = f"INSERT DATA {{ GRAPH <{graph_iri}> {{ {statements} }} }}"
53 else:
54 insert_string: str = f"INSERT DATA {{ {statements} }}"
55 return insert_string, num_of_statements
57def get_update_query(a_set: OCDMGraphCommons|Dataset|Graph, entity: URIRef, entity_type = 'graph') -> Tuple[str, int, int]:
58 if entity_type == 'graph':
59 to_be_deleted: bool = a_set.entity_index[entity]['to_be_deleted'] if entity in a_set.entity_index else False
60 preexisting_graph = get_entity_subgraph(a_set.preexisting_graph, entity)
61 graph_iri = None
62 elif entity_type == 'prov': 62 ↛ 67line 62 didn't jump to line 67 because the condition on line 62 was always true
63 to_be_deleted = False
64 preexisting_graph = Dataset()
66 # Extract graph_iri: prefer entity_index (OCDMDataset), fallback to helper function (regular Dataset)
67 if isinstance(a_set, Dataset):
68 if hasattr(a_set, 'entity_index') and entity in a_set.entity_index:
69 # Clean architectural solution: use stored graph_iri from entity_index
70 graph_iri = a_set.entity_index[entity].get('graph_iri')
71 else:
72 # Fallback for regular Dataset (e.g., provenance graphs): use DRY helper function
73 graph_iri = _extract_graph_iri(a_set, entity)
74 elif isinstance(a_set, Graph): 74 ↛ 76line 74 didn't jump to line 76 because the condition on line 74 was always true
75 graph_iri = None
76 if to_be_deleted:
77 delete_string, removed_triples = get_delete_query(preexisting_graph, graph_iri)
78 if delete_string != "": 78 ↛ 81line 78 didn't jump to line 81 because the condition on line 78 was always true
79 return delete_string, 0, removed_triples
80 else:
81 return "", 0, 0
82 else:
83 current_graph = get_entity_subgraph(a_set, entity)
85 # Convert Dataset to Graph for isomorphic comparison if needed
86 if isinstance(preexisting_graph, Dataset):
87 preexisting_for_comparison = Graph()
88 for s, p, o, _ in preexisting_graph.quads((None, None, None, None)):
89 preexisting_for_comparison.add((s, p, o))
90 else:
91 preexisting_for_comparison = preexisting_graph
93 if isinstance(current_graph, Dataset):
94 current_for_comparison = Graph()
95 for s, p, o, _ in current_graph.quads((None, None, None, None)):
96 current_for_comparison.add((s, p, o))
97 else:
98 current_for_comparison = current_graph
100 preexisting_iso: IsomorphicGraph = to_isomorphic(preexisting_for_comparison)
101 current_iso: IsomorphicGraph = to_isomorphic(current_for_comparison)
102 if preexisting_iso == current_iso:
103 # Both graphs have exactly the same content!
104 return "", 0, 0
105 _, in_first, in_second = graph_diff(preexisting_iso, current_iso)
106 delete_string, removed_triples = get_delete_query(in_first, graph_iri)
107 insert_string, added_triples = get_insert_query(in_second, graph_iri)
108 if delete_string != "" and insert_string != "":
109 return delete_string + '; ' + insert_string, added_triples, removed_triples
110 elif delete_string != "": 110 ↛ 111line 110 didn't jump to line 111 because the condition on line 110 was never true
111 return delete_string, 0, removed_triples
112 elif insert_string != "": 112 ↛ 115line 112 didn't jump to line 115 because the condition on line 112 was always true
113 return insert_string, added_triples, 0
114 else:
115 return "", 0, 0