mrradix commited on
Commit
8071ef6
·
verified ·
1 Parent(s): c358a13

Update utils/storage.py

Browse files
Files changed (1) hide show
  1. utils/storage.py +120 -188
utils/storage.py CHANGED
@@ -1,239 +1,171 @@
1
  import json
2
  import os
 
3
  from pathlib import Path
4
- from typing import Dict, List, Any, Union, Optional
 
5
 
6
- from utils.config import FILE_PATHS, DATA_DIR, EXPORT_DIR
7
- from utils.logging import get_logger, log_performance
8
  from utils.error_handling import handle_data_exceptions, DataError, ValidationError
 
9
 
10
- # Initialize logger
11
  logger = get_logger(__name__)
12
 
13
- @handle_data_exceptions
14
- def load_data(file_path: Union[str, Path], default: Any = None) -> Any:
15
- """
16
- Load data from a JSON file
17
-
18
- Args:
19
- file_path: Path to the JSON file
20
- default: Default value to return if file doesn't exist
21
-
22
- Returns:
23
- Data from the JSON file or default value
24
-
25
- Raises:
26
- DataError: If there's an error loading the data
27
- """
28
- file_path = Path(file_path)
29
-
30
- if not file_path.exists():
31
- logger.debug(f"File not found: {file_path}. Returning default value.")
32
- return default if default is not None else {}
33
-
34
- try:
35
- logger.debug(f"Loading data from {file_path}")
36
- with open(file_path, 'r', encoding='utf-8') as f:
37
- data = json.load(f)
38
- logger.debug(f"Successfully loaded data from {file_path}")
39
- return data
40
- except json.JSONDecodeError as e:
41
- logger.error(f"JSON decode error in {file_path}: {str(e)}")
42
- raise DataError(f"Invalid JSON format in {file_path}", {"original_error": str(e)}) from e
43
- except FileNotFoundError as e:
44
- logger.error(f"File not found: {file_path}")
45
- return default if default is not None else {}
46
- except Exception as e:
47
- logger.error(f"Unexpected error loading {file_path}: {str(e)}")
48
- raise DataError(f"Error loading data from {file_path}", {"original_error": str(e)}) from e
49
 
50
  @handle_data_exceptions
51
- def save_data(file_path: Union[str, Path], data: Any) -> bool:
52
  """
53
- Save data to a JSON file
54
 
55
  Args:
56
- file_path: Path to the JSON file
57
  data: Data to save
58
-
 
 
59
  Returns:
60
- True if successful, False otherwise
61
-
62
- Raises:
63
- DataError: If there's an error saving the data
64
  """
65
- file_path = Path(file_path)
66
-
67
- # Create directory if it doesn't exist
68
- file_path.parent.mkdir(parents=True, exist_ok=True)
69
-
70
  try:
71
- logger.debug(f"Saving data to {file_path}")
72
- with open(file_path, 'w', encoding='utf-8') as f:
73
- json.dump(data, f, indent=2, ensure_ascii=False)
74
- logger.debug(f"Successfully saved data to {file_path}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  return True
 
76
  except Exception as e:
77
- logger.error(f"Error saving data to {file_path}: {str(e)}")
78
- raise DataError(f"Error saving data to {file_path}", {"original_error": str(e)}) from e
79
 
80
  @handle_data_exceptions
81
- def export_data(data: Any, format_type: str = 'json', file_path: Optional[Union[str, Path]] = None) -> Union[str, bool]:
82
  """
83
- Export data to various formats
84
 
85
  Args:
86
- data: Data to export
87
- format_type: Format to export to ('json', 'csv', 'markdown')
88
- file_path: Path to save the exported data (optional)
89
-
90
  Returns:
91
- Exported data as string if file_path is None, otherwise True if successful
92
-
93
- Raises:
94
- ValidationError: If the data format is invalid
95
- DataError: If there's an error exporting the data
96
  """
97
- logger.debug(f"Exporting data to {format_type} format")
98
-
99
- # Validate format type
100
- if format_type not in ['json', 'csv', 'markdown']:
101
- logger.error(f"Unsupported format type: {format_type}")
102
- raise ValidationError(f"Unsupported format type: {format_type}")
103
-
104
  try:
105
- if format_type == 'json':
106
- result = json.dumps(data, indent=2, ensure_ascii=False)
107
- elif format_type == 'csv':
108
- # Simple CSV conversion for list of dictionaries
109
- if not isinstance(data, list) or not all(isinstance(item, dict) for item in data):
110
- logger.error("CSV export only supports list of dictionaries")
111
- raise ValidationError("CSV export only supports list of dictionaries")
112
-
113
- if not data:
114
- return ""
115
-
116
- headers = list(data[0].keys())
117
- result = ",".join(headers) + "\n"
118
-
119
- for item in data:
120
- row = ",".join([str(item.get(header, "")).replace(",", ";") for header in headers])
121
- result += row + "\n"
122
- elif format_type == 'markdown':
123
- # Simple Markdown conversion for list of dictionaries
124
- if not isinstance(data, list) or not all(isinstance(item, dict) for item in data):
125
- logger.error("Markdown export only supports list of dictionaries")
126
- raise ValidationError("Markdown export only supports list of dictionaries")
127
-
128
- if not data:
129
- return ""
130
-
131
- headers = list(data[0].keys())
132
- result = "| " + " | ".join(headers) + " |\n"
133
- result += "| " + " | ".join(["---" for _ in headers]) + " |\n"
134
-
135
- for item in data:
136
- row = "| " + " | ".join([str(item.get(header, "")).replace("|", "\|") for header in headers]) + " |"
137
- result += row + "\n"
138
- except Exception as e:
139
- logger.error(f"Error formatting data for export: {str(e)}")
140
- raise DataError(f"Error formatting data for export", {"original_error": str(e)}) from e
141
-
142
- if file_path is None:
143
- return result
144
-
145
- file_path = Path(file_path)
146
- file_path.parent.mkdir(parents=True, exist_ok=True)
147
-
148
- try:
149
- logger.debug(f"Writing exported data to {file_path}")
150
- with open(file_path, 'w', encoding='utf-8') as f:
151
- f.write(result)
152
- logger.debug(f"Successfully exported data to {file_path}")
153
- return True
154
  except Exception as e:
155
- logger.error(f"Error writing exported data to {file_path}: {str(e)}")
156
- raise DataError(f"Error exporting data to {file_path}", {"original_error": str(e)}) from e
157
 
158
  @handle_data_exceptions
159
- def get_file_path(data_type: str) -> Path:
160
  """
161
- Get the file path for a specific data type
162
 
163
  Args:
164
- data_type: Type of data (e.g., 'tasks', 'notes', 'goals')
165
-
166
  Returns:
167
- Path object for the specified data type
168
-
169
- Raises:
170
- ValidationError: If the data type is invalid
171
  """
172
- if data_type not in FILE_PATHS:
173
- logger.error(f"Invalid data type: {data_type}")
174
- raise ValidationError(f"Invalid data type: {data_type}")
175
-
176
- return FILE_PATHS[data_type]
 
 
 
 
 
 
 
 
 
177
 
178
  @handle_data_exceptions
179
- def create_backup(backup_name: str = None) -> Optional[Path]:
180
  """
181
- Create a backup of all data files
182
 
183
- Args:
184
- backup_name: Optional name for the backup file
185
-
186
  Returns:
187
- Path to the backup file if successful, None otherwise
188
  """
189
- import zipfile
190
- from datetime import datetime
191
-
192
- if backup_name is None:
193
- backup_name = f"mona_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
194
-
195
- backup_file = EXPORT_DIR / f"{backup_name}.zip"
196
-
197
  try:
198
- logger.info(f"Creating backup: {backup_file}")
199
- with zipfile.ZipFile(backup_file, 'w', zipfile.ZIP_DEFLATED) as zipf:
200
- for data_type, file_path in FILE_PATHS.items():
201
- if file_path.exists():
202
- zipf.write(file_path, arcname=file_path.name)
203
- logger.debug(f"Added {file_path} to backup")
204
-
205
- logger.info(f"Backup created successfully: {backup_file}")
206
- return backup_file
207
  except Exception as e:
208
- logger.error(f"Error creating backup: {str(e)}")
209
- raise DataError(f"Error creating backup", {"original_error": str(e)}) from e
210
 
211
- @handle_data_exceptions
212
- def restore_backup(backup_file: Union[str, Path]) -> bool:
213
  """
214
- Restore data from a backup file
215
 
216
  Args:
217
- backup_file: Path to the backup file
218
-
219
  Returns:
220
- True if successful, False otherwise
221
  """
222
- import zipfile
223
-
224
- backup_file = Path(backup_file)
225
-
226
- if not backup_file.exists():
227
- logger.error(f"Backup file not found: {backup_file}")
228
- raise DataError(f"Backup file not found: {backup_file}")
229
-
230
  try:
231
- logger.info(f"Restoring from backup: {backup_file}")
232
- with zipfile.ZipFile(backup_file, 'r') as zipf:
233
- zipf.extractall(DATA_DIR)
234
-
235
- logger.info(f"Backup restored successfully from {backup_file}")
236
- return True
237
  except Exception as e:
238
- logger.error(f"Error restoring backup: {str(e)}")
239
- raise DataError(f"Error restoring backup", {"original_error": str(e)}) from e
 
 
 
 
 
 
1
  import json
2
  import os
3
+ import pickle
4
  from pathlib import Path
5
+ from typing import Any, Dict, Optional, Union
6
+ import pandas as pd
7
 
 
 
8
  from utils.error_handling import handle_data_exceptions, DataError, ValidationError
9
+ from utils.logging import get_logger
10
 
 
11
  logger = get_logger(__name__)
12
 
13
+ # Create data directory if it doesn't exist
14
+ DATA_DIR = Path("data")
15
+ DATA_DIR.mkdir(exist_ok=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  @handle_data_exceptions
18
+ def save_data(data: Any, filename: str, format_type: str = "json") -> bool:
19
  """
20
+ Save data to file
21
 
22
  Args:
 
23
  data: Data to save
24
+ filename: Name of the file
25
+ format_type: Format to save ('json', 'pickle', 'csv', 'excel')
26
+
27
  Returns:
28
+ bool: True if successful, False otherwise
 
 
 
29
  """
 
 
 
 
 
30
  try:
31
+ filepath = DATA_DIR / filename
32
+
33
+ if format_type == "json":
34
+ with open(filepath, 'w', encoding='utf-8') as f:
35
+ json.dump(data, f, indent=2, ensure_ascii=False, default=str)
36
+
37
+ elif format_type == "pickle":
38
+ with open(filepath, 'wb') as f:
39
+ pickle.dump(data, f)
40
+
41
+ elif format_type == "csv":
42
+ if isinstance(data, pd.DataFrame):
43
+ data.to_csv(filepath, index=False)
44
+ else:
45
+ raise DataError("CSV format requires pandas DataFrame")
46
+
47
+ elif format_type == "excel":
48
+ if isinstance(data, pd.DataFrame):
49
+ data.to_excel(filepath, index=False)
50
+ else:
51
+ raise DataError("Excel format requires pandas DataFrame")
52
+
53
+ else:
54
+ raise DataError(f"Unsupported format: {format_type}")
55
+
56
+ logger.info(f"Data saved successfully to {filepath}")
57
  return True
58
+
59
  except Exception as e:
60
+ logger.error(f"Failed to save data to {filename}: {str(e)}")
61
+ raise DataError(f"Failed to save data: {str(e)}")
62
 
63
  @handle_data_exceptions
64
+ def load_data(filename: str, format_type: str = "json") -> Any:
65
  """
66
+ Load data from file
67
 
68
  Args:
69
+ filename: Name of the file
70
+ format_type: Format to load ('json', 'pickle', 'csv', 'excel')
71
+
 
72
  Returns:
73
+ Any: Loaded data or None if failed
 
 
 
 
74
  """
 
 
 
 
 
 
 
75
  try:
76
+ filepath = DATA_DIR / filename
77
+
78
+ if not filepath.exists():
79
+ logger.warning(f"File not found: {filepath}")
80
+ return None
81
+
82
+ if format_type == "json":
83
+ with open(filepath, 'r', encoding='utf-8') as f:
84
+ data = json.load(f)
85
+
86
+ elif format_type == "pickle":
87
+ with open(filepath, 'rb') as f:
88
+ data = pickle.load(f)
89
+
90
+ elif format_type == "csv":
91
+ data = pd.read_csv(filepath)
92
+
93
+ elif format_type == "excel":
94
+ data = pd.read_excel(filepath)
95
+
96
+ else:
97
+ raise DataError(f"Unsupported format: {format_type}")
98
+
99
+ logger.info(f"Data loaded successfully from {filepath}")
100
+ return data
101
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  except Exception as e:
103
+ logger.error(f"Failed to load data from {filename}: {str(e)}")
104
+ raise DataError(f"Failed to load data: {str(e)}")
105
 
106
  @handle_data_exceptions
107
+ def delete_data(filename: str) -> bool:
108
  """
109
+ Delete data file
110
 
111
  Args:
112
+ filename: Name of the file to delete
113
+
114
  Returns:
115
+ bool: True if successful, False otherwise
 
 
 
116
  """
117
+ try:
118
+ filepath = DATA_DIR / filename
119
+
120
+ if filepath.exists():
121
+ filepath.unlink()
122
+ logger.info(f"File deleted successfully: {filepath}")
123
+ return True
124
+ else:
125
+ logger.warning(f"File not found for deletion: {filepath}")
126
+ return False
127
+
128
+ except Exception as e:
129
+ logger.error(f"Failed to delete file {filename}: {str(e)}")
130
+ raise DataError(f"Failed to delete file: {str(e)}")
131
 
132
  @handle_data_exceptions
133
+ def list_data_files() -> list:
134
  """
135
+ List all data files in the data directory
136
 
 
 
 
137
  Returns:
138
+ list: List of filenames
139
  """
 
 
 
 
 
 
 
 
140
  try:
141
+ files = [f.name for f in DATA_DIR.iterdir() if f.is_file()]
142
+ logger.info(f"Found {len(files)} data files")
143
+ return files
144
+
 
 
 
 
 
145
  except Exception as e:
146
+ logger.error(f"Failed to list data files: {str(e)}")
147
+ return []
148
 
149
+ def get_file_size(filename: str) -> Optional[int]:
 
150
  """
151
+ Get file size in bytes
152
 
153
  Args:
154
+ filename: Name of the file
155
+
156
  Returns:
157
+ int: File size in bytes or None if file doesn't exist
158
  """
 
 
 
 
 
 
 
 
159
  try:
160
+ filepath = DATA_DIR / filename
161
+ if filepath.exists():
162
+ return filepath.stat().st_size
163
+ return None
 
 
164
  except Exception as e:
165
+ logger.error(f"Failed to get file size for {filename}: {str(e)}")
166
+ return None
167
+
168
+ def ensure_data_directory():
169
+ """Ensure data directory exists"""
170
+ DATA_DIR.mkdir(exist_ok=True)
171
+ return DATA_DIR