Problem
Two efficiency issues in utils/dashboard_widgets.py:
1. InvoiceActionsWidgetBuilder — overdue/unpaid double queries
_get_overdue_invoices queries Invoice with status="sent" and invoice_date__lt=cutoff — a subset of _get_unpaid_invoices which queries the same with no cutoff. Both querysets are evaluated twice each (once for [:5], once for .count()). That's 6 DB hits for what could be 2.
Fix: evaluate unpaid invoices once into a list, derive overdue by filtering in Python ([inv for inv in unpaid if inv.invoice_date < cutoff]), use len() for counts.
2. ClientAttentionWidgetBuilder._get_tagged_clients — 12 sequential queries for 4 tags
For each of 4 hardcoded attention tags: ClientTag.objects.get(name=tag_name) (1 query), then clients.exists() (1 query) and clients.count() (1 query) — up to 12 sequential queries.
Fix: fetch all relevant tags in one query (ClientTag.objects.filter(name__in=attention_tags)), then one Client.objects.filter(..., tags__name__in=attention_tags).prefetch_related("tags") and group by tag in Python.
Identified in simplify review of P-117 chunk 4 (PR #140).
Problem
Two efficiency issues in
utils/dashboard_widgets.py:1.
InvoiceActionsWidgetBuilder— overdue/unpaid double queries_get_overdue_invoicesqueriesInvoicewithstatus="sent"andinvoice_date__lt=cutoff— a subset of_get_unpaid_invoiceswhich queries the same with no cutoff. Both querysets are evaluated twice each (once for[:5], once for.count()). That's 6 DB hits for what could be 2.Fix: evaluate unpaid invoices once into a list, derive overdue by filtering in Python (
[inv for inv in unpaid if inv.invoice_date < cutoff]), uselen()for counts.2.
ClientAttentionWidgetBuilder._get_tagged_clients— 12 sequential queries for 4 tagsFor each of 4 hardcoded attention tags:
ClientTag.objects.get(name=tag_name)(1 query), thenclients.exists()(1 query) andclients.count()(1 query) — up to 12 sequential queries.Fix: fetch all relevant tags in one query (
ClientTag.objects.filter(name__in=attention_tags)), then oneClient.objects.filter(..., tags__name__in=attention_tags).prefetch_related("tags")and group by tag in Python.Identified in simplify review of P-117 chunk 4 (PR #140).