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 akt_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci, asi... resitele = resi_v_rocniku(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. zadane_problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic() def get_queryset(self): 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): 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