From 5bd439ca240602801ae4e01205a2ca31679a0941 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Thu, 22 Jun 2023 13:48:41 +0500 Subject: [PATCH 01/22] organization message displayed --- src/static/riot/competitions/detail/submission_upload.tag | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/static/riot/competitions/detail/submission_upload.tag b/src/static/riot/competitions/detail/submission_upload.tag index 792842582..b93b84dab 100644 --- a/src/static/riot/competitions/detail/submission_upload.tag +++ b/src/static/riot/competitions/detail/submission_upload.tag @@ -55,6 +55,9 @@ + You can either submit as yourself of as an organization. +
+ Click to create an Organization @@ -571,5 +574,9 @@ .graph-container display block height 250px + + #submit-as + color #8c8c8c + font-style italic From 5b2e73c696a402458122db3f21b7eefc9d5cf0f5 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Thu, 22 Jun 2023 13:51:45 +0500 Subject: [PATCH 02/22] spelling mistake fixed --- src/static/riot/competitions/detail/submission_upload.tag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/riot/competitions/detail/submission_upload.tag b/src/static/riot/competitions/detail/submission_upload.tag index b93b84dab..d754eb57e 100644 --- a/src/static/riot/competitions/detail/submission_upload.tag +++ b/src/static/riot/competitions/detail/submission_upload.tag @@ -55,7 +55,7 @@ - You can either submit as yourself of as an organization. + You can either submit as yourself or as an organization.
Click to create an Organization From ab6997294b81a5fb25e015a1271fc616c028a0fe Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Thu, 22 Jun 2023 17:34:37 +0500 Subject: [PATCH 03/22] add organization added in dropdown --- .../competitions/detail/submission_upload.tag | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/static/riot/competitions/detail/submission_upload.tag b/src/static/riot/competitions/detail/submission_upload.tag index d754eb57e..fe4211b77 100644 --- a/src/static/riot/competitions/detail/submission_upload.tag +++ b/src/static/riot/competitions/detail/submission_upload.tag @@ -50,14 +50,20 @@
- + + - You can either submit as yourself or as an organization. -
- Click to create an Organization + + +
@@ -182,8 +188,19 @@ $(self.refs.data_file.refs.file_input).on('change', self.check_can_upload) self.setup_autoscroll() self.setup_websocket() + }) + + // Function to capture change of `submit as` dropdown + // Redirect to Add organization if selected option is Add Organizaiton + $(document).on('change','#organization_dropdown',function(){ + let selected_option_value = $('#organization_dropdown option:selected').val(); + if(selected_option_value == 'add_organization'){ + // Open Add organization in new tab + window.open('/profiles/organization/create/', '_blank') + } }) + self.setup_autoscroll = function () { if (!self.refs.autoscroll_checkbox) { return @@ -425,7 +442,9 @@ self.lines = {} let dropdown = $('#organization_dropdown') let organization = dropdown.dropdown('get value') - organization = organization === 'None' ? null : organization + if(organization === 'add_organization' | organization === 'None'){ + organization = null + } dropdown.attr('disabled', 'disabled') @@ -574,9 +593,6 @@ .graph-container display block height 250px - - #submit-as - color #8c8c8c - font-style italic + From b954507b84fa9d63f570b79261483f1310108e06 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Mon, 31 Jul 2023 18:56:56 +0500 Subject: [PATCH 04/22] hide organization members emails from non-members --- src/apps/profiles/views.py | 4 ++++ src/templates/profiles/organization_detail.html | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/apps/profiles/views.py b/src/apps/profiles/views.py index cfa4f2e10..e37b46710 100644 --- a/src/apps/profiles/views.py +++ b/src/apps/profiles/views.py @@ -261,8 +261,12 @@ def get_context_data(self, **kwargs): membership = self.object.membership_set.filter(user=self.request.user) if len(membership) == 1: context['is_editor'] = membership.first().group in Membership.EDITORS_GROUP + context['is_member'] = membership.first().group in Membership.SETTABLE_PERMISSIONS else: context['is_editor'] = False + context['is_member'] = False + + print(context) return context diff --git a/src/templates/profiles/organization_detail.html b/src/templates/profiles/organization_detail.html index 386403a22..e92408038 100644 --- a/src/templates/profiles/organization_detail.html +++ b/src/templates/profiles/organization_detail.html @@ -71,7 +71,10 @@

Users

Name - E-mail + {% if is_editor or is_member %} + E-mail + {% endif %} + @@ -79,7 +82,10 @@

Users

{% if user.id in organization.user_record %} {{ user.name }} - {{ user.email }} + {% if is_editor or is_member %} + {{ user.email }} + {% endif %} + {% endif %} {% endfor %} From 07e9584451da47d2ee16040667f22c90f483d58e Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Tue, 1 Aug 2023 17:07:44 +0500 Subject: [PATCH 05/22] Competition api security solved --- src/apps/api/views/competitions.py | 31 ++++++++++--- src/apps/competitions/views.py | 44 ++++++++++++++++++- src/static/js/ours/client.js | 10 ++++- .../riot/competitions/detail/detail.tag | 2 +- src/templates/competitions/detail.html | 2 +- src/templates/competitions/form.html | 2 +- 6 files changed, 78 insertions(+), 13 deletions(-) diff --git a/src/apps/api/views/competitions.py b/src/apps/api/views/competitions.py index 4dc0e4b30..d5cf28875 100644 --- a/src/apps/api/views/competitions.py +++ b/src/apps/api/views/competitions.py @@ -45,15 +45,23 @@ class CompetitionViewSet(ModelViewSet): permission_classes = (AllowAny,) def get_queryset(self): + qs = super().get_queryset() + # Filter for search bar search_query = self.request.query_params.get('search') + + # Competition Secret key check + secret_key = self.request.query_params.get('secret_key') + # If user is logged in if self.request.user.is_authenticated: # filter by competition_type first, 'competition' by default competition_type = self.request.query_params.get('type', Competition.COMPETITION) if competition_type != 'any' and self.detail is False: qs = qs.filter(competition_type=competition_type) + + # `mine` is true when this is called from "Benchmarks I'm Running" # Filter to only see competitions you own mine = self.request.query_params.get('mine', None) if mine: @@ -65,6 +73,7 @@ def get_queryset(self): (Q(collaborators__in=[self.request.user])) ).distinct() + # `participating_in` is true when this is called from "Benchmarks I'm in" participating_in = self.request.query_params.get('participating_in', None) if participating_in: qs = qs.filter(participants__user=self.request.user, participants__status="approved") @@ -73,11 +82,9 @@ def get_queryset(self): user=self.request.user ).values_list('status')[:1] qs = qs.annotate(participant_status=Subquery(participant_status_query)) - # `mine` is true when this is called from "Benchmarks I'm Running" - # `participating_in` is true when this is called from "Benchmarks I'm in" - # `search_query` is true when this is called from the search bar + + # if `search_query` is true, this is called form search bar if search_query: - # User is logged in then filter # competitions which this user owns # or # competitions in which this user is collaborator @@ -91,10 +98,20 @@ def get_queryset(self): (Q(published=True) & ~Q(created_by=self.request.user)) | (Q(participants__user=self.request.user) & Q(participants__status="approved")) ).distinct() + + # if `secret_key` is true, this is called for a secret competition + if secret_key: + qs = qs.filter(Q(secret_key=secret_key)) + else: - # if user is not authenticated only show public competitions in the search - if (search_query): - qs = qs.filter(Q(published=True)) + # if user is not authenticated only show + # public competitions + # or + # competition with valid secret key + qs = qs.filter( + (Q(published=True)) | + (Q(secret_key=secret_key)) + ) # On GETs lets optimize the query to reduce DB calls if self.request.method == 'GET': diff --git a/src/apps/competitions/views.py b/src/apps/competitions/views.py index 61d09e0f3..4651357ce 100644 --- a/src/apps/competitions/views.py +++ b/src/apps/competitions/views.py @@ -13,8 +13,39 @@ class CompetitionPublic(TemplateView): template_name = 'competitions/public.html' -class CompetitionForm(LoginRequiredMixin, TemplateView): +class CompetitionForm(LoginRequiredMixin, DetailView): template_name = 'competitions/form.html' + queryset = Competition.objects.all() + + def get_object(self, *args, **kwargs): + competition = super().get_object(*args, **kwargs) + + is_admin, is_creator, is_collaborator, is_participant = False, False, False, False + + # check if user is loggedin + if self.request.user.is_authenticated: + + # check if user is admin + is_admin = self.request.user.is_superuser + + # check if user is the creator of this competition + is_creator = self.request.user == competition.created_by + + # check if user is collaborator of this competition + is_collaborator = self.request.user in competition.collaborators.all() + + # check if user is a participant of this competition + # get participants from CompetitionParticipant where user=user and competition=competition + is_participant = CompetitionParticipant.objects.filter(user=self.request.user, competition=competition).count() > 0 + + if ( + is_admin or + is_creator or + is_collaborator or + is_participant + ): + return competition + raise Http404() class CompetitionUpload(LoginRequiredMixin, TemplateView): @@ -60,6 +91,17 @@ def get_object(self, *args, **kwargs): return competition raise Http404() + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + # Retrieve the secret_key from the request.GET dictionary + secret_key = self.request.GET.get('secret_key') + + # Add the secret_key to the context dictionary + context['secret_key'] = secret_key + + return context + class CompetitionDetailedResults(LoginRequiredMixin, TemplateView): template_name = 'competitions/detailed_results.html' diff --git a/src/static/js/ours/client.js b/src/static/js/ours/client.js index f3e1a21dc..80f7cf3a0 100644 --- a/src/static/js/ours/client.js +++ b/src/static/js/ours/client.js @@ -22,8 +22,14 @@ CODALAB.api = { /*--------------------------------------------------------------------- Competitions ---------------------------------------------------------------------*/ - get_competition: function (pk) { - return CODALAB.api.request('GET', URLS.API + "competitions/" + pk + "/") + get_competition: function (pk, secret_key) { + + if(secret_key == undefined || secret_key == 'None'){ + return CODALAB.api.request('GET', URLS.API + "competitions/" + pk + "/") + }else{ + return CODALAB.api.request('GET', URLS.API + "competitions/" + pk + "/?secret_key="+secret_key) + } + }, get_competitions: function (query) { return CODALAB.api.request('GET', URLS.API + "competitions/", query) diff --git a/src/static/riot/competitions/detail/detail.tag b/src/static/riot/competitions/detail/detail.tag index 9c2c074b4..06da45698 100644 --- a/src/static/riot/competitions/detail/detail.tag +++ b/src/static/riot/competitions/detail/detail.tag @@ -17,7 +17,7 @@ }) self.update_competition_data = function () { - CODALAB.api.get_competition(self.opts.competition_pk) + CODALAB.api.get_competition(self.opts.competition_pk, self.opts.secret_key) .done(function (data) { self.competition = data CODALAB.events.trigger('competition_loaded', self.competition) diff --git a/src/templates/competitions/detail.html b/src/templates/competitions/detail.html index 143dbee2e..e89853323 100644 --- a/src/templates/competitions/detail.html +++ b/src/templates/competitions/detail.html @@ -1,4 +1,4 @@ {% extends "base.html" %} {% block content %} - + {% endblock %} \ No newline at end of file diff --git a/src/templates/competitions/form.html b/src/templates/competitions/form.html index 6f2196ce1..a3b9d9ccb 100644 --- a/src/templates/competitions/form.html +++ b/src/templates/competitions/form.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block content %} - + {# NOTE: First added for phase task sorting, SemanticUI does not come with sorting..! #} From 0c3175c54494d1603cc70bde97e9cff7bcf4d8f0 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Tue, 1 Aug 2023 17:09:34 +0500 Subject: [PATCH 06/22] 404 page on edit competition which does not belong to you --- src/apps/competitions/views.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/apps/competitions/views.py b/src/apps/competitions/views.py index 4651357ce..984305d33 100644 --- a/src/apps/competitions/views.py +++ b/src/apps/competitions/views.py @@ -20,7 +20,7 @@ class CompetitionForm(LoginRequiredMixin, DetailView): def get_object(self, *args, **kwargs): competition = super().get_object(*args, **kwargs) - is_admin, is_creator, is_collaborator, is_participant = False, False, False, False + is_admin, is_creator, is_collaborator = False, False, False # check if user is loggedin if self.request.user.is_authenticated: @@ -34,15 +34,10 @@ def get_object(self, *args, **kwargs): # check if user is collaborator of this competition is_collaborator = self.request.user in competition.collaborators.all() - # check if user is a participant of this competition - # get participants from CompetitionParticipant where user=user and competition=competition - is_participant = CompetitionParticipant.objects.filter(user=self.request.user, competition=competition).count() > 0 - if ( is_admin or is_creator or - is_collaborator or - is_participant + is_collaborator ): return competition raise Http404() From 734c02e6fb6332bd198bf47ec87d1d04f8e9169c Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Tue, 1 Aug 2023 19:13:50 +0500 Subject: [PATCH 07/22] competition create and update view changed --- src/apps/competitions/urls.py | 4 ++-- src/apps/competitions/views.py | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/apps/competitions/urls.py b/src/apps/competitions/urls.py index a3d4419a9..705c1d77c 100644 --- a/src/apps/competitions/urls.py +++ b/src/apps/competitions/urls.py @@ -8,8 +8,8 @@ # path('', views.CompetitionList.as_view(), name="list"), path('', views.CompetitionManagement.as_view(), name="management"), path('/', views.CompetitionDetail.as_view(), name="detail"), - path('create/', views.CompetitionForm.as_view(), name="create"), - path('edit//', views.CompetitionForm.as_view(), name="edit"), + path('create/', views.CompetitionCreateForm.as_view(), name="create"), + path('edit//', views.CompetitionUpdateForm.as_view(), name="edit"), path('upload/', views.CompetitionUpload.as_view(), name="upload"), path('public/', views.CompetitionPublic.as_view(), name="public"), path('/detailed_results//', views.CompetitionDetailedResults.as_view(), name="detailed_results"), diff --git a/src/apps/competitions/views.py b/src/apps/competitions/views.py index 984305d33..6d2aa3c3e 100644 --- a/src/apps/competitions/views.py +++ b/src/apps/competitions/views.py @@ -13,7 +13,11 @@ class CompetitionPublic(TemplateView): template_name = 'competitions/public.html' -class CompetitionForm(LoginRequiredMixin, DetailView): +class CompetitionCreateForm(LoginRequiredMixin, TemplateView): + template_name = 'competitions/form.html' + + +class CompetitionUpdateForm(LoginRequiredMixin, DetailView): template_name = 'competitions/form.html' queryset = Competition.objects.all() From e3b4c305032717e5d0b78feb456404e944288725 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Wed, 2 Aug 2023 12:09:55 +0500 Subject: [PATCH 08/22] extra print removed --- src/apps/profiles/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/apps/profiles/views.py b/src/apps/profiles/views.py index e37b46710..ab5296e9a 100644 --- a/src/apps/profiles/views.py +++ b/src/apps/profiles/views.py @@ -266,7 +266,6 @@ def get_context_data(self, **kwargs): context['is_editor'] = False context['is_member'] = False - print(context) return context From a17036e65a68b0b361a13fdc1c27bb57ce942c89 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Wed, 2 Aug 2023 20:18:45 +0500 Subject: [PATCH 09/22] organization shown if submission is submitted by it --- src/static/riot/competitions/detail/leaderboards.tag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/riot/competitions/detail/leaderboards.tag b/src/static/riot/competitions/detail/leaderboards.tag index d289060ef..d7f85c9ef 100644 --- a/src/static/riot/competitions/detail/leaderboards.tag +++ b/src/static/riot/competitions/detail/leaderboards.tag @@ -57,9 +57,9 @@ {index + 1} { submission.owner } + { submission.organization.name } {submission.num_entries} {submission.last_entry_date} - { submission.organization.name } { get_score(column, submission) } Show detailed results From c7aeccf82e42525499517b87dede5ffb53282b4e Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Thu, 3 Aug 2023 13:44:46 +0500 Subject: [PATCH 10/22] V2 unpacker: raise error when phase1 end and phase2 start dates are same --- src/apps/competitions/unpackers/base_unpacker.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/apps/competitions/unpackers/base_unpacker.py b/src/apps/competitions/unpackers/base_unpacker.py index a769dc2eb..bf9f686db 100644 --- a/src/apps/competitions/unpackers/base_unpacker.py +++ b/src/apps/competitions/unpackers/base_unpacker.py @@ -127,6 +127,12 @@ def _validate_phase_ordering(self): f'Phases must be sequential. Phase: {phase2.get("name", phase2["index"])}' f'starts before Phase: {phase1.get("name", phase1["index"])} has ended' ) + elif phase1['end'] == phase2['start']: + # Current phase start date and previous phase end dates are same, raise error + raise CompetitionUnpackingException( + f'Phases dates conflict. Phase: {phase2.get("name", phase2["index"])} ' + f'should start after Phase: {phase1.get("name", phase1["index"])} has ended' + ) def _unpack_pages(self): """ From 8e290a35e58e3db4ff3b42c248845fe83ae5b720 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Thu, 3 Aug 2023 14:29:24 +0500 Subject: [PATCH 11/22] V1 unpacker: phase with no end date, use next phase start date -1 as the end date --- src/apps/competitions/unpackers/v1.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/apps/competitions/unpackers/v1.py b/src/apps/competitions/unpackers/v1.py index 27f9c9956..dca60ed19 100644 --- a/src/apps/competitions/unpackers/v1.py +++ b/src/apps/competitions/unpackers/v1.py @@ -4,6 +4,7 @@ from competitions.unpackers.utils import CompetitionUnpackingException, get_datetime import logging logger = logging.getLogger() +import datetime class V15Unpacker(BaseUnpacker): @@ -90,7 +91,11 @@ def _unpack_phases(self): new_phase['has_max_submissions'] = True try: next_phase = phases[index + 1] - new_phase['end'] = get_datetime(next_phase['start_date']) + # V1 phases have no end dates. + # to set an end date of a phase, get the next phase starting date + next_phase_start_date = get_datetime(next_phase['start_date']) + # subtract one day from it and use it as this phase end date + new_phase['end'] = next_phase_start_date - datetime.timedelta(days=1) except IndexError: end = self.competition.get('end_date') if end and end != 'null': From 673ec9a79aad7f319f146782bb13c65655f8849a Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Thu, 3 Aug 2023 14:34:54 +0500 Subject: [PATCH 12/22] import resolved --- src/apps/competitions/unpackers/v1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/competitions/unpackers/v1.py b/src/apps/competitions/unpackers/v1.py index dca60ed19..63a808629 100644 --- a/src/apps/competitions/unpackers/v1.py +++ b/src/apps/competitions/unpackers/v1.py @@ -1,10 +1,10 @@ import os +import datetime from competitions.unpackers.base_unpacker import BaseUnpacker from competitions.unpackers.utils import CompetitionUnpackingException, get_datetime import logging logger = logging.getLogger() -import datetime class V15Unpacker(BaseUnpacker): From 06b17a9c483ad282a64add971f662572ecc04a1e Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Thu, 3 Aug 2023 14:42:49 +0500 Subject: [PATCH 13/22] Phase testing data updated to match the new change --- src/apps/competitions/tests/unpacker_test_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/competitions/tests/unpacker_test_data.py b/src/apps/competitions/tests/unpacker_test_data.py index 45cd096d5..e49e851ee 100644 --- a/src/apps/competitions/tests/unpacker_test_data.py +++ b/src/apps/competitions/tests/unpacker_test_data.py @@ -204,7 +204,7 @@ 'max_submissions_per_person': None, 'auto_migrate_to_this_phase': False, 'has_max_submissions': True, - 'end': datetime.datetime(2019, 9, 30, 0, 0, tzinfo=timezone.now().tzinfo), + 'end': datetime.datetime(2019, 9, 29, 0, 0, tzinfo=timezone.now().tzinfo), 'public_data': None, 'starting_kit': None, 'tasks': [0], From 4201dd6f54d7194eb0bb60db0b8fa8af5b847849 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Thu, 3 Aug 2023 14:49:37 +0500 Subject: [PATCH 14/22] V2 test data updated to match the new logic --- src/apps/competitions/tests/unpacker_test_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/competitions/tests/unpacker_test_data.py b/src/apps/competitions/tests/unpacker_test_data.py index e49e851ee..0d7aa3630 100644 --- a/src/apps/competitions/tests/unpacker_test_data.py +++ b/src/apps/competitions/tests/unpacker_test_data.py @@ -106,7 +106,7 @@ "execution_time_limit": 500, "max_submissions_per_day": 5, "start": "2019-01-01", - "end": "2019-09-30", + "end": "2019-09-29", "tasks": [0] }, { From a04b5a6ceb45f5011abcab2df7d5fb012702ac98 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Thu, 3 Aug 2023 16:10:16 +0500 Subject: [PATCH 15/22] submission panel see your org and orgs where you are added --- src/apps/api/views/profiles.py | 2 +- src/apps/profiles/models.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/apps/api/views/profiles.py b/src/apps/api/views/profiles.py index 6492a5efb..d2068b4c0 100644 --- a/src/apps/api/views/profiles.py +++ b/src/apps/api/views/profiles.py @@ -45,7 +45,7 @@ def update(self, request, *args, **kwargs): @action(detail=False, methods=['get'], permission_classes=[IsAuthenticated]) def participant_organizations(self, request): - memberships = request.user.membership_set.filter(group__in=Membership.PARTICIPANT_GROUP).prefetch_related('organization') + memberships = request.user.membership_set.filter(group__in=Membership.ALL_GROUP).prefetch_related('organization') data = SimpleOrganizationSerializer([member.organization for member in memberships], many=True).data return Response(data) diff --git a/src/apps/profiles/models.py b/src/apps/profiles/models.py index a6b92e944..86493a778 100644 --- a/src/apps/profiles/models.py +++ b/src/apps/profiles/models.py @@ -199,6 +199,7 @@ class Membership(models.Model): EDITORS_GROUP = [OWNER, MANAGER] PARTICIPANT_GROUP = EDITORS_GROUP + [PARTICIPANT] SETTABLE_PERMISSIONS = [MANAGER, PARTICIPANT, MEMBER] + ALL_GROUP = EDITORS_GROUP + [PARTICIPANT, MEMBER] group = models.TextField(choices=PERMISSIONS, default=INVITED, null=False, blank=False) organization = models.ForeignKey(Organization, on_delete=models.CASCADE) From badb078a9e181e1fb2918f98391d0f1189c4bce0 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Sat, 5 Aug 2023 21:38:15 +0500 Subject: [PATCH 16/22] score in bold if more than one score and score is primary i.e. index == 0 --- src/apps/api/serializers/leaderboards.py | 1 + src/apps/api/views/competitions.py | 1 - .../riot/competitions/detail/leaderboards.tag | 22 ++++++++++++++++--- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/apps/api/serializers/leaderboards.py b/src/apps/api/serializers/leaderboards.py index 74aede7f5..9e2fef623 100644 --- a/src/apps/api/serializers/leaderboards.py +++ b/src/apps/api/serializers/leaderboards.py @@ -25,6 +25,7 @@ class Meta: 'index', 'hidden', 'precision', + 'index' ) def validate(self, attrs): diff --git a/src/apps/api/views/competitions.py b/src/apps/api/views/competitions.py index 4dc0e4b30..4cce61f1a 100644 --- a/src/apps/api/views/competitions.py +++ b/src/apps/api/views/competitions.py @@ -619,7 +619,6 @@ def get_leaderboard(self, request, pk): # put detailed results in its submission for k, v in submissions_keys.items(): response['submissions'][v]['detailed_results'] = submission_detailed_results[k] - print(f"\n{response['submissions']}\n") for task in query['tasks']: # This can be used to rendered variable columns on each task diff --git a/src/static/riot/competitions/detail/leaderboards.tag b/src/static/riot/competitions/detail/leaderboards.tag index 7176cc213..dce50fe6a 100644 --- a/src/static/riot/competitions/detail/leaderboards.tag +++ b/src/static/riot/competitions/detail/leaderboards.tag @@ -63,7 +63,7 @@ - {get_score(column, submission)} + {get_score(column, submission)} @@ -81,6 +81,22 @@ self.competition_id = null self.enable_detailed_results = false + + self.bold_class = function(column, submission){ + // Return `text-bold` if submission has + // more than one scores and score index == 0 + // otherwise return empty string + return_class = '' // default class value + if(column.task_id != -1){ // factsheet check + if(submission.scores.length > 1){ // score length check + let column_index = _.get(column, 'index') + if(column_index === 0){ // column index check + return_class = 'text-bold' + } + } + } + return return_class + } self.get_score = function(column, submission) { if(column.task_id === -1){ return _.get(submission, 'fact_sheet_answers[' + column.key + ']', 'n/a') @@ -106,7 +122,6 @@ self.filter_columns = () => { let search_key = self.refs.leaderboardFilter.value.toLowerCase() self.filtered_tasks = JSON.parse(JSON.stringify(self.selected_leaderboard.tasks)) - console.log(self.filtered_tasks) if(search_key){ self.filtered_columns = [] for (const column of self.columns){ @@ -162,7 +177,6 @@ }) task.colWidth += 1 } - console.log(task) } self.filter_columns() $('#leaderboardTable').tablesort() @@ -220,5 +234,7 @@ top: 50% left: 50% transform: translate(-50%, -50%) + .text-bold + font-weight: bold From 933d9f98ec8d350ee9471118da6667acb1b77266 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Sat, 5 Aug 2023 22:18:14 +0500 Subject: [PATCH 17/22] when queue is updated to private, make it null in all competitions belonging to other users --- src/apps/api/views/queues.py | 22 +++++++++++++++++++++- src/static/riot/queues/management.tag | 5 ++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/apps/api/views/queues.py b/src/apps/api/views/queues.py index 4a5870c64..db96188c6 100644 --- a/src/apps/api/views/queues.py +++ b/src/apps/api/views/queues.py @@ -46,7 +46,27 @@ def update(self, request, *args, **kwargs): queue = self.get_object() if request.user != queue.owner and not request.user.is_superuser: raise PermissionDenied("Cannot update a queue that is not yours") - return super().update(request, *args, **kwargs) + + # Get the original value of is_public before updating + before_update_queue_is_public = queue.is_public + + # Get the competitions that are using this queue + competitions = queue.competitions.all() + + # Update the queue + updated_queue_response = super().update(request, *args, **kwargs) + + # If the queue `is_public`` field is updated to False, then update competitions + if 'is_public' in request.data and not request.data['is_public'] and before_update_queue_is_public: + + # Set the queue field in all competitions to NULL + # which do not belong to the user + for competition in competitions: + if competition.created_by != request.user: + competition.queue = None + competition.save() + + return updated_queue_response def destroy(self, request, *args, **kwargs): instance = self.get_object() diff --git a/src/static/riot/queues/management.tag b/src/static/riot/queues/management.tag index 062693bc2..fafb43ede 100644 --- a/src/static/riot/queues/management.tag +++ b/src/static/riot/queues/management.tag @@ -69,9 +69,8 @@ - + +