Skip to content

refactor: heatmap single-query + decouple ActionQueueBuilder (#141, #143)#179

Merged
dholbach merged 3 commits into
mainfrom
fix/heatmap-query-and-action-queue
Jul 6, 2026
Merged

refactor: heatmap single-query + decouple ActionQueueBuilder (#141, #143)#179
dholbach merged 3 commits into
mainfrom
fix/heatmap-query-and-action-queue

Conversation

@dholbach

@dholbach dholbach commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Fixes #141, fixes #143.

#141 — heatmap single range query

get_heatmap_data() previously called get_sessions_for_month() once per month in a loop (12 queries by default) then ran a 13th range query for client totals — fetching the same data twice. Now:

  • One InvoiceItem query covers the full date range with select_related
  • Items are partitioned by (year, month) key in a single Python pass
  • Client totals fall out of the same pass at no extra cost
  • Default view: 13 queries → 1

get_sessions_for_month() is preserved unchanged for its existing callers in the test suite.

#143 — decouple ActionQueueBuilder from widget context dict internals

Each widget builder now owns get_action_items(today) -> list[dict] that emits ActionQueueItem-shaped dicts directly:

Builder Items produced
InvoiceActionsWidgetBuilder overdue + draft rows
ClientAttentionWidgetBuilder tagged + inactive client rows
TaxQuarterWidgetBuilder tax prepayment warning
ChecklistWidgetBuilder pending checklist row
BankImportReminderWidgetBuilder bank import reminder

ActionQueueBuilder.build() is now a thin aggregator (~10 lines) that calls get_action_items() on each builder and sorts. It no longer references ctx["overdue_invoices"], ctx["tagged_clients"], etc. — a key rename in a widget builder can no longer silently break the action queue.

Shared helpers (_fmt_eur, _join_truncated, _TAG_LABELS) moved to dashboard_widgets.py where the logic lives; action_queue_builder.py is stripped to its aggregation role.

P-039 — i18n wrapping for dashboard_widgets.py

All user-facing strings in dashboard_widgets.py are now wrapped with gettext/ngettext/_(). Four msgids collided with existing .po entries that carry different German translations in other UI contexts:

msgid Existing (wrong context) Action queue (correct)
"Invoice" "Rechnungsnr." "Rechnung"
"Tax" "Steuernummer" "Steuer"
"Operations" "Dauer" "Verwaltung"
"Complete" "Aufnahme abgeschlossen" "Abschließen"

Resolved with pgettext("action category", ...) / pgettext("action label", ...) so each context gets its own .po entry. All fuzzy auto-matches from unrelated strings replaced with correct German translations.

Tests

test_action_queue_builder, test_analytics_integration, test_calculation_consistency, test_views_dashboard, test_operational_checklist — all pass. Lint clean.

🤖 Generated with Claude Code

dholbach and others added 3 commits July 6, 2026 09:03
Fixes #141.

get_heatmap_data() previously called get_sessions_for_month() once per
month in a loop (12 queries) then ran a 13th range query for client
totals. The per-month results are a strict subset of the range data.

Now: one InvoiceItem query covering the full date range; items are
partitioned by (year, month) key in Python for the heatmap rows, and
client totals fall out of the same pass at no extra cost.

get_sessions_for_month() is kept as-is for its existing callers
(test_analytics_integration, test_calculation_consistency).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fixes #143.

Each widget builder now owns a get_action_items(today) method that
returns ActionQueueItem-shaped dicts directly:
  - InvoiceActionsWidgetBuilder: overdue + draft rows
  - ClientAttentionWidgetBuilder: tagged + inactive client rows
    (includes the Session last-date query, formerly in ActionQueueBuilder)
  - TaxQuarterWidgetBuilder: tax prepayment warning row
  - ChecklistWidgetBuilder: pending checklist row
  - BankImportReminderWidgetBuilder: bank import reminder row

ActionQueueBuilder.build() is now a thin aggregator that calls
get_action_items() on each builder and sorts the result. It no longer
walks context dict internals (ctx["overdue_invoices"], etc.) and is
immune to key renames in widget builders.

Shared helpers (_fmt_eur, _join_truncated, _TAG_LABELS) moved to
dashboard_widgets.py where the logic lives; action_queue_builder.py
no longer defines them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Import pgettext alongside gettext/ngettext
- Use pgettext("action category", ...) for "Invoice", "Tax", "Operations"
  to avoid collisions with existing non-action-queue translations for
  those same English msgids (e.g. "Invoice" → "Rechnungsnr.")
- Use pgettext("action label", "Complete") for the draft-invoice CTA,
  which collided with "Aufnahme abgeschlossen"
- Fill all new German msgstrs; defuzz auto-matched entries (no session,
  invoice ready, Revenue, etc.) with correct German translations
- Re-extract + compile both locales

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dholbach dholbach merged commit 57b10a6 into main Jul 6, 2026
1 check passed
@dholbach dholbach deleted the fix/heatmap-query-and-action-queue branch July 6, 2026 07:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant