Skip to content

Commit 99d8ef2

Browse files
authored
Merge branch 'main' into security
2 parents d05866d + 2664f52 commit 99d8ef2

10 files changed

+590
-42
lines changed

.cursorrules

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
when using cursor rules, show two emojis that are related to the task at hand also give a confidence score 1-100
1+
when using cursor rules, show five emojis that are related to the task at hand also give a confidence score 1-100 and a haiku of the change you're about ot make
22
{
33
"urlModules": {
44
"website/urls.py": "blt/urls.py"
@@ -18,4 +18,5 @@ if you are not sure about the user's intent, ask the user for clarification
1818
dont include exceptions in error messages but be very detailed in text what the error is - don't do this: messages.error(request, f"Error: {str(e)}")
1919
keep javascript in separate files and don't add it to html files
2020
avoid installing packages that are not needed
21-
remove any <style tags and use tailwind css instead
21+
remove any <style tags and use tailwind css instead
22+
when fixing an issue, always fix the root cause

blt/urls.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
HackathonPrizeCreateView,
146146
HackathonSponsorCreateView,
147147
HackathonUpdateView,
148+
refresh_repository_data,
148149
)
149150
from website.views.issue import (
150151
AllIssuesView,
@@ -1044,13 +1045,26 @@
10441045
path("api/get-wallet-balance/", get_wallet_balance, name="get_wallet_balance"),
10451046
path("extension/", TemplateView.as_view(template_name="extension.html"), name="extension"),
10461047
path("roadmap/", RoadmapView.as_view(), name="roadmap"),
1047-
# Hackathon URLs.
1048-
path("hackathons/", HackathonListView.as_view(), name="hackathons"),
1049-
path("hackathons/create/", HackathonCreateView.as_view(), name="hackathon_create"),
1050-
path("hackathons/<slug:slug>/", HackathonDetailView.as_view(), name="hackathon_detail"),
1051-
path("hackathons/<slug:slug>/edit/", HackathonUpdateView.as_view(), name="hackathon_edit"),
1052-
path("hackathons/<slug:slug>/add-sponsor/", HackathonSponsorCreateView.as_view(), name="hackathon_add_sponsor"),
1053-
path("hackathons/<slug:slug>/add-prize/", HackathonPrizeCreateView.as_view(), name="hackathon_add_prize"),
1048+
# Hackathon URLs
1049+
path(
1050+
"hackathons/",
1051+
include(
1052+
[
1053+
path("", HackathonListView.as_view(), name="hackathons"),
1054+
path("create/", HackathonCreateView.as_view(), name="hackathon_create"),
1055+
path("<slug:slug>/", HackathonDetailView.as_view(), name="hackathon_detail"),
1056+
path("<slug:slug>/edit/", HackathonUpdateView.as_view(), name="hackathon_update"),
1057+
path("<slug:slug>/add-sponsor/", HackathonSponsorCreateView.as_view(), name="hackathon_sponsor_create"),
1058+
path("<slug:slug>/add-prize/", HackathonPrizeCreateView.as_view(), name="hackathon_prize_create"),
1059+
# Add the new URL pattern for refreshing repository data
1060+
path(
1061+
"<slug:hackathon_slug>/refresh-repo/<int:repo_id>/",
1062+
refresh_repository_data,
1063+
name="refresh_repository_data",
1064+
),
1065+
]
1066+
),
1067+
),
10541068
path("page-vote/", page_vote, name="page_vote"),
10551069
# Queue Management URLs
10561070
path("queue/", queue_list, name="queue_list"),

website/forms.py

+15
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ class Meta:
211211
"registration_open",
212212
"max_participants",
213213
"repositories",
214+
"sponsor_note",
215+
"sponsor_link",
214216
]
215217
widgets = {
216218
"description": forms.Textarea(
@@ -225,6 +227,19 @@ class Meta:
225227
"class": "w-full rounded-md border-gray-300 shadow-sm focus:border-[#e74c3c] focus:ring focus:ring-[#e74c3c] focus:ring-opacity-50",
226228
}
227229
),
230+
"sponsor_note": forms.Textarea(
231+
attrs={
232+
"rows": 4,
233+
"class": "w-full rounded-md border-gray-300 shadow-sm focus:border-[#e74c3c] focus:ring focus:ring-[#e74c3c] focus:ring-opacity-50",
234+
"placeholder": "Provide information about sponsorship opportunities for this hackathon",
235+
}
236+
),
237+
"sponsor_link": forms.URLInput(
238+
attrs={
239+
"class": "w-full rounded-md border-gray-300 shadow-sm focus:border-[#e74c3c] focus:ring focus:ring-[#e74c3c] focus:ring-opacity-50",
240+
"placeholder": "https://example.com/sponsor",
241+
}
242+
),
228243
"start_time": forms.DateTimeInput(
229244
attrs={
230245
"type": "datetime-local",

website/management/commands/sync_repo_contributors.py

+46-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from django.conf import settings
66
from django.core.management.base import BaseCommand
77

8-
from website.models import Contributor, Repo
8+
from website.models import Contributor, GitHubIssue, Repo
99

1010
logger = logging.getLogger(__name__)
1111

@@ -59,6 +59,7 @@ def handle(self, *args, **options):
5959
time.sleep(1)
6060

6161
# Batch create/update contributors
62+
contributors_by_login = {}
6263
for contrib_data in all_contributors:
6364
github_id = contrib_data.get("id")
6465
if not github_id:
@@ -76,7 +77,51 @@ def handle(self, *args, **options):
7677
)
7778
repo.contributor.add(contributor)
7879

80+
# Store contributor by login for later use
81+
contributors_by_login[contrib_data.get("login")] = contributor
82+
7983
repo.contributor_count = len(all_contributors)
8084
repo.save()
8185

86+
# Link contributors to GitHubIssue records
87+
# Get all pull requests for this repo that don't have a contributor
88+
pull_requests = GitHubIssue.objects.filter(repo=repo, type="pull_request", contributor__isnull=True)
89+
90+
logger.info(f"Found {pull_requests.count()} pull requests without contributors")
91+
92+
for pr in pull_requests:
93+
try:
94+
# Extract GitHub username from URL
95+
# URL format: https://github.com/owner/repo/pull/123
96+
parts = pr.url.split("/")
97+
pr_number = parts[6]
98+
99+
# Get PR details from GitHub API
100+
api_url = f"https://api.github.com/repos/{owner_repo}/pulls/{pr_number}"
101+
response = requests.get(api_url, headers=headers)
102+
103+
if response.status_code != 200:
104+
logger.warning(f"Failed to fetch PR #{pr_number}: {response.status_code}")
105+
continue
106+
107+
pr_data = response.json()
108+
github_username = pr_data["user"]["login"]
109+
110+
# Find contributor by username
111+
contributor = contributors_by_login.get(github_username)
112+
113+
if contributor:
114+
# Link contributor to PR
115+
pr.contributor = contributor
116+
117+
# Also check if PR is merged
118+
if pr_data.get("merged_at") and not pr.is_merged:
119+
pr.is_merged = True
120+
121+
pr.save()
122+
logger.info(f"Linked PR #{pr_number} to contributor {github_username}")
123+
124+
except Exception as e:
125+
logger.error(f"Error processing PR #{pr.issue_id}: {str(e)}")
126+
82127
logger.info(f"Synced {len(all_contributors)} contributors for {repo.name}")

website/management/commands/update_github_issues.py

+38-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from django.utils import timezone
88

99
from website.management.base import LoggedBaseCommand
10-
from website.models import GitHubIssue, GitHubReview, Repo, UserProfile
10+
from website.models import Contributor, GitHubIssue, GitHubReview, Repo, UserProfile
1111

1212

1313
class Command(LoggedBaseCommand):
@@ -60,9 +60,45 @@ def handle(self, *args, **options):
6060
merged = True if pr["pull_request"].get("merged_at") else False
6161
repo = Repo.objects.get(name__iexact=repo_name)
6262

63+
# Get or create contributor record
64+
try:
65+
# Get user details from GitHub API
66+
user_api_url = pr["user"]["url"]
67+
user_response = requests.get(user_api_url, headers=headers)
68+
user_response.raise_for_status()
69+
user_data = user_response.json()
70+
71+
# Find or create contributor
72+
contributor, created = Contributor.objects.get_or_create(
73+
github_id=user_data["id"],
74+
defaults={
75+
"name": user_data["login"],
76+
"github_url": user_data["html_url"],
77+
"avatar_url": user_data["avatar_url"],
78+
"contributor_type": user_data["type"],
79+
"contributions": 1,
80+
},
81+
)
82+
83+
if not created:
84+
# Update existing contributor data
85+
contributor.name = user_data["login"]
86+
contributor.github_url = user_data["html_url"]
87+
contributor.avatar_url = user_data["avatar_url"]
88+
contributor.contributions += 1
89+
contributor.save()
90+
91+
# Add contributor to repo
92+
repo.contributor.add(contributor)
93+
94+
except Exception as e:
95+
self.stdout.write(self.style.WARNING(f"Error creating contributor: {str(e)}"))
96+
contributor = None
97+
6398
# Create or update the pull request
6499
github_issue, created = GitHubIssue.objects.update_or_create(
65100
issue_id=pr["number"],
101+
repo=repo, # Add repo to the lookup to ensure uniqueness
66102
defaults={
67103
"title": pr["title"],
68104
"body": pr.get("body", ""),
@@ -86,8 +122,8 @@ def handle(self, *args, **options):
86122
else None,
87123
"is_merged": merged,
88124
"url": pr["html_url"],
89-
"repo": repo,
90125
"user_profile": user,
126+
"contributor": contributor, # Link to contributor
91127
},
92128
)
93129

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Generated by Django 5.1.6 on 2025-03-11 22:50
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
("website", "0228_githubissue_contributor"),
9+
]
10+
11+
operations = [
12+
migrations.AddField(
13+
model_name="hackathon",
14+
name="sponsor_link",
15+
field=models.URLField(blank=True, help_text="Link to sponsorship information or application", null=True),
16+
),
17+
migrations.AddField(
18+
model_name="hackathon",
19+
name="sponsor_note",
20+
field=models.TextField(
21+
blank=True, help_text="Additional information about sponsorship opportunities", null=True
22+
),
23+
),
24+
]

website/models.py

+24-2
Original file line numberDiff line numberDiff line change
@@ -2087,6 +2087,11 @@ class Hackathon(models.Model):
20872087
max_participants = models.PositiveIntegerField(null=True, blank=True)
20882088
# Link to repositories that are part of this hackathon
20892089
repositories = models.ManyToManyField(Repo, related_name="hackathons", blank=True)
2090+
# Sponsor information
2091+
sponsor_note = models.TextField(
2092+
blank=True, null=True, help_text="Additional information about sponsorship opportunities"
2093+
)
2094+
sponsor_link = models.URLField(blank=True, null=True, help_text="Link to sponsorship information or application")
20902095

20912096
class Meta:
20922097
ordering = ["-start_time"]
@@ -2139,8 +2144,8 @@ def get_leaderboard(self):
21392144
repo__in=self.repositories.all(),
21402145
type="pull_request",
21412146
is_merged=True,
2142-
merged_at__gte=self.start_time,
2143-
merged_at__lte=self.end_time,
2147+
created_at__gte=self.start_time,
2148+
created_at__lte=self.end_time,
21442149
)
21452150

21462151
# Group by user_profile and count PRs
@@ -2153,6 +2158,23 @@ def get_leaderboard(self):
21532158
leaderboard[user_id]["prs"].append(pr)
21542159
else:
21552160
leaderboard[user_id] = {"user": pr.user_profile.user, "count": 1, "prs": [pr]}
2161+
elif pr.contributor and pr.contributor.github_id:
2162+
# If no user profile but has contributor, use contributor as key
2163+
contributor_id = f"contributor_{pr.contributor.id}"
2164+
if contributor_id in leaderboard:
2165+
leaderboard[contributor_id]["count"] += 1
2166+
leaderboard[contributor_id]["prs"].append(pr)
2167+
else:
2168+
leaderboard[contributor_id] = {
2169+
"user": {
2170+
"username": pr.contributor.name or pr.contributor.github_id,
2171+
"email": "",
2172+
"id": contributor_id,
2173+
},
2174+
"count": 1,
2175+
"prs": [pr],
2176+
"is_contributor": True,
2177+
}
21562178

21572179
# Convert to list and sort by count (descending)
21582180
leaderboard_list = list(leaderboard.values())

0 commit comments

Comments
 (0)