Browse Source

Fix: #1427

pull/14/head
Jonas Havelka 2 years ago
parent
commit
c17afece9d
  1. 22
      odevzdavatko/static/odevzdavatko/check_for_detail.js
  2. 56
      odevzdavatko/static/odevzdavatko/dynamic_formsets_for_detail.js
  3. 101
      odevzdavatko/templates/odevzdavatko/detail.html
  4. 4
      odevzdavatko/urls.py
  5. 63
      odevzdavatko/views.py

22
odevzdavatko/static/odevzdavatko/check_for_detail.js

@ -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;
}

56
odevzdavatko/static/odevzdavatko/dynamic_formsets_for_detail.js

@ -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);
});
});

101
odevzdavatko/templates/odevzdavatko/detail.html

@ -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 %}

4
odevzdavatko/urls.py

@ -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'),
] ]

63
odevzdavatko/views.py

@ -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
) 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['poznamka_form'] = f.PoznamkaReseniForm(instance=self.reseni)
ctx['edit'] = True
return ctx 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…
Cancel
Save