diff --git a/README.md b/README.md index 8c6da464e..c35898589 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,10 @@ If you wish to configure your own instance of Codabench platform, here are the i ``` $ cp .env_sample .env -$ docker-compose up -d -$ docker-compose exec django ./manage.py migrate -$ docker-compose exec django ./manage.py generate_data -$ docker-compose exec django ./manage.py collectstatic --noinput +$ docker compose up -d +$ docker compose exec django ./manage.py migrate +$ docker compose exec django ./manage.py generate_data +$ docker compose exec django ./manage.py collectstatic --noinput ``` You can now login as username "admin" with password "admin" at http://localhost/ diff --git a/src/apps/api/views/submissions.py b/src/apps/api/views/submissions.py index 745cdb521..6c2489245 100644 --- a/src/apps/api/views/submissions.py +++ b/src/apps/api/views/submissions.py @@ -312,18 +312,18 @@ def re_run_many_submissions(self, request): submission.re_run() return Response({}) - # New methods impleted! @action(detail=False, methods=['get']) def download_many(self, request): pks = request.query_params.get('pks') if pks: pks = json.loads(pks) # Convert JSON string to list + # Doing a local import here to avoid circular imports from competitions.tasks import stream_batch_download - # Call the task and get the result (stream) + # in_memory_zip = stream_batch_download.apply_async((pks,)).get() in_memory_zip = stream_batch_download(pks) - # Stream the response + response = StreamingHttpResponse(in_memory_zip, content_type='application/zip') response['Content-Disposition'] = 'attachment; filename="bulk_submissions.zip"' return response diff --git a/src/apps/api/views/tasks.py b/src/apps/api/views/tasks.py index 2c1642448..71bfb59bd 100644 --- a/src/apps/api/views/tasks.py +++ b/src/apps/api/views/tasks.py @@ -84,9 +84,26 @@ def get_serializer_context(self): return context def update(self, request, *args, **kwargs): + + # Get task task = self.get_object() + + # Raise error if user is not the creator of the task or not a super user 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) def destroy(self, request, *args, **kwargs): diff --git a/src/apps/competitions/tasks.py b/src/apps/competitions/tasks.py index 1e5544b7b..f56696940 100644 --- a/src/apps/competitions/tasks.py +++ b/src/apps/competitions/tasks.py @@ -292,13 +292,10 @@ def retrieve_data(url, data=None): def zip_generator(submission_pks): in_memory_zip = BytesIO() - # logger.info("IN zip generator") with zipfile.ZipFile(in_memory_zip, 'w', zipfile.ZIP_DEFLATED) as zip_file: for submission_id in submission_pks: submission = Submission.objects.get(id=submission_id) - # logger.info(submission.data.data_file) - - short_name = submission.data.data_file.name.split('/')[-1] + short_name = "ID_" + str(submission_id) + '_' + submission.data.data_file.name.split('/')[-1] url = make_url_sassy(path=submission.data.data_file.name) for block in retrieve_data(url): zip_file.writestr(short_name, block) @@ -310,8 +307,6 @@ def zip_generator(submission_pks): @app.task(queue='site-worker', soft_time_limit=60 * 60) def stream_batch_download(submission_pks): - # logger.info("In stream_batch_download") - # logger.info(submission_pks) return zip_generator(submission_pks) diff --git a/src/apps/pages/urls.py b/src/apps/pages/urls.py index 084bd2c7e..bbba17d71 100644 --- a/src/apps/pages/urls.py +++ b/src/apps/pages/urls.py @@ -11,5 +11,6 @@ path('search', views.SearchView.as_view(), name="search"), path('organize', views.OrganizeView.as_view(), name="organize"), path('server_status', views.ServerStatusView.as_view(), name="server_status"), + path('monitor_queues', views.MonitorQueuesView.as_view(), name="monitor_queues"), # path('test', views.CompetitionListTestView.as_view()), ] diff --git a/src/apps/pages/views.py b/src/apps/pages/views.py index 4afa0c11d..0f21b8cf2 100644 --- a/src/apps/pages/views.py +++ b/src/apps/pages/views.py @@ -1,5 +1,4 @@ -from datetime import timedelta -from django.utils.timezone import now +from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage from django.views.generic import TemplateView from django.db.models import Count, Q @@ -55,6 +54,8 @@ class ServerStatusView(TemplateView): def get_context_data(self, *args, **kwargs): show_child_submissions = self.request.GET.get('show_child_submissions', False) + page = self.request.GET.get('page', 1) + submissions_per_page = 50 # Get all submissions qs = Submission.objects.all() @@ -73,9 +74,6 @@ def get_context_data(self, *args, **kwargs): else: qs = qs.none() # This returns an empty queryset - # Filter for fetching last 2 days submissions - qs = qs.filter(created_when__gte=now() - timedelta(days=2)) - # Filter out child submissions i.e. submission has no parent if not show_child_submissions: qs = qs.filter(parent__isnull=True) @@ -83,8 +81,20 @@ def get_context_data(self, *args, **kwargs): qs = qs.order_by('-created_when') qs = qs.select_related('phase__competition', 'owner') + # Paginate the queryset + paginator = Paginator(qs, submissions_per_page) + + try: + submissions = paginator.page(page) + except PageNotAnInteger: + # If page is not an integer, deliver the first page. + submissions = paginator.page(1) + except EmptyPage: + # If page is out of range, deliver last page of results. + submissions = paginator.page(paginator.num_pages) + context = super().get_context_data(*args, **kwargs) - context['submissions'] = qs[:250] + context['submissions'] = submissions context['show_child_submissions'] = show_child_submissions for submission in context['submissions']: @@ -103,6 +113,9 @@ def get_context_data(self, *args, **kwargs): # Add submission owner display name submission.owner_display_name = submission.owner.display_name if submission.owner.display_name else submission.owner.username + context['paginator'] = paginator + context['is_paginated'] = paginator.num_pages > 1 + return context def format_file_size(self, file_size): @@ -123,6 +136,10 @@ def format_file_size(self, file_size): return f"{n:.1f} {units[i]}" +class MonitorQueuesView(TemplateView): + template_name = 'pages/monitor_queues.html' + + def page_not_found_view(request, exception): print(request) return render(request, '404.html', status=404) diff --git a/src/static/riot/tasks/management.tag b/src/static/riot/tasks/management.tag index 78c0f793d..37510275e 100644 --- a/src/static/riot/tasks/management.tag +++ b/src/static/riot/tasks/management.tag @@ -274,7 +274,7 @@ @@ -283,7 +283,7 @@ @@ -294,7 +294,7 @@ @@ -303,7 +303,7 @@ @@ -540,7 +540,7 @@ } self.edit_form_updated = () => { - self.edit_modal_is_valid = $(self.refs.edit_name).val() && $(self.refs.edit_description).val() && self.form_datasets.scoring_program + self.edit_modal_is_valid = $(self.refs.edit_name).val() && $(self.refs.edit_description).val() self.update() } @@ -555,17 +555,42 @@ self.edit_modal_is_valid = false } self.update_task = () => { + // Get filled data from the edit fom let data = get_form_data($(self.refs.edit_form)) + + // Show error when there is no scoring program in the task + if(data.edit_scoring_program == ""){ + toastr.error('Scoring program is required in a task!') + return + } // replace property names in the data object data.name = data.edit_name; data.description = data.edit_description; + // If ingestion program is not removed, add the new ingestion program from form_datasets to data + if(data.edit_ingestion_program != ""){ + data.ingestion_program = self.form_datasets.ingestion_program + } + // If input data is not removed, add the new input data from form_datasets to data + if(data.edit_input_data != ""){ + data.input_data = self.form_datasets.input_data + } + // If reference data is not removed, add the new reference data from form_datasets to data + if(data.edit_reference_data != ""){ + data.reference_data = self.form_datasets.reference_data + } + // add the new scoring from form_datasets to data + data.scoring_program = self.form_datasets.scoring_program + // delete the old property names delete data.edit_name delete data.edit_description + delete data.edit_ingestion_program + delete data.edit_scoring_program + delete data.edit_input_data + delete data.edit_reference_data - _.assign(data, self.form_datasets) task_id = self.selected_task.id CODALAB.api.update_task(task_id, data) .done((response) => { diff --git a/src/static/stylus/index.styl b/src/static/stylus/index.styl index f68e56b2b..58cfc96bc 100644 --- a/src/static/stylus/index.styl +++ b/src/static/stylus/index.styl @@ -7,4 +7,5 @@ @import "src/static/stylus/simple_page.styl" @import "src/static/stylus/toastr.styl" @import "src/static/stylus/forum.styl" +@import "src/static/stylus/server_status.styl" diff --git a/src/static/stylus/server_status.styl b/src/static/stylus/server_status.styl new file mode 100644 index 000000000..2f96a3fe9 --- /dev/null +++ b/src/static/stylus/server_status.styl @@ -0,0 +1,19 @@ +.pagination-nav + padding 10px 0 + width 100% + margin-bottom 20px + display flex + justify-content center + align-items center + position relative + +.float-left + position absolute + left 0 + +.float-right + position absolute + right 0 + +.center + text-align center diff --git a/src/templates/base.html b/src/templates/base.html index de6296321..f6f650d5a 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -128,6 +128,7 @@ {# Customize Codalab#} Server Status {% if request.user.is_staff %} + Monitor Queues Django Admin Change User Analytics diff --git a/src/templates/emails/base_email.html b/src/templates/emails/base_email.html index 9ea0c1a89..6e4c5e522 100644 --- a/src/templates/emails/base_email.html +++ b/src/templates/emails/base_email.html @@ -21,7 +21,10 @@ background-color: #ffffff; } .header_img { - width: 40%; + width: 100%; + height: 100%; + object-fit: contain; + box-sizing: border-box; } .header { width: 100%; @@ -31,6 +34,8 @@ justify-content: center; align-items: center; margin-bottom: 10px; + padding: 10px; + box-sizing: border-box; } .content{ padding: 20px; @@ -105,13 +110,13 @@

Hello{% if user %} {{ user.username }}{% endif %},

Thanks,

Codabench Team

- codabench_logo + Codabench Logo
{% endif %} diff --git a/src/templates/emails/participation/organizer/participation_accepted.html b/src/templates/emails/participation/organizer/participation_accepted.html index f25d3dfc9..d0942e6c3 100644 --- a/src/templates/emails/participation/organizer/participation_accepted.html +++ b/src/templates/emails/participation/organizer/participation_accepted.html @@ -1,6 +1,10 @@ {% extends 'emails/base_email.html' %} {% block content %} -

We're writing to inform you that user {{ participant.user.username }} has been accepted into your competition - {{ participant.competition.title }}.You can manage all participants from the admin panel of your competition.

+

+ User {{ participant.user.username }} has been accepted into your competition + {{ participant.competition.title }}. +
+ You can manage all participants from the admin panel of your competition. +

{% endblock %} diff --git a/src/templates/emails/participation/organizer/participation_accepted.txt b/src/templates/emails/participation/organizer/participation_accepted.txt index f25d3dfc9..d0942e6c3 100644 --- a/src/templates/emails/participation/organizer/participation_accepted.txt +++ b/src/templates/emails/participation/organizer/participation_accepted.txt @@ -1,6 +1,10 @@ {% extends 'emails/base_email.html' %} {% block content %} -

We're writing to inform you that user {{ participant.user.username }} has been accepted into your competition - {{ participant.competition.title }}.You can manage all participants from the admin panel of your competition.

+

+ User {{ participant.user.username }} has been accepted into your competition + {{ participant.competition.title }}. +
+ You can manage all participants from the admin panel of your competition. +

{% endblock %} diff --git a/src/templates/emails/participation/organizer/participation_denied.html b/src/templates/emails/participation/organizer/participation_denied.html index 43ec224c0..6d19430e9 100644 --- a/src/templates/emails/participation/organizer/participation_denied.html +++ b/src/templates/emails/participation/organizer/participation_denied.html @@ -1,6 +1,10 @@ {% extends 'emails/base_email.html' %} {% block content %} -

We're writing to inform you that user {{ participant.user.username }} has been denied to your competition - {{ participant.competition.title }}.You can manage all participants from the admin panel of your competition.

+

+ A user {{ participant.user.username }} has been denied access to your competition. + {{ participant.competition.title }}. +
+ You can manage all participants from the admin panel of your competition. +

{% endblock %} diff --git a/src/templates/emails/participation/organizer/participation_denied.txt b/src/templates/emails/participation/organizer/participation_denied.txt index 43ec224c0..6d19430e9 100644 --- a/src/templates/emails/participation/organizer/participation_denied.txt +++ b/src/templates/emails/participation/organizer/participation_denied.txt @@ -1,6 +1,10 @@ {% extends 'emails/base_email.html' %} {% block content %} -

We're writing to inform you that user {{ participant.user.username }} has been denied to your competition - {{ participant.competition.title }}.You can manage all participants from the admin panel of your competition.

+

+ A user {{ participant.user.username }} has been denied access to your competition. + {{ participant.competition.title }}. +
+ You can manage all participants from the admin panel of your competition. +

{% endblock %} diff --git a/src/templates/emails/participation/organizer/participation_requested.html b/src/templates/emails/participation/organizer/participation_requested.html index dad10d911..9adfe5c73 100644 --- a/src/templates/emails/participation/organizer/participation_requested.html +++ b/src/templates/emails/participation/organizer/participation_requested.html @@ -1,6 +1,10 @@ {% extends 'emails/base_email.html' %} {% block content %} -

We're writing to inform you that user {{ participant.user.username }} has requested access to your competition - {{ participant.competition.title }}.You can manage all participants from the admin panel of your competition.

+

+ A user {{ participant.user.username }} has requested access to your competition + {{ participant.competition.title }}. +
+ You can manage all participants from the admin panel of your competition. +

{% endblock %} diff --git a/src/templates/emails/participation/organizer/participation_requested.txt b/src/templates/emails/participation/organizer/participation_requested.txt index dad10d911..9adfe5c73 100644 --- a/src/templates/emails/participation/organizer/participation_requested.txt +++ b/src/templates/emails/participation/organizer/participation_requested.txt @@ -1,6 +1,10 @@ {% extends 'emails/base_email.html' %} {% block content %} -

We're writing to inform you that user {{ participant.user.username }} has requested access to your competition - {{ participant.competition.title }}.You can manage all participants from the admin panel of your competition.

+

+ A user {{ participant.user.username }} has requested access to your competition + {{ participant.competition.title }}. +
+ You can manage all participants from the admin panel of your competition. +

{% endblock %} diff --git a/src/templates/emails/participation/participant/participation_accepted.html b/src/templates/emails/participation/participant/participation_accepted.html index 27a0ddfd9..d125565df 100644 --- a/src/templates/emails/participation/participant/participation_accepted.html +++ b/src/templates/emails/participation/participant/participation_accepted.html @@ -1,10 +1,11 @@ {% extends 'emails/base_email.html' %} {% block content %} -

We're thrilled to inform you that your application for the - {{ participant.competition.title }} - competition has been accepted. Get ready to showcase your skills and compete with other talented individuals.

-

We look forward to seeing your participation.

+

+ Your application for the + {{ participant.competition.title }} + competition has been accepted. Get ready to showcase your skills and compete with other talented individuals. +

{% endblock %} diff --git a/src/templates/emails/participation/participant/participation_accepted.txt b/src/templates/emails/participation/participant/participation_accepted.txt index 27a0ddfd9..d125565df 100644 --- a/src/templates/emails/participation/participant/participation_accepted.txt +++ b/src/templates/emails/participation/participant/participation_accepted.txt @@ -1,10 +1,11 @@ {% extends 'emails/base_email.html' %} {% block content %} -

We're thrilled to inform you that your application for the - {{ participant.competition.title }} - competition has been accepted. Get ready to showcase your skills and compete with other talented individuals.

-

We look forward to seeing your participation.

+

+ Your application for the + {{ participant.competition.title }} + competition has been accepted. Get ready to showcase your skills and compete with other talented individuals. +

{% endblock %} diff --git a/src/templates/emails/participation/participant/participation_denied.html b/src/templates/emails/participation/participant/participation_denied.html index 23d4701be..9bef8c5ca 100644 --- a/src/templates/emails/participation/participant/participation_denied.html +++ b/src/templates/emails/participation/participant/participation_denied.html @@ -1,12 +1,10 @@ {% extends 'emails/base_email.html' %} {% block content %} -

We're writing to inform you about your application for the - {{ participant.competition.title }} - competition. While your application was carefully considered, we regret to inform you that your participation privileges - have been denied. We encourage you to review the competition details and rules to see if there are areas where you can - improve your application for future competitions.

-

Thank you for your interest in the competition. We wish you the best of luck in your future endeavors. +

+ Your application for the + {{ participant.competition.title }} + competition has been denied. You can reach out to the organizing team for more details.

{% endblock %} diff --git a/src/templates/emails/participation/participant/participation_denied.txt b/src/templates/emails/participation/participant/participation_denied.txt index 23d4701be..4b592667a 100644 --- a/src/templates/emails/participation/participant/participation_denied.txt +++ b/src/templates/emails/participation/participant/participation_denied.txt @@ -1,14 +1,11 @@ {% extends 'emails/base_email.html' %} {% block content %} -

We're writing to inform you about your application for the - {{ participant.competition.title }} - competition. While your application was carefully considered, we regret to inform you that your participation privileges - have been denied. We encourage you to review the competition details and rules to see if there are areas where you can - improve your application for future competitions.

-

Thank you for your interest in the competition. We wish you the best of luck in your future endeavors. +

+ Your application for the + {{ participant.competition.title }} + competition has been denied. You can reach out to the organizing team for more details.

- {% endblock %} diff --git a/src/templates/emails/participation/participant/participation_organizer_direct_email.html b/src/templates/emails/participation/participant/participation_organizer_direct_email.html index 715d61d23..ad5b88dc1 100644 --- a/src/templates/emails/participation/participant/participation_organizer_direct_email.html +++ b/src/templates/emails/participation/participant/participation_organizer_direct_email.html @@ -1,13 +1,14 @@ {% extends 'emails/base_email.html' %} {% block title %} -

This is a message from the organizer for the competition:

- {{ competition }} - +

+ This is a message from the organizer for the competition: + {{ competition }} +



{% endblock %} {% block content %}

Message:

-
{{ body }}
+
{{ body }}
{% endblock %} diff --git a/src/templates/emails/participation/participant/participation_organizer_direct_email.txt b/src/templates/emails/participation/participant/participation_organizer_direct_email.txt index ecb20a46a..44e044305 100644 --- a/src/templates/emails/participation/participant/participation_organizer_direct_email.txt +++ b/src/templates/emails/participation/participant/participation_organizer_direct_email.txt @@ -1,11 +1,14 @@ {% extends 'emails/base_email.txt' %} {% block title %} -This is a message from the organizer for the competition: -{{ competition }} -> http://{{ site.domain }}{{ competition.get_absolute_url }} +

+ This is a message from the organizer for the competition: + {{ competition }} +

+

{% endblock %} {% block content %} -Message: -{{ body }} +

Message:

+
{{ body }}
{% endblock %} diff --git a/src/templates/emails/participation/participant/participation_requested.html b/src/templates/emails/participation/participant/participation_requested.html index 582d174b5..3412695c7 100644 --- a/src/templates/emails/participation/participant/participation_requested.html +++ b/src/templates/emails/participation/participant/participation_requested.html @@ -1,12 +1,13 @@ {% extends 'emails/base_email.html' %} {% block content %} -

Thanks for your interest in the - {{ participant.competition.title }} - competition. We have received your request to participate.

- -

We will carefully review your application and notify you by email regarding your participation status (accepted or denied). - In the meantime, you can review the competition details and rules to familiarize yourself with the challenge.

- -

We look forward to your participation!

+

+ Thank you for your interest in the + {{ participant.competition.title }} + competition. We have received your request to participate. +

+

+ We will carefully review your application and notify you by email regarding your participation status (accepted or denied). + In the meantime, you can review the competition details and rules to familiarize yourself with the challenge. +

{% endblock %} diff --git a/src/templates/emails/participation/participant/participation_requested.txt b/src/templates/emails/participation/participant/participation_requested.txt index 582d174b5..3412695c7 100644 --- a/src/templates/emails/participation/participant/participation_requested.txt +++ b/src/templates/emails/participation/participant/participation_requested.txt @@ -1,12 +1,13 @@ {% extends 'emails/base_email.html' %} {% block content %} -

Thanks for your interest in the - {{ participant.competition.title }} - competition. We have received your request to participate.

- -

We will carefully review your application and notify you by email regarding your participation status (accepted or denied). - In the meantime, you can review the competition details and rules to familiarize yourself with the challenge.

- -

We look forward to your participation!

+

+ Thank you for your interest in the + {{ participant.competition.title }} + competition. We have received your request to participate. +

+

+ We will carefully review your application and notify you by email regarding your participation status (accepted or denied). + In the meantime, you can review the competition details and rules to familiarize yourself with the challenge. +

{% endblock %} diff --git a/src/templates/pages/monitor_queues.html b/src/templates/pages/monitor_queues.html new file mode 100644 index 000000000..bcbf0c4c5 --- /dev/null +++ b/src/templates/pages/monitor_queues.html @@ -0,0 +1,49 @@ +{% extends "base.html" %} +{% load staticfiles %} + +{% block extra_head %} +{% endblock %} + +{% block content %} +
+

Monitor queues

+
+
+
+ + + +
+ RabbitMQ +
+ This page allows admins to view connections, queued messages, message rates, channels, + exchanges, and other administrative features relating to RabbitMQ e.g. Creating users, + adding v-hosts, and creating policies. +
+
+
+
+
+
+ + + +
+ Flower +
+ Flower is a powerful web-based Celery monitoring tool designed to keep track of our + tasks. + Admins may view the state of which tasks were run, with what arguments, and many more + features. Here you may also view which queues your celery workers are consuming, and the + state of any tasks in them. At last, there is also a great monitoring page for viewing + the + systemic impact of your workers. +
+
+
+
+
+
+ + +{% endblock %} diff --git a/src/templates/pages/server_status.html b/src/templates/pages/server_status.html index debf5ba1a..dde0138f1 100644 --- a/src/templates/pages/server_status.html +++ b/src/templates/pages/server_status.html @@ -6,7 +6,7 @@ {% block content %}
-

Recent submissions (up to 250 or 2 days old)

+

Recent Submissions

@@ -55,48 +55,31 @@

Recent submissions (up to 250 or 2 days old)

+ + {% if is_paginated %} +
+ {% if submissions.has_previous %} + + First + Previous + + {% endif %} + + + Page {{ submissions.number }} of {{ paginator.num_pages }} + + + + {% if submissions.has_next %} + + Next + Last + + {% endif %} - {% if user.is_superuser %} -

Monitor queues

-
-
-
- - - -
- RabbitMQ -
- This page allows admins to view connections, queued messages, message rates, channels, - exchanges, and other administrative features relating to RabbitMQ e.g. Creating users, - adding v-hosts, and creating policies. -
-
-
-
-
-
- - - -
- Flower -
- Flower is a powerful web-based Celery monitoring tool designed to keep track of our - tasks. - Admins may view the state of which tasks were run, with what arguments, and many more - features. Here you may also view which queues your celery workers are consuming, and the - state of any tasks in them. At last, there is also a great monitoring page for viewing - the - systemic impact of your workers. -
-
-
-
{% endif %}
-