Skip to content

Commit

Permalink
Add dates extension form to contest page (#57)
Browse files Browse the repository at this point in the history
* Add date extension forms

* Fix when dates can be extended

* Django template `now` can be used for printing only
and comparison is buggy

* Fix display of modal if invalid input is entered

* De-duplicate code, add doc string and add entry in forms.rst

* Fix lint
  • Loading branch information
prateekkumarweb authored and vbsinha committed May 1, 2019
1 parent cd1256b commit 64161f9
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 33 deletions.
3 changes: 3 additions & 0 deletions docs/source/forms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Forms and input pre-processing
.. autoclass:: AddTestCaseForm
:members:

.. autoclass:: UpdateContestForm
:members:

.. autoclass:: EditProblemForm
:members:

Expand Down
46 changes: 39 additions & 7 deletions judge/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
from django.core.validators import RegexValidator, validate_email, EMPTY_VALUES


def _check_valid_date(cleaned_data):
cont_start = cleaned_data.get("contest_start")
cont_soft_end = cleaned_data.get("contest_soft_end")
cont_hard_end = cleaned_data.get("contest_hard_end")
if cont_start > cont_soft_end:
raise forms.ValidationError("Contest cannot end before it starts!")
if cont_soft_end > cont_hard_end:
raise forms.ValidationError("The final deadline cannot be before the soft deadline")


class MultiEmailField(forms.Field):
"""
Subclass of forms.Field to support a list of email addresses.
Expand Down Expand Up @@ -75,13 +85,35 @@ class NewContestForm(forms.Form):

def clean(self):
cleaned_data = super().clean()
cont_start = cleaned_data.get("contest_start")
cont_soft_end = cleaned_data.get("contest_soft_end")
cont_hard_end = cleaned_data.get("contest_hard_end")
if cont_start > cont_soft_end:
raise forms.ValidationError("Contest cannot end before it contest starts!")
if cont_soft_end > cont_hard_end:
raise forms.ValidationError("The final deadline cannot be before the soft deadline")
_check_valid_date(cleaned_data)


class UpdateContestForm(forms.Form):
"""
Form to update the timeline of the Contest
"""

contest_start = forms.DateTimeField(label='Start Date',
widget=forms.DateTimeInput(attrs={'class': 'form-control'}),
help_text='Specify when the contest begins.')
"""Contest Start Timestamp"""

contest_soft_end = forms.DateTimeField(label='Soft End Date',
widget=forms.DateTimeInput(
attrs={'class': 'form-control'}),
help_text='Specify after when would you like to \
penalize submissions.')
"""Contest Soft End Timestamp"""

contest_hard_end = forms.DateTimeField(label='Hard End Date',
widget=forms.DateTimeInput(
attrs={'class': 'form-control'}),
help_text='Specify when the contest completely ends.')
"""Contest Hard End Timestamp"""

def clean(self):
cleaned_data = super().clean()
_check_valid_date(cleaned_data)


class AddPersonToContestForm(forms.Form):
Expand Down
6 changes: 4 additions & 2 deletions judge/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -764,9 +764,11 @@ def update_leaderboard(contest: int, person: str):
Updates the leaderboard for the passed contest for the rank of the person
Pass pk for contest and email for person
Only call this function when some submission for some problem of the contest
has scored more than its previous submission.
has scored more than its previous submission.
Remember to call this function whenever PersonProblemFinalScore is updated.
Returns True if update was successfull else returns False
Returns:
True if update was successful, False otherwise
"""

os.makedirs(os.path.join('content', 'contests'), exist_ok=True)
Expand Down
2 changes: 1 addition & 1 deletion judge/templates/judge/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<body>
{% url 'judge:index' as homepage %}
<nav class="navbar navbar-expand-lg navbar-dark bg-default">
<div class="container">
<div class="container-fluid">
<a class="navbar-brand" href="{{ homepage }}">PDP</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar-default"
aria-controls="navbar-default" aria-expanded="false" aria-label="Toggle navigation">
Expand Down
110 changes: 92 additions & 18 deletions judge/templates/judge/contest_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,108 @@
<li class="breadcrumb-item active" aria-current="page">Contest {{ contest.pk }}</li>
{% endblock %}

{% block scripts %}
<!-- TODO Fix this -->
<!-- <script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script>
var date_options = {
enableTime: true,
dateFormat: "Y-m-d H:i:S",
allowInput: true,
enableSeconds: true,
}
$("#id_contest_start").flatpickr(date_options)
$("#id_contest_soft_end").flatpickr(date_options)
$("#id_contest_hard_end").flatpickr(date_options)
</script> -->
{% if form.errors %}
<script>
$(window).on('load', function () {
$('#modal-update-dates').modal('show');
});
</script>
{% endif %}
{% endblock %}

{% block content %}
<div class="row">
<div class="col-12">
<div class="col-12 col-md-7">
<h2 class="d-inline">{{ contest.name }}</h2>
<div class="col-md-5 float-right">
<i class="ni ni-calendar-grid-58"></i> Starts at {{ contest.start_datetime|localtime }}<br>
{% if contest.soft_end_datetime != contest.hard_end_datetime %}
<i class="ni ni-calendar-grid-58"></i> Submissions penalized after
{{ contest.soft_end_datetime|localtime }}<br>
<div class="col-12">
{% if type == 'Poster' %}
{% if curr_time < contest.start_datetime %}<a class="btn btn-primary"
href="{% url 'judge:new_problem' contest.pk %}">Add Problem</a>{% endif %}
<a class="btn btn-primary" href="{% url 'judge:contest_scores_csv' contest.pk %}">Download Scores</a>
{% endif %}
<i class="ni ni-calendar-grid-58"></i> Ends at {{ contest.hard_end_datetime|localtime }}
<a class="btn btn-primary" href="{% url 'judge:get_posters' contest.pk %}">See posters</a>
{% if not contest.public %}<a class="btn btn-primary"
href="{% url 'judge:get_participants' contest.pk %}">See
participants</a>{% endif %}
{% if type == 'Poster' and curr_time < contest.hard_end_datetime %}<button type="button"
class="btn btn-primary my-2" data-toggle="modal" data-target="#modal-update-dates">Update
dates</button>{% endif %}
</div>
</div>
{% if type == 'Poster' and curr_time < contest.hard_end_datetime %}
<div class="modal fade" id="modal-update-dates" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal- modal-dialog-centered modal-sm" role="document">
<div class="modal-content">
<div class="modal-body p-0">
<div class="card bg-secondary shadow border-0">
<div class="card-body px-lg-5 py-lg-5">
<div class="text-center text-muted mb-4">
UPDATE CONTEST
</div>
<form method="POST" role="form">
{% if form.non_field_errors %}
<div class="alert alert-danger alter-dismissible fade show" role="alert">
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
{% for field in form %}
<div class="form-group mb-1">
{{ field.label_tag }}
<div class="input-group input-group-alternative">
<div class="input-group-prepend">
<span class="input-group-text"><i class="ni ni-calendar-grid-58"></i></span>
</div>
{{ field }}
</div>
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text|safe }}</small>
{% endif %}
{% if field.errors %}
<div class="alert alert-danger mt-2" role="alert">
{{ field.errors|striptags }}
</div>
{% endif %}
</div>
{% endfor %}
<div class="text-center">
<button type="submit" class="btn btn-primary my-4">Update</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endif %}
<div class="col-md-5 float-right">
<i class="ni ni-calendar-grid-58"></i> Starts at {{ contest.start_datetime|localtime }}<br>
{% if contest.soft_end_datetime != contest.hard_end_datetime %}
<i class="ni ni-calendar-grid-58"></i> Submissions penalized after
{{ contest.soft_end_datetime|localtime }}<br>
{% endif %}
<i class="ni ni-calendar-grid-58"></i> Ends at {{ contest.hard_end_datetime|localtime }}<br>
</div>
</div>

<div class="row">
<div class="col-12">
{% if type == 'Poster' %}
{% if curr_time < contest.start_datetime %}<a class="btn btn-primary"
href="{% url 'judge:new_problem' contest.pk %}">Add Problem</a>{% endif %}
<a class="btn btn-primary" href="{% url 'judge:contest_scores_csv' contest.pk %}">Download Scores</a>
{% endif %}
<a class="btn btn-primary" href="{% url 'judge:get_posters' contest.pk %}">See posters</a>
{% if not contest.public %}<a class="btn btn-primary" href="{% url 'judge:get_participants' contest.pk %}">See
participants</a>{% endif %}
{% for problem in problems %}
</div>
<div class="col-12 col-md-7">
{% for problem in problems %}
<div class="card m-3">
<div class="card-body">
<h4><code>[{{ problem.code }}]</code> {{ problem.name }}</h4>
Expand Down
2 changes: 1 addition & 1 deletion judge/templates/judge/new_contest.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{% block scripts %}
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script>
date_options = {
var date_options = {
enableTime: true,
dateFormat: "Y-m-d H:i:S",
allowInput: true,
Expand Down
33 changes: 29 additions & 4 deletions judge/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .models import Contest, Problem, TestCase, Submission
from .forms import NewContestForm, AddPersonToContestForm, DeletePersonFromContest
from .forms import NewProblemForm, EditProblemForm, NewSubmissionForm, AddTestCaseForm
from .forms import NewCommentForm, AddPosterScoreForm
from .forms import NewCommentForm, UpdateContestForm, AddPosterScoreForm
from . import handler


Expand Down Expand Up @@ -208,14 +208,39 @@ def contest_detail(request, contest_id):
return handler404(request)
problems = Problem.objects.filter(contest_id=contest_id)
status, leaderboard = handler.get_leaderboard(contest_id)
return render(request, 'judge/contest_detail.html', {
curr_time = timezone.now()
context = {
'contest': contest,
'type': 'Poster' if perm else 'Participant',
'problems': problems,
'leaderboard_status': status,
'leaderboard': leaderboard,
'curr_time': timezone.now(),
})
'curr_time': curr_time,
}
if perm is True:
if request.method == 'POST':
form = UpdateContestForm(request.POST)
if form.is_valid():
if (curr_time < contest.soft_end_datetime or
(form.cleaned_data['contest_soft_end'] == contest.soft_end_datetime and
curr_time < contest.hard_end_datetime)):
try:
contest.start_datetime = form.cleaned_data['contest_start']
contest.soft_end_datetime = form.cleaned_data['contest_soft_end']
contest.hard_end_datetime = form.cleaned_data['contest_hard_end']
contest.save()
except Exception as e:
form.add_error(None, e.__str__())
else:
form.add_error(None, 'Deadline cannot be extended if it has passed')
else:
form = UpdateContestForm(initial={
'contest_start': contest.start_datetime,
'contest_soft_end': contest.soft_end_datetime,
'contest_hard_end': contest.hard_end_datetime,
})
context['form'] = form
return render(request, 'judge/contest_detail.html', context)


def contest_scores_csv(request, contest_id):
Expand Down

0 comments on commit 64161f9

Please sign in to comment.