|
|
@ -1,12 +1,21 @@ |
|
|
|
from django.views.generic import ListView, DetailView |
|
|
|
from django.views.generic.base import TemplateView |
|
|
|
from django.views.generic import ListView, DetailView, FormView |
|
|
|
from django.views.generic.list import MultipleObjectTemplateResponseMixin,MultipleObjectMixin |
|
|
|
from django.views.generic.base import View |
|
|
|
from django.views.generic.detail import SingleObjectMixin |
|
|
|
from django.shortcuts import redirect, get_object_or_404 |
|
|
|
from django.urls import reverse |
|
|
|
from django.db import transaction |
|
|
|
|
|
|
|
from dataclasses import dataclass |
|
|
|
import datetime |
|
|
|
import logging |
|
|
|
|
|
|
|
import seminar.models as m |
|
|
|
import seminar.forms as f |
|
|
|
from seminar.utils import aktivniResitele, resi_v_rocniku |
|
|
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
# Co chceme? |
|
|
|
# - "Tabulku" aktuální řešitelé x zveřejněné problémy, v buňkách počet řešení |
|
|
|
# - TabulkaOdevzdanychReseniView |
|
|
@ -30,15 +39,22 @@ class TabulkaOdevzdanychReseniView(ListView): |
|
|
|
template_name = 'seminar/odevzdavatko/tabulka.html' |
|
|
|
model = m.Hodnoceni |
|
|
|
|
|
|
|
def inicializuj_osy_tabulky(self): |
|
|
|
"""Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů""" |
|
|
|
# NOTE: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistují ty objekty (?). TODO: Otestovat |
|
|
|
# TODO: Prefetches, Select related, ... |
|
|
|
self.resitele = m.Resitel.objects.all() |
|
|
|
self.problemy = m.Problem.objects.all() |
|
|
|
|
|
|
|
def get_queryset(self): |
|
|
|
# FIXME: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistuje Nastavení. |
|
|
|
self.inicializuj_osy_tabulky() |
|
|
|
self.akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi... |
|
|
|
self.resitele = resi_v_rocniku(self.akt_rocnik) |
|
|
|
# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. |
|
|
|
self.zadane_problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() |
|
|
|
self.problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() |
|
|
|
|
|
|
|
qs = super().get_queryset() |
|
|
|
qs = qs.filter(problem__in=self.zadane_problemy).select_related('reseni', 'problem').prefetch_related('reseni__resitele__osoba') |
|
|
|
qs = qs.filter(problem__in=self.problemy).select_related('reseni', 'problem').prefetch_related('reseni__resitele__osoba') |
|
|
|
return qs |
|
|
|
|
|
|
|
def get_context_data(self, *args, **kwargs): |
|
|
@ -46,10 +62,10 @@ class TabulkaOdevzdanychReseniView(ListView): |
|
|
|
self.akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi... |
|
|
|
self.resitele = resi_v_rocniku(self.akt_rocnik) |
|
|
|
# NOTE: Protože řešení odkazuje přímo na Problém a QuerySet na Hodnocení je nepolymorfní, musíme porovnávat taky s nepolymorfními Problémy. |
|
|
|
self.zadane_problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() |
|
|
|
self.problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() |
|
|
|
|
|
|
|
ctx = super().get_context_data(*args, **kwargs) |
|
|
|
ctx['problemy'] = self.zadane_problemy |
|
|
|
ctx['problemy'] = self.problemy |
|
|
|
ctx['resitele'] = self.resitele |
|
|
|
tabulka = dict() |
|
|
|
|
|
|
@ -76,7 +92,7 @@ class TabulkaOdevzdanychReseniView(ListView): |
|
|
|
hodnoty = [] |
|
|
|
for resitel in self.resitele: |
|
|
|
resiteluv_radek = [] |
|
|
|
for problem in self.zadane_problemy: |
|
|
|
for problem in self.problemy: |
|
|
|
if problem in tabulka and resitel in tabulka[problem]: |
|
|
|
resiteluv_radek.append(tabulka[problem][resitel]) |
|
|
|
else: |
|
|
@ -86,7 +102,8 @@ class TabulkaOdevzdanychReseniView(ListView): |
|
|
|
|
|
|
|
return ctx |
|
|
|
|
|
|
|
class ReseniProblemuView(ListView): |
|
|
|
# Velmi silně inspirováno zdrojáky, FIXME: Nedá se to udělat smysluplněji? |
|
|
|
class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixin, View): |
|
|
|
model = m.Reseni |
|
|
|
template_name = 'seminar/odevzdavatko/seznam.html' |
|
|
|
|
|
|
@ -107,12 +124,73 @@ class ReseniProblemuView(ListView): |
|
|
|
) |
|
|
|
return qs |
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs): |
|
|
|
self.object_list = self.get_queryset() |
|
|
|
if self.object_list.count() == 1: |
|
|
|
jedine_reseni = self.object_list.first() |
|
|
|
return redirect(reverse("odevzdavatko_detail_reseni", kwargs={"pk": jedine_reseni.id})) |
|
|
|
context = self.get_context_data() |
|
|
|
return self.render_to_response(context) |
|
|
|
# Kontext automaticky? |
|
|
|
|
|
|
|
## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex |
|
|
|
class DetailReseniView(DetailView): |
|
|
|
model = m.Reseni |
|
|
|
template_name = 'seminar/odevzdavatko/detail.html' |
|
|
|
# To je všechno? Najde se to podle pk... |
|
|
|
|
|
|
|
def aktualni_hodnoceni(self): |
|
|
|
reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk']) |
|
|
|
result = [] # Slovníky s klíči problem, body, cislo_body -- initial data pro f.OhodnoceniReseniFormSet |
|
|
|
for hodn in m.Hodnoceni.objects.filter(reseni=reseni): |
|
|
|
result.append( |
|
|
|
{"problem": hodn.problem, |
|
|
|
"body": hodn.body, |
|
|
|
"cislo_body": hodn.cislo_body, |
|
|
|
}) |
|
|
|
return result |
|
|
|
|
|
|
|
def get_context_data(self, **kw): |
|
|
|
ctx = super().get_context_data(**kw) |
|
|
|
ctx['form'] = f.OhodnoceniReseniFormSet( |
|
|
|
initial = self.aktualni_hodnoceni() |
|
|
|
) |
|
|
|
return ctx |
|
|
|
|
|
|
|
|
|
|
|
def hodnoceniReseniView(request, pk, *args, **kwargs): |
|
|
|
reseni = get_object_or_404(m.Reseni, pk=pk) |
|
|
|
template_name = 'seminar/odevzdavatko/detail.html' |
|
|
|
form_class = f.OhodnoceniReseniFormSet |
|
|
|
success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk}) |
|
|
|
|
|
|
|
# FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově |
|
|
|
# Also: https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#django.forms.ModelForm |
|
|
|
formset = f.OhodnoceniReseniFormSet(request.POST) |
|
|
|
# TODO: Napsat validaci formuláře a formsetu |
|
|
|
# TODO: Implementovat větev, kdy formulář validní není. |
|
|
|
if formset.is_valid(): |
|
|
|
with transaction.atomic(): |
|
|
|
# Smažeme všechna dosavadní hodnocení tohoto řešení |
|
|
|
qs = m.Hodnoceni.objects.filter(reseni=reseni) |
|
|
|
logger.info(f"Will delete {qs.count()} objects: {qs}") |
|
|
|
qs.delete() |
|
|
|
|
|
|
|
# Vyrobíme nová podle formsetu |
|
|
|
for form in formset: |
|
|
|
problem = form.cleaned_data['problem'] |
|
|
|
body = form.cleaned_data['body'] |
|
|
|
cislo_body = form.cleaned_data['cislo_body'] |
|
|
|
hodnoceni = m.Hodnoceni( |
|
|
|
problem=problem, |
|
|
|
body=body, |
|
|
|
cislo_body=cislo_body, |
|
|
|
reseni=reseni, |
|
|
|
) |
|
|
|
logger.info(f"Creating Hodnoceni: {hodnoceni}") |
|
|
|
hodnoceni.save() |
|
|
|
|
|
|
|
return redirect(success_url) |
|
|
|
|
|
|
|
|
|
|
|
# Přehled všech řešení kvůli debugování |
|
|
|
|
|
|
|