PaperNest Function-by-Function Call Graph

This is a function-level call graph for the current runtime architecture.

Legend:

  • A -> B means function A directly calls function B.
  • UI thread means Kivy main thread.
  • Worker thread means Python background thread.
  • Callback means nested function/lambda scheduled or bound to events.

1) App startup and dependency wiring

main.py

  • module entrypoint -> ReceiptTrackerApp().run()

receipt_app/views/app.py: ReceiptTrackerApp

  • init -> super().init; initializes busy-token registry
  • begin_busy -> updates busy token map, busy_task_count, busy_message
  • end_busy -> removes token, updates busy_task_count, resolves latest busy message
  • build -> _configure_logging, ReceiptRepository(…), ImageStorageService(…), CsvExportService(…), OCRService(…), ReceiptService(…), _load_kv_files, ReceiptRoot(…)
  • _load_kv_files -> Builder.load_file for shared/components/screens KV files
  • _configure_logging -> logging.basicConfig with file + stream handlers

receipt_app/views/screens/root/root_screen.py: ReceiptRoot

  • on_kv_post -> add_receipt_screen.configure, timeline_screen.configure, gallery_screen.configure
  • switch_screen -> sets screen_manager.current

2) Core + data layer call graph

receipt_app/core/models.py

  • Receipt.from_row -> date.fromisoformat, float(…), datetime.fromisoformat, Receipt(…)
  • OCRSuggestion.has_suggestion -> bool(…) on suggestion/candidate fields

receipt_app/data/receipt_repository.py: ReceiptRepository

  • init -> _initialize_schema
  • _connect -> sqlite3.connect; sets row_factory
  • _initialize_schema -> _connect; executes schema script
  • add_receipt -> _connect; INSERT; returns cursor.lastrowid
  • get_receipt_by_id -> _connect; SELECT; Receipt.from_row
  • update_receipt -> _connect; UPDATE; get_receipt_by_id
  • delete_receipt -> _connect; SELECT then DELETE; Receipt.from_row
  • count_by_image_path -> _connect; SELECT COUNT(*)
  • get_receipts_between -> _connect; SELECT range; list map Receipt.from_row
  • get_total_between -> _connect; SELECT SUM(amount)
  • get_all_receipts -> _connect; SELECT all; list map Receipt.from_row

3) Business service layer call graph

receipt_app/business/image_storage_service.py: ImageStorageService

  • init -> ensures image directory exists
  • _build_file_name -> datetime.now().strftime
  • allocate_capture_target -> _build_file_name
  • validate_captured_image -> Path(…).exists; returns resolved path
  • store_existing_image -> _build_file_name, shutil.copy2 (unless already in app image dir)
  • delete_image -> Path.unlink (best effort)

receipt_app/business/csv_export_service.py: CsvExportService

  • init -> ensures export directory exists
  • _resolve_export_dir -> validates/creates destination directory
  • _build_padded_rows -> computes max widths and padded headers/rows
  • export_timeline_report -> _resolve_export_dir, _build_padded_rows, csv.writer writes report

receipt_app/business/ocr_service.py: OCRService

  • init -> _setup_engine
  • available -> checks selected engine and unavailable_reason
  • unavailable_reason -> returns reason text
  • _setup_engine -> _setup_android_mlkit OR import pytesseract/Pillow and validate tesseract binary
  • _setup_android_mlkit -> pyjnius autoclass bindings for Android OCR classes
  • _resolve_android_uri -> Uri.parse or Uri.fromFile
  • _request_android_gc -> java.lang.System.gc best effort
  • _load_android_sampled_bitmap -> BitmapFactory bounds pass, inSampleSize decode retries, _request_android_gc
  • _extract_android_text -> _resolve_android_uri/_load_android_sampled_bitmap, ML Kit recognizer, Tasks.await, recognizer.close, bitmap.recycle, _request_android_gc
  • extract_suggestions -> validates image path, dispatches OCR engine, _extract_date_candidates, _extract_amount_candidates, _select_largest_amount
  • _select_largest_amount -> float parse and max selection
  • _extract_date -> _extract_date_candidates first result
  • _extract_date_candidates -> _normalize_ocr_digits, datetime.strptime formats, cue-aware compact token parsing
  • _extract_amount -> _extract_amount_candidates first result
  • _extract_amount_candidates -> _rank_amount_candidates OR _parse_amount_tokens fallback
  • _rank_amount_candidates -> _extract_item_line_amount, _extract_line_amount_candidates, _infer_itemized_total
  • _extract_item_line_amount -> _normalize_amount_token
  • _infer_itemized_total -> sum item line amounts
  • _extract_line_amount_candidates -> _normalize_amount_token
  • _normalize_ocr_digits -> translate OCR-confusable chars
  • _normalize_amount_token -> normalize locale punctuation then float parse
  • _parse_amount_tokens -> _extract_line_amount_candidates

receipt_app/business/receipt_service.py: ReceiptService

  • init -> stores repository, image_storage, OCR service, CSV service
  • ocr_available -> delegates to ocr_service.available
  • ocr_unavailable_reason -> delegates to ocr_service.unavailable_reason
  • _parse_date -> datetime.strptime
  • _parse_amount -> float parse, non-negative check, rounding
  • add_receipt -> _parse_date, _parse_amount, image_storage.validate_captured_image OR image_storage.store_existing_image, repository.add_receipt, returns Receipt(…)
  • suggest_fields_from_image -> image_storage.validate_captured_image, ocr_service.extract_suggestions
  • update_receipt -> _parse_date, _parse_amount, repository.update_receipt
  • delete_receipt -> repository.delete_receipt, repository.count_by_image_path, image_storage.delete_image (best effort)
  • get_timeline_summary -> _parse_date start/end, repository.get_receipts_between, repository.get_total_between
  • export_timeline_csv -> get_timeline_summary, csv_export_service.export_timeline_report
  • export_receipt_images -> repository.get_all_receipts, shutil.copy2 to year/month-name folders
  • get_gallery_grouped_by_month -> repository.get_all_receipts grouped by YYYY-MM
  • get_gallery_grouped_by_year_month -> repository.get_all_receipts grouped by YYYY then MM

4) Add receipt screen call graph (full runtime controller)

File: receipt_app/views/screens/add_receipt/add_receipt_screen.py

Lifecycle and layout update

  • init -> super().init, initializes camera state, draft manager, busy token, scan generation
  • on_kv_post -> widget binds; _schedule_preview_resize; _schedule_suggestion_refresh
  • on_size -> _schedule_preview_resize
  • on_selected_image_path -> _schedule_preview_resize
  • on_resolved_date -> _format_iso_date_for_display
  • on_pre_leave -> s(False)
  • on_touch_down -> captures swipe start if touch on preview image
  • on_touch_up -> show_next_receipt/show_previous_receipt on swipe gesture
  • _schedule_preview_resize -> Clock.unschedule/_schedule _update_preview_layout
  • _update_preview_layout -> computes preview_image_height from texture/card width

Setup, status, and busy overlay

  • configure -> _clear_selection_state, _set_status using OCR availability
  • _set_status -> updates status_message
  • s -> toggles local loading state; _begin_global_busy/_end_global_busy
  • _begin_global_busy -> MDApp.get_running_app().begin_busy
  • _end_global_busy -> MDApp.get_running_app().end_busy

Suggestion UI generation

  • _schedule_suggestion_refresh -> Clock.unschedule/_schedule _refresh_suggestion_buttons
  • _refresh_suggestion_buttons -> _current_date_candidates, _current_amount_candidates, _populate_suggestions_grid
  • _populate_suggestions_grid -> builds suggestion buttons and binds on_select callback

Draft state and image navigation

  • _current_receipt -> draft_manager.current
  • _update_current_receipt_label -> draft_manager.current_label
  • _apply_current_receipt_to_ui -> syncs selected paths/values/preview + _schedule_suggestion_refresh
  • _set_selected_images -> draft_manager.set_selected_images, _apply_current_receipt_to_ui
  • _format_iso_date_for_display -> converts YYYY-MM-DD to Mon DD, YYYY
  • _clear_selection_state -> draft_manager.clear, _apply_current_receipt_to_ui
  • _persist_current_receipt_values -> draft_manager.persist_current_values
  • show_previous_receipt -> _persist_current_receipt_values, draft_manager.move_previous, _apply_current_receipt_to_ui
  • show_next_receipt -> _persist_current_receipt_values, draft_manager.move_next, _apply_current_receipt_to_ui

Android permission helpers

  • _run_with_android_permissions -> check/request permissions; schedules on_granted or popup/status
  • _camera_permissions -> builds CAMERA + media-read list
  • _media_read_permissions -> returns media read permissions

Selection and detail editing helpers

  • _apply_selected_images_and_scan -> _set_selected_images, _set_status, _auto_fill_after_image
  • _clear_resolved_values -> clears date/amount and _refresh_details_summary
  • _refresh_details_summary -> computes details_summary from current resolved values
  • _validate_amount_only -> float parse, non-negative, 2dp normalize
  • _current_date_candidates -> from current draft
  • _current_amount_candidates -> from current draft
  • _dedupe_non_empty -> normalization/dedup helper
  • _open_candidate_selector -> popup UI for selecting candidates or manual input
  • _open_resolved_date_picker -> MDDatePicker; on save updates state + _persist_current_receipt_values
  • _apply_selected_date_candidate -> applies date candidate + refreshes UI state
  • _format_amount_for_display -> formats candidate display amount
  • _open_resolved_amount_numpad -> open_amount_numpad
  • _apply_selected_amount_candidate -> _validate_amount_only then persist/refresh
  • edit_resolved_date -> candidate selector OR _open_resolved_date_picker
  • edit_resolved_amount -> candidate selector OR _open_resolved_amount_numpad
  • _apply_resolved_amount_edit -> _validate_amount_only + persist/refresh
  • _apply_resolved_values -> applies date/amount and status
  • _auto_fill_after_image -> run_ocr(auto_trigger=True, scan_all=True)
  • _validate_manual_values -> validates popup manual date/amount
  • _safe_parse_iso_date -> safe parser helper
  • _prompt_detected_values -> confirmation popup; confirm uses _apply_resolved_values; edit opens _prompt_manual_values
  • _prompt_manual_values -> manual entry popup; save uses _validate_manual_values then _apply_resolved_values

Camera capture and file picker flow

  • capture_photo -> permission flow then _start_capture_photo
  • _start_capture_photo -> _allocate_camera_target_path; Android route _start_android_camera_capture else plyer.camera.take_picture
  • _start_android_camera_capture -> binds android activity result callback; schedules _handle_camera_result on UI thread; writes bitmap to target; _on_capture_complete
  • _allocate_camera_target_path -> image_storage.allocate_capture_target
  • _on_capture_complete -> Clock.schedule_once(_apply_capture_result)
  • _apply_capture_result -> image_storage.validate_captured_image; _apply_selected_images_and_scan
  • open_file_picker -> permission flow then _open_android_media_picker or _open_file_picker_internal
  • _open_android_media_picker -> plyer.filechooser.open_file callback to _on_android_media_selected
  • _on_android_media_selected -> Clock.schedule_once(_apply_android_media_selection)
  • _apply_android_media_selection -> _on_image_files_selected with selected paths
  • _copy_android_content_uri_to_local_image -> pyjnius stream copy content:// URI into app storage
  • _open_file_picker_internal -> FilePickerHelper.open_file_picker(callback=_on_image_files_selected)
  • _on_image_files_selected -> normalize/copy paths; _apply_selected_images_and_scan

OCR worker-thread flow

  • run_ocr -> builds image batch; sets scan generation; s(True); starts worker thread _scan_receipts_worker
  • _scan_receipts_worker (worker thread) -> _attach_android_jnius_thread, receipt_service.suggest_fields_from_image for each image, _release_android_memory_pressure between items, Clock.schedule_once(_apply_scan_results_safely), finally _detach_android_jnius_thread
  • _attach_android_jnius_thread -> jnius.attach on Android
  • _detach_android_jnius_thread -> jnius.detach on Android
  • _release_android_memory_pressure -> gc.collect + java.lang.System.gc
  • _apply_scan_results_safely (UI thread) -> _apply_scan_results with exception guard
  • _handle_scan_worker_failure (UI thread) -> s(False) and error popup/status
  • _apply_scan_results (UI thread) -> writes draft suggestions, _apply_current_receipt_to_ui, _set_status summary

Save worker-thread flow

  • save_receipt -> validates current draft, builds save payload, s(True), starts worker thread _save_receipt_worker
  • _save_receipt_worker (worker thread) -> receipt_service.add_receipt, Clock.schedule_once(_apply_saved_receipt) or _handle_save_error
  • _handle_save_error (UI thread) -> s(False), _set_status, show_popup
  • _apply_saved_receipt (UI thread) -> s(False), remove saved draft, _clear_selection_state OR _apply_current_receipt_to_ui, _notify_data_changed
  • _notify_data_changed -> refreshes timeline and gallery screens if present

5) Timeline screen call graph

File: receipt_app/views/screens/timeline/timeline_screen.py

TimelineReceiptRow

  • trigger_edit -> on_edit callback with receipt id
  • trigger_delete -> on_delete callback with receipt id

TimelineScreen

  • init -> initializes receipt map and editor placeholder
  • configure -> assigns service, creates ReceiptEditor, initializes date range
  • _parse_iso_date -> safe parser
  • _open_date_picker -> MDDatePicker; callback writes start/end date text
  • open_start_date_picker -> _open_date_picker(target=start)
  • open_end_date_picker -> _open_date_picker(target=end)
  • on_pre_enter -> refresh_summary(silent=True)
  • refresh_summary -> receipt_service.get_timeline_summary, updates summary, renders TimelineReceiptRow widgets
  • open_edit_popup -> ReceiptEditor.open with callback _after_receipt_edited
  • _after_receipt_edited -> refresh_summary + _notify_data_changed
  • confirm_delete_receipt -> show_confirm_popup -> delete_receipt
  • delete_receipt -> receipt_service.delete_receipt, refresh_summary, _notify_data_changed
  • export_csv -> _downloads_export_dir, show_confirm_popup -> _export_csv_to_directory
  • export_images -> _downloads_export_dir, show_confirm_popup -> _export_images_to_directory
  • _downloads_export_dir -> platform-aware Downloads path
  • _export_images_to_directory -> receipt_service.export_receipt_images, show_popup status
  • _export_csv_to_directory -> receipt_service.export_timeline_csv, show_popup status
  • _notify_data_changed -> refreshes gallery screen if present

File: receipt_app/views/screens/gallery/gallery_screen.py

GalleryReceiptCard

  • trigger_edit -> on_edit callback
  • trigger_delete -> on_delete callback
  • trigger_popup -> show_popup with Edit/Delete/Cancel
  • on_touch_up -> opens popup or custom on_open callback

GalleryScreen

  • init -> initializes hierarchy cache, editor ref, loading state, tap suppression timer
  • _suppress_receipt_taps -> updates suppression deadline
  • _receipt_taps_suppressed -> checks suppression window
  • _open_receipt_card_popup -> opens card popup unless loading/suppressed
  • configure -> stores service and creates ReceiptEditor
  • on_pre_enter -> refresh_gallery(silent=True)
  • on_pre_leave -> _set_loading(False)
  • _set_loading -> toggles loading popup + app global busy token
  • _begin_global_busy -> MDApp.begin_busy
  • _end_global_busy -> MDApp.end_busy
  • navigate_up -> adjusts selected year/month and _render_gallery
  • open_year -> set year and schedule _render_gallery
  • open_month -> set month and schedule _render_gallery
  • _render_year_list -> creates year buttons bound to open_year
  • _render_month_list -> creates month buttons bound to open_month
  • _render_receipt_cards -> creates GalleryReceiptCard widgets and binds callbacks
  • _render_gallery -> renders year/month/receipt level depending on selection
  • refresh_gallery -> _set_loading(True), starts worker thread _refresh_gallery_worker
  • _refresh_gallery_worker (worker thread) -> receipt_service.get_gallery_grouped_by_year_month, schedules _apply_refreshed_gallery or _on_gallery_refresh_error
  • _on_gallery_refresh_error (UI thread) -> _set_loading(False), optional popup
  • _apply_refreshed_gallery (UI thread) -> _set_loading(False), update caches, _render_gallery
  • open_edit_popup -> ReceiptEditor.open callback _after_receipt_edited
  • _after_receipt_edited -> refresh_gallery + timeline.refresh_summary
  • confirm_delete_receipt -> show_confirm_popup -> delete_receipt
  • delete_receipt -> _set_loading(True), starts worker thread _delete_receipt_worker
  • _delete_receipt_worker (worker thread) -> receipt_service.delete_receipt, schedules _on_delete_succeeded/_on_delete_failed
  • _on_delete_failed (UI thread) -> _set_loading(False), show_popup
  • _on_delete_succeeded (UI thread) -> _set_loading(False), refresh_gallery, refresh timeline

7) Shared UI component call graph

receipt_app/views/components/receipt_editor/receipt_editor.py

  • TapToEditTextInput.on_touch_down -> invokes tap_callback if tapped
  • ReceiptEditor.init -> stores receipt_service
  • ReceiptEditor.open -> builds popup, binds date/amount tap callbacks, save callback calls receipt_service.update_receipt
  • ReceiptEditor._open_date_picker_for_input -> MDDatePicker updates input text
  • ReceiptEditor._parse_iso_date -> safe parser

receipt_app/views/components/amount_numpad/amount_numpad_popup.py

  • AmountNumpadPopup.init -> _clean_initial, _refresh_display
  • _clean_initial -> parse/normalize initial amount
  • _display_text -> current or initial hint
  • _refresh_display -> display text/color update
  • _append_char -> mutates current input buffer
  • _backspace -> removes trailing char
  • _clear -> clears current input
  • _normalize_for_apply -> validates/normalizes value to 2dp
  • _apply -> _normalize_for_apply, invokes on_apply callback
  • open_amount_numpad -> singleton-like active popup guard, bind dismiss cleanup, open popup

receipt_app/views/shared/file_picker_helper.py

  • _normalize_start_path -> resolves usable chooser start folder
  • open_file_picker -> popup chooser, selection callback returns paths
  • open_directory_picker -> popup chooser, returns selected/current directory

receipt_app/views/shared/popup_utils.py

  • bind_label_for_wrapping -> binds label size to text wrapping
  • create_themed_popup -> applies popup style + optional content surface
  • _apply_content_surface -> draws popup content background rectangle
  • style_button -> assigns role-based colors
  • _button_role_from_text -> heuristics for primary/danger role
  • show_popup -> generic popup with optional button callbacks
  • show_loading_popup -> spinner popup, returns popup handle
  • show_confirm_popup -> confirm/cancel popup and on_confirm callback

receipt_app/views/shared/outlined_card.py

  • TappableOutlinedCard.on_touch_down -> consume touch for feedback/ripple
  • TappableOutlinedCard.on_touch_up -> invoke tap_callback and consume event

8) Add-receipt draft manager call graph

File: receipt_app/views/screens/add_receipt/receipt_draft_manager.py

  • init -> initializes draft list/index
  • items -> returns draft list
  • count -> returns list length
  • current_index getter/setter -> bounds index
  • clear -> resets list/index
  • set_selected_images -> normalize, dedupe, create ReceiptDraft list
  • current -> returns current draft or None
  • current_label -> human-readable image index label
  • persist_current_values -> writes resolved date/amount to current draft
  • move_previous -> decrements index if possible
  • move_next -> increments index if possible
  • remove_by_image_path -> removes one draft and re-bounds current index

9) Optional/non-primary runtime call graph

Batch review module (currently not wired in root ScreenManager)

File: receipt_app/views/screens/batch_review/batch_review_screen.py

  • start_review -> builds BatchReviewItem list from payload and applies first item
  • confirm_current_receipt -> validates current item and advances or _finish_review
  • _finish_review -> emits normalized payload via on_apply callback
  • cancel_review -> emits on_cancel callback
  • Other methods mirror add-receipt helpers for suggestions/date/amount editing and preview navigation

Image processing test utility

Files:

  • image_processing_test_ui.py
  • receipt_app/business/image_processing_service.py

Used for OCR preprocessing experiments; not required for core app flow.