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/apps/pages/views.py b/src/apps/pages/views.py index 7395d416a..bf32519b7 100644 --- a/src/apps/pages/views.py +++ b/src/apps/pages/views.py @@ -62,15 +62,45 @@ 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 + submission.file_size = self.format_file_size(submission.data.file_size) + # 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 + 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/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 09647588c..1a482839f 100644 --- a/src/static/riot/competitions/detail/_tabs.tag +++ b/src/static/riot/competitions/detail/_tabs.tag @@ -128,6 +128,7 @@
| Competition | Submission PK | + {% if show_child_submissions %} +Parent PK | + {% endif %} +Size | Submitter | +Queue | Hostname | Submitted at | Status | @@ -26,7 +39,12 @@|
|---|---|---|---|---|---|---|---|---|---|
| {{ submission.phase.competition.title }} | {{ submission.pk }} | -{{ submission.owner.username }} | + {% if show_child_submissions %} +{{ submission.parent.pk }} | + {% endif %} +{{ submission.file_size }} | +{{ submission.owner.username }} | +{{ submission.competition_queue }} | {{ submission.worker_hostname }} | {{ submission.created_when|timesince }} ago | {{ submission.status }} | @@ -73,4 +91,22 @@