Skip to content

Commit bcaff0f

Browse files
bbearceObada HaddadDidayolo
authored andcommitted
Django to 4.2.0 (#1959)
* django_to_3.2.25 * django_to_4.0.0 * django to 4.20 - close but still failing tests * pdb in test_competitions * STATICFILES_STORAGE is old pattern * some migrations for changes to default field behaviors * flake adjustments * remove a pdb for test * channels to 4.2.0 * merged develop into django4 branch * Added Daphne middleware + whitenoise for static files; fixed csrf errors * CSRF fixed * CSRF fixes * Fixed potential botocore problem with use_ssl state * rebase and modifed migration * remove daphne and selenium * flake8 fixes * makemigrations results after rebase * Cleanup * Update packages --------- Co-authored-by: Obada Haddad <obada.haddad@lisn.fr> Co-authored-by: didayolo <adrien.pavao@gmail.com>
1 parent 1c10299 commit bcaff0f

38 files changed

Lines changed: 1758 additions & 1060 deletions

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.9.20
1+
FROM python:3.10.0
22

33
RUN apt-get update && apt-get install -y gcc build-essential && rm -rf /var/lib/apt/lists/*
44

docker-compose.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
version: '3'
21
services:
32
#-----------------------------------------------
43
# Web Services

poetry.lock

Lines changed: 954 additions & 940 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,45 @@ readme = "README.md"
77
package-mode = false
88

99
[tool.poetry.dependencies]
10-
python = "3.9.20"
11-
django = "3.2.0"
12-
django-oauth-toolkit = "1.2.0"
10+
python = ">=3.10,<3.11"
11+
django = "^4.2.0"
12+
django-oauth-toolkit = "1.6.3"
1313
django-cors-middleware = "1.5.0"
14-
social-auth-core = "2.0.0"
15-
social-auth-app-django = "3.1.0"
14+
social-auth-core = "^4.1.0"
15+
social-auth-app-django = "^5.0.0"
1616
six = "1.16.0"
17-
django-extensions = "2.2.6"
18-
channels = "3.0.0"
17+
django-extensions = "^3.2"
18+
channels = "4.2.0"
1919
channels-redis = "4.0.0"
20-
django-extra-fields = "0.9"
2120
pillow = "10.3.0"
2221
celery = "4.4.7"
2322
gunicorn = "22.0.0"
24-
urllib3 = ">=1.21.1,<1.25"
25-
uvicorn = {version = "0.13.3", extras = ["standard"]}
23+
urllib3 = ">=1.25.4,<1.27"
24+
uvicorn = "^0.22.0"
2625
pyyaml = "5.3.1"
2726
watchdog = "2.1.1"
2827
argh = "0.26.2"
2928
python-dateutil = "2.7.3"
3029
bpython = "^0.21.0"
31-
websockets = "8.1"
30+
websockets = "^10.4.0"
3231
aiofiles = "0.4.0"
3332
oyaml = "0.7"
3433
factory-boy = "2.11.1"
35-
bleach = "3.1.4"
34+
bleach = ">=5.0.0"
3635
django-debug-toolbar = "3.2"
3736
django-querycount = "0.7.0"
3837
blessings = "1.7"
39-
django-su = "0.9.0"
38+
django-su = "^1.0.0"
4039
django-ajax-selects = "2.0.0"
4140
dj-database-url = "0.4.2"
42-
psycopg2-binary = "2.8.6"
41+
psycopg2-binary = "^2.9.9"
4342
django-redis = "4.12.1"
44-
django-storages = "1.9.1"
45-
azure-storage-blob = "2.1.0"
43+
django-storages = {extras = ["azure"], version = "^1.14.6"}
44+
azure-storage-blob = "^12"
4645
azure-storage-common = "2.1.0"
47-
boto3 = "1.9.68"
46+
boto3 = "1.26.76"
4847
whitenoise = "5.2.0"
49-
coreapi = "2.3.3"
50-
djangorestframework = "3.12.0"
48+
djangorestframework = ">=3.13.0"
5149
djangorestframework-csv = "3.0.1"
5250
drf-extensions = "0.4.0"
5351
markdown = "2.6.11"
@@ -60,16 +58,21 @@ django-enforce-host = "1.0.1"
6058
twisted = "24.7.0"
6159
ipdb = "0.13"
6260
flake8 = "3.8.4"
63-
pytest = "6.2.1"
64-
pytest-django = "4.1.0"
65-
pytest-pythonpath = "0.7.3"
61+
pytest = "^7.0.0"
62+
pytest-django = "^4.5.0"
6663
jinja2 = "3.1.4"
6764
requests = "2.32.2"
68-
drf-extra-fields = "3.0.2"
69-
drf-yasg2 = "^1.19.4"
70-
swagger-spec-validator = "^3.0.4"
65+
drf-extra-fields = ">=3.5.0"
66+
botocore = "1.29.76"
67+
s3transfer = "0.6.0"
68+
drf-spectacular = "^0.28.0"
69+
coreapi = "^2.3.3"
7170
loguru = "^0.7.3"
72-
71+
[tool.pytest.ini_options]
72+
DJANGO_SETTINGS_MODULE = "settings.develop" # Just "settings" since pytest will be running from src/
73+
pythonpath = [".", "src", "src/apps"]
74+
testpaths = ["src"] # Tell pytest to look for tests in src/
75+
addopts = "-v --tb=short --reuse-db"
7376
[build-system]
7477
requires = ["poetry-core"]
7578
build-backend = "poetry.core.masonry.api"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Generated by Django 4.2.23 on 2025-09-08 12:32
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("analytics", "0002_auto_20250218_1143"),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name="adminstoragedatapoint",
15+
name="id",
16+
field=models.BigAutoField(
17+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
18+
),
19+
),
20+
migrations.AlterField(
21+
model_name="competitionstoragedatapoint",
22+
name="id",
23+
field=models.BigAutoField(
24+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
25+
),
26+
),
27+
migrations.AlterField(
28+
model_name="storageusagehistory",
29+
name="id",
30+
field=models.BigAutoField(
31+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
32+
),
33+
),
34+
migrations.AlterField(
35+
model_name="userstoragedatapoint",
36+
name="id",
37+
field=models.BigAutoField(
38+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
39+
),
40+
),
41+
]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Generated by Django 4.2.23 on 2025-09-08 12:32
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("announcements", "0003_auto_20230616_1326"),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name="announcement",
15+
name="id",
16+
field=models.BigAutoField(
17+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
18+
),
19+
),
20+
migrations.AlterField(
21+
model_name="newspost",
22+
name="id",
23+
field=models.BigAutoField(
24+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
25+
),
26+
),
27+
]

src/apps/api/tests/test_competitions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ def test_manual_migration_checks_permissions_must_be_collaborator_to_migrate(sel
109109

110110
def test_manual_migration_makes_submissions_from_one_phase_in_another(self):
111111
self.client.login(username='creator', password='creator')
112-
113112
# make 5 submissions in phase 1
114113
for _ in range(5):
115114
SubmissionFactory(owner=self.creator, phase=self.phase_1, status=Submission.FINISHED, leaderboard=self.leaderboard)
@@ -123,6 +122,7 @@ def test_manual_migration_makes_submissions_from_one_phase_in_another(self):
123122
assert resp.status_code == 200
124123
assert run_submission_mock.call_count == 5
125124

125+
self.phase_2.refresh_from_db()
126126
# check phase 2 has the 5 submissions
127127
assert self.phase_1.submissions.count() == 5
128128
assert self.phase_2.submissions.count() == 5

src/apps/api/urls.py

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from django.conf.urls import include
2-
from django.urls import path, re_path
3-
from drf_yasg2 import openapi
4-
from drf_yasg2.views import get_schema_view
2+
from django.urls import path
3+
4+
5+
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, SpectacularRedocView
6+
7+
58
from rest_framework.authtoken.views import obtain_auth_token
69
from rest_framework.routers import SimpleRouter
7-
from rest_framework.permissions import AllowAny
810
from rest_framework.urlpatterns import format_suffix_patterns
911

1012
from .views import (
@@ -33,15 +35,6 @@
3335
router.register('users', profiles.UserViewSet, 'users')
3436
router.register('organizations', profiles.OrganizationViewSet, 'organizations')
3537

36-
schema_view = get_schema_view(
37-
openapi.Info(
38-
title="Codabench API",
39-
default_version='v1',
40-
),
41-
validators=['flex', 'ssv'],
42-
public=True,
43-
permission_classes=(AllowAny,),
44-
)
4538

4639
urlpatterns = [
4740
path('my_profile/', profiles.GetMyProfile.as_view()),
@@ -74,9 +67,9 @@
7467
path('analytics/check_orphans_deletion_status/', analytics.check_orphans_deletion_status, name="check_orphans_deletion_status"),
7568

7669
# API Docs
77-
re_path(r'docs(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
78-
path('docs/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
79-
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
70+
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
71+
path('docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='schema-swagger-ui'),
72+
path('redoc/', SpectacularRedocView.as_view(url_name='schema'), name='schema-redoc'),
8073

8174
# Include this at the end so our URLs above run first, like /datasets/completed/<pk>/ before /datasets/<pk>/
8275
path('', include(format_suffix_patterns(router.urls, allowed=['html', 'json', 'csv', 'zip']))),

src/apps/api/views/competitions.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from django.db import IntegrityError
99
from django.db.models import Subquery, OuterRef, Q
1010
from django_filters.rest_framework import DjangoFilterBackend
11-
from drf_yasg2.utils import swagger_auto_schema, no_body
11+
from drf_spectacular.utils import extend_schema
1212
from rest_framework import status
1313
from rest_framework.decorators import action
1414
from rest_framework.exceptions import PermissionDenied, ValidationError
@@ -505,7 +505,8 @@ def results(self, request, pk, format='json'):
505505
dict_writer.writerow(row)
506506
return response
507507

508-
@swagger_auto_schema(responses={200: CompetitionCreationTaskStatusSerializer()})
508+
# @swagger_auto_schema(responses={200: CompetitionCreationTaskStatusSerializer()})
509+
@extend_schema(responses={200: CompetitionCreationTaskStatusSerializer})
509510
@action(detail=True, methods=('GET',))
510511
def creation_status(self, request, pk):
511512
"""This endpoint gets the creation status for a competition during upload"""
@@ -518,7 +519,8 @@ def creation_status(self, request, pk):
518519

519520
return Response(serializer.data)
520521

521-
@swagger_auto_schema(responses={200: FrontPageCompetitionsSerializer()})
522+
# @swagger_auto_schema(responses={200: FrontPageCompetitionsSerializer()})
523+
@extend_schema(responses={200: FrontPageCompetitionsSerializer})
522524
@action(detail=False, methods=('GET',), permission_classes=(AllowAny,))
523525
def front_page(self, request):
524526
popular_comps = get_popular_competitions()
@@ -530,7 +532,8 @@ def front_page(self, request):
530532
"recent_comps": recent_comps_serializer.data
531533
})
532534

533-
@swagger_auto_schema(request_body=no_body, responses={201: CompetitionCreationTaskStatusSerializer()})
535+
# @swagger_auto_schema(request_body=no_body, responses={201: CompetitionCreationTaskStatusSerializer()})
536+
@extend_schema(request=None, responses={201: CompetitionCreationTaskStatusSerializer})
534537
@action(detail=True, methods=('POST',), serializer_class=CompetitionCreationTaskStatusSerializer)
535538
def create_dump(self, request, pk=None):
536539
competition = self.get_object()
@@ -708,6 +711,7 @@ def manually_migrate(self, request, pk):
708711
{"detail": "You do not have administrative permissions for this competition"},
709712
status=status.HTTP_403_FORBIDDEN
710713
)
714+
# import pdb; pdb.set_trace()
711715
manual_migration.apply_async((pk,))
712716
return Response({}, status=status.HTTP_200_OK)
713717

@@ -764,7 +768,8 @@ def rerun_submissions(self, request, pk):
764768
else:
765769
raise PermissionDenied(error_message)
766770

767-
@swagger_auto_schema(responses={200: PhaseResultsSerializer})
771+
# @swagger_auto_schema(responses={200: PhaseResultsSerializer})
772+
@extend_schema(responses={200: PhaseResultsSerializer})
768773
@action(detail=True, methods=['GET'], permission_classes=[AllowAny])
769774
def get_leaderboard(self, request, pk):
770775
phase = self.get_object()

src/apps/competitions/migrations/0001_initial.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33
from django.db import migrations, models
44
import django.db.models.deletion
55
import django.utils.timezone
6-
import storages.backends.s3boto3
76
import utils.data
87
import uuid
98

9+
# New
10+
from storages.backends.s3boto3 import S3Boto3Storage
11+
12+
13+
class _MigrationPrivateStorage(S3Boto3Storage):
14+
bucket_name = 'private'
15+
1016

1117
class Migration(migrations.Migration):
1218

@@ -109,8 +115,8 @@ class Migration(migrations.Migration):
109115
('status', models.CharField(choices=[('None', 'None'), ('Submitting', 'Submitting'), ('Submitted', 'Submitted'), ('Preparing', 'Preparing'), ('Running', 'Running'), ('Scoring', 'Scoring'), ('Cancelled', 'Cancelled'), ('Finished', 'Finished'), ('Failed', 'Failed')], default='Submitting', max_length=128)),
110116
('status_details', models.TextField(blank=True, null=True)),
111117
('appear_on_leaderboards', models.BooleanField(default=False)),
112-
('prediction_result', models.FileField(blank=True, null=True, storage=storages.backends.s3boto3.S3Boto3Storage(bucket='private'), upload_to=utils.data.PathWrapper('prediction_result'))),
113-
('scoring_result', models.FileField(blank=True, null=True, storage=storages.backends.s3boto3.S3Boto3Storage(bucket='private'), upload_to=utils.data.PathWrapper('scoring_result'))),
118+
('prediction_result', models.FileField(blank=True, null=True, storage=_MigrationPrivateStorage(), upload_to=utils.data.PathWrapper('prediction_result'))),
119+
('scoring_result', models.FileField(blank=True, null=True, storage=_MigrationPrivateStorage(), upload_to=utils.data.PathWrapper('scoring_result'))),
114120
('secret', models.UUIDField(default=uuid.uuid4)),
115121
('task_id', models.UUIDField(blank=True, null=True)),
116122
('name', models.CharField(blank=True, default='', max_length=120, null=True)),
@@ -127,7 +133,7 @@ class Migration(migrations.Migration):
127133
fields=[
128134
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
129135
('name', models.CharField(max_length=50)),
130-
('data_file', models.FileField(storage=storages.backends.s3boto3.S3Boto3Storage(bucket='private'), upload_to=utils.data.PathWrapper('submission_details'))),
136+
('data_file', models.FileField(storage=_MigrationPrivateStorage(), upload_to=utils.data.PathWrapper('submission_details'))),
131137
('is_scoring', models.BooleanField(default=False)),
132138
('submission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='details', to='competitions.Submission')),
133139
],

0 commit comments

Comments
 (0)