Fix: #1427
This commit is contained in:
parent
6ee9c4e380
commit
c17afece9d
5 changed files with 132 additions and 116 deletions
22
odevzdavatko/static/odevzdavatko/check_for_detail.js
Normal file
22
odevzdavatko/static/odevzdavatko/check_for_detail.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// Kontrola, že org neposílá nějakou blbost v detail.html
|
||||||
|
|
||||||
|
function zkontroluj_hodnoceni() {
|
||||||
|
const pocet = $('.hodnoceni').length;
|
||||||
|
if (pocet === 1) { // vydím pouze plusko
|
||||||
|
const vysledek = confirm("Odstranil jsi všechny problémy tohoto řešení. Nepůjde tedy dohledat přes problémy, co řeší, tj. například v došlých řešeních. Přesto odeslat?");
|
||||||
|
if (!vysledek) {
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function problem_is_empty(elem, index, array) {return elem.firstElementChild.children.length !== 1 && elem.firstElementChild.children[1].textContent === "";}
|
||||||
|
|
||||||
|
if ($('.hodnoceni').toArray().some(problem_is_empty)) {
|
||||||
|
alert("Neuloženo! Nezadal jsi problém, ke kterému posíláš hodnocení. Pokud je toto hodnocení navíc, smaž ho prosím křížkem a znovu odešli.")
|
||||||
|
event.preventDefault()
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
// FIXME: Necopypastovat! Tohle je zkopírované ze static/odevzdavatko/dynamic_formsets.js
|
||||||
|
|
||||||
|
|
||||||
|
// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
|
||||||
|
function updateElementIndex(el, prefix, ndx) {
|
||||||
|
var id_regex = new RegExp('(' + prefix + '-\\d+)');
|
||||||
|
var replacement = prefix + '-' + ndx;
|
||||||
|
if ($(el).attr("for")) {
|
||||||
|
$(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
|
||||||
|
}
|
||||||
|
if (el.id) {
|
||||||
|
el.id = el.id.replace(id_regex, replacement);
|
||||||
|
}
|
||||||
|
if (el.name) {
|
||||||
|
el.name = el.name.replace(id_regex, replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
|
||||||
|
function deleteForm(prefix, btn) {
|
||||||
|
var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
|
||||||
|
if (total >= 1){
|
||||||
|
btn.closest('tr').remove();
|
||||||
|
var forms = $('.hodnoceni');
|
||||||
|
var formCount = forms.length - 1; // There is one extra such form hidden as template!
|
||||||
|
$('#id_' + prefix + '-TOTAL_FORMS').val(formCount);
|
||||||
|
for (var i=0; i<formCount; i++) {
|
||||||
|
$(forms.get(i)).find(':input').each(function() {
|
||||||
|
updateElementIndex(this, prefix, i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Credit: https://simpleit.rocks/python/django/dynamic-add-form-with-add-button-in-django-modelformset-template/
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#pridat_hodnoceni').click(function() {
|
||||||
|
var form_idx = $('#id_form-TOTAL_FORMS').val();
|
||||||
|
var new_form = $('#empty_form').html().replace(/__prefix__/g, form_idx);
|
||||||
|
$('#form_set').append(new_form);
|
||||||
|
// Newly created form has not the binding between remove button and remove function
|
||||||
|
// We need to add it manually
|
||||||
|
$('.smazat_hodnoceni').click(function(){
|
||||||
|
deleteForm("form",this);
|
||||||
|
});
|
||||||
|
// Copy deadline
|
||||||
|
if (form_idx !== "0") {
|
||||||
|
$('#id_form-' + form_idx + '-deadline_body')[0].value = $('#id_form-' + (form_idx - 1) + '-deadline_body')[0].value
|
||||||
|
}
|
||||||
|
$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
||||||
|
});
|
||||||
|
$('.smazat_hodnoceni').click(function(){
|
||||||
|
deleteForm("form",this);
|
||||||
|
});
|
||||||
|
});
|
|
@ -4,67 +4,15 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{# FIXME: Necopypastovat! Tohle je zkopírované ze static/odevzdavatko/dynamic_formsets.js #}
|
{% if edit %}
|
||||||
<script type='text/javascript'>
|
<script src="{% static 'odevzdavatko/dynamic_formsets_for_detail.js' %}"></script>
|
||||||
// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
|
<script src="{% static 'odevzdavatko/check_for_detail.js' %}"></script>
|
||||||
function updateElementIndex(el, prefix, ndx) {
|
{% endif %}
|
||||||
var id_regex = new RegExp('(' + prefix + '-\\d+)');
|
|
||||||
var replacement = prefix + '-' + ndx;
|
|
||||||
if ($(el).attr("for")) {
|
|
||||||
$(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
|
|
||||||
}
|
|
||||||
if (el.id) {
|
|
||||||
el.id = el.id.replace(id_regex, replacement);
|
|
||||||
}
|
|
||||||
if (el.name) {
|
|
||||||
el.name = el.name.replace(id_regex, replacement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
|
|
||||||
function deleteForm(prefix, btn) {
|
|
||||||
var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
|
|
||||||
if (total >= 1){
|
|
||||||
btn.closest('tr').remove();
|
|
||||||
var forms = $('.hodnoceni');
|
|
||||||
var formCount = forms.length - 1; // There is one extra such form hidden as template!
|
|
||||||
$('#id_' + prefix + '-TOTAL_FORMS').val(formCount);
|
|
||||||
for (var i=0; i<formCount; i++) {
|
|
||||||
$(forms.get(i)).find(':input').each(function() {
|
|
||||||
updateElementIndex(this, prefix, i);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Credit: https://simpleit.rocks/python/django/dynamic-add-form-with-add-button-in-django-modelformset-template/
|
|
||||||
$(document).ready(function(){
|
|
||||||
$('#pridat_hodnoceni').click(function() {
|
|
||||||
var form_idx = $('#id_form-TOTAL_FORMS').val();
|
|
||||||
var new_form = $('#empty_form').html().replace(/__prefix__/g, form_idx);
|
|
||||||
$('#form_set').append(new_form);
|
|
||||||
// Newly created form has not the binding between remove button and remove function
|
|
||||||
// We need to add it manually
|
|
||||||
$('.smazat_hodnoceni').click(function(){
|
|
||||||
deleteForm("form",this);
|
|
||||||
});
|
|
||||||
// Copy deadline
|
|
||||||
if (form_idx !== "0") {
|
|
||||||
$('#id_form-' + form_idx + '-deadline_body')[0].value = $('#id_form-' + (form_idx - 1) + '-deadline_body')[0].value
|
|
||||||
}
|
|
||||||
$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
|
||||||
});
|
|
||||||
$('.smazat_hodnoceni').click(function(){
|
|
||||||
deleteForm("form",this);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<p>Řešené problémy: {{ object.problem.all | join:", " }}</p>
|
<p>Řešené problémy: {{ object.problem.all | join:", " }}</p>
|
||||||
|
|
||||||
<p>Řešitelé: {% for r in object.resitele.all %} {{ r }} (<a href="mailto:{{ r.osoba.email }}?subject={{ "Oprava řešení M&M " | urlencode }}{{ object.problem.all.0.hlavni_problem | urlencode }}">{{ r.osoba.email }}</a>)
|
<p>Řešitelé: {% for r in object.resitele.all %} {{ r }} {% if edit %}(<a href="mailto:{{ r.osoba.email }}?subject={{ "Oprava řešení M&M " | urlencode }}{{ object.problem.all.0.hlavni_problem | urlencode }}">{{ r.osoba.email }}</a>){% endif %}
|
||||||
{% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}</p>
|
{% if forloop.revcounter0 != 0 %}, {% endif %} {% endfor %}</p>
|
||||||
|
|
||||||
{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #}
|
{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #}
|
||||||
|
@ -82,13 +30,13 @@ $(document).ready(function(){
|
||||||
<td><a href="{{ priloha.soubor.url }}" download>{{ priloha.split | last }}</a></td>
|
<td><a href="{{ priloha.soubor.url }}" download>{{ priloha.split | last }}</a></td>
|
||||||
<td>{{ priloha.res_poznamka }}</td>
|
<td>{{ priloha.res_poznamka }}</td>
|
||||||
<td>{{ priloha.vytvoreno }}</td></tr>
|
<td>{{ priloha.vytvoreno }}</td></tr>
|
||||||
{# TODO: Orgo-poznámka, ideálně jako formulář #}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>Žádné přílohy</p>
|
<p>Žádné přílohy</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if edit %}
|
||||||
<form method=post onsubmit="return zkontroluj_hodnoceni();">
|
<form method=post onsubmit="return zkontroluj_hodnoceni();">
|
||||||
{# Poznámka #}
|
{# Poznámka #}
|
||||||
<h3>Neveřejná poznámka:</h3>
|
<h3>Neveřejná poznámka:</h3>
|
||||||
|
@ -116,7 +64,7 @@ $(document).ready(function(){
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
<a href="#"> <img src="{% static "odevzdavatko/plus.png" %}" id="pridat_hodnoceni" alt="Přidat hodnocení"></a> </br>
|
<a href="#"> <img src="{% static "odevzdavatko/plus.png" %}" id="pridat_hodnoceni" alt="Přidat hodnocení"></a> <br/>
|
||||||
<input type=submit value="Uložit"></form>
|
<input type=submit value="Uložit"></form>
|
||||||
|
|
||||||
<table id="empty_form" style="display: none;">
|
<table id="empty_form" style="display: none;">
|
||||||
|
@ -129,28 +77,19 @@ $(document).ready(function(){
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<h3>Hodnocení:</h3>
|
||||||
|
<table class="dosla_reseni">
|
||||||
|
<tr><th>Problém</th><th>Body</th><th>Zpětná vazba od opravovatele</th></tr>
|
||||||
|
{% for h in hodnoceni %}
|
||||||
|
<tr class="hodnoceni">
|
||||||
|
<td>{{ h.problem }}</td>
|
||||||
|
<td>{{ h.body }}</td>
|
||||||
|
<td>{{ h.feedback }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
function zkontroluj_hodnoceni() {
|
|
||||||
const pocet = $('.hodnoceni').length;
|
|
||||||
if (pocet === 1) { {# vydím pouze plusko #}
|
|
||||||
const vysledek = confirm("Odstranil jsi všechny problémy tohoto řešení. Nepůjde tedy dohledat přes problémy, co řeší, tj. například v došlých řešeních. Přesto odeslat?");
|
|
||||||
if (!vysledek) {
|
|
||||||
event.preventDefault();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function problem_is_empty(elem, index, array) {return elem.firstElementChild.children.length !== 1 && elem.firstElementChild.children[1].textContent === "";}
|
|
||||||
|
|
||||||
if ($('.hodnoceni').toArray().some(problem_is_empty)) {
|
|
||||||
alert("Neuloženo! Nezadal jsi problém, ke kterému posíláš hodnocení. Pokud je toto hodnocení navíc, smaž ho prosím křížkem a znovu odešli.")
|
|
||||||
event.preventDefault()
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -26,9 +26,9 @@ urlpatterns = [
|
||||||
path('org/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'),
|
path('org/reseni/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'),
|
||||||
path('org/reseni/rocnik/<int:rocnik>/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'),
|
path('org/reseni/rocnik/<int:rocnik>/', org_required(views.TabulkaOdevzdanychReseniView.as_view()), name='odevzdavatko_tabulka'),
|
||||||
path('org/reseni/<int:problem>/<int:resitel>/', org_required(views.ReseniProblemuView.as_view()), name='odevzdavatko_reseni_resitele_k_problemu'),
|
path('org/reseni/<int:problem>/<int:resitel>/', org_required(views.ReseniProblemuView.as_view()), name='odevzdavatko_reseni_resitele_k_problemu'),
|
||||||
path('org/reseni/<int:pk>', org_required(viewMethodSwitch(get=views.DetailReseniView.as_view(), post=views.hodnoceniReseniView)), name='odevzdavatko_detail_reseni'),
|
path('org/reseni/<int:pk>', org_required(viewMethodSwitch(get=views.EditReseniView.as_view(), post=views.hodnoceniReseniView)), name='odevzdavatko_detail_reseni'),
|
||||||
path('org/reseni/all', org_required(views.SeznamReseniView.as_view())),
|
path('org/reseni/all', org_required(views.SeznamReseniView.as_view())),
|
||||||
path('org/reseni/akt', org_required(views.SeznamAktualnichReseniView.as_view())),
|
path('org/reseni/akt', org_required(views.SeznamAktualnichReseniView.as_view())),
|
||||||
|
|
||||||
path('resitel/reseni/<int:pk>', resitel_or_org_required(views.ResitelReseniView.as_view()), name='odevzdavatko_resitel_reseni'),
|
path('resitel/reseni/<int:pk>', resitel_or_org_required(views.DetailReseniView.as_view()), name='odevzdavatko_resitel_reseni'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -211,6 +211,7 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi
|
||||||
|
|
||||||
## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex
|
## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex
|
||||||
class DetailReseniView(DetailView):
|
class DetailReseniView(DetailView):
|
||||||
|
""" Náhled na řešení. Editace je v :py:class:`EditReseniView`. """
|
||||||
model = m.Reseni
|
model = m.Reseni
|
||||||
template_name = 'odevzdavatko/detail.html'
|
template_name = 'odevzdavatko/detail.html'
|
||||||
|
|
||||||
|
@ -227,18 +228,43 @@ class DetailReseniView(DetailView):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_context_data(self, **kw):
|
def get_context_data(self, **kw):
|
||||||
|
self.check_access()
|
||||||
ctx = super().get_context_data(**kw)
|
ctx = super().get_context_data(**kw)
|
||||||
ctx['form'] = f.OhodnoceniReseniFormSet(
|
hodnoceni = self.aktualni_hodnoceni()
|
||||||
initial = self.aktualni_hodnoceni()
|
ctx["hodnoceni"] = hodnoceni
|
||||||
)
|
|
||||||
ctx['poznamka_form'] = f.PoznamkaReseniForm(instance=self.reseni)
|
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Oproti :py:class:`django.views.generic.detail.BaseDetailView`
|
||||||
|
kontroluje přístup pomocí :py:meth:`check_access`
|
||||||
|
"""
|
||||||
|
response = super().get(self, request, *args, **kwargs)
|
||||||
|
self.check_access()
|
||||||
|
return response
|
||||||
|
|
||||||
|
def check_access(self):
|
||||||
|
""" Řešitel musí být součástí řešení, jinak se na něj nemá co dívat. """
|
||||||
|
if not self.object.resitele.filter(osoba__user=self.request.user).exists():
|
||||||
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
|
||||||
|
class EditReseniView(DetailReseniView):
|
||||||
|
""" Editace (hlavně hodnocení) řešení. """
|
||||||
|
def get_context_data(self, **kw):
|
||||||
|
ctx = super().get_context_data(**kw)
|
||||||
|
ctx['form'] = f.OhodnoceniReseniFormSet(initial=ctx["hodnoceni"])
|
||||||
|
ctx['poznamka_form'] = f.PoznamkaReseniForm(instance=self.reseni)
|
||||||
|
ctx['edit'] = True
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
def check_access(self):
|
||||||
|
""" Na orga máme nároky už v urls.py """
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def hodnoceniReseniView(request, pk, *args, **kwargs):
|
def hodnoceniReseniView(request, pk, *args, **kwargs):
|
||||||
reseni = get_object_or_404(m.Reseni, pk=pk)
|
reseni = get_object_or_404(m.Reseni, pk=pk)
|
||||||
template_name = 'odevzdavatko/detail.html'
|
|
||||||
form_class = f.OhodnoceniReseniFormSet
|
|
||||||
success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk})
|
success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk})
|
||||||
|
|
||||||
# FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově
|
# FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově
|
||||||
|
@ -270,33 +296,6 @@ def hodnoceniReseniView(request, pk, *args, **kwargs):
|
||||||
return redirect(success_url)
|
return redirect(success_url)
|
||||||
|
|
||||||
|
|
||||||
class ResitelReseniView(DetailView):
|
|
||||||
model = m.Reseni
|
|
||||||
template_name = 'odevzdavatko/detail_resitele.html'
|
|
||||||
|
|
||||||
def aktualni_hodnoceni(self):
|
|
||||||
self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk'])
|
|
||||||
result = []
|
|
||||||
for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni):
|
|
||||||
result.append(
|
|
||||||
{
|
|
||||||
"problem": hodn.problem,
|
|
||||||
"body": hodn.body,
|
|
||||||
"feedback": hodn.feedback,
|
|
||||||
# "deadline_body": hodn.deadline_body,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def get_context_data(self, **kw):
|
|
||||||
ctx = super().get_context_data(**kw)
|
|
||||||
hodnoceni = self.aktualni_hodnoceni()
|
|
||||||
if not self.reseni.resitele.filter(osoba__user=self.request.user).exists():
|
|
||||||
raise PermissionDenied()
|
|
||||||
# ctx['poznamka'] = f.PoznamkaReseniForm(instance=self.reseni)
|
|
||||||
ctx["hodnoceni"] = hodnoceni
|
|
||||||
return ctx
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PrehledOdevzdanychReseni(ListView):
|
class PrehledOdevzdanychReseni(ListView):
|
||||||
|
|
Loading…
Reference in a new issue