commit 4973edc147bf4be0040644ddaea9ecb4e4a8734b
parent 31736d748cecf7458a6d4091fa259293955fc4b5
Author: Carlosokumu <carlosokumu254@gmail.com>
Date: Fri, 6 Mar 2026 08:08:45 +0300
catch exceptions
Diffstat:
4 files changed, 119 insertions(+), 60 deletions(-)
diff --git a/dummy/usawa/core/entry_service.py b/dummy/usawa/core/entry_service.py
@@ -2,7 +2,6 @@ import logging
from datetime import datetime
import uuid
-from usawa.service import UnixClient
from .models import LedgerEntry
from usawa.storage.ledger_repository import LedgerRepository
@@ -14,28 +13,49 @@ class EntryService:
def __init__(self, repository: LedgerRepository):
self.repository = repository
-
- def save_entry(self, entry: LedgerEntry) -> bool:
+
+ def save_entry(self, entry: LedgerEntry) -> tuple[bool, str]:
"""
Save entry with business logic
:param entry: Entry to save
:type entry: LedgerEntry
- :return: True if saved successfully
- :rtype: bool
+ :return: (success, error_message) tuple
+ :rtype: tuple[bool, str]
"""
-
- entry.tx_date = datetime.now()
- entry.date_registered = datetime.now()
- entry.transaction_ref = self._generate_transaction_ref()
-
- is_valid, error_msg = entry.validate()
- if not is_valid:
- logg.error(f"Entry validation failed: {error_msg}")
- return False
-
- return self.repository.save(entry)
-
+ try:
+ entry.tx_date = datetime.now()
+ entry.date_registered = datetime.now()
+ entry.transaction_ref = self._generate_transaction_ref()
+
+ is_valid, error_msg = entry.validate()
+ if not is_valid:
+ logg.error(f"Entry validation failed: {error_msg}")
+ return False, error_msg
+
+ self.repository.save(entry)
+
+ logg.info(f"Entry saved successfully")
+ return True, ""
+
+ except FileExistsError as e:
+ error_msg = "Some file info for this entry is already recorded in the ledger"
+ return False, error_msg
+
+ except ValueError as e:
+ error_msg = f"Invalid entry data: {str(e)}"
+ logg.error(f"Validation error: {e}")
+ return False, error_msg
+
+ except IOError as e:
+ error_msg = f"File error: {str(e)}"
+ logg.error(f"File operation failed: {e}")
+ return False, error_msg
+
+ except Exception as e:
+ error_msg = f"Failed to save entry: {str(e)}"
+ logg.error(f"Unexpected error: {e}", exc_info=True)
+ return False, error_msg
def get_all_entries(self):
diff --git a/dummy/usawa/gui/controllers/entry_controller.py b/dummy/usawa/gui/controllers/entry_controller.py
@@ -39,19 +39,15 @@ class EntryController:
logg.error(f"Failed to collect entry data: {e}")
return None
- def finalize_entry(self, entry: LedgerEntry) -> bool:
- """Save the entry to the ledger"""
- try:
- logg.debug("Entry: %s", entry)
- success = self.entry_service.save_entry(entry)
- if success:
- logg.info(f"Entry saved successfully")
- else:
- logg.error("Failed to save entry")
- return True
- except Exception as e:
- logg.error(f"Failed to save entry: {e}")
- return False
+
+ def finalize_entry(self, entry: LedgerEntry) -> tuple[bool, str]:
+ logg.debug("Finalizing entry: %s", entry)
+ success, error_msg = self.entry_service.save_entry(entry)
+
+ if success:
+ return True, ""
+ else:
+ return False, error_msg
def get_all_entries(self):
return self.entry_service.get_all_entries()
diff --git a/dummy/usawa/gui/views/create_entry_view.py b/dummy/usawa/gui/views/create_entry_view.py
@@ -440,7 +440,7 @@ class CreateEntryView(Gtk.Box):
def _get_icon_for_file(self, filename: str, metadata: str):
"""Get appropriate icon for file type"""
- icon_name = "text-x-generic-symbolic" # Default
+ icon_name = "text-x-generic-symbolic"
if "pdf" in metadata.lower():
icon_name = "application-pdf-symbolic"
@@ -486,23 +486,38 @@ class CreateEntryView(Gtk.Box):
return f"{size_bytes:.1f} TB"
def _on_finalize(self, button):
- """Handle finalize button - delegates to controller"""
entry = self.controller.collect_entry_data(self)
- if self.attachment_paths:
- entry.attachments.extend(self.attachment_paths)
if entry is None:
- self._show_error_dialog("Invalid Input", "Please check your entries and try again.")
+ self._show_error_dialog(
+ "Invalid Input",
+ "Please check your entries and try again."
+ )
return
- success = self.controller.finalize_entry(entry)
+ if self.attachment_paths:
+ entry.add_attachment(self.attachment_paths)
+
+ if entry.attachments:
+ for attachment_path in entry.attachments:
+ from pathlib import Path
+ if not Path(attachment_path).exists():
+ self._show_error_dialog(
+ "Attachment Missing",
+ f"Attachment file not found: {Path(attachment_path).name}"
+ )
+ return
+
+ success, error_msg = self.controller.finalize_entry(entry)
+
if success:
self.controller.notify_entry_created()
- self.nav_view.pop()
+ logg.info("Entry saved successfully, returning to list")
+ self.nav_view.pop()
else:
- self._show_error_dialog("Save Failed",
- "Could not save the entry. Please try again.")
+ self._show_error_dialog("Save Failed", error_msg)
+
def _show_error_dialog(self, title, message):
"""Show error dialog"""
dialog = Adw.MessageDialog(
diff --git a/dummy/usawa/storage/ledger_repository.py b/dummy/usawa/storage/ledger_repository.py
@@ -86,37 +86,65 @@ class LedgerRepository:
return self.store, ledger, self._wallet
- def save(self, domain_entry: LedgerEntry) -> bool:
- """Save a domain entry to storage"""
+ def save(self, domain_entry: LedgerEntry) -> None:
+ """
+ Save a domain entry to storage
+
+ :param domain_entry: Entry to save
+ :type domain_entry: LedgerEntry
+ :raises ValueError: If validation fails
+ :raises FileExistsError: If attachment already exists in the store
+ :raises IOError: If file operations fail
+ :raises Exception: For other storage errors
+ """
try:
store, ledger, wallet = self._init_store(write=True)
-
+
entry = EntryMapper.to_entry(domain_entry, ledger=ledger)
entry.sign(wallet)
- logg.debug(f"Mapped entry - Serial: {entry.serial}, Parent: {entry.parent.hex()}, Attachments: {entry.attachment}")
-
+
+ logg.debug("Mapped entry - Serial: %s, Parent: %s, Attachments: %s",entry.serial,entry.parent.hex(),entry.attachment)
+
for attachment in domain_entry.attachments:
- info = self.get_file_info(attachment)
- asset = Asset.from_file(attachment,slug=info["slug"], description= info["description"],mimetype= info["mimetype"])
try:
+ info = self.get_file_info(attachment)
+ asset = Asset.from_file(
+ attachment,
+ slug=info["slug"],
+ description=info["description"],
+ mimetype=info["mimetype"]
+ )
store.add_asset(asset)
- except Exception as e:
- logg.exception("Failed to add asset for attachment %s: %s", attachment, e)
- return False
-
- entry.attach(asset)
-
- with open(attachment, "rb") as f:
- data = f.read()
- self.resolver.put(asset.get_digest(binary=True), data)
-
+ entry.attach(asset)
+
+
+ with open(attachment, "rb") as f:
+ data = f.read()
+ self.resolver.put(asset.get_digest(binary=True), data)
+
+ except FileNotFoundError as e:
+ raise IOError(f"Attachment file not found: {attachment}") from e
+ except PermissionError as e:
+ raise IOError(f"Cannot read attachment file: {attachment}") from e
+
store.add_entry(entry, update_ledger=True)
+
ledger.truncate()
ledger.sign()
- return True
- except Exception:
- logg.exception("Failed to save entry")
- return False
+ logg.info(f"Successfully saved entry #{entry.serial}")
+
+ except FileExistsError as e:
+ logg.debug(f"Entry fileinfo already exists: {e}")
+ raise
+ except ValueError as e:
+ logg.debug(f"Validation error: {e}")
+ raise
+ except IOError as e:
+ logg.debug(f"File operation failed: {e}")
+ raise
+ except Exception as e:
+ logg.debug(f"Failed to save entry: {e}", exc_info=True)
+ raise
def get_all_entries(self) -> List[LedgerEntry]:
"""Get all entries"""