usawa

Signed, immutable accounting.
Info | Log | Files | Refs | Submodules | LICENSE

main_window.py (4471B)


      1 import logging
      2 from usawa.service import UnixClient
      3 from usawa.core.entry_service import EntryService
      4 from usawa.storage.ledger_repository import LedgerRepository
      5 from gi.repository import Adw, Gtk, Gio
      6 
      7 from usawa.gui.controllers.entry_controller import EntryController
      8 from usawa.gui.views.entry_list_view import EntryListView
      9 from datetime import datetime
     10 from whee.valkey import ValkeyStore
     11 
     12 logg = logging.getLogger("gui.mainwindow")
     13 
     14 
     15 class UsawaMainWindow(Adw.ApplicationWindow):
     16 
     17     def __init__(self, application, ledger_path=None, **kwargs):
     18         super().__init__(application=application, **kwargs)
     19 
     20         self.set_title("Usawa")
     21         self.set_default_size(1000, 600)
     22 
     23         toolbar_view = Adw.ToolbarView()
     24         self.set_content(toolbar_view)
     25 
     26         header = Adw.HeaderBar()
     27 
     28         menu_button = self._create_menu_button()
     29         header.pack_end(menu_button)
     30 
     31         toolbar_view.add_top_bar(header)
     32 
     33         self.toast_overlay = Adw.ToastOverlay()
     34         toolbar_view.set_content(self.toast_overlay)
     35 
     36         cfg = self.get_application().cfg
     37         self.client = UnixClient(path=cfg.get("SERVER_SOCKET_FILE_PATH"))
     38         self.valkey_store = ValkeyStore(
     39             "", host=cfg.get("VALKEY_HOST"), port=cfg.get("VALKEY_PORT")
     40         )
     41 
     42         repository = LedgerRepository(
     43             ledger_path=ledger_path,
     44             unix_client=self.client,
     45             valkey_store=self.valkey_store,
     46             cfg=cfg,
     47         )
     48         entry_service = EntryService(repository=repository)
     49         self.entry_controller = EntryController(entry_service=entry_service)
     50         self.entry_controller.add_entry_created_listener(self.refresh_entries)
     51 
     52         self.nav_view = Adw.NavigationView()
     53         self.toast_overlay.set_child(self.nav_view)
     54 
     55         entry_list_page = self._create_entry_list_page()
     56         self.nav_view.add(entry_list_page)
     57 
     58         self._setup_actions()
     59 
     60     def _create_menu_button(self):
     61         menu = Gio.Menu()
     62         menu.append("Export Ledger", "app.export-ledger")
     63 
     64         menu_button = Gtk.MenuButton()
     65         menu_button.set_icon_name("open-menu-symbolic")
     66         menu_button.set_menu_model(menu)
     67         menu_button.set_tooltip_text("Main menu")
     68 
     69         return menu_button
     70 
     71     def _setup_actions(self):
     72         export_action = Gio.SimpleAction.new("export-ledger", None)
     73         export_action.connect("activate", lambda a, p: self._on_export_clicked())
     74         self.get_application().add_action(export_action)
     75 
     76     def _on_export_clicked(self):
     77         file_dialog = Gtk.FileDialog()
     78         file_dialog.set_title("Export Ledger to XML")
     79         default_name = f"ledger_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xml"
     80         file_dialog.set_initial_name(default_name)
     81 
     82         filters = Gio.ListStore.new(Gtk.FileFilter)
     83 
     84         xml_filter = Gtk.FileFilter()
     85         xml_filter.set_name("XML Files")
     86         xml_filter.add_pattern("*.xml")
     87         filters.append(xml_filter)
     88 
     89         file_dialog.set_filters(filters)
     90         file_dialog.set_default_filter(xml_filter)
     91 
     92         file_dialog.save(parent=self, callback=self._on_export_file_selected)
     93 
     94     def _on_export_file_selected(self, dialog, result):
     95         try:
     96             file = dialog.save_finish(result)
     97             if file:
     98                 file_path = file.get_path()
     99                 success, error_msg = self.entry_controller.export_ledger(file_path)
    100 
    101                 if success:
    102                     self._show_success_toast(f"Exported to {file_path}")
    103                 else:
    104                     self._show_error_dialog("Export Failed", error_msg)
    105         except Exception as e:
    106             logg.debug(f"Export cancelled: {e}")
    107 
    108     def _show_success_toast(self, message):
    109         toast = Adw.Toast.new(message)
    110         toast.set_timeout(3)
    111         self.toast_overlay.add_toast(toast)
    112 
    113     def _create_entry_list_page(self):
    114         page = Adw.NavigationPage(title="Ledger Entries", tag="entry-list")
    115         entries = []
    116         self.entry_list_view = EntryListView(
    117             nav_view=self.nav_view,
    118             entry_controller=self.entry_controller,
    119             entries=entries,
    120             refresh_callback=self.refresh_entries,
    121             toast_overlay=self.toast_overlay,
    122         )
    123         self.entry_list_view._load_entries()
    124         page.set_child(self.entry_list_view)
    125 
    126         return page
    127 
    128     def refresh_entries(self):
    129         logg.info("MainWindow refreshing entries")
    130         self.entry_list_view._load_entries()