rdflib-ocdm 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rdflib_ocdm-0.1.0/PKG-INFO +27 -0
- rdflib_ocdm-0.1.0/README.md +7 -0
- rdflib_ocdm-0.1.0/pyproject.toml +23 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/__init__.py +15 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/abstract_entity.py +174 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/counter_handler/__init__.py +15 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/counter_handler/counter_handler.py +61 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/counter_handler/filesystem_counter_handler.py +125 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/counter_handler/in_memory_counter_handler.py +81 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/counter_handler/sqlite_counter_handler.py +87 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/ocdm_graph.py +102 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/prov/__init__.py +15 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/prov/prov_entity.py +64 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/prov/provenance.py +144 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/prov/snapshot_entity.py +331 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/query_utils.py +96 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/reader.py +48 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/storer.py +111 -0
- rdflib_ocdm-0.1.0/rdflib_ocdm/support.py +66 -0
- rdflib_ocdm-0.1.0/setup.py +30 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: rdflib-ocdm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary:
|
|
5
|
+
License: ISC
|
|
6
|
+
Author: arcangelo7
|
|
7
|
+
Author-email: arcangelomas@gmail.com
|
|
8
|
+
Requires-Python: >=3.7.4,<4.0.0
|
|
9
|
+
Classifier: License :: OSI Approved
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Requires-Dist: oc-ocdm (>=7.1.7,<8.0.0)
|
|
16
|
+
Requires-Dist: rdflib (>=6.2.0,<7.0.0)
|
|
17
|
+
Requires-Dist: sparqlwrapper (>=2.0.0,<3.0.0)
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
[<img src="https://img.shields.io/badge/powered%20by-OpenCitations-%239931FC?labelColor=2D22DE" />](http://opencitations.net)
|
|
21
|
+
[](https://github.com/opencitations/rdflib-ocdm/actions/workflows/run_tests.yml)
|
|
22
|
+

|
|
23
|
+

|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
# rdflib-ocdm
|
|
27
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
[<img src="https://img.shields.io/badge/powered%20by-OpenCitations-%239931FC?labelColor=2D22DE" />](http://opencitations.net)
|
|
2
|
+
[](https://github.com/opencitations/rdflib-ocdm/actions/workflows/run_tests.yml)
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
# rdflib-ocdm
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "rdflib-ocdm"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = ["arcangelo7 <arcangelomas@gmail.com>"]
|
|
6
|
+
license = "ISC"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
packages = [{include = "rdflib_ocdm"}]
|
|
9
|
+
|
|
10
|
+
[tool.poetry.dependencies]
|
|
11
|
+
python = "^3.7.4"
|
|
12
|
+
rdflib = "^6.2.0"
|
|
13
|
+
oc-ocdm = "^7.1.7"
|
|
14
|
+
sparqlwrapper = "^2.0.0"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
[tool.poetry.group.dev.dependencies]
|
|
18
|
+
coverage = "^7.2.3"
|
|
19
|
+
coverage-badge = "^1.1.0"
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["poetry-core"]
|
|
23
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Copyright 2023 Arcangelo Massari <arcangelo.massari@unibo.it>
|
|
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.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Copyright (c) 2016, Silvio Peroni <essepuntato@gmail.com>
|
|
4
|
+
# Copyright (c) 2023, Arcangelo Massari <arcangelo.massari@unibo.it>
|
|
5
|
+
#
|
|
6
|
+
# Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
7
|
+
# with or without fee is hereby granted, provided that the above copyright notice
|
|
8
|
+
# and this permission notice appear in all copies.
|
|
9
|
+
#
|
|
10
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
11
|
+
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
12
|
+
# FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
|
13
|
+
# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
14
|
+
# DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
|
15
|
+
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
16
|
+
# SOFTWARE.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from abc import ABC
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
|
|
24
|
+
from rdflib import RDF, RDFS, Graph, Literal, URIRef
|
|
25
|
+
|
|
26
|
+
from rdflib_ocdm.support import create_literal, create_type
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from typing import ClassVar, Dict, Iterable, List, Optional
|
|
30
|
+
|
|
31
|
+
class AbstractEntity(ABC):
|
|
32
|
+
"""
|
|
33
|
+
Abstract class which represents a generic entity.
|
|
34
|
+
It sits at the top of the entity class hierarchy.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
short_name_to_type_iri: ClassVar[Dict[str, URIRef]] = {}
|
|
38
|
+
|
|
39
|
+
def __init__(self) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Constructor of the ``AbstractEntity`` class.
|
|
42
|
+
"""
|
|
43
|
+
self.g: Graph = Graph()
|
|
44
|
+
self.res: URIRef = URIRef("")
|
|
45
|
+
|
|
46
|
+
def remove_every_triple(self) -> None:
|
|
47
|
+
"""
|
|
48
|
+
Remover method that removes every triple from the current entity.
|
|
49
|
+
|
|
50
|
+
:return: None
|
|
51
|
+
"""
|
|
52
|
+
self.g.remove((None, None, None))
|
|
53
|
+
|
|
54
|
+
# LABEL
|
|
55
|
+
def get_label(self) -> Optional[str]:
|
|
56
|
+
"""
|
|
57
|
+
Getter method corresponding to the ``rdfs:label`` RDF predicate.
|
|
58
|
+
|
|
59
|
+
:return: The requested value if found, None otherwise
|
|
60
|
+
"""
|
|
61
|
+
return self._get_literal(RDFS.label)
|
|
62
|
+
|
|
63
|
+
def create_label(self, string: str) -> None:
|
|
64
|
+
"""
|
|
65
|
+
Setter method corresponding to the ``rdfs:label`` RDF predicate.
|
|
66
|
+
|
|
67
|
+
**WARNING: this is a functional property, hence any existing value will be overwritten!**
|
|
68
|
+
|
|
69
|
+
:param string: The value that will be set as the object of the property related to this method
|
|
70
|
+
:type string: str
|
|
71
|
+
:return: None
|
|
72
|
+
"""
|
|
73
|
+
self.remove_label()
|
|
74
|
+
self._create_literal(RDFS.label, string)
|
|
75
|
+
|
|
76
|
+
def remove_label(self) -> None:
|
|
77
|
+
"""
|
|
78
|
+
Remover method corresponding to the ``rdfs:label`` RDF predicate.
|
|
79
|
+
|
|
80
|
+
:return: None
|
|
81
|
+
"""
|
|
82
|
+
self.g.remove((self.res, RDFS.label, None))
|
|
83
|
+
|
|
84
|
+
def _create_literal(self, p: URIRef, s: str, dt: URIRef = None, nor: bool = True) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Adds an RDF triple with a literal object inside the graph of the entity
|
|
87
|
+
|
|
88
|
+
:param p: The predicate
|
|
89
|
+
:type p: URIRef
|
|
90
|
+
:param s: The string to add as a literal value
|
|
91
|
+
:type s: str
|
|
92
|
+
:param dt: The object's datatype, if present
|
|
93
|
+
:type dt: URIRef, optional
|
|
94
|
+
:param nor: Whether to normalize the graph or not
|
|
95
|
+
:type nor: bool, optional
|
|
96
|
+
:return: None
|
|
97
|
+
"""
|
|
98
|
+
create_literal(self.g, self.res, p, s, dt, nor)
|
|
99
|
+
|
|
100
|
+
# TYPE
|
|
101
|
+
def get_types(self) -> List[URIRef]:
|
|
102
|
+
"""
|
|
103
|
+
Getter method corresponding to the ``rdf:type`` RDF predicate.
|
|
104
|
+
|
|
105
|
+
:return: A list containing the requested values if found, None otherwise
|
|
106
|
+
"""
|
|
107
|
+
uri_list: List[URIRef] = self._get_multiple_uri_references(RDF.type)
|
|
108
|
+
return uri_list
|
|
109
|
+
|
|
110
|
+
def _create_type(self, res_type: URIRef, identifier: str = None) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Setter method corresponding to the ``rdf:type`` RDF predicate.
|
|
113
|
+
|
|
114
|
+
:param res_type: The value that will be set as the object of the property related to this method
|
|
115
|
+
:type res_type: URIRef
|
|
116
|
+
:return: None
|
|
117
|
+
"""
|
|
118
|
+
create_type(self.g, self.res, res_type, identifier)
|
|
119
|
+
|
|
120
|
+
def remove_type(self) -> None:
|
|
121
|
+
"""
|
|
122
|
+
Remover method corresponding to the ``rdf:type`` RDF predicate.
|
|
123
|
+
|
|
124
|
+
:return: None
|
|
125
|
+
"""
|
|
126
|
+
self.g.remove((self.res, RDF.type, None))
|
|
127
|
+
|
|
128
|
+
# Overrides __str__ method
|
|
129
|
+
def __str__(self) -> str:
|
|
130
|
+
return str(self.res)
|
|
131
|
+
|
|
132
|
+
def add_triples(self, iterable_of_triples: Iterable) -> None:
|
|
133
|
+
"""
|
|
134
|
+
A utility method that allows to add a batch of triples into the graph of the entity.
|
|
135
|
+
|
|
136
|
+
**WARNING: Only triples that have this entity as their subject will be imported!**
|
|
137
|
+
|
|
138
|
+
:param iterable_of_triples: A collection of triples to be added to the entity
|
|
139
|
+
:type iterable_of_triples: Iterable
|
|
140
|
+
:return: None
|
|
141
|
+
"""
|
|
142
|
+
for s, p, o in iterable_of_triples:
|
|
143
|
+
if s == self.res: # This guarantees that only triples belonging to the resource will be added
|
|
144
|
+
self.g.add((s, p, o))
|
|
145
|
+
|
|
146
|
+
def _get_literal(self, predicate: URIRef) -> Optional[str]:
|
|
147
|
+
result: Optional[str] = None
|
|
148
|
+
for o in self.g.objects(self.res, predicate):
|
|
149
|
+
if type(o) == Literal:
|
|
150
|
+
result = str(o)
|
|
151
|
+
break
|
|
152
|
+
return result
|
|
153
|
+
|
|
154
|
+
def _get_multiple_literals(self, predicate: URIRef) -> List[str]:
|
|
155
|
+
result: List[str] = []
|
|
156
|
+
for o in self.g.objects(self.res, predicate):
|
|
157
|
+
if type(o) == Literal:
|
|
158
|
+
result.append(str(o))
|
|
159
|
+
return result
|
|
160
|
+
|
|
161
|
+
def _get_uri_reference(self, predicate: URIRef) -> Optional[URIRef]:
|
|
162
|
+
result: Optional[URIRef] = None
|
|
163
|
+
for o in self.g.objects(self.res, predicate):
|
|
164
|
+
if type(o) == URIRef:
|
|
165
|
+
result = o
|
|
166
|
+
break
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
def _get_multiple_uri_references(self, predicate: URIRef) -> List[URIRef]:
|
|
170
|
+
result: List[URIRef] = []
|
|
171
|
+
for o in self.g.objects(self.res, predicate):
|
|
172
|
+
if type(o) == URIRef:
|
|
173
|
+
result.append(o)
|
|
174
|
+
return result
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Copyright 2023 Arcangelo Massari <arcangelo.massari@unibo.it>
|
|
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.
|
|
@@ -0,0 +1,61 @@
|
|
|
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.
|
|
16
|
+
from abc import ABC, abstractmethod
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CounterHandler(ABC):
|
|
20
|
+
"""Abstract class representing the interface for every concrete counter handler."""
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def set_counter(self, new_value: int, entity_name: str) -> None:
|
|
24
|
+
"""
|
|
25
|
+
Method signature for concrete implementations that allow setting the counter value
|
|
26
|
+
of provenance entities.
|
|
27
|
+
|
|
28
|
+
:param new_value: The new counter value to be set
|
|
29
|
+
:type new_value: int
|
|
30
|
+
:param entity_name: The entity name
|
|
31
|
+
:type entity_name: str
|
|
32
|
+
:raises NotImplementedError: always
|
|
33
|
+
:return: None
|
|
34
|
+
"""
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def read_counter(self, entity_name: str) -> int:
|
|
39
|
+
"""
|
|
40
|
+
Method signature for concrete implementations that allow reading the counter value
|
|
41
|
+
of graph and provenance entities.
|
|
42
|
+
|
|
43
|
+
:param entity_name: The entity name
|
|
44
|
+
:type entity_name: str
|
|
45
|
+
:raises NotImplementedError: always
|
|
46
|
+
:return: The requested counter value.
|
|
47
|
+
"""
|
|
48
|
+
raise NotImplementedError
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def increment_counter(self, entity_name: str) -> int:
|
|
52
|
+
"""
|
|
53
|
+
Method signature for concrete implementations that allow incrementing by one unit
|
|
54
|
+
the counter value of graph and provenance entities.
|
|
55
|
+
|
|
56
|
+
:param entity_name: The entity name
|
|
57
|
+
:type entity_name: str
|
|
58
|
+
:raises NotImplementedError: always
|
|
59
|
+
:return: The newly-updated (already incremented) counter value.
|
|
60
|
+
"""
|
|
61
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,125 @@
|
|
|
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.
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
|
|
21
|
+
from rdflib_ocdm.counter_handler.counter_handler import CounterHandler
|
|
22
|
+
from rdflib_ocdm.support import is_string_empty
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class FilesystemCounterHandler(CounterHandler):
|
|
26
|
+
"""A concrete implementation of the ``CounterHandler`` interface that persistently stores
|
|
27
|
+
the counter values within the filesystem."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, info_dir: str) -> None:
|
|
30
|
+
"""
|
|
31
|
+
Constructor of the ``FilesystemCounterHandler`` class.
|
|
32
|
+
|
|
33
|
+
:param info_dir: The path to the folder that does/will contain the counter values.
|
|
34
|
+
:type info_dir: str
|
|
35
|
+
:raises ValueError: if ``info_dir`` is None or an empty string.
|
|
36
|
+
"""
|
|
37
|
+
if info_dir is None or is_string_empty(info_dir):
|
|
38
|
+
raise ValueError("info_dir parameter is required!")
|
|
39
|
+
|
|
40
|
+
if info_dir[-1] != os.sep:
|
|
41
|
+
info_dir += os.sep
|
|
42
|
+
|
|
43
|
+
self.info_dir: str = info_dir
|
|
44
|
+
self.prov_files = dict()
|
|
45
|
+
self.provenance_index_filename = 'provenance_index.json'
|
|
46
|
+
|
|
47
|
+
def set_counter(self, new_value: int, entity_name: str) -> None:
|
|
48
|
+
"""
|
|
49
|
+
It allows to set the counter value of provenance entities.
|
|
50
|
+
|
|
51
|
+
:param new_value: The new counter value to be set
|
|
52
|
+
:type new_value: int
|
|
53
|
+
:param entity_name: The entity name
|
|
54
|
+
:type entity_name: str
|
|
55
|
+
:raises ValueError: if ``new_value`` is a negative integer.
|
|
56
|
+
:return: None
|
|
57
|
+
"""
|
|
58
|
+
entity_name = str(entity_name)
|
|
59
|
+
if new_value < 0:
|
|
60
|
+
raise ValueError("new_value must be a non negative integer!")
|
|
61
|
+
file_path: str = self._get_prov_path()
|
|
62
|
+
self.__initialize_file_if_not_existing(file_path, entity_name)
|
|
63
|
+
with open(file_path, 'r', encoding='utf8') as file:
|
|
64
|
+
data = json.load(file)
|
|
65
|
+
with open(file_path, 'w', encoding='utf8') as outfile:
|
|
66
|
+
data[entity_name] = new_value
|
|
67
|
+
outfile.write(data)
|
|
68
|
+
|
|
69
|
+
def read_counter(self, entity_name: str) -> int:
|
|
70
|
+
"""
|
|
71
|
+
It allows to read the counter value of provenance entities.
|
|
72
|
+
|
|
73
|
+
:param entity_name: The entity name
|
|
74
|
+
:type entity_name: str
|
|
75
|
+
:return: The requested counter value.
|
|
76
|
+
"""
|
|
77
|
+
entity_name = str(entity_name)
|
|
78
|
+
file_path: str = self._get_prov_path()
|
|
79
|
+
return self._read_number(file_path, entity_name)
|
|
80
|
+
|
|
81
|
+
def increment_counter(self, entity_name: str) -> int:
|
|
82
|
+
"""
|
|
83
|
+
It allows to increment the counter value of provenance entities by one unit.
|
|
84
|
+
|
|
85
|
+
:param entity_name: The entity name
|
|
86
|
+
:type entity_name: str
|
|
87
|
+
:return: The newly-updated (already incremented) counter value.
|
|
88
|
+
"""
|
|
89
|
+
entity_name = str(entity_name)
|
|
90
|
+
file_path: str = self._get_prov_path()
|
|
91
|
+
return self._add_number(file_path, entity_name)
|
|
92
|
+
|
|
93
|
+
def _get_prov_path(self) -> str:
|
|
94
|
+
return os.path.join(self.info_dir, self.provenance_index_filename)
|
|
95
|
+
|
|
96
|
+
def __initialize_file_if_not_existing(self, file_path: str, entity_name: str):
|
|
97
|
+
entity_name = str(entity_name)
|
|
98
|
+
if not os.path.exists(os.path.dirname(file_path)):
|
|
99
|
+
os.makedirs(os.path.dirname(file_path))
|
|
100
|
+
if not os.path.isfile(file_path):
|
|
101
|
+
with open(file_path, 'w', encoding='utf8') as outfile:
|
|
102
|
+
json_object = json.dumps({entity_name: 0}, ensure_ascii=False, indent=None)
|
|
103
|
+
outfile.write(json_object)
|
|
104
|
+
|
|
105
|
+
def _read_number(self, file_path: str, entity_name: str) -> int:
|
|
106
|
+
self.__initialize_file_if_not_existing(file_path, entity_name)
|
|
107
|
+
with open(file_path, 'r', encoding='utf8') as file:
|
|
108
|
+
data = json.load(file)
|
|
109
|
+
if entity_name in data:
|
|
110
|
+
self.prov_files[entity_name] = data[entity_name]
|
|
111
|
+
else:
|
|
112
|
+
self.prov_files[entity_name] = 0
|
|
113
|
+
return self.prov_files[entity_name]
|
|
114
|
+
|
|
115
|
+
def _add_number(self, file_path: str, entity_name: str) -> int:
|
|
116
|
+
self.__initialize_file_if_not_existing(file_path, entity_name)
|
|
117
|
+
cur_number = self._read_number(file_path, entity_name)
|
|
118
|
+
cur_number += 1
|
|
119
|
+
with open(file_path, 'r', encoding='utf8') as file:
|
|
120
|
+
data = json.load(file)
|
|
121
|
+
with open(file_path, 'w', encoding='utf8') as outfile:
|
|
122
|
+
data[entity_name] = cur_number
|
|
123
|
+
json_object = json.dumps(data, ensure_ascii=False, indent=None)
|
|
124
|
+
outfile.write(json_object)
|
|
125
|
+
return cur_number
|
|
@@ -0,0 +1,81 @@
|
|
|
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.
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from typing import List, Dict
|
|
23
|
+
|
|
24
|
+
from rdflib_ocdm.counter_handler.counter_handler import CounterHandler
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class InMemoryCounterHandler(CounterHandler):
|
|
28
|
+
"""A concrete implementation of the ``CounterHandler`` interface that temporarily stores
|
|
29
|
+
the counter values in the volatile system memory."""
|
|
30
|
+
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Constructor of the ``InMemoryCounterHandler`` class.
|
|
34
|
+
"""
|
|
35
|
+
self.prov_counters = dict()
|
|
36
|
+
|
|
37
|
+
def set_counter(self, new_value: int, entity_name: str) -> None:
|
|
38
|
+
"""
|
|
39
|
+
It allows to set the counter value of graph and provenance entities.
|
|
40
|
+
|
|
41
|
+
:param new_value: The new counter value to be set
|
|
42
|
+
:type new_value: int
|
|
43
|
+
:param entity_name: The entity name
|
|
44
|
+
:type entity_name: str
|
|
45
|
+
:raises ValueError: if ``new_value`` is a negative integer.
|
|
46
|
+
:return: None
|
|
47
|
+
"""
|
|
48
|
+
entity_name = str(entity_name)
|
|
49
|
+
if new_value < 0:
|
|
50
|
+
raise ValueError("new_value must be a non negative integer!")
|
|
51
|
+
self.prov_counters[entity_name] = new_value
|
|
52
|
+
|
|
53
|
+
def read_counter(self, entity_name: str) -> int:
|
|
54
|
+
"""
|
|
55
|
+
It allows to read the counter value of provenance entities.
|
|
56
|
+
|
|
57
|
+
:param entity_name: The entity name
|
|
58
|
+
:type entity_name: str
|
|
59
|
+
:return: The requested counter value.
|
|
60
|
+
"""
|
|
61
|
+
entity_name = str(entity_name)
|
|
62
|
+
if entity_name in self.prov_counters:
|
|
63
|
+
return self.prov_counters[entity_name]
|
|
64
|
+
else:
|
|
65
|
+
self.prov_counters[entity_name] = 0
|
|
66
|
+
return 0
|
|
67
|
+
|
|
68
|
+
def increment_counter(self, entity_name: str) -> int:
|
|
69
|
+
"""
|
|
70
|
+
It allows to increment the counter value of graph and provenance entities by one unit.
|
|
71
|
+
|
|
72
|
+
:param entity_name: The entity name
|
|
73
|
+
:type entity_name: str
|
|
74
|
+
:return: The newly-updated (already incremented) counter value.
|
|
75
|
+
"""
|
|
76
|
+
entity_name = str(entity_name)
|
|
77
|
+
if entity_name in self.prov_counters:
|
|
78
|
+
self.prov_counters[entity_name] += 1
|
|
79
|
+
else:
|
|
80
|
+
self.prov_counters[entity_name] = 1
|
|
81
|
+
return self.prov_counters[entity_name]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Copyright (c) 2023, Arcangelo Massari <arcangelo.massari@unibo.it>
|
|
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.
|
|
16
|
+
|
|
17
|
+
import sqlite3
|
|
18
|
+
|
|
19
|
+
from rdflib_ocdm.counter_handler.counter_handler import CounterHandler
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SqliteCounterHandler(CounterHandler):
|
|
23
|
+
"""A concrete implementation of the ``CounterHandler`` interface that persistently stores
|
|
24
|
+
the counter values within a SQLite database."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, database: str) -> None:
|
|
27
|
+
"""
|
|
28
|
+
Constructor of the ``SqliteCounterHandler`` class.
|
|
29
|
+
|
|
30
|
+
:param database: The name of the database
|
|
31
|
+
:type info_dir: str
|
|
32
|
+
"""
|
|
33
|
+
sqlite3.threadsafety = 3
|
|
34
|
+
self.con = sqlite3.connect(database)
|
|
35
|
+
self.cur = self.con.cursor()
|
|
36
|
+
self.cur.execute("""CREATE TABLE IF NOT EXISTS info(
|
|
37
|
+
entity TEXT PRIMARY KEY,
|
|
38
|
+
count INTEGER)""")
|
|
39
|
+
|
|
40
|
+
def set_counter(self, new_value: int, entity_name: str) -> None:
|
|
41
|
+
"""
|
|
42
|
+
It allows to set the counter value of provenance entities.
|
|
43
|
+
|
|
44
|
+
:param new_value: The new counter value to be set
|
|
45
|
+
:type new_value: int
|
|
46
|
+
:param entity_name: The entity name
|
|
47
|
+
:type entity_name: str
|
|
48
|
+
:raises ValueError: if ``new_value`` is a negative integer.
|
|
49
|
+
:return: None
|
|
50
|
+
"""
|
|
51
|
+
entity_name = str(entity_name)
|
|
52
|
+
if new_value < 0:
|
|
53
|
+
raise ValueError("new_value must be a non negative integer!")
|
|
54
|
+
self.cur.execute(f"INSERT OR REPLACE INTO info (entity, count) VALUES ('{entity_name}', {new_value})")
|
|
55
|
+
self.con.commit()
|
|
56
|
+
|
|
57
|
+
def read_counter(self, entity_name: str) -> int:
|
|
58
|
+
"""
|
|
59
|
+
It allows to read the counter value of provenance entities.
|
|
60
|
+
|
|
61
|
+
:param entity_name: The entity name
|
|
62
|
+
:type entity_name: str
|
|
63
|
+
:return: The requested counter value.
|
|
64
|
+
"""
|
|
65
|
+
entity_name = str(entity_name)
|
|
66
|
+
result = self.cur.execute(f"SELECT count FROM info WHERE entity='{entity_name}'")
|
|
67
|
+
rows = result.fetchall()
|
|
68
|
+
if len(rows) == 1:
|
|
69
|
+
return rows[0][0]
|
|
70
|
+
elif len(rows) == 0:
|
|
71
|
+
return 0
|
|
72
|
+
else:
|
|
73
|
+
raise(Exception("There is more than one counter for this entity. The databse id broken"))
|
|
74
|
+
|
|
75
|
+
def increment_counter(self, entity_name: str) -> int:
|
|
76
|
+
"""
|
|
77
|
+
It allows to increment the counter value of graph and provenance entities by one unit.
|
|
78
|
+
|
|
79
|
+
:param entity_name: The entity name
|
|
80
|
+
:type entity_name: str
|
|
81
|
+
:return: The newly-updated (already incremented) counter value.
|
|
82
|
+
"""
|
|
83
|
+
entity_name = str(entity_name)
|
|
84
|
+
cur_count = self.read_counter(entity_name)
|
|
85
|
+
count = cur_count + 1
|
|
86
|
+
self.set_counter(count, entity_name)
|
|
87
|
+
return count
|