Dashboard: the counters widget runs four COUNTs where one aggregate would do #1114
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#1114
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What exists today
src/postulo/applications/widgets.py:38-51_counters()runs fourcount()s on the person's applications: open, interviewing (three statuses), offer, applied in the lastRECENT_DAYS. On the second GET ofcore:homefor a fresh user under the test settings (probeaudit/m_scripts/test_probe_queries.py) the page made 20 queries, 5 of them onapplications_application.What changed from the candidate:
_gone_quiet()(:54-59) runssources.quiet[:10]andsources.quiet.count(), two different queries: the first ten to show and the total for the line that says how many. Both are needed as long as the widget says the total. Note that #1099 (gone-quiet days per status and channel) will rewritequiet(); any change here belongs with it.applications/analytics.py:339, used byinsights_for()at:422) is deliberate (#231, #396): a handful of indexed aggregates in place of building every figure from the whole history on every view, with a cache hit after it. Folding them into one statement means subqueries over seven tables, no cheaper on either database. Leave it.What would change
Replace the four counts with one
aggregate()using filteredCounts, asanalytics.fingerprint()already does for reminders:applications.aggregate(open=Count("pk", filter=<open Q>), interviewing=Count("pk", filter=Q(status__in=[...])), offer=Count("pk", filter=Q(status=Status.OFFER)), recent=Count("pk", filter=Q(applied_at__gte=...)))open()is a queryset method; its condition has to be available as aQ(or the method rewritten to use one) so the two do not drift. The widget's context keys stay the same.What it saves or protects
small: three queries per dashboard view; the other two parts of the candidate are not worth changing
Accessibility
none
Decisions
Found by the efficiency and reliability audit of 2026-10-30 (finding Q3; index postulo/postulo#1108). Checked against
mainatdaee30b15(2026-10-30).