Coverage for rdflib_ocdm/counter_handler/filesystem_counter_handler.py: 100%
61 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.
16from __future__ import annotations
18import json
19import os
21from rdflib_ocdm.counter_handler.counter_handler import CounterHandler
22from rdflib_ocdm.support import is_string_empty
25class FilesystemCounterHandler(CounterHandler):
26 """A concrete implementation of the ``CounterHandler`` interface that persistently stores
27 the counter values within the filesystem."""
29 def __init__(self, info_dir: str) -> None:
30 """
31 Constructor of the ``FilesystemCounterHandler`` class.
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!")
40 if info_dir[-1] != os.sep:
41 info_dir += os.sep
43 self.info_dir: str = info_dir
44 self.prov_files = dict()
45 self.provenance_index_filename = 'provenance_index.json'
47 def set_counter(self, new_value: int, entity_name: str) -> None:
48 """
49 It allows to set the counter value of provenance entities.
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 json.dump(obj=data, fp=outfile, ensure_ascii=False, indent=False)
69 def read_counter(self, entity_name: str) -> int:
70 """
71 It allows to read the counter value of provenance entities.
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)
81 def increment_counter(self, entity_name: str) -> int:
82 """
83 It allows to increment the counter value of provenance entities by one unit.
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)
93 def _get_prov_path(self) -> str:
94 return os.path.join(self.info_dir, self.provenance_index_filename)
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.dump({entity_name: 0}, ensure_ascii=False, indent=None, fp=outfile)
104 def _read_number(self, file_path: str, entity_name: str) -> int:
105 self.__initialize_file_if_not_existing(file_path, entity_name)
106 with open(file_path, 'r', encoding='utf8') as file:
107 data = json.load(file)
108 if entity_name in data:
109 self.prov_files[entity_name] = data[entity_name]
110 else:
111 self.prov_files[entity_name] = 0
112 return self.prov_files[entity_name]
114 def _add_number(self, file_path: str, entity_name: str) -> int:
115 self.__initialize_file_if_not_existing(file_path, entity_name)
116 cur_number = self._read_number(file_path, entity_name)
117 cur_number += 1
118 with open(file_path, 'r', encoding='utf8') as file:
119 data = json.load(file)
120 with open(file_path, 'w', encoding='utf8') as outfile:
121 data[entity_name] = cur_number
122 json_object = json.dumps(data, ensure_ascii=False, indent=None)
123 outfile.write(json_object)
124 return cur_number