diff --git a/src/apps/api/serializers/datasets.py b/src/apps/api/serializers/datasets.py index 21f0d6f13..0b44b7ec1 100644 --- a/src/apps/api/serializers/datasets.py +++ b/src/apps/api/serializers/datasets.py @@ -24,6 +24,8 @@ class Meta: 'created_by', 'data_file', 'was_created_by_competition', + 'competition', + 'file_name', ) read_only_fields = ( 'key', @@ -71,6 +73,7 @@ class Meta: class DataDetailSerializer(serializers.ModelSerializer): created_by = serializers.CharField(source='created_by.username') + competition = serializers.SerializerMethodField() class Meta: model = Data @@ -86,8 +89,20 @@ class Meta: 'was_created_by_competition', 'in_use', 'file_size', + 'competition', + 'file_name', ) + def get_competition(self, obj): + + # return competition dict with id and title if available + if obj.competition: + return { + "id": obj.competition.id, + "title": obj.competition.title, + } + return None + class DataGroupSerializer(serializers.ModelSerializer): class Meta: diff --git a/src/apps/api/serializers/tasks.py b/src/apps/api/serializers/tasks.py index 521c8f64c..deac682cc 100644 --- a/src/apps/api/serializers/tasks.py +++ b/src/apps/api/serializers/tasks.py @@ -7,6 +7,7 @@ from competitions.models import PhaseTaskInstance, Phase from datasets.models import Data from tasks.models import Task, Solution +from competitions.models import Competition class SolutionSerializer(WritableNestedModelSerializer): @@ -138,7 +139,12 @@ class Meta: ) def get_competitions(self, instance): - return self.context['task_titles'][instance.pk] + + # Fech competitions which hase phases with this task + # competitions = Phase.objects.filter(tasks__in=[instance.pk]).values('competition') + competitions = Competition.objects.filter(phases__tasks__in=[instance.pk]).values("id", "title").distinct() + + return competitions def get_shared_with(self, instance): return self.context['shared_with'][instance.pk] diff --git a/src/apps/api/tests/test_datasets.py b/src/apps/api/tests/test_datasets.py index 9ac798553..664178116 100644 --- a/src/apps/api/tests/test_datasets.py +++ b/src/apps/api/tests/test_datasets.py @@ -21,6 +21,7 @@ def test_dataset_api_checks_duplicate_names_for_same_user(self): 'name': 'Test!', 'type': Data.COMPETITION_BUNDLE, 'request_sassy_file_name': faker.file_name(), + 'file_name': faker.file_name(), }) assert resp.status_code == 400 diff --git a/src/apps/api/views/competitions.py b/src/apps/api/views/competitions.py index b104c69a6..12a40cf6e 100644 --- a/src/apps/api/views/competitions.py +++ b/src/apps/api/views/competitions.py @@ -62,7 +62,8 @@ def get_queryset(self): qs = Competition.objects.filter( (Q(created_by=self.request.user)) | (Q(collaborators__in=[self.request.user])) - ) + ).distinct() + participating_in = self.request.query_params.get('participating_in', None) if participating_in: qs = qs.filter(participants__user=self.request.user, participants__status="approved") @@ -155,6 +156,23 @@ def create(self, request, *args, **kwargs): for index in range(len(phase['tasks'])): phase['tasks'][index] = phase['tasks'][index]['task'] + # TODO - This is Temporary. Need to change Leaderboard to Phase connect to M2M and handle this correctly. + # save leaderboard individually, then pass pk to each phase + print(f"{request.data['leaderboards']}") + data = request.data + if 'leaderboards' in data: + leaderboard_data = data['leaderboards'][0] + if(leaderboard_data['id']): + leaderboard_instance = Leaderboard.objects.get(id=leaderboard_data['id']) + leaderboard = LeaderboardSerializer(leaderboard_instance, data=data['leaderboards'][0]) + else: + leaderboard = LeaderboardSerializer(data=data['leaderboards'][0]) + leaderboard.is_valid() + leaderboard.save() + leaderboard_id = leaderboard["id"].value + for phase in data['phases']: + phase['leaderboard'] = leaderboard_id + serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) diff --git a/src/apps/api/views/datasets.py b/src/apps/api/views/datasets.py index 7bc235171..c000278da 100644 --- a/src/apps/api/views/datasets.py +++ b/src/apps/api/views/datasets.py @@ -20,14 +20,51 @@ class DataViewSet(ModelViewSet): queryset = Data.objects.all() filter_backends = (DjangoFilterBackend, SearchFilter) - filter_fields = ('type', 'name', 'key', 'was_created_by_competition') + filter_fields = ('type', 'name', 'key', 'was_created_by_competition', 'is_public') search_fields = ('name', 'description', 'key',) pagination_class = BasicPagination def get_queryset(self): - filters = Q(is_public=True) | Q(created_by=self.request.user) - qs = Data.objects.filter(filters) + if self.request.method == 'GET': + + # new filters + # ----------- + + # _public = true if want to show public datasets/submissions + is_public = self.request.query_params.get('_public', 'false') == 'true' + + # _type = submission if called from submissions tab to filter only submissions + is_submission = self.request.query_params.get('_type', '') == 'submission' + + # _type = dataset if called from datasets and programs tab to filter datasets and programs + is_dataset = self.request.query_params.get('_type', '') == 'dataset' + + # get queryset + qs = self.queryset + + # filter submissions + if is_submission: + qs = qs.filter(Q(type=Data.SUBMISSION)) + + # filter datasets and programs + if is_dataset: + qs = qs.filter(~Q(type=Data.SUBMISSION)) + + # public filter check + if is_public: + qs = qs.filter(Q(created_by=self.request.user) | Q(is_public=True)) + else: + qs = qs.filter(Q(created_by=self.request.user)) + + # if GET is called but provided no filters, fall back to default behaviour + if (not is_submission) and (not is_dataset) and (not is_public): + qs = self.queryset + qs = qs.filter(Q(is_public=True) | Q(created_by=self.request.user)) + + else: + qs = self.queryset + qs = qs.filter(Q(is_public=True) | Q(created_by=self.request.user)) qs = qs.exclude(Q(type=Data.COMPETITION_BUNDLE) | Q(name__isnull=True)) @@ -42,6 +79,7 @@ def get_serializer_class(self): return serializers.DataSerializer def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) new_dataset = serializer.save() # request_sassy_file_name is temporarily set via this serializer diff --git a/src/apps/datasets/migrations/0005_auto_20230527_0837.py b/src/apps/datasets/migrations/0005_auto_20230527_0837.py new file mode 100644 index 000000000..7fd349ef9 --- /dev/null +++ b/src/apps/datasets/migrations/0005_auto_20230527_0837.py @@ -0,0 +1,25 @@ +# Generated by Django 2.2.17 on 2023-05-27 08:37 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('competitions', '0032_submission_worker_hostname'), + ('datasets', '0004_data_deleted'), + ] + + operations = [ + migrations.AddField( + model_name='data', + name='competition', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='submission', to='competitions.Competition'), + ), + migrations.AddField( + model_name='data', + name='file_name', + field=models.CharField(default='', max_length=64), + ), + ] diff --git a/src/apps/datasets/migrations/0006_auto_20230528_0829.py b/src/apps/datasets/migrations/0006_auto_20230528_0829.py new file mode 100644 index 000000000..8e51ba6c3 --- /dev/null +++ b/src/apps/datasets/migrations/0006_auto_20230528_0829.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.17 on 2023-05-28 08:29 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('datasets', '0005_auto_20230527_0837'), + ] + + operations = [ + migrations.AlterField( + model_name='data', + name='competition', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='submission', to='competitions.Competition'), + ), + ] diff --git a/src/apps/datasets/models.py b/src/apps/datasets/models.py index 9165814e8..3d97e8b45 100644 --- a/src/apps/datasets/models.py +++ b/src/apps/datasets/models.py @@ -10,6 +10,7 @@ from chahub.models import ChaHubSaveMixin from utils.data import PathWrapper from utils.storage import BundleStorage +from competitions.models import Competition class Data(ChaHubSaveMixin, models.Model): @@ -57,6 +58,9 @@ class Data(ChaHubSaveMixin, models.Model): # are NOT marked True, since they are not created by unpacking! was_created_by_competition = models.BooleanField(default=False) + competition = models.ForeignKey(Competition, on_delete=models.PROTECT, null=True, related_name='submission') + file_name = models.CharField(max_length=64, default="") + def get_download_url(self): return reverse('datasets:download', kwargs={'key': self.key}) @@ -84,7 +88,7 @@ def in_use(self): Q(phases__tasks__input_data=self) | Q(phases__tasks__reference_data=self) | Q(phases__tasks__scoring_program=self) - ).values_list('pk', flat=True).distinct() + ).values('pk', 'title').distinct() return competitions_in_use def __str__(self): diff --git a/src/apps/profiles/views.py b/src/apps/profiles/views.py index 8abbf94b2..9a19090dd 100644 --- a/src/apps/profiles/views.py +++ b/src/apps/profiles/views.py @@ -130,6 +130,10 @@ def sign_up(request): def log_in(request): + # Fectch next redirect page after login + # default : None + next = request.GET.get('next', None) + context = {} context['chahub_signup_url'] = "{}/profiles/signup?next={}/social/login/chahub".format( settings.SOCIAL_AUTH_CHAHUB_BASE_URL, @@ -144,7 +148,13 @@ def log_in(request): user = authenticate(username=username, password=password) if user: login(request, user) - return redirect('pages:home') + + # if next is none redirect to home + # otherwise redirect to requested page + if next is None: + return redirect('pages:home') + else: + return redirect(next) else: messages.error(request, "Wrong Credentials!") else: diff --git a/src/static/js/ours/client.js b/src/static/js/ours/client.js index fd747afd1..5a912ffb4 100644 --- a/src/static/js/ours/client.js +++ b/src/static/js/ours/client.js @@ -153,6 +153,7 @@ CODALAB.api = { create_dataset: function (metadata, data_file, progress_update_callback) { // Pass the requested file name for the SAS url metadata.request_sassy_file_name = data_file.name + metadata.file_name = data_file.name // This will be set on successful dataset creation, then used to complete the dataset upload var dataset = {} diff --git a/src/static/riot/competitions/detail/submission_upload.tag b/src/static/riot/competitions/detail/submission_upload.tag index f1405cfb9..76728a5ee 100644 --- a/src/static/riot/competitions/detail/submission_upload.tag +++ b/src/static/riot/competitions/detail/submission_upload.tag @@ -11,8 +11,8 @@