Skip to content
35 changes: 34 additions & 1 deletion src/apps/api/serializers/competitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from api.serializers.queues import QueueSerializer
from datetime import datetime
from django.utils.timezone import now


class PhaseSerializer(WritableNestedModelSerializer):
Expand Down Expand Up @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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)
Expand Down
30 changes: 30 additions & 0 deletions src/apps/pages/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
28 changes: 28 additions & 0 deletions src/static/js/ours/latex_markdown_html.js
Original file line number Diff line number Diff line change
@@ -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
}
6 changes: 5 additions & 1 deletion src/static/riot/competitions/detail/_registration.tag
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 13 additions & 13 deletions src/static/riot/competitions/detail/_tabs.tag
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
</div>
</div>
<div>
<submission-limit></submission-limit>
<submission-upload is_admin="{competition.is_admin}" competition="{ competition }" phases="{ competition.phases }" fact_sheet="{ competition.fact_sheet }"></submission-upload>
</div>
<div>
Expand Down Expand Up @@ -318,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
Expand Down Expand Up @@ -355,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()

Expand Down
94 changes: 94 additions & 0 deletions src/static/riot/competitions/detail/submission_limit.tag
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<submission-limit>
<div class="ui sixteen wide column submission-container">
<div class="col">
<div class="col-content">
Number of submissions used for the day
</div>
<span if="{selected_phase.max_submissions_per_day}" class="badge {badgeColor(selected_phase.used_submissions_per_day, selected_phase.max_submissions_per_day)}">
{selected_phase.used_submissions_per_day} out of {selected_phase.max_submissions_per_day}
</span>
</div>
<div class="col">
<div class="col-content">
Number of total submissions used
</div>
<span if="{selected_phase.max_submissions_per_person}" class="badge {badgeColor(selected_phase.used_submissions_per_person, selected_phase.max_submissions_per_person)}">
{selected_phase.used_submissions_per_person} out of {selected_phase.max_submissions_per_person}
</span>
</div>
</div>
<script>
var self = this;
self.selected_phase = {}

self.badgeColor = function(used, max) {

// Calculate the percentage of used submissions
var percentage = (used / max) * 100;

// Determine the badge color based on the percentage
if (percentage < 5) {
return "badge-green";
} else if (percentage < 25) {
return "badge-yellow";
} else if (percentage >= 25 && percentage < 50) {
return "badge-orange";
} else if (percentage >= 50 && percentage < 75) {
return "badge-pink";
} else {
return "badge-red";
}
};

CODALAB.events.on('phase_selected', function (selected_phase) {
self.selected_phase = selected_phase;
self.update();
});

</script>

<style type="text/stylus">
.submission-container
margin-top 1em
background-color white
padding 2em
border solid 1px #dcdcdcdc
display flex

.col
flex 1
display flex
flex-direction column
justify-content space-between

.col-content
font-weight bold
text-align center

.badge
padding 0.5em 1em
text-align center
display inline-block
width max-content
margin 0 auto
margin-top 0.5em
border-radius 5px

.badge-green
background-color #a5d6a7

.badge-yellow
background-color #fff59d

.badge-orange
background-color #ffcc80

.badge-pink
background-color #ff80ab

.badge-red
background-color #e57373


</style>
</submission-limit>
6 changes: 5 additions & 1 deletion src/static/riot/competitions/editor/_pages.tag
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand Down
8 changes: 8 additions & 0 deletions src/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
<!-- Ours -->
<link rel="stylesheet" href="{% static "generated/output.css" %}">

<!-- Latex -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.css">

{% block extra_head %}
{% endblock %}
</head>
Expand Down Expand Up @@ -239,6 +242,10 @@ <h4 class="ui inverted header">CodaBench</h4>
<script src="https://cdnjs.cloudflare.com/ajax/libs/filesize/4.2.1/filesize.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.address/1.6/jquery.address.min.js" integrity="sha256-mLCPYHfNREhSETFQGuowilY3zBAZGnDO2cxCnCEm8/I=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.0.1/dist/gsap.min.js"></script>
<!-- Markdown-->
<script src="https://cdn.jsdelivr.net/npm/marked@3.0.7/marked.min.js"></script>
<!-- Latex -->
<script src="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.js"></script>
<!-- Ours -->


Expand Down Expand Up @@ -357,6 +364,7 @@ <h4 class="ui inverted header">CodaBench</h4>
<script src="{% static "js/Chart.bundle.js" %}"></script>
<script src="{% static "js/reconnecting-websocket.min.js" %}"></script>
<script src="{% static "generated/riot.js" %}"></script>
<script src="{% static "js/ours/latex_markdown_html.js" %}"></script>

{% block extra_js %}
{% endblock %}
Expand Down
Loading