From d6d6f238c10287ebbaacff7ffe658d49564c33bc Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Sun, 20 Aug 2023 13:40:07 +0500 Subject: [PATCH 1/6] username is a clickable link --- src/templates/pages/server_status.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/pages/server_status.html b/src/templates/pages/server_status.html index 8e4bc172b..2a775edf6 100644 --- a/src/templates/pages/server_status.html +++ b/src/templates/pages/server_status.html @@ -26,7 +26,7 @@

Recent submissions (up to 250 or 2 days old)

{{ submission.phase.competition.title }} {{ submission.pk }} - {{ submission.owner.username }} + {{ submission.owner.username }} {{ submission.worker_hostname }} {{ submission.created_when|timesince }} ago {{ submission.status }} From 819ebabb1238f3b92f84b50d3aae090958bc7f26 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Sun, 20 Aug 2023 13:56:05 +0500 Subject: [PATCH 2/6] submission file size added --- src/apps/pages/views.py | 22 ++++++++++++++++++++++ src/templates/pages/server_status.html | 2 ++ 2 files changed, 24 insertions(+) diff --git a/src/apps/pages/views.py b/src/apps/pages/views.py index 7395d416a..f5be309d3 100644 --- a/src/apps/pages/views.py +++ b/src/apps/pages/views.py @@ -69,8 +69,30 @@ def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) context['submissions'] = qs[:250] + + for submission in context['submissions']: + # Get filesize from each submissions's data + submission.file_size = self.format_file_size(submission.data.file_size) + return context + def format_file_size(self, file_size): + """ + A custom function to convert file size to KB, MB, GB and return with the unit + """ + try: + n = float(file_size) + except ValueError: + return "" + + units = ['KB', 'MB', 'GB'] + i = 0 + while n >= 1000 and i < len(units) - 1: + n /= 1000 + i += 1 + + return f"{n:.1f} {units[i]}" + def page_not_found_view(request, exception): print(request) diff --git a/src/templates/pages/server_status.html b/src/templates/pages/server_status.html index 8e4bc172b..26afaa0c9 100644 --- a/src/templates/pages/server_status.html +++ b/src/templates/pages/server_status.html @@ -11,6 +11,7 @@

Recent submissions (up to 250 or 2 days old)

Competition Submission PK + Size Submitter Hostname Submitted at @@ -26,6 +27,7 @@

Recent submissions (up to 250 or 2 days old)

{{ submission.phase.competition.title }} {{ submission.pk }} + {{ submission.file_size }} {{ submission.owner.username }} {{ submission.worker_hostname }} {{ submission.created_when|timesince }} ago From 7d7d4a9484f97eebd44a35cf4da7176ce7de661b Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Mon, 21 Aug 2023 15:24:30 +0500 Subject: [PATCH 3/6] Server status - Queue name displayed (#1091) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * queue name shown in server status page * comment added, space added * Default changed to * --------- Co-authored-by: Adrien Pavão --- src/apps/pages/views.py | 6 ++++++ src/templates/pages/server_status.html | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/apps/pages/views.py b/src/apps/pages/views.py index 7395d416a..429202571 100644 --- a/src/apps/pages/views.py +++ b/src/apps/pages/views.py @@ -69,6 +69,12 @@ def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) context['submissions'] = qs[:250] + + for submission in context['submissions']: + # Get queue from each submission's competition + queue_name = "*" if submission.phase.competition.queue is None else submission.phase.competition.queue.name + submission.competition_queue = queue_name + return context diff --git a/src/templates/pages/server_status.html b/src/templates/pages/server_status.html index 2a775edf6..c8b2398e5 100644 --- a/src/templates/pages/server_status.html +++ b/src/templates/pages/server_status.html @@ -12,6 +12,7 @@

Recent submissions (up to 250 or 2 days old)

Competition Submission PK Submitter + Queue Hostname Submitted at Status @@ -27,6 +28,7 @@

Recent submissions (up to 250 or 2 days old)

{{ submission.phase.competition.title }} {{ submission.pk }} {{ submission.owner.username }} + {{ submission.competition_queue }} {{ submission.worker_hostname }} {{ submission.created_when|timesince }} ago {{ submission.status }} From f5bc8fe6c8d40f1d46400a91be8feb9207344542 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Mon, 21 Aug 2023 15:35:23 +0500 Subject: [PATCH 4/6] Submissions panel - show remaining submissions count (#1094) * remaining submissions count added in submissions tab * white space removed * style updated, comment fixed * check request in context and check if user is authenticated * left replaced by used * remaining replace by used --- src/apps/api/serializers/competitions.py | 35 ++++++- src/static/riot/competitions/detail/_tabs.tag | 1 + .../competitions/detail/submission_limit.tag | 94 +++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/static/riot/competitions/detail/submission_limit.tag diff --git a/src/apps/api/serializers/competitions.py b/src/apps/api/serializers/competitions.py index d29ca7ba7..81e233523 100644 --- a/src/apps/api/serializers/competitions.py +++ b/src/apps/api/serializers/competitions.py @@ -17,6 +17,7 @@ from api.serializers.queues import QueueSerializer from datetime import datetime +from django.utils.timezone import now class PhaseSerializer(WritableNestedModelSerializer): @@ -92,9 +93,10 @@ def validate_leaderboard(self, value): class PhaseDetailSerializer(serializers.ModelSerializer): tasks = PhaseTaskInstanceSerializer(source='task_instances', many=True) status = serializers.SerializerMethodField() - public_data = DataDetailSerializer(read_only=True) starting_kit = DataDetailSerializer(read_only=True) + used_submissions_per_day = serializers.SerializerMethodField() + used_submissions_per_person = serializers.SerializerMethodField() class Meta: model = Phase @@ -117,6 +119,9 @@ class Meta: 'public_data', 'starting_kit', 'is_final_phase', + 'used_submissions_per_day', + 'used_submissions_per_person' + ) def get_status(self, obj): @@ -155,6 +160,34 @@ def get_status(self, obj): elif not phase_started: return Phase.NEXT + def get_used_submissions_per_day(self, obj): + + # Check if 'request' key exists in the context + if 'request' in self.context: + # Get user from the request + user = self.context['request'].user + if user.is_authenticated: + # Get all submissions which are not failed and belongs to this user for this phase + qs = obj.submissions.filter(owner=user, parent__isnull=True).exclude(status='Failed') + # Count submissions made today + daily_submission_count = qs.filter(created_when__day=now().day).count() + return daily_submission_count + return 0 + + def get_used_submissions_per_person(self, obj): + + # Check if 'request' key exists in the context + if 'request' in self.context: + # Get user from the request + user = self.context['request'].user + if user.is_authenticated: + # Get all submissions which are not failed and belongs to this user for this phase + qs = obj.submissions.filter(owner=user, parent__isnull=True).exclude(status='Failed') + # Count all submissions + total_submission_count = qs.count() + return total_submission_count + return 0 + class PhaseUpdateSerializer(PhaseSerializer): tasks = PhaseTaskInstanceSerializer(source='task_instances', many=True) diff --git a/src/static/riot/competitions/detail/_tabs.tag b/src/static/riot/competitions/detail/_tabs.tag index 09647588c..71d7380f9 100644 --- a/src/static/riot/competitions/detail/_tabs.tag +++ b/src/static/riot/competitions/detail/_tabs.tag @@ -128,6 +128,7 @@
+
diff --git a/src/static/riot/competitions/detail/submission_limit.tag b/src/static/riot/competitions/detail/submission_limit.tag new file mode 100644 index 000000000..b8cc7dbe0 --- /dev/null +++ b/src/static/riot/competitions/detail/submission_limit.tag @@ -0,0 +1,94 @@ + +
+
+
+ Number of submissions used for the day +
+ + {selected_phase.used_submissions_per_day} out of {selected_phase.max_submissions_per_day} + +
+
+
+ Number of total submissions used +
+ + {selected_phase.used_submissions_per_person} out of {selected_phase.max_submissions_per_person} + +
+
+ + + +
From 4abb9225b2fc700ac547fbf6fde951987843a5be Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Mon, 21 Aug 2023 15:48:10 +0500 Subject: [PATCH 5/6] HTML - Markdown - Latex : Rendering Fixed (#1090) * markdown, html and latex rendered properly in compettion pages, competition pages view in editor, registration model, phase description * commented code removed * null content condition handled * marked lib version updated to fix issue of loading function --- src/static/js/ours/latex_markdown_html.js | 28 +++++++++++++++++++ .../competitions/detail/_registration.tag | 6 +++- src/static/riot/competitions/detail/_tabs.tag | 25 ++++++++--------- .../riot/competitions/editor/_pages.tag | 6 +++- src/templates/base.html | 8 ++++++ 5 files changed, 58 insertions(+), 15 deletions(-) create mode 100644 src/static/js/ours/latex_markdown_html.js diff --git a/src/static/js/ours/latex_markdown_html.js b/src/static/js/ours/latex_markdown_html.js new file mode 100644 index 000000000..4a0e645c2 --- /dev/null +++ b/src/static/js/ours/latex_markdown_html.js @@ -0,0 +1,28 @@ +// Function to render Markdown, HTML and Latex and return updated content +function renderMarkdownWithLatex(content) { + if(content === null){ + return [] + } + const parsedHtml = new DOMParser().parseFromString(marked(content), "text/html") + + const traverseAndRenderLatex = (node) => { + if (node.nodeType === Node.ELEMENT_NODE) { + const latexPattern = /\$\$([\s\S]*?)\$\$|\$([^\$\n]*?)\$/g + const hasLatex = latexPattern.test(node.textContent) + if (hasLatex) { + const tempDiv = document.createElement('div') + tempDiv.innerHTML = node.innerHTML.replace(latexPattern, (_, formula1, formula2) => { + const formula = formula1 || formula2 + return katex.renderToString(formula, { throwOnError: false }) + }); + node.innerHTML = tempDiv.innerHTML + } + } + + node.childNodes.forEach(traverseAndRenderLatex) + }; + + traverseAndRenderLatex(parsedHtml.body) + + return parsedHtml.body.childNodes +} \ No newline at end of file diff --git a/src/static/riot/competitions/detail/_registration.tag b/src/static/riot/competitions/detail/_registration.tag index 224341018..6bfe16511 100644 --- a/src/static/riot/competitions/detail/_registration.tag +++ b/src/static/riot/competitions/detail/_registration.tag @@ -72,7 +72,11 @@ CODALAB.events.on('competition_loaded', (competition) => { self.competition_id = competition.id if (self.refs.terms_content) { - self.refs.terms_content.innerHTML = render_markdown(competition.terms) + const rendered_content = renderMarkdownWithLatex(competition.terms) + self.refs.terms_content.innerHTML = "" + rendered_content.forEach(node => { + self.refs.terms_content.appendChild(node.cloneNode(true)); // Append each node + }); } self.registration_auto_approve = competition.registration_auto_approve self.status = competition.participant_status diff --git a/src/static/riot/competitions/detail/_tabs.tag b/src/static/riot/competitions/detail/_tabs.tag index 71d7380f9..1a482839f 100644 --- a/src/static/riot/competitions/detail/_tabs.tag +++ b/src/static/riot/competitions/detail/_tabs.tag @@ -319,16 +319,21 @@ self.update() _.forEach(self.competition.pages, (page, index) => { - if (self.isHTML(page.content)){ - $(`#page_${index}`)[0].innerHTML = sanitize_HTML(page.content) - }else{ - $(`#page_${index}`)[0].innerHTML = render_markdown(page.content) - } - + // Render html pages + const rendered_content = renderMarkdownWithLatex(page.content) + $(`#page_${index}`)[0].innerHTML = "" + rendered_content.forEach(node => { + $(`#page_${index}`)[0].appendChild(node.cloneNode(true)); // Append each node + }); }) _.forEach(self.competition.phases, (phase, index) => { - $(`#phase_${index}`)[0].innerHTML = render_markdown(phase.description) + // Render phase description + const rendered_content = renderMarkdownWithLatex(phase.description) + $(`#phase_${index}`)[0].innerHTML = "" + rendered_content.forEach(node => { + $(`#phase_${index}`)[0].appendChild(node.cloneNode(true)); // Append each node + }); }) _.delay(() => { self.loading = false @@ -356,12 +361,6 @@ CODALAB.events.trigger('phase_selected', data) } } - // To check if page content has HTML - // Return true if content is html - // Return false if content is not html i.e. MarkDown - self.isHTML = function (page_content) { - return /<(?=.*? .*?\/ ?>|br|hr|input|!--|wbr)[a-z]+.*?>|<([a-z]+).*?<\/\1>/i.test(page_content); - } self.update() diff --git a/src/static/riot/competitions/editor/_pages.tag b/src/static/riot/competitions/editor/_pages.tag index 45b56d6b2..369f7fcc0 100644 --- a/src/static/riot/competitions/editor/_pages.tag +++ b/src/static/riot/competitions/editor/_pages.tag @@ -159,7 +159,11 @@ self.view_page = function (page_index) { self.selected_page_index = page_index $(self.refs.view_modal).modal('show') - self.refs.page_content.innerHTML = render_markdown(self.pages[page_index].content) + const rendered_content = renderMarkdownWithLatex(self.pages[page_index].content) + self.refs.page_content.innerHTML = "" + rendered_content.forEach(node => { + self.refs.page_content.appendChild(node.cloneNode(true)); // Append each node + }); } self.form_updated = function () { diff --git a/src/templates/base.html b/src/templates/base.html index c65de9078..e6530429f 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -23,6 +23,9 @@ + + + {% block extra_head %} {% endblock %} @@ -239,6 +242,10 @@

CodaBench

+ + + + @@ -357,6 +364,7 @@

CodaBench

+ {% block extra_js %} {% endblock %} From f384b362d64d7d21343a3e55427fa52e009094ba Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Mon, 21 Aug 2023 17:17:13 +0500 Subject: [PATCH 6/6] Server status - show/hide child submissions (#1096) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * show/hide child submissions * spelling mistake fixed, show parent id when has parent, show explanation for child submissions * Update server_status.html --------- Co-authored-by: Adrien Pavão --- src/apps/pages/views.py | 5 ++++ src/templates/pages/server_status.html | 32 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/apps/pages/views.py b/src/apps/pages/views.py index cd8a9d946..bf32519b7 100644 --- a/src/apps/pages/views.py +++ b/src/apps/pages/views.py @@ -62,13 +62,18 @@ def get_context_data(self, *args, **kwargs): if not self.request.user.is_staff: raise HttpResponse(status=404) + show_child_submissions = self.request.GET.get('show_child_submissions', False) + qs = Submission.objects.all() qs = qs.filter(created_when__gte=now() - timedelta(days=2)) + if not show_child_submissions: + qs = qs.filter(parent__isnull=True) qs = qs.order_by('-created_when') qs = qs.select_related('phase__competition', 'owner') context = super().get_context_data(*args, **kwargs) context['submissions'] = qs[:250] + context['show_child_submissions'] = show_child_submissions for submission in context['submissions']: # Get filesize from each submissions's data diff --git a/src/templates/pages/server_status.html b/src/templates/pages/server_status.html index cecb57e91..a6dbc741f 100644 --- a/src/templates/pages/server_status.html +++ b/src/templates/pages/server_status.html @@ -7,10 +7,21 @@ {% block content %}

Recent submissions (up to 250 or 2 days old)

+ + + + + {% if show_child_submissions %} + + {% endif %} @@ -28,6 +39,9 @@

Recent submissions (up to 250 or 2 days old)

+ {% if show_child_submissions %} + + {% endif %} @@ -77,4 +91,22 @@

Monitor queues

+ + {% endblock %}
Competition Submission PKParent PKSize Submitter Queue
{{ submission.phase.competition.title }} {{ submission.pk }}{{ submission.parent.pk }}{{ submission.file_size }} {{ submission.owner.username }} {{ submission.competition_queue }}