Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 27 additions & 17 deletions LiteJsonDb/LiteJsonDb.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import logging
import sys
from typing import Any, Dict, Optional
from .handler import (
Encryption, DatabaseOperations, DataManipulation
Expand All @@ -24,6 +25,12 @@
def setup_logging(enable_log):
if enable_log:
logging.basicConfig(filename=os.path.join(DATABASE_DIR, 'LiteJsonDb.log'), level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Add console handler for user messages
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
console_formatter = logging.Formatter('%(message)s')
console_handler.setFormatter(console_formatter)
logging.getLogger().addHandler(console_handler)

class JsonDB(Encryption, DatabaseOperations, DataManipulation):
"""
Expand Down Expand Up @@ -57,7 +64,8 @@ def __init__(self, filename="db.json", backup_filename="db_backup.json",
self.observers = {}
self.csv_exporter = CSVExporter(DATABASE_DIR)
setup_logging(self.enable_log)
Encryption.__init__(self, encryption_method, encryption_key)
self.logger = logging.getLogger('LiteJsonDb')
Encryption.__init__(self, encryption_method, encryption_key)
DatabaseOperations.__init__(self, enable_log, auto_backup)
DataManipulation.__init__(self)
self._load_db()
Expand All @@ -75,7 +83,7 @@ def backup_to_telegram(self, token: str, chat_id: str):
try:
telegram_bot.backup_to_telegram(self.filename)
except Exception as e:
print(f"\033[90m#bugs\033[0m Telegram backup took a wrong turn! Error: {e}")
self.logger.error(f"\033[90m#bugs\033[0m Telegram backup took a wrong turn! Error: {e}")
if self.enable_log:
logging.error(f"Error sending backup to Telegram: {e}")

Expand All @@ -91,40 +99,42 @@ def export_to_csv(self, data_key: Optional[str] = None):
data = self.db[data_key]
csv_path = self.csv_exporter.export(data, f"{data_key}_export.csv")
if csv_path:
print(f"🎉 Hooray! CSV exported to: {csv_path}")
self.logger.info(f"🎉 Hooray! CSV exported to: {csv_path}")
else:
print(f"\033[90m#bugs\033[0m Could not export '{data_key}' to CSV!")
self.logger.error(f"\033[90m#bugs\033[0m Could not export '{data_key}' to CSV!")
else:
print(f"\033[90m#bugs\033[0m Key '{data_key}' not found, is it hiding? Tip: Double-check it!")
self.logger.error(f"\033[90m#bugs\033[0m Key '{data_key}' not found, is it hiding? Tip: Double-check it!")
else:
if self.db:
csv_path = self.csv_exporter.export(self.db, "full_database_export.csv")
if csv_path:
print(f"🎉 Full database exported to: {csv_path}")
self.logger.info(f"🎉 Full database exported to: {csv_path}")
else:
print("\033[90m#bugs\033[0m Database export failed. It's shy!")
self.logger.error("\033[90m#bugs\033[0m Database export failed. It's shy!")
else:
print("\033[90m#bugs\033[0m Database is empty, ghost town vibes!")
self.logger.error("\033[90m#bugs\033[0m Database is empty, ghost town vibes!")

def search_data(self, value: Any, key: Optional[str] = None) -> Optional[Dict[str, Any]]:
def search_data(self, value: Any, key: Optional[str] = None, substring: bool = False, case_sensitive: bool = True) -> Optional[Dict[str, Any]]:
"""
Searches for a value within the database.

Args:
value (Any): The value to search for.
key (Optional[str]): If provided, searches only within the values associated with this key.
Returns:
Optional[Dict[str, Any]]: Returns the matching dictionary or None if not found.
Args:
value (Any): The value to search for.
key (Optional[str]): If provided, searches only within the values associated with this key.
substring (bool): If True, perform substring search. Defaults to False.
case_sensitive (bool): If False, perform case-insensitive search. Defaults to True.
Returns:
Optional[Dict[str, Any]]: Returns the matching dictionary or None if not found.
"""
try:
result = search_data(self.db, value, key)
result = search_data(self.db, value, key, substring=substring, case_sensitive=case_sensitive)
if result:
return result
else:
print("\033[90m#info\033[0m Not found! Try another quest?")
self.logger.info("Not found! Try another quest?")
return None
except Exception as e:
print(f"\033[90m#bugs\033[0m Search party got lost! Error: {e}")
self.logger.error(f"Search party got lost! Error: {e}")
return None

@staticmethod
Expand Down
Binary file added LiteJsonDb/__pycache__/LiteJsonDb.cpython-311.pyc
Binary file not shown.
Binary file added LiteJsonDb/__pycache__/__init__.cpython-311.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
12 changes: 6 additions & 6 deletions LiteJsonDb/handler/db_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def _load_db(self) -> None:
if self.enable_log:
logging.info(f"Database file created: {self.filename}")
except OSError as e:
print(f"\033[91m#bugs\033[0m Unable to create database file: {e}")
self.logger.error(f"\033[91m#bugs\033[0m Unable to create database file: {e}")
raise
try:
with open(self.filename, 'r') as file:
Expand All @@ -45,7 +45,7 @@ def _load_db(self) -> None:
if self.enable_log:
logging.info(f"Database loaded from: {self.filename}")
except (OSError, json.JSONDecodeError) as e:
print(f"\033[91m#bugs\033[0m Unable to load database file: {e}")
self.logger.error(f"\033[91m#bugs\033[0m Unable to load database file: {e}")
raise

def _save_db(self) -> None:
Expand All @@ -59,7 +59,7 @@ def _save_db(self) -> None:
if self.enable_log:
logging.info(f"Database saved to {self.filename}")
except OSError as e:
print(f"\033[91m#bugs\033[0m Could not save database: {e}")
self.logger.error(f"\033[91m#bugs\033[0m Could not save database: {e}")
raise

def _backup_db(self) -> None:
Expand All @@ -72,7 +72,7 @@ def _backup_db(self) -> None:
if self.enable_log:
logging.info(f"Backup created: {self.backup_filename}")
except OSError as e:
print(f"\033[91m#bugs\033[0m Unable to create backup: {e}")
self.logger.error(f"\033[91m#bugs\033[0m Unable to create backup: {e}")
raise

def _restore_db(self) -> None:
Expand All @@ -86,9 +86,9 @@ def _restore_db(self) -> None:
if self.enable_log:
logging.info(f"Database restored from backup: {self.backup_filename}")
except OSError as e:
print(f"\033[91m#bugs\033[0m Unable to restore database: {e}")
self.logger.error(f"\033[91m#bugs\033[0m Unable to restore database: {e}")
raise
else:
print("\033[91m#bugs\033[0m No backup file found.")
self.logger.error("\033[91m#bugs\033[0m No backup file found.")
if self.enable_log:
logging.error("No backup file found to restore.")
2 changes: 1 addition & 1 deletion LiteJsonDb/handler/encrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,5 @@ def _fernet_decrypt(self, encoded_data: str) -> Dict[str, Any]:
decoded_data = self.fernet.decrypt(encoded_data.encode('utf-8'))
return json.loads(decoded_data)
except Exception as e:
print("\033[91m#bugs\033[0m Fernet decryption failed.")
self.logger.error("\033[91m#bugs\033[0m Fernet decryption failed.")
raise ValueError("\033[91m#bugs\033[0m Decryption failed: invalid key or data.")
41 changes: 21 additions & 20 deletions LiteJsonDb/handler/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@ def validate_data(self, data: Any) -> bool:
types = {}
for key, value in data.items():
if not isinstance(key, str):
print(f"\033[91m#bugs\033[0m Key '{key}' must be a string. Did we stumble upon a non-string key?")
self.logger.error(f"\033[91m#bugs\033[0m Key '{key}' must be a string. Did we stumble upon a non-string key?")
return False
if key in types and types[key] != type(value):
print(f"\033[91m#bugs\033[0m Conflicting types for key '{key}'.")
self.logger.error(f"\033[91m#bugs\033[0m Conflicting types for key '{key}'.")
return False
types[key] = type(value)
return all(isinstance(value, (str, int, float, list, dict, bool, None)) for value in data.values())
self.logger.error(f"\033[91m#bugs\033[0m Data must be a dictionary.")
return False
print(f"\033[91m#bugs\033[0m Data must be a dictionary.")
return False

Expand Down Expand Up @@ -107,7 +108,7 @@ def get_data(self, key: str) -> Optional[Any]:
if k in data:
data = data[k]
else:
print(f"\033[91m#bugs\033[0m No data found at key '{key}'. Double-check the key or try a different path.")
self.logger.error(f"\033[91m#bugs\033[0m No data found at key '{key}'. Double-check the key or try a different path.")
return None
return data

Expand All @@ -123,11 +124,11 @@ def set_data(self, key: str, value: Optional[Any] = None) -> None:
value = {}

if not self.validate_data(value):
print(f"\033[91m#bugs\033[0m Invalid data format. Ensure your data is a dictionary with consistent types.")
self.logger.error(f"\033[91m#bugs\033[0m Invalid data format. Ensure your data is a dictionary with consistent types.")
return

if self.key_exists(key):
print(f"\033[91m#bugs\033[0m Key '{key}' already exists. Use db.edit_data('{key}', new_value) to update or add new data.")
self.logger.error(f"\033[91m#bugs\033[0m Key '{key}' already exists. Use db.edit_data('{key}', new_value) to update or add new data.")
return

self._set_child(self.db, key, value)
Expand All @@ -144,11 +145,11 @@ def edit_data(self, key: str, value: Any) -> None:
value (Any): The new value.
"""
if not self.key_exists(key):
print(f"\033[91m#bugs\033[0m Key '{key}' doesn't exist, cannot edit. Use 'set_data' to add new data.")
self.logger.error(f"\033[91m#bugs\033[0m Key '{key}' doesn't exist, cannot edit. Use 'set_data' to add new data.")
return

if not self.validate_data(value):
print(f"\033[91m#bugs\033[0m Invalid data format. Ensure your data is a dictionary with consistent types.")
self.logger.error(f"\033[91m#bugs\033[0m Invalid data format. Ensure your data is a dictionary with consistent types.")
return

keys = key.split('/')
Expand All @@ -165,13 +166,13 @@ def edit_data(self, key: str, value: Any) -> None:
if isinstance(increment_value, (int, float)):
current_data[field] += increment_value
else:
print(f"\033[91m#bugs\033[0m Increment value for '{field}' is not a number. Provide a numeric value for incrementing (e.g., db.edit_data('users/1', {{'increment': {{'score': 5}}}})).")
self.logger.error(f"\033[91m#bugs\033[0m Increment value for '{field}' is not a number. Provide a numeric value for incrementing (e.g., db.edit_data('users/1', {{'increment': {{'score': 5}}}})).")
return
else:
print(f"\033[91m#bugs\033[0m Field '{field}' is not a number. Ensure the field exists and is a number before incrementing.")
self.logger.error(f"\033[91m#bugs\033[0m Field '{field}' is not a number. Ensure the field exists and is a number before incrementing.")
return
else:
print(f"\033[91m#bugs\033[0m Field '{field}' doesn't exist. Make sure the field exists in the data structure; use db.edit_data to set initial values.")
self.logger.error(f"\033[91m#bugs\033[0m Field '{field}' doesn't exist. Make sure the field exists in the data structure; use db.edit_data to set initial values.")
return
else:
if isinstance(current_data, dict):
Expand Down Expand Up @@ -238,14 +239,14 @@ def remove_data(self, key: str) -> None:
if k in data:
data = data[k]
else:
print(f"\033[91m#bugs\033[0m Key '{key}' doesn't exist, cannot remove. Make sure the key path is correct.")
self.logger.error(f"\033[91m#bugs\033[0m Key '{key}' doesn't exist, cannot remove. Make sure the key path is correct.")
return
if keys[-1] in data:
del data[keys[-1]]
self._backup_db()
self._save_db()
else:
print(f"\033[91m#bugs\033[0m Key '{key}' doesn't exist, cannot remove. Make sure the key path is correct.")
self.logger.error(f"\033[91m#bugs\033[0m Key '{key}' doesn't exist, cannot remove. Make sure the key path is correct.")

# ==================================================
# WHOLE DATABASE
Expand Down Expand Up @@ -289,7 +290,7 @@ def get_subcollection(self, collection_name: str, item_id: Optional[str] = None)
if item_id in collection:
return collection[item_id]
else:
print(f"\033[91m#bugs\033[0m ID '{item_id}' not found in collection '{collection_name}'. Check if the ID is correct; use get_subcollection('{collection_name}') to see all items.")
self.logger.error(f"\033[91m#bugs\033[0m ID '{item_id}' not found in collection '{collection_name}'. Check if the ID is correct; use get_subcollection('{collection_name}') to see all items.")
return None
return collection

Expand All @@ -303,14 +304,14 @@ def set_subcollection(self, collection_name: str, item_id: str, value: Any) -> N
value (Any): The value to set.
"""
if not self.validate_data(value):
print(f"\033[91m#bugs\033[0m Invalid data format. Your data should look like this: {{'name': 'Aliou', 'age': 30}}.")
self.logger.error(f"\033[91m#bugs\033[0m Invalid data format. Your data should look like this: {{'name': 'Aliou', 'age': 30}}.")
return

if collection_name not in self.db:
self.db[collection_name] = {}

if item_id in self.db[collection_name]:
print(f"\033[91m#bugs\033[0m ID '{item_id}' already exists in collection '{collection_name}'. Use db.edit_subcollection('{collection_name}', '{item_id}', new_value) to update or add new data.")
self.logger.error(f"\033[91m#bugs\033[0m ID '{item_id}' already exists in collection '{collection_name}'. Use db.edit_subcollection('{collection_name}', '{item_id}', new_value) to update or add new data.")
return

self.db[collection_name][item_id] = value
Expand All @@ -327,7 +328,7 @@ def edit_subcollection(self, collection_name: str, item_id: str, value: Any) ->
value (Any): The new value.
"""
if not self.validate_data(value):
print(f"\033[91m#bugs\033[0m Invalid data format. Your data should look like this: {{'name': 'Aliou', 'age': 30}}.")
self.logger.error(f"\033[91m#bugs\033[0m Invalid data format. Your data should look like this: {{'name': 'Aliou', 'age': 30}}.")
return

if collection_name in self.db and item_id in self.db[collection_name]:
Expand All @@ -338,7 +339,7 @@ def edit_subcollection(self, collection_name: str, item_id: str, value: Any) ->
self._backup_db()
self._save_db()
else:
print(f"\033[91m#bugs\033[0m ID '{item_id}' not found in collection '{collection_name}', cannot edit. Use 'set_subcollection' to create a new item.")
self.logger.error(f"\033[91m#bugs\033[0m ID '{item_id}' not found in collection '{collection_name}', cannot edit. Use 'set_subcollection' to create a new item.")

def remove_subcollection(self, collection_name: str, item_id: Optional[str] = None) -> None:
"""
Expand All @@ -354,13 +355,13 @@ def remove_subcollection(self, collection_name: str, item_id: Optional[str] = No
self._backup_db()
self._save_db()
else:
print(f"\033[91m#bugs\033[0m Collection '{collection_name}' not found, cannot remove. Make sure the collection name is correct.")
self.logger.error(f"\033[91m#bugs\033[0m Collection '{collection_name}' not found, cannot remove. Make sure the collection name is correct.")
return
else:
if collection_name in self.db and item_id in self.db[collection_name]:
del self.db[collection_name][item_id]
self._backup_db()
self._save_db()
else:
print(f"\033[91m#bugs\033[0m ID '{item_id}' not found in collection '{collection_name}', cannot remove. Check the ID and collection name; use get_subcollection('{collection_name}') to see all items.")
self.logger.error(f"\033[91m#bugs\033[0m ID '{item_id}' not found in collection '{collection_name}', cannot remove. Check the ID and collection name; use get_subcollection('{collection_name}') to see all items.")
return
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
3 changes: 2 additions & 1 deletion LiteJsonDb/modules/csv.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import csv
import os
import logging
from typing import Dict, Any, Union

class CSVExporter:
Expand Down Expand Up @@ -42,5 +43,5 @@ def export(self, data: Union[Dict[str, Any], Any], filename: str = "export.csv")
writer.writerows(data if isinstance(data, list) else [data])
return filepath
except Exception as e:
print(f"\033[91m#bugs\033[0m CSV export error: {e}")
logging.getLogger('LiteJsonDb').error(f"\033[91m#bugs\033[0m CSV export error: {e}")
return ""
Loading