Skip to content
Merged
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
66 changes: 66 additions & 0 deletions expense_tracker/core/repositories.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,72 @@ def update_transaction(self, transaction_id: int, data: dict) -> None:
self.conn.execute(query, values)
self.conn.commit()

def get_daily_spending_for_month(self, year: int, month: int) -> dict[int, float]:
"""
Returns a dictionary mapping day-of-month (1-31) to total spending.
Only includes expenses (negative amounts).
"""
# Create date range for the month
start_date = date(year, month, 1)
if month == 12:
end_date = date(year + 1, 1, 1)
else:
end_date = date(year, month + 1, 1)

rows = self.conn.execute(
"""
SELECT CAST(strftime('%d', date) AS INTEGER) as day,
SUM(ABS(amount)) as total
FROM transactions
WHERE date >= ? AND date < ?
AND amount < 0
GROUP BY day
""",
(start_date.isoformat(), end_date.isoformat()),
)

result: dict[int, float] = {}
for row in rows.fetchall():
result[row["day"]] = row["total"]
return result

def get_transactions_for_date(self, target_date: date) -> list[Transaction]:
"""
Query transactions matching exact date.
Order by amount DESC (largest expenses first).
"""
rows = self.conn.execute(
"SELECT * FROM transactions WHERE date = ? ORDER BY amount ASC",
(target_date.isoformat(),),
)
transactions: list[Transaction] = []
for row in rows.fetchall():
transaction = self._row_to_transaction(row)
if transaction:
transactions.append(transaction)
return transactions

def get_months_with_expenses(self) -> list[tuple[int, int]]:
"""
Returns a list of (year, month) tuples for all months that have expenses.
Only includes months with negative amounts (expenses).
Ordered by year and month descending (most recent first).
"""
rows = self.conn.execute(
"""
SELECT DISTINCT
CAST(strftime('%Y', date) AS INTEGER) as year,
CAST(strftime('%m', date) AS INTEGER) as month
FROM transactions
WHERE amount < 0
ORDER BY year DESC, month DESC
"""
)
result: list[tuple[int, int]] = []
for row in rows.fetchall():
result.append((row["year"], row["month"]))
return result


class MerchantCategoryRepository:
"""
Expand Down
24 changes: 22 additions & 2 deletions expense_tracker/gui/main_window.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import tkinter as tk
from datetime import date
from tkinter import ttk

from expense_tracker.gui.tabs import TransactionsTab
from expense_tracker.gui.tabs import TransactionsTab, HeatmapTab


class MainWindow(tk.Frame):
Expand All @@ -23,8 +24,15 @@ def __init__(self, master, transaction_repo, merchant_repo):
self.notebook, transaction_repo, merchant_repo, self
)

# Add tab to notebook
# Create Heatmap tab
self.heatmap_tab = HeatmapTab(self.notebook, transaction_repo, self)

# Add tabs to notebook
self.notebook.add(self.transactions_tab, text="Transactions")
self.notebook.add(self.heatmap_tab, text="Heatmap")

# Bind tab change event for lazy loading
self.notebook.bind("<<NotebookTabChanged>>", self._on_tab_changed)

def _open_dialog(self, dialog_class, *args, **kwargs):
if self._active_dialog is not None and self._active_dialog.winfo_exists():
Expand All @@ -51,3 +59,15 @@ def on_close():

self._active_dialog = None
self.transactions_tab.refresh()

def _on_tab_changed(self, event):
"""Refresh tab content when user switches tabs."""
current_tab = self.notebook.select()
tab_index = self.notebook.index(current_tab)
if tab_index == 1: # Heatmap tab
self.heatmap_tab.refresh()

def show_transactions_for_date(self, target_date: date):
"""Switch to Transactions tab with date filter applied."""
self.notebook.select(0) # Switch to Transactions tab (index 0)
self.transactions_tab.filter_by_date(target_date)
3 changes: 2 additions & 1 deletion expense_tracker/gui/tabs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .transactions_tab import TransactionsTab
from .heatmap_tab import HeatmapTab

__all__ = ["TransactionsTab"]
__all__ = ["TransactionsTab", "HeatmapTab"]
Loading