|
|
|
from django.views.generic import ListView, DetailView
|
|
|
|
from django.views.generic.base import TemplateView
|
|
|
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
import datetime
|
|
|
|
|
|
|
|
import seminar.models as m
|
|
|
|
from seminar.utils import aktivniResitele, resi_v_rocniku
|
|
|
|
|
|
|
|
# Co chceme?
|
|
|
|
# - "Tabulku" aktuální řešitelé x zveřejněné problémy, v buňkách počet řešení
|
|
|
|
# - TabulkaOdevzdanychReseniView
|
|
|
|
# - Detail konkrétního problému a řešitele -- přehled všech řešení odevzdaných k tomuto problému
|
|
|
|
# - ReseniProblemuView
|
|
|
|
# - Detail konkrétního řešení -- všechny soubory, datum, ...
|
|
|
|
# - DetailReseniView
|
|
|
|
#
|
|
|
|
# Taky se může hodit:
|
|
|
|
# - Tabulka všech řešitelů x všech problémů?
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class SouhrnReseni:
|
|
|
|
"""Dataclass reprezentující data o odevzdaných řešeních pro zobrazení v tabulce."""
|
|
|
|
pocet_reseni : int
|
|
|
|
posledni_odevzdani : datetime.datetime
|
|
|
|
body : float
|
|
|
|
|
|
|
|
|
|
|
|
class TabulkaOdevzdanychReseniView(ListView):
|
|
|
|
template_name = 'seminar/odevzdavatko/tabulka.html'
|
|
|
|
model = m.Hodnoceni
|
|
|
|
|
|
|
|
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.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()
|
|
|
|
|
|
|
|
qs = super().get_queryset()
|
|
|
|
qs = qs.filter(problem__in=self.zadane_problemy).select_related('reseni', 'problem').prefetch_related('reseni__resitele__osoba')
|
|
|
|
return qs
|
|
|
|
|
|
|
|
def get_context_data(self, *args, **kwargs):
|
|
|
|
# FIXME: Tenhle blok nemůže být přímo ve třídě, protože před vyrobením databáze neexistuje Nastavení.
|
|
|
|
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()
|
|
|
|
|
|
|
|
ctx = super().get_context_data(*args, **kwargs)
|
|
|
|
ctx['problemy'] = self.zadane_problemy
|
|
|
|
ctx['resitele'] = self.resitele
|
|
|
|
tabulka = dict()
|
|
|
|
|
|
|
|
def pridej_reseni(problem, resitel, body, cas):
|
|
|
|
if problem not in tabulka:
|
|
|
|
tabulka[problem] = dict()
|
|
|
|
if resitel not in tabulka[problem]:
|
|
|
|
tabulka[problem][resitel] = SouhrnReseni(pocet_reseni=1, posledni_odevzdani=cas, body=body)
|
|
|
|
else:
|
|
|
|
tabulka[problem][resitel].posledni_odevzdani = max(tabulka[problem][resitel].posledni_odevzdani, cas)
|
|
|
|
tabulka[problem][resitel].body = max(tabulka[problem][resitel].body, body,
|
|
|
|
key=lambda x: x if x is not None else -1 # None je malé číslo
|
|
|
|
# FIXME: Možná dává smysl i mít None jako velké číslo -- jakože "TODO: zadat body"
|
|
|
|
)
|
|
|
|
tabulka[problem][resitel].pocet_reseni += 1
|
|
|
|
# Pro jednoduchost template si ještě poznamenáme ID problému a řešitele
|
|
|
|
tabulka[problem][resitel].problem_id = problem.id
|
|
|
|
tabulka[problem][resitel].resitel_id = resitel.id
|
|
|
|
|
|
|
|
for hodnoceni in self.get_queryset():
|
|
|
|
for resitel in hodnoceni.reseni.resitele.all():
|
|
|
|
pridej_reseni(hodnoceni.problem, resitel, hodnoceni.body, hodnoceni.reseni.cas_doruceni)
|
|
|
|
|
|
|
|
hodnoty = []
|
|
|
|
for resitel in self.resitele:
|
|
|
|
resiteluv_radek = []
|
|
|
|
for problem in self.zadane_problemy:
|
|
|
|
if problem in tabulka and resitel in tabulka[problem]:
|
|
|
|
resiteluv_radek.append(tabulka[problem][resitel])
|
|
|
|
else:
|
|
|
|
resiteluv_radek.append(None)
|
|
|
|
hodnoty.append(resiteluv_radek)
|
|
|
|
ctx['radky'] = list(zip(self.resitele, hodnoty))
|
|
|
|
|
|
|
|
return ctx
|
|
|
|
|
|
|
|
class ReseniProblemuView(ListView):
|
|
|
|
model = m.Reseni
|
|
|
|
template_name = 'seminar/odevzdavatko/seznam.html'
|
|
|
|
|
|
|
|
def get_queryset(self):
|
|
|
|
qs = super().get_queryset()
|
|
|
|
resitel_id = self.kwargs['resitel']
|
|
|
|
if resitel_id is None:
|
|
|
|
raise ValueError("Nemám řešitele!")
|
|
|
|
problem_id = self.kwargs['problem']
|
|
|
|
if problem_id is None:
|
|
|
|
raise ValueError("Nemám problém! (To je problém!)")
|
|
|
|
|
|
|
|
resitel = m.Resitel.objects.get(id=resitel_id)
|
|
|
|
problem = m.Problem.objects.get(id=problem_id)
|
|
|
|
qs = qs.filter(
|
|
|
|
problem__in=[problem],
|
|
|
|
resitele__in=[resitel],
|
|
|
|
)
|
|
|
|
return qs
|
|
|
|
|
|
|
|
# Kontext automaticky?
|
|
|
|
|
|
|
|
class DetailReseniView(DetailView):
|
|
|
|
model = m.Reseni
|
|
|
|
template_name = 'seminar/odevzdavatko/detail.html'
|
|
|
|
# To je všechno? Najde se to podle pk...
|
|
|
|
|
|
|
|
# Přehled všech řešení kvůli debugování
|
|
|
|
|
|
|
|
class SeznamReseniView(ListView):
|
|
|
|
model = m.Reseni
|
|
|
|
template_name = 'seminar/odevzdavatko/seznam.html'
|
|
|
|
|
|
|
|
class SeznamAktualnichReseniView(SeznamReseniView):
|
|
|
|
def get_queryset(self):
|
|
|
|
qs = super().get_queryset()
|
|
|
|
akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi...
|
|
|
|
resitele = resi_v_rocniku(akt_rocnik)
|
|
|
|
qs = qs.filter(resitele__in=resitele) # FIXME: Najde řešení i ze starých ročníků, která odevzdal alespoň jeden aktuální řešitel
|
|
|
|
return qs
|