Skip to content
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

Public tasks -- updates to ensure public tasks can be viewed and used #1581

Merged
merged 11 commits into from
Oct 25, 2024
Merged
19 changes: 6 additions & 13 deletions src/apps/api/serializers/tasks.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from drf_writable_nested import WritableNestedModelSerializer
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from api.mixins import DefaultUserCreateMixin
from api.serializers.datasets import DataDetailSerializer, DataSimpleSerializer
from competitions.models import PhaseTaskInstance, Phase
Expand Down Expand Up @@ -79,12 +77,6 @@ class Meta:
'created_by',
)

def validate_is_public(self, is_public):
validated = Task.objects.get(id=self.instance.id)._validated
if is_public and not validated:
raise ValidationError('Task must be validated before it can be published')
return is_public

def get_validated(self, instance):
return hasattr(instance, 'validated') and instance.validated is not None

Expand All @@ -107,7 +99,6 @@ class Meta:
'id',
'name',
'description',
'key',
'created_by',
'owner_display_name',
'created_when',
Expand All @@ -124,6 +115,9 @@ class Meta:
'solutions',
)

def get_validated(self, instance):
return hasattr(instance, 'validated') and instance.validated is not None

def get_competitions(self, instance):

# Fech competitions which hase phases with this task
Expand All @@ -132,11 +126,10 @@ def get_competitions(self, instance):

return competitions

def get_validated(self, task):
return task.validated is not None

def get_shared_with(self, instance):
return self.context['shared_with'][instance.pk]
# Fetch the users with whom the task is shared
shared_users = instance.shared_with.all()
return [user.username for user in shared_users]

def get_owner_display_name(self, instance):
# Get the user's display name if not None, otherwise return username
Expand Down
47 changes: 33 additions & 14 deletions src/apps/api/views/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ def get_queryset(self):
)

task_filter = Q(created_by=self.request.user) | Q(shared_with=self.request.user)
if self.request.query_params.get('public'):
# when there is `public` in the query params, it means user has checked on the front-end
# the Show public tasks checkbox.
# When a user clicks that public task that may not belong to the user, we want to show
# the public task to the user and hence we check the `retrieve` action
if self.request.query_params.get('public') or self.action == 'retrieve':
task_filter |= Q(is_public=True)

qs = qs.filter(task_filter)
Expand Down Expand Up @@ -92,19 +96,34 @@ def update(self, request, *args, **kwargs):
if request.user != task.created_by and not request.user.is_superuser:
raise PermissionDenied("Cannot update a task that is not yours")

# If the key is not in the request data, set the corresponding field to None
# No condition for scoring program because a task must have a scoring program
if "ingestion_program" not in request.data:
task.ingestion_program = None
if "input_data" not in request.data:
task.input_data = None
if "reference_data" not in request.data:
task.reference_data = None

# Save the task to apply the changes
task.save()

return super().update(request, *args, **kwargs)
# Check if 'is_public' is sent in the data
# This means that from the front end the update is_public api is calle
# with `is_public` in the data
if 'is_public' in request.data:
# Perform the update using the parent class's update method
super().update(request, *args, **kwargs)
else:
# If the key is not in the request data, set the corresponding field to None
# No condition for scoring program because a task must have a scoring program
if "ingestion_program" not in request.data:
task.ingestion_program = None
if "input_data" not in request.data:
task.input_data = None
if "reference_data" not in request.data:
task.reference_data = None

# Save the task to apply the changes
task.save()
super().update(request, *args, **kwargs)

# Fetch the updated task from the database to ensure it reflects all changes
task.refresh_from_db()

# Serialize the updated task using TaskDetailSerializer
task_detail_serializer = serializers.TaskSerializer(task)

# Return the serialized data as a response
return Response(task_detail_serializer.data)

def destroy(self, request, *args, **kwargs):
instance = self.get_object()
Expand Down
4 changes: 0 additions & 4 deletions src/apps/tasks/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ def get_chahub_data(self, include_solutions=True):
data['solutions'] = [solution.get_chahub_data(include_tasks=False) for solution in self.solutions.all()]
return self.clean_private_data(data)

def save(self, *args, **kwargs):
self.is_public = self.is_public and self._validated
return super().save(*args, **kwargs)


class Solution(ChaHubSaveMixin, models.Model):
name = models.CharField(max_length=256)
Expand Down
2 changes: 1 addition & 1 deletion src/static/riot/competitions/editor/_phases.tag
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
// semantic multiselect
$(self.refs.multiselect).dropdown({
apiSettings: {
url: `${URLS.API}tasks/?search={query}`,
url: `${URLS.API}tasks/?public=true&search={query}`,
cache: false,
onResponse: (data) => {
return {success: true, results: _.values(data.results)}
Expand Down
3 changes: 2 additions & 1 deletion src/static/riot/tasks/management.tag
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@
<i class="question circle icon"></i>
</span>:</strong> {selected_task.validated ? "Yes" : "No"}</div>
<div><strong>Is Public:</strong> {selected_task.is_public ? "Yes" : "No"}</div>
<div if="{selected_task.validated}"
<div
if="{selected_task.created_by === CODALAB.state.user.username}"
class="ui right floated small green icon button"
onclick="{toggle_task_is_public}">
<i class="share icon"></i> {selected_task.is_public ? 'Make Private' : 'Make Public'}
Expand Down