-
-
Notifications
You must be signed in to change notification settings - Fork 888
Add field to store github_branch for challenges #4745
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 18 commits
8d6ae67
9164bf0
3cf0b12
2f6e1a3
69b9535
4bff095
75a7868
e2d7f53
f6a06d7
3287cc9
34cbec3
a1bd1ea
6bbcea7
4188b9c
831fec2
1cb4fcf
8af18d5
350936f
fb379aa
8a8e2d8
1355c62
d136023
c8bd7c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,32 @@ | |
from django.db import migrations, models | ||
|
||
|
||
def populate_github_branch_default(apps, schema_editor): | ||
""" | ||
Populate existing challenges with empty github_branch fields to use "challenge" as default. | ||
""" | ||
Challenge = apps.get_model("challenges", "Challenge") | ||
# Update all challenges that have github_repository but empty github_branch | ||
Challenge.objects.filter(github_repository__isnull=False).exclude( | ||
github_repository="" | ||
).filter(github_branch__isnull=True).update(github_branch="challenge") | ||
|
||
# Also update challenges with empty string github_branch | ||
Challenge.objects.filter(github_repository__isnull=False).exclude( | ||
github_repository="" | ||
).filter(github_branch="").update(github_branch="challenge") | ||
|
||
|
||
def reverse_populate_github_branch_default(apps, schema_editor): | ||
""" | ||
Reverse migration - set github_branch back to empty string for challenges that were set to "challenge". | ||
""" | ||
Challenge = apps.get_model("challenges", "Challenge") | ||
# Only reverse if the field was set to "challenge" by this migration | ||
Challenge.objects.filter(github_repository__isnull=False).exclude( | ||
github_repository="" | ||
).filter(github_branch="challenge").update(github_branch="") | ||
|
||
def fix_duplicate_github_fields(apps, schema_editor): | ||
""" | ||
No data migration needed since we're using a partial unique constraint. | ||
|
@@ -28,9 +54,14 @@ class Migration(migrations.Migration): | |
model_name="challenge", | ||
name="github_branch", | ||
field=models.CharField( | ||
blank=True, default="", max_length=200, null=True | ||
blank=True, default="challenge", max_length=200, null=True | ||
), | ||
), | ||
# Data migration to populate existing records | ||
migrations.RunPython( | ||
populate_github_branch_default, | ||
reverse_populate_github_branch_default, | ||
), | ||
|
||
migrations.RunPython( | ||
fix_duplicate_github_fields, | ||
reverse_fix_duplicate_github_fields, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -188,7 +188,7 @@ def __init__(self, *args, **kwargs): | |
) | ||
# The github branch name used to create/update the challenge | ||
github_branch = models.CharField( | ||
max_length=200, null=True, blank=True, default="" | ||
max_length=200, null=True, blank=True, default=challenge | ||
|
||
) | ||
# The number of vCPU for a Fargate worker for the challenge. Default value | ||
# is 0.25 vCPU. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3898,8 +3898,9 @@ def create_or_update_github_challenge(request, challenge_host_team_pk): | |
return Response(response_data, status=status.HTTP_406_NOT_ACCEPTABLE) | ||
|
||
# Get branch name with default fallback | ||
github_branch = request.data.get("GITHUB_BRANCH_NAME", "") | ||
|
||
github_branch = request.data.get("GITHUB_BRANCH_NAME") or request.data.get( | ||
"BRANCH_NAME", "challenge" | ||
|
||
) | ||
challenge_queryset = Challenge.objects.filter( | ||
github_repository=request.data["GITHUB_REPOSITORY"], | ||
github_branch=github_branch, | ||
|
@@ -4288,6 +4289,8 @@ def create_or_update_github_challenge(request, challenge_host_team_pk): | |
"challenge_evaluation_script_file" | ||
], | ||
"worker_image_url": worker_image_url, | ||
"github_repository": request.data["GITHUB_REPOSITORY"], | ||
"github_branch": github_branch, | ||
}, | ||
) | ||
if serializer.is_valid(): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
#!/usr/bin/env python | ||
# Command to run: python manage.py shell < scripts/migration/populate_github_branch.py | ||
""" | ||
Populate existing challenges with github_branch="challenge" for backward compatibility. | ||
This script should be run after the migration to ensure all existing challenges | ||
have the github_branch field populated with the default value. | ||
""" | ||
|
||
import traceback | ||
|
||
from challenges.models import Challenge | ||
from django.db import models | ||
|
||
|
||
def populate_github_branch_fields(): | ||
""" | ||
Populate existing challenges with empty github_branch fields to use "challenge" as default. | ||
""" | ||
print("Starting github_branch field population...") | ||
|
||
challenges_to_update = ( | ||
Challenge.objects.filter(github_repository__isnull=False) | ||
.exclude(github_repository="") | ||
.filter( | ||
models.Q(github_branch__isnull=True) | models.Q(github_branch="") | ||
) | ||
Comment on lines
+1
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we already have this script to populate and back-fill, why do we need to the populate/reverse populate methods at all? |
||
) | ||
|
||
count = challenges_to_update.count() | ||
|
||
if count == 0: | ||
print("No challenges found that need github_branch population.") | ||
return | ||
|
||
print(f"Found {count} challenges that need github_branch population.") | ||
|
||
updated_count = challenges_to_update.update(github_branch="challenge") | ||
|
||
print( | ||
f"Successfully updated {updated_count} challenges with github_branch='challenge'" | ||
) | ||
|
||
remaining_empty = ( | ||
Challenge.objects.filter(github_repository__isnull=False) | ||
.exclude(github_repository="") | ||
.filter( | ||
models.Q(github_branch__isnull=True) | models.Q(github_branch="") | ||
) | ||
.count() | ||
) | ||
|
||
if remaining_empty == 0: | ||
print("✅ All challenges now have github_branch populated!") | ||
else: | ||
print( | ||
f"⚠️ Warning: {remaining_empty} challenges still have empty github_branch fields" | ||
) | ||
|
||
sample_challenges = ( | ||
Challenge.objects.filter(github_repository__isnull=False) | ||
.exclude(github_repository="") | ||
.values("id", "title", "github_repository", "github_branch")[:5] | ||
) | ||
|
||
print("\nSample updated challenges:") | ||
for challenge in sample_challenges: | ||
print( | ||
f" ID: {challenge['id']}, Title: {challenge['title']}, " | ||
f"Repo: {challenge['github_repository']}, Branch: {challenge['github_branch']}" | ||
) | ||
|
||
|
||
try: | ||
populate_github_branch_fields() | ||
print("\n✅ Script completed successfully!") | ||
except Exception as e: | ||
print(f"\n❌ Error occurred: {e}") | ||
print(traceback.print_exc()) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6020,14 +6020,13 @@ def test_create_challenge_using_github_without_branch_name(self): | |
self.assertEqual(response.status_code, 201) | ||
self.assertEqual(response.json(), expected) | ||
|
||
# Verify github_branch defaults to empty string when not provided | ||
# Verify github_branch defaults to "challenge" when not provided | ||
|
||
challenge = Challenge.objects.first() | ||
self.assertEqual( | ||
challenge.github_repository, | ||
"https://github.com/yourusername/repository", | ||
) | ||
self.assertEqual(challenge.github_branch, "") | ||
|
||
self.assertEqual(challenge.github_branch, "challenge") | ||
|
||
class ValidateChallengeTest(APITestCase): | ||
def setUp(self): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this also be okay with just "challenge" (default) if passed?