fosKeyMan
Loading...
Searching...
No Matches
foldercontent.py
Go to the documentation of this file.
2r"""
3Implements the backend to manage the content sensor keys on disk.
4
5\author Bertram Richter, Xiaoli Song
6\date 2025
7"""
8
9
10from datetime import datetime
11import os
12import json
13
14
16 r"""
17 Handle reading JSON files (userProperties.json and gageSegment.json) from keyfile folders located
18 in activated or deactivated directories. Also reads and updates the metadata.json file.
19 """
20 def __init__(self, activated_path, deactivated_path):
21 r"""
22 Initialize the FolderContent with paths for activated and deactivated keyfile directories.
23 \param activated_path (str): The path to the directory containing activated keyfiles.
24 \param deactivated_path (str): The path to the directory containing deactivated keyfiles.
25 """
26 self.activated_path = activated_path
27 self.deactivated_path = deactivated_path
28
29 def read_user_properties(self, key):
30 r"""
31 Read the content of the userProperties.json file from the specified key's folder.
32 The method checks if the key's folder exists in the activated or deactivated directories,
33 and attempts to load the 'userProperties.json' file from within the folder.
34 \param key (str): The name of the key to locate its corresponding key file folder.
35 \return (dict or None): A dictionary containing the parsed JSON data, or None if the file does not exist.
36 """
37 keyfile_path_act = os.path.join(self.activated_path, key, 'userProperties.json')
38 keyfile_path_deact = os.path.join(self.deactivated_path, key, 'userProperties.json')
39 if os.path.exists(keyfile_path_act):
40 return self.load_json(keyfile_path_act)
41 elif os.path.exists(keyfile_path_deact):
42 return self.load_json(keyfile_path_deact)
43 else:
44 return None
45
46 def read_gage_segment(self, key):
47 r"""
48 Read the content of the gageSegment.json file from the specified key's keyfile folder.
49 Similar to `read_user_properties`, this method checks if the key's keyfile folder exists in the activated or
50 deactivated directories and attempts to load the 'gageSegment.json' file from within the folder.
51 \param key (str): The name of the key to locate its corresponding key file folder.
52 \return (dict or None): A dictionary containing the parsed JSON data, or None if the file does not exist.
53 """
54 keyfile_path_act = os.path.join(self.activated_path, key, 'gageSegment.json')
55 keyfile_path_deact = os.path.join(self.deactivated_path, key, 'gageSegment.json')
56 if os.path.exists(keyfile_path_act):
57 return self.load_json(keyfile_path_act)
58 elif os.path.exists(keyfile_path_deact):
59 return self.load_json(keyfile_path_deact)
60 else:
61 return None
62
63 def read_od6ref_file(self, key):
64 r"""
65 Read the first line of the .od6ref file from the specified key's keyfile folder.
66 \param key (str): The name of the key to locate its corresponding folder.
67 \return (dict or None): A dictionary containing the parsed JSON data, or None if the file does not exist.
68 """
69 keyfile_path_act = os.path.join(self.activated_path, key, f'{key}.od6ref')
70 keyfile_path_deact = os.path.join(self.deactivated_path, key, f'{key}.od6ref')
71 if os.path.exists(keyfile_path_act):
72 return self.load_json(keyfile_path_act)
73 elif os.path.exists(keyfile_path_deact):
74 return self.load_json(keyfile_path_deact)
75 else:
76 return None
77
78 def load_json(self, file_path):
79 r"""
80 Load and parse a JSON file from within a key file.
81 \param file_path (str): The path to the JSON or binary file.
82 \return (dict or None): A dictionary containing the parsed JSON data, or None if the file could not be read.
83 """
84 try:
85 with open(file_path, 'rb') as file:
86 # data = json.load(file)
87 # return data
88 first_line = file.readline().strip()
89 return json.loads(first_line)
90 except json.JSONDecodeError:
91 return None
92 except Exception as e:
93 print(f"Error reading JSON from {file_path}: {e}")
94 return None
95
96 def full_text_search(self, search_term, keyfile=None):
97 r"""
98 Full-text search inside all JSON files.
99 \param search_term (str): The term to search within the JSON files.
100 \param keyfile (str or None): The specific key for keyfile to search within. If None, search in all keyfile folders.
101 \return (list of tuples): A list of (keyfile name, matching content) tuples where the search term was found.
102 """
103 results = []
104 for directory in [self.activated_path, self.deactivated_path]:
105 if not os.path.exists(directory):
106 continue
107 if keyfile:
108 folder_path = os.path.join(directory, keyfile)
109 if os.path.exists(folder_path) and os.path.isdir(folder_path):
110 result = self.search_in_folder(folder_path, search_term)
111 if result:
112 results.append(result)
113 continue
114 for folder_name in os.listdir(directory):
115 folder_path = os.path.join(directory, folder_name)
116 if os.path.isdir(folder_path):
117 result = self.search_in_folder(folder_path, search_term)
118 if result:
119 results.append(result)
120 return results
121
122 def search_in_folder(self, folder_path, search_term):
123 r"""
124 Search inside a key file folder for the given search term in the JSON files.
125 \param folder_path (str): The path to the key file.
126 \param search_term (str): The term to search for.
127 \return (tuple or None): The serial number and matching content if the search term is found, None otherwise.
128 """
129 try:
130 keyfile = os.path.basename(folder_path)
131 all_matches = []
132 for file_name in os.listdir(folder_path):
133 if file_name.endswith(".json"):
134 file_path = os.path.join(folder_path, file_name)
135 with open(file_path, 'r', encoding='utf-8') as file:
136 try:
137 data = json.load(file)
138 result = self.search_in_json(data, search_term)
139 if result:
140 all_matches.extend(result)
141 except json.JSONDecodeError:
142 continue
143 if all_matches:
144 return keyfile, all_matches
145 except Exception as e:
146 print(f"Error reading folder {folder_path}: {e}")
147 return None
148
149 def search_in_json(self, data, search_term):
150 r"""
151 Recursively search through a JSON for the search term, case-insensitive, with partial matching.
152 \param data (dict or list): The JSON data to search through.
153 \param search_term (str): The term to search for.
154 \return (list): A list of matching key-value pairs if the search term is found, otherwise an empty list.
155 """
156 search_term_lower = search_term.lower()
157 matches = []
158 if isinstance(data, dict):
159 for key, value in data.items():
160 key_str = str(key).lower()
161 if search_term_lower in key_str:
162 matches.append({key: value})
163 if isinstance(value, (str, int, float, bool)):
164 value_str = str(value).lower()
165 if search_term_lower in value_str:
166 matches.append({key: value})
167 elif isinstance(value, (dict, list)):
168 matches.extend(self.search_in_json(value, search_term))
169 elif isinstance(data, list):
170 for item in data:
171 matches.extend(self.search_in_json(item, search_term))
172 return matches
173
174 def get_last_edit_date(self, key):
175 r"""
176 Retrieve the lastEditDate from the userProperties.json file of the specified key's ZIP file.
177 \param key (str): The name of the key to locate its corresponding ZIP file.
178 \return (datetime or None): The lastEditDate as a datetime object, or None if not found or not valid.
179 """
180 user_properties = self.read_user_properties(key)
181 if user_properties:
182 last_edit_date_str = user_properties.get('lastEditDate')
183 if last_edit_date_str:
184 try:
185 return datetime.strptime(last_edit_date_str, "%a %b %d %Y")
186 except ValueError as e:
187 print(f"Error parsing date: {e}")
188 return None
189 return None
190
192 r"""
193 Read the userSensorName from the userProperties.json file in the specified key's folder.
194 \param key (str): The name of the key to locate its corresponding folder.
195 \return (str or None): The value of userSensorName if it exists, or None otherwise.
196 """
197 user_properties = self.read_user_properties(key)
198 if user_properties:
199 return user_properties.get("userSensorName", None)
200 else:
201 return None
202
203 def edit_sensor_name_for_key(self, key, new_sensor_name):
204 r"""
205 Edit the userSensorName for a specified key in the userProperties.json file and synchronize the lastEditDate.
206 \param key (str): The key whose userSensorName needs to be updated.
207 \param new_sensor_name (str): The new sensor name to set.
208 \return (bool): True if the update was successful, False otherwise.
209 """
210 keyfile_path_act = os.path.join(self.activated_path, key, 'userProperties.json')
211 keyfile_path_deact = os.path.join(self.deactivated_path, key, 'userProperties.json')
212 json_file_path = keyfile_path_act if os.path.exists(keyfile_path_act) else keyfile_path_deact
213 if not json_file_path:
214 print(f"No userProperties.json found for key: {key}")
215 return False
216 try:
217 with open(json_file_path, 'r', encoding='utf-8') as file:
218 first_line = file.readline().strip()
219 user_properties = json.loads(first_line)
220 user_properties["userSensorName"] = new_sensor_name
221 user_properties["lastEditDate"] = datetime.now().strftime("%a %b %d %Y")
222 with open(json_file_path, 'w', encoding='utf-8') as file:
223 file.write(json.dumps(user_properties, ensure_ascii=False))
224 return True
225 except (json.JSONDecodeError, IOError) as e:
226 return False
227
229 r"""
230 Read the "sensorLength (m)" value from the .od6ref file in the specified key's folder.
231 \param key (str): The name of the key to locate its corresponding folder.
232 \return (float or None): The value of "sensorLength (m)" if it exists, or None otherwise.
233 """
234 od6ref_data = self.read_od6ref_file(key)
235 if od6ref_data:
236 return od6ref_data.get("sensorDataProcParams", {}).get("sensorLength (m)", None)
237 else:
238 return None
239
240 def read_metadata(self, key):
241 r"""
242 Read the content of the metadata.json file from the specified key file.
243
244 \param key: The name of the key to locate its corresponding folder.
245 \return: A dictionary containing the parsed JSON data, or an empty dict if the file does not exist.
246 """
247 keyfile_path_act = os.path.join(self.activated_path, key, "metadata.json")
248 keyfile_path_deact = os.path.join(self.deactivated_path, key, "metadata.json")
249
250 file_path = keyfile_path_act if os.path.exists(keyfile_path_act) else keyfile_path_deact
251 if file_path and os.path.exists(file_path):
252 try:
253 with open(file_path, "r", encoding="utf-8") as file:
254 return json.load(file)
255 except json.JSONDecodeError:
256 print(f"Error: Could not decode JSON in {file_path}")
257 return {}
258 return {}
259
260 def update_metadata(self, key, metadata):
261 r"""
262 Update the metadata.json for a given key (serial number).
263
264 \param key: The name of the key to locate its corresponding folder.
265 \param metadata: Dictionary to save.
266 """
267 keyfile_path_act = os.path.join(self.activated_path, key, "metadata.json")
268 keyfile_path_deact = os.path.join(self.deactivated_path, key, "metadata.json")
269
270 file_path = keyfile_path_act if os.path.exists(keyfile_path_act) else keyfile_path_deact
271
272 if file_path and os.path.exists(file_path):
273 try:
274 with open(file_path, "w", encoding="utf-8") as f:
275 json.dump(metadata, f, indent=4, ensure_ascii=False)
276 except Exception as e:
277 print(f"Failed to save metadata for '{key}': {e}")
278
279
Handle reading JSON files (userProperties.json and gageSegment.json) from keyfile folders located in ...
read_user_properties(self, key)
Read the content of the userProperties.json file from the specified key's folder.
full_text_search(self, search_term, keyfile=None)
Full-text search inside all JSON files.
read_sensor_length_for_key(self, key)
Read the "sensorLength (m)" value from the .od6ref file in the specified key's folder.
search_in_json(self, data, search_term)
Recursively search through a JSON for the search term, case-insensitive, with partial matching.
read_od6ref_file(self, key)
Read the first line of the .od6ref file from the specified key's keyfile folder.
read_gage_segment(self, key)
Read the content of the gageSegment.json file from the specified key's keyfile folder.
get_last_edit_date(self, key)
Retrieve the lastEditDate from the userProperties.json file of the specified key's ZIP file.
search_in_folder(self, folder_path, search_term)
Search inside a key file folder for the given search term in the JSON files.
read_metadata(self, key)
Read the content of the metadata.json file from the specified key file.
edit_sensor_name_for_key(self, key, new_sensor_name)
Edit the userSensorName for a specified key in the userProperties.json file and synchronize the lastE...
update_metadata(self, key, metadata)
Update the metadata.json for a given key (serial number).
load_json(self, file_path)
Load and parse a JSON file from within a key file.
__init__(self, activated_path, deactivated_path)
Initialize the FolderContent with paths for activated and deactivated keyfile directories.
read_sensor_name_for_key(self, key)
Read the userSensorName from the userProperties.json file in the specified key's folder.