Skip to content

Commit 0f8b802

Browse files
authored
Merge pull request #1550 from codalab/develop
Rebase "shell_plus" branch
2 parents 4378b90 + f1bc022 commit 0f8b802

File tree

14 files changed

+394
-40
lines changed

14 files changed

+394
-40
lines changed

Dockerfile.flower

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
FROM python:3.9-alpine
1+
FROM python:3.9
2+
3+
# PYTHONUNBUFFERED: Force stdin, stdout and stderr to be totally unbuffered. (equivalent to `python -u`)
4+
# PYTHONHASHSEED: Enable hash randomization (equivalent to `python -R`)
5+
# PYTHONDONTWRITEBYTECODE: Do not write byte files to disk, since we maintain it as readonly. (equivalent to `python -B`)
6+
ENV PYTHONUNBUFFERED=1 PYTHONHASHSEED=random PYTHONDONTWRITEBYTECODE=1
27

38
# Get latest root certificates
4-
RUN apk add --no-cache ca-certificates && update-ca-certificates curl
5-
RUN apk add curl
9+
RUN apt-get update && apt-get install -y ca-certificates && update-ca-certificates
610

711
# # Install the required packages
812
RUN curl -sSL https://install.python-poetry.org | python3 -
@@ -18,13 +22,6 @@ RUN poetry add redis=3.0.1
1822
RUN poetry add flower=0.9.3
1923
RUN poetry add celery="<5.0.0"
2024

21-
# PYTHONUNBUFFERED: Force stdin, stdout and stderr to be totally unbuffered. (equivalent to `python -u`)
22-
# PYTHONHASHSEED: Enable hash randomization (equivalent to `python -R`)
23-
# PYTHONDONTWRITEBYTECODE: Do not write byte files to disk, since we maintain it as readonly. (equivalent to `python -B`)
24-
25-
26-
ENV PYTHONUNBUFFERED=1 PYTHONHASHSEED=random PYTHONDONTWRITEBYTECODE=1
27-
2825
# Default port
2926
EXPOSE 5555
3027

src/apps/api/serializers/datasets.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from api.mixins import DefaultUserCreateMixin
55
from datasets.models import Data, DataGroup
6+
from competitions.models import CompetitionCreationTaskStatus, CompetitionDump
67

78

89
class DataSerializer(DefaultUserCreateMixin, serializers.ModelSerializer):
@@ -101,13 +102,32 @@ class Meta:
101102
)
102103

103104
def get_competition(self, obj):
104-
105-
# return competition dict with id and title if available
106105
if obj.competition:
106+
# Submission
107107
return {
108108
"id": obj.competition.id,
109109
"title": obj.competition.title,
110110
}
111+
else:
112+
competition = None
113+
try:
114+
# Check if it is a bundle
115+
competition = CompetitionCreationTaskStatus.objects.get(dataset=obj).resulting_competition
116+
except CompetitionCreationTaskStatus.DoesNotExist:
117+
competition = None
118+
if not competition:
119+
# Check if it is a dump
120+
try:
121+
competition = CompetitionDump.objects.get(dataset=obj).competition
122+
except CompetitionDump.DoesNotExist:
123+
competition = None
124+
125+
if competition:
126+
return {
127+
"id": competition.id,
128+
"title": competition.title
129+
}
130+
111131
return None
112132

113133
def get_owner_display_name(self, instance):

src/apps/api/tests/test_submissions.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,17 @@ def setUp(self):
2626
# Extra dummy user to test permissions, they shouldn't have access to many things
2727
self.other_user = UserFactory(username='other_user', password='other')
2828

29-
# Make a participant and submission into competition
30-
self.participant = UserFactory(username='participant', password='other')
31-
CompetitionParticipantFactory(user=self.participant, competition=self.comp)
29+
# Make participants
30+
self.participant = UserFactory(username='participant_approved', password='other')
31+
self.pending_participant = UserFactory(username='participant_pending', password='other')
32+
self.denied_participant = UserFactory(username='participant_denied', password='other')
33+
34+
# Add user as participants in a competition with different statuses
35+
CompetitionParticipantFactory(user=self.participant, competition=self.comp, status=CompetitionParticipant.APPROVED)
36+
CompetitionParticipantFactory(user=self.pending_participant, competition=self.comp, status=CompetitionParticipant.PENDING)
37+
CompetitionParticipantFactory(user=self.denied_participant, competition=self.comp, status=CompetitionParticipant.DENIED)
38+
39+
# add submission with owner = approved participant
3240
self.existing_submission = SubmissionFactory(
3341
phase=self.phase,
3442
owner=self.participant,
@@ -232,11 +240,21 @@ def test_who_can_see_detailed_result_when_visualization_is_true(self):
232240
resp = self.client.get(url)
233241
assert resp.status_code == 200
234242

235-
# Actual user can see their submission detail result
243+
# approved user can see submission detail result
236244
self.client.force_login(self.participant)
237245
resp = self.client.get(url)
238246
assert resp.status_code == 200
239247

248+
# pending user cannot see submission detail result
249+
self.client.force_login(self.pending_participant)
250+
resp = self.client.get(url)
251+
assert resp.status_code == 403
252+
253+
# denied user cannot see submission detail result
254+
self.client.force_login(self.denied_participant)
255+
resp = self.client.get(url)
256+
assert resp.status_code == 403
257+
240258
# Regular user cannot see submission detail result
241259
self.client.force_login(self.other_user)
242260
resp = self.client.get(url)

src/apps/api/views/datasets.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ def get_queryset(self):
4040
# _type = dataset if called from datasets and programs tab to filter datasets and programs
4141
is_dataset = self.request.query_params.get('_type', '') == 'dataset'
4242

43+
# _type = dataset if called from datasets and programs tab to filter datasets and programs
44+
is_bundle = self.request.query_params.get('_type', '') == 'bundle'
45+
4346
# get queryset
4447
qs = self.queryset
4548

@@ -50,6 +53,11 @@ def get_queryset(self):
5053
# filter datasets and programs
5154
if is_dataset:
5255
qs = qs.filter(~Q(type=Data.SUBMISSION))
56+
qs = qs.exclude(Q(type=Data.COMPETITION_BUNDLE))
57+
58+
# filter bundles
59+
if is_bundle:
60+
qs = qs.filter(Q(type=Data.COMPETITION_BUNDLE))
5361

5462
# public filter check
5563
if is_public:
@@ -58,15 +66,15 @@ def get_queryset(self):
5866
qs = qs.filter(Q(created_by=self.request.user))
5967

6068
# if GET is called but provided no filters, fall back to default behaviour
61-
if (not is_submission) and (not is_dataset) and (not is_public):
69+
if (not is_submission) and (not is_dataset) and (not is_bundle) and (not is_public):
6270
qs = self.queryset
6371
qs = qs.filter(Q(is_public=True) | Q(created_by=self.request.user))
6472

6573
else:
6674
qs = self.queryset
6775
qs = qs.filter(Q(is_public=True) | Q(created_by=self.request.user))
6876

69-
qs = qs.exclude(Q(type=Data.COMPETITION_BUNDLE) | Q(name__isnull=True))
77+
qs = qs.exclude(Q(name__isnull=True))
7078

7179
qs = qs.select_related('created_by').order_by('-created_when')
7280

src/apps/api/views/leaderboards.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from rest_framework.permissions import IsAuthenticated
21
from rest_framework.viewsets import ModelViewSet
32
from rest_framework.response import Response
4-
from api.permissions import LeaderboardNotHidden, LeaderboardIsOrganizerOrCollaborator
3+
from rest_framework.status import HTTP_405_METHOD_NOT_ALLOWED
4+
from api.permissions import LeaderboardNotHidden
55
from api.serializers.leaderboards import LeaderboardEntriesSerializer
66
from api.serializers.submissions import SubmissionScoreSerializer
77
from leaderboards.models import Leaderboard, SubmissionScore
@@ -10,24 +10,32 @@
1010
class LeaderboardViewSet(ModelViewSet):
1111
queryset = Leaderboard.objects.all()
1212
serializer_class = LeaderboardEntriesSerializer
13+
http_method_names = ['get'] # Only allow GET requests
1314

14-
# TODO: The retrieve and list actions are the only ones used, apparently. Delete other permission checks soon!
15-
def get_permissions(self):
16-
if self.action in ['update', 'partial_update', 'destroy']:
17-
raise Exception('Unexpected code branch execution.')
18-
self.permission_classes = [LeaderboardIsOrganizerOrCollaborator]
19-
elif self.action in ['create']:
20-
raise Exception('Unexpected code branch execution.')
21-
self.permission_classes = [IsAuthenticated]
22-
elif self.action in ['retrieve', 'list']:
23-
self.permission_classes = [LeaderboardNotHidden]
15+
def create(self, request, *args, **kwargs):
16+
return Response({'detail': 'Method not allowed.'}, status=HTTP_405_METHOD_NOT_ALLOWED)
2417

25-
return [permission() for permission in self.permission_classes]
18+
def update(self, request, *args, **kwargs):
19+
return Response({'detail': 'Method not allowed.'}, status=HTTP_405_METHOD_NOT_ALLOWED)
20+
21+
def partial_update(self, request, *args, **kwargs):
22+
return Response({'detail': 'Method not allowed.'}, status=HTTP_405_METHOD_NOT_ALLOWED)
23+
24+
def destroy(self, request, *args, **kwargs):
25+
return Response({'detail': 'Method not allowed.'}, status=HTTP_405_METHOD_NOT_ALLOWED)
2626

2727
def list(self, request, *args, **kwargs):
2828
# Return an empty list for the leaderboard-list endpoint
2929
return Response([])
3030

31+
def get_permissions(self):
32+
if self.action in ['create', 'update', 'partial_update', 'destroy']:
33+
return [] # No permissions, effectively disables the action
34+
elif self.action in ['retrieve', 'list']:
35+
self.permission_classes = [LeaderboardNotHidden]
36+
37+
return [permission() for permission in self.permission_classes]
38+
3139

3240
class SubmissionScoreViewSet(ModelViewSet):
3341
queryset = SubmissionScore.objects.all()

src/apps/api/views/submissions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,14 +325,14 @@ def get_detail_result(self, request, pk):
325325
submission = Submission.objects.get(pk=pk)
326326
# Check if competition show visualization is true
327327
if submission.phase.competition.enable_detailed_results:
328-
# get submission's competition participants
329-
participants = submission.phase.competition.participants.all()
330-
participant_usernames = [participant.user.username for participant in participants]
328+
# get submission's competition approved participants
329+
approved_participants = submission.phase.competition.participants.filter(status=CompetitionParticipant.APPROVED)
330+
participant_usernames = [participant.user.username for participant in approved_participants]
331331

332332
# check if in this competition
333333
# user is collaborator
334334
# or
335-
# user is participant
335+
# user is approved participant
336336
# or
337337
# user is creator
338338
# or

src/apps/competitions/tasks.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,8 @@ def _get_error_string(error_dict):
410410
# call again, to make sure phases get sent to chahub
411411
competition.save()
412412
logger.info("Competition saved!")
413+
status.dataset.name += f" - {competition.title}"
414+
status.dataset.save()
413415

414416
except CompetitionUnpackingException as e:
415417
# We want to catch well handled exceptions and display them to the user

0 commit comments

Comments
 (0)