Refactor: extract shared code, add TransactionService, consolidate utilities#21
Merged
Refactor: extract shared code, add TransactionService, consolidate utilities#21
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR tackles several architectural issues discovered during a code review, reducing duplication and improving consistency across the codebase:
AddExpenseDialogandEditExpenseDialogintoexpense_form.pywithbuild_expense_form()andvalidate_amount()MerchantCategoryServicebut manually added transactions skipped categorization entirelyUploadDialog._parse_date()andextract._parse_date()) into a singleparse_date_from_str()utility inutils/date.pyget_top_spending_category()was nearly identical toget_spending_by_category()(differed only byLIMIT 1). Removed the former;StatisticsServicenow usesget_spending_by_category()[0]Key changes
New files
expense_tracker/gui/dialogs/expense_form.py— Shared form builder and amount validatorexpense_tracker/services/transaction.py—TransactionServicewith auto-categorization, merchant mapping updates, bulk import with dedupexpense_tracker/utils/date.py— Consolidatedparse_date_from_str()utilitytests/services/test_transaction.py— 11 tests covering all TransactionService methodsModified files
app.py— CreatesMerchantCategoryServiceonce, injects intoTransactionServicemain_window.py,transactions_tab.py— AcceptTransactionServiceinstead of raw reposadd_expense.py,edit_expense.py,upload.py— Refactored to use shared form + TransactionServicestatistics.py— Usesget_spending_by_category()[0]instead of removedget_top_spending_category()transaction_repository.py— Removedget_top_spending_category()extract.py— Uses sharedparse_date_from_str(), returnsdateobjects instead of stringstest_extract.py— Updated assertions to expectdateobjectstest_repository.py— Removed 4 obsolete tests for deletedget_top_spending_categoryDesign decisions
TransactionServicewrapsMerchantCategoryServicerather than exposing it to dialogs directly. This keeps categorization logic centralized — adding a new entry point (e.g., CSV import) only needs to callTransactionService.import_transactions().update_transaction()returnsboolto indicate whether merchant categories were updated, so the edit dialog can show appropriate feedback without needing to track category changes itself.extract.pynow returnsdateobjects instead of ISO strings. This is more type-safe and avoids unnecessary string↔date conversions downstream.Test plan
ruff check .clean🤖 Generated with Claude Code