From 25a8d637c09c22a03dfa47d12c85e0b5866fccd3 Mon Sep 17 00:00:00 2001 From: Sandro Date: Thu, 2 Apr 2026 23:22:11 +0100 Subject: [PATCH 1/2] feat(accounts): Show today's delta on dashboard counters Display a "+N" green indicator next to each absolute-count metric when today's count is non-zero. Percentage metrics are excluded. Today's counts are computed within the same aggregate queries using conditional filters to avoid extra database round-trips. Also refactors DashboardView.get_context_data into two focused private methods (_get_activity_counters, _get_merge_counters) and extracts a _format_pct helper for percentage formatting. --- .../templates/accounts/dashboard.html | 14 +++- daiv/accounts/views.py | 71 ++++++++++++------- 2 files changed, 56 insertions(+), 29 deletions(-) diff --git a/daiv/accounts/templates/accounts/dashboard.html b/daiv/accounts/templates/accounts/dashboard.html index 2ffcd9c8..d1a0bad9 100644 --- a/daiv/accounts/templates/accounts/dashboard.html +++ b/daiv/accounts/templates/accounts/dashboard.html @@ -56,7 +56,12 @@

Agent Activit {% for counter in counters %}
{{ counter.label }} - {{ counter.value }} + + {% if counter.today %} + +{{ counter.today }} + {% endif %} + {{ counter.value }} +
{% endfor %} @@ -89,7 +94,12 @@

Code Velocity {% endif %} - {{ counter.value }} + + {% if counter.today %} + +{{ counter.today }} + {% endif %} + {{ counter.value }} + {% endfor %} diff --git a/daiv/accounts/views.py b/daiv/accounts/views.py index 56365d06..373b0c8d 100644 --- a/daiv/accounts/views.py +++ b/daiv/accounts/views.py @@ -1,5 +1,5 @@ import logging -from datetime import timedelta +from datetime import date, timedelta from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin @@ -35,6 +35,11 @@ def homepage(request): DEFAULT_PERIOD = "30d" +def _format_pct(numerator: int, denominator: int) -> str: + """Format a ratio as a rounded percentage string, or a dash when the denominator is zero.""" + return f"{round(numerator / denominator * 100)}%" if denominator else "—" + + class DashboardView(LoginRequiredMixin, TemplateView): template_name = "accounts/dashboard.html" @@ -45,39 +50,50 @@ def get_context_data(self, **kwargs): if period not in PERIOD_DAYS: period = DEFAULT_PERIOD days = PERIOD_DAYS[period] - cutoff_date = localdate() - timedelta(days=days) if days is not None else None + today = localdate() + cutoff_date = today - timedelta(days=days) if days is not None else None + + context["counters"] = self._get_activity_counters(cutoff_date, today) + context["active_api_keys"] = APIKey.objects.filter(user=self.request.user, revoked=False).count() + context["periods"] = [{"key": key, "label": label} for key, label, _ in PERIOD_CHOICES] + context["current_period"] = period + context["merge_counters"] = self._get_merge_counters(cutoff_date, today) + return context + + def _get_activity_counters(self, cutoff_date: date | None, today: date) -> list[dict]: tasks = DBTaskResult.objects.filter(task_path__in=TASK_PATHS) if cutoff_date is not None: tasks = tasks.filter(enqueued_at__date__gte=cutoff_date) successful = Q(status=TaskResultStatus.SUCCESSFUL) code_changes = Q(return_value__code_changes=True) + today_q = Q(enqueued_at__date=today) stats = tasks.aggregate( total=Count("id"), successful=Count("id", filter=successful), issues=Count("id", filter=successful & code_changes & Q(task_path=ISSUE_TASK_PATH)), mrs=Count("id", filter=successful & code_changes & Q(task_path=MR_TASK_PATH)), + today_total=Count("id", filter=today_q), + today_issues=Count("id", filter=today_q & successful & code_changes & Q(task_path=ISSUE_TASK_PATH)), + today_mrs=Count("id", filter=today_q & successful & code_changes & Q(task_path=MR_TASK_PATH)), ) - active_api_keys = APIKey.objects.filter(user=self.request.user, revoked=False).count() total = stats["total"] - context["counters"] = [ - {"label": "Jobs processed", "value": total}, - {"label": "Success rate", "value": f"{round(stats['successful'] / total * 100)}%" if total else "—"}, - {"label": "Issues resolved", "value": stats["issues"]}, - {"label": "MR reviews addressed", "value": stats["mrs"]}, + return [ + {"label": "Jobs processed", "value": total, "today": stats["today_total"]}, + {"label": "Success rate", "value": _format_pct(stats["successful"], total)}, + {"label": "Issues resolved", "value": stats["issues"], "today": stats["today_issues"]}, + {"label": "MR reviews addressed", "value": stats["mrs"], "today": stats["today_mrs"]}, ] - context["active_api_keys"] = active_api_keys - context["periods"] = [{"key": key, "label": label} for key, label, _ in PERIOD_CHOICES] - context["current_period"] = period - # Merge metrics + def _get_merge_counters(self, cutoff_date: date | None, today: date) -> list[dict]: merges = MergeMetric.objects.all() if cutoff_date is not None: merges = merges.filter(merged_at__date__gte=cutoff_date) - merge_stats = merges.aggregate( + today_q = Q(merged_at__date=today) + stats = merges.aggregate( total=Count("id"), total_added=Sum("lines_added", default=0), total_removed=Sum("lines_removed", default=0), @@ -85,47 +101,48 @@ def get_context_data(self, **kwargs): daiv_removed=Sum("daiv_lines_removed", default=0), total_commits_sum=Sum("total_commits", default=0), daiv_commits_sum=Sum("daiv_commits", default=0), + today_total=Count("id", filter=today_q), + today_added=Sum("lines_added", default=0, filter=today_q), + today_removed=Sum("lines_removed", default=0, filter=today_q), ) - merge_total = merge_stats["total"] - daiv_lines = merge_stats["daiv_added"] + merge_stats["daiv_removed"] - total_lines = merge_stats["total_added"] + merge_stats["total_removed"] - daiv_line_pct = f"{round(daiv_lines / total_lines * 100)}%" if total_lines else "—" - total_commits = merge_stats["total_commits_sum"] - daiv_commit_pct = f"{round(merge_stats['daiv_commits_sum'] / total_commits * 100)}%" if total_commits else "—" + total_lines = stats["total_added"] + stats["total_removed"] + daiv_lines = stats["daiv_added"] + stats["daiv_removed"] + total_commits = stats["total_commits_sum"] - context["merge_counters"] = [ + return [ { "label": "Total merges", - "value": merge_total, + "value": stats["total"], "tooltip": "Number of MRs/PRs merged into default branches.", + "today": stats["today_total"], }, { "label": "Lines added", - "value": merge_stats["total_added"], + "value": stats["total_added"], "tooltip": "Total lines added across all merged MRs/PRs.", + "today": stats["today_added"], }, { "label": "Lines removed", - "value": merge_stats["total_removed"], + "value": stats["total_removed"], "tooltip": "Total lines removed across all merged MRs/PRs.", + "today": stats["today_removed"], }, { "label": "DAIV contribution", - "value": daiv_line_pct, + "value": _format_pct(daiv_lines, total_lines), "tooltip": "Percentage of total lines (added + removed) authored by DAIV, based on commit authorship.", "plain": True, }, { "label": "DAIV commit share", - "value": daiv_commit_pct, + "value": _format_pct(stats["daiv_commits_sum"], total_commits), "tooltip": "Percentage of total commits authored by DAIV across all merged MRs/PRs.", "plain": True, }, ] - return context - class APIKeyListView(LoginRequiredMixin, TemplateView): template_name = "accounts/api_keys.html" From 89679b54b0a1b5c15b96a57acffccaf6f3bf5bad Mon Sep 17 00:00:00 2001 From: Sandro Date: Fri, 3 Apr 2026 00:34:25 +0100 Subject: [PATCH 2/2] feat(accounts): Stack today delta below counter value on dashboard Rearrange counter display to show the main value on top with the "+N today" indicator below it, replacing the side-by-side layout for better visual hierarchy. --- daiv/accounts/templates/accounts/dashboard.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/daiv/accounts/templates/accounts/dashboard.html b/daiv/accounts/templates/accounts/dashboard.html index d1a0bad9..2bfe6f49 100644 --- a/daiv/accounts/templates/accounts/dashboard.html +++ b/daiv/accounts/templates/accounts/dashboard.html @@ -56,12 +56,12 @@

Agent Activit {% for counter in counters %}
{{ counter.label }} - +
+ {{ counter.value }} {% if counter.today %} - +{{ counter.today }} +
+{{ counter.today }} today
{% endif %} - {{ counter.value }} - +
{% endfor %} @@ -94,12 +94,12 @@

Code Velocity {% endif %} - +
+ {{ counter.value }} {% if counter.today %} - +{{ counter.today }} +
+{{ counter.today }} today
{% endif %} - {{ counter.value }} - +
{% endfor %}