From ad78bf6b32fbf9cc98ecfdeafb760f71bd419282 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 17:04:34 +0000 Subject: [PATCH 1/3] Initial plan From 9017e15be4f097858196962647de5b4560f35421 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 17:10:55 +0000 Subject: [PATCH 2/3] Add leaderboard and statistics to bounties page Co-authored-by: DonnieBLT <128622481+DonnieBLT@users.noreply.github.com> --- website/templates/bounties_list.html | 141 +++++++++++++++++++++++++++ website/views/organization.py | 35 +++++++ 2 files changed, 176 insertions(+) diff --git a/website/templates/bounties_list.html b/website/templates/bounties_list.html index 4dd0ad4584..916e3111a2 100644 --- a/website/templates/bounties_list.html +++ b/website/templates/bounties_list.html @@ -40,6 +40,147 @@

Manage Bounty Payouts

View Payouts + +
+
+ +
+
+
+

Total $5 Issues

+

{{ total_issues_count|default:0 }}

+
+
+ + + + +
+
+

Closed issues with $5 bounty

+
+ +
+
+
+

Paid Issues

+

{{ paid_count|default:0 }}

+
+
+ + + + +
+
+

Successfully processed payments

+
+ +
+
+
+

Total Payouts

+

${{ grand_total_payouts|default:0 }}

+
+
+ + + + +
+
+

Total amount distributed

+
+
+ + {% if leaderboard %} +
+
+

+ + + + + Top Earners Leaderboard +

+ Top 10 contributors +
+
+ + + + + + + + + + + {% for earner in leaderboard %} + + + + + + + {% endfor %} + +
+ Rank + + Contributor + + Issues Completed + + Total Earned +
+
+ {% if forloop.counter <= 3 %} + + #{{ forloop.counter }} + + {% else %} + #{{ forloop.counter }} + {% endif %} +
+
+ + + + {{ earner.issues_completed }} + + + ${{ earner.total_earned }} +
+
+
+ {% endif %} +
diff --git a/website/views/organization.py b/website/views/organization.py index 7a86441d87..bdd417db49 100644 --- a/website/views/organization.py +++ b/website/views/organization.py @@ -503,12 +503,47 @@ def get(self, request, *args, **kwargs): logger.error(f"Error fetching GitHub issues: {str(e)}") github_issues = [] + # Calculate bounty statistics + dollar5_issues = GitHubIssue.objects.filter(has_dollar_tag=True, state="closed") + total_issues_count = dollar5_issues.count() + paid_issues = dollar5_issues.filter(Q(sponsors_tx_id__isnull=False) | Q(bch_tx_id__isnull=False)) + paid_count = paid_issues.count() + grand_total_payouts = paid_count * 5 # Each issue is $5 + + # Build leaderboard of top earners + # Group by assignee and count their paid issues + from django.db.models import Count + + top_earners = ( + paid_issues.filter(assignee__isnull=False) + .values("assignee__name", "assignee__github_url", "assignee__avatar_url") + .annotate(issues_completed=Count("id")) + .order_by("-issues_completed")[:10] # Top 10 earners + ) + + # Calculate earnings for each top earner + leaderboard = [] + for earner in top_earners: + leaderboard.append( + { + "name": earner["assignee__name"], + "github_url": earner["assignee__github_url"], + "avatar_url": earner["assignee__avatar_url"], + "issues_completed": earner["issues_completed"], + "total_earned": earner["issues_completed"] * 5, + } + ) + context = { "hunts": hunts, "domains": Domain.objects.values("id", "name").all(), "github_issues": github_issues, "current_page": 1, "selected_issue_state": issue_state, + "total_issues_count": total_issues_count, + "paid_count": paid_count, + "grand_total_payouts": grand_total_payouts, + "leaderboard": leaderboard, } return render(request, self.template_name, context) From 5f30dcde09c92f292d0b515497bf10b9bef8afe4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 17:19:24 +0000 Subject: [PATCH 3/3] Address code review feedback - optimize queries and add constants Co-authored-by: DonnieBLT <128622481+DonnieBLT@users.noreply.github.com> --- website/views/organization.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/website/views/organization.py b/website/views/organization.py index bdd417db49..e0458cc533 100644 --- a/website/views/organization.py +++ b/website/views/organization.py @@ -504,18 +504,18 @@ def get(self, request, *args, **kwargs): github_issues = [] # Calculate bounty statistics + BOUNTY_AMOUNT = 5 # Dollar amount per bounty issue dollar5_issues = GitHubIssue.objects.filter(has_dollar_tag=True, state="closed") total_issues_count = dollar5_issues.count() paid_issues = dollar5_issues.filter(Q(sponsors_tx_id__isnull=False) | Q(bch_tx_id__isnull=False)) paid_count = paid_issues.count() - grand_total_payouts = paid_count * 5 # Each issue is $5 + grand_total_payouts = paid_count * BOUNTY_AMOUNT # Build leaderboard of top earners # Group by assignee and count their paid issues - from django.db.models import Count - top_earners = ( paid_issues.filter(assignee__isnull=False) + .select_related("assignee") .values("assignee__name", "assignee__github_url", "assignee__avatar_url") .annotate(issues_completed=Count("id")) .order_by("-issues_completed")[:10] # Top 10 earners @@ -530,7 +530,7 @@ def get(self, request, *args, **kwargs): "github_url": earner["assignee__github_url"], "avatar_url": earner["assignee__avatar_url"], "issues_completed": earner["issues_completed"], - "total_earned": earner["issues_completed"] * 5, + "total_earned": earner["issues_completed"] * BOUNTY_AMOUNT, } )