Browse Source

Merge branch 'odevzdavatko' into data_migrations

middleware_test
Pavel "LEdoian" Turinsky 4 years ago
parent
commit
eb8ba371f5
  1. 74
      seminar/forms.py
  2. 8
      seminar/templates/seminar/odevzdavatko/tabulka.html
  3. 54
      seminar/views/odevzdavatko.py

74
seminar/forms.py

@ -316,3 +316,77 @@ class JednoHodnoceniForm(forms.ModelForm):
OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm, OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm,
extra = 0, extra = 0,
) )
# FIXME: Ideálně by mělo být součástí třídy níž, ale neumím to udělat
DATE_FORMAT = '%Y-%m-%d'
class OdevzdavatkoTabulkaFiltrForm(forms.Form):
"""Form pro filtrování přehledové odevzdávátkové tabulky
Inspirováno https://kam.mff.cuni.cz/mffzoom/"""
# Věci definované níž se importují i ve views pro odevzdávátko (Inspirováno https://docs.djangoproject.com/en/3.1/ref/models/fields/#field-choices)
RESITELE_RELEVANTNI = 'relevantni'
RESITELE_LETOSNI = 'letosni'
RESITELE_CHOICES = [
(RESITELE_RELEVANTNI, 'Relevantní řešitelé'), # I.e. nezobrazovat prázdné řádky tabulky
(RESITELE_LETOSNI, 'Všichni letošní'),
# Možná: všechny vč. historických?
]
PROBLEMY_MOJE = 'moje'
PROBLEMY_LETOSNI = 'letosni'
PROBLEMY_CHOICES = [
(PROBLEMY_MOJE, 'Moje problémy'), # Letošní problémy, které mají v sobě nebo v nadproblémech přiřazeného daného orga
(PROBLEMY_LETOSNI, 'Všechny letošní'),
# TODO: *hlavní problémy, možná všechny...
# XXX: Chtělo by to i "aktuálně zadané...
]
# TODO: Typy problémů (problémy, úlohy, ostatní, všechny)? Jen některá řešení (obodovaná/neobodovaná, víc řešitelů, ...)?
def gen_terminy():
import datetime
from time import strftime
aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik
aktualni_cislo = m.Nastaveni.get_solo().aktualni_cislo
result = []
for cislo in m.Cislo.objects.filter(
rocnik=aktualni_rocnik,
poradi__lte=aktualni_cislo.poradi,
).reverse(): # Standardně se řadí od nejnovějšího čísla
# Předem je mi líto kohokoliv, kdo tyhle řádky bude číst...
if cislo.datum_vydani is not None and cislo.datum_vydani <= datetime.date.today():
result.append((
strftime(DATE_FORMAT, cislo.datum_vydani.timetuple()),
f"Vydání {cislo.poradi}. čísla"))
if cislo.datum_preddeadline is not None and cislo.datum_preddeadline <= datetime.date.today():
result.append((
strftime(DATE_FORMAT, cislo.datum_preddeadline.timetuple()),
f"Předdeadline {cislo.poradi}. čísla"))
if cislo.datum_deadline_soustredeni is not None and cislo.datum_deadline_soustredeni <= datetime.date.today():
result.append((
strftime(DATE_FORMAT, cislo.datum_deadline_soustredeni.timetuple()),
f"Sous. deadline {cislo.poradi}. čísla"))
if cislo.datum_deadline is not None and cislo.datum_deadline <= datetime.date.today():
result.append((
strftime(DATE_FORMAT, cislo.datum_deadline.timetuple()),
f"Finální deadline {cislo.poradi}. čísla"))
result.append((
strftime(DATE_FORMAT, datetime.date.today().timetuple()), f"Dnes"))
return result
# NOTE: Initial definuji pro jednotlivé fieldy, aby to bylo tady a nebylo potřeba to řešit ve views...
resitele = forms.ChoiceField(choices=RESITELE_CHOICES, initial=RESITELE_RELEVANTNI)
problemy = forms.ChoiceField(choices=PROBLEMY_CHOICES, initial=PROBLEMY_MOJE)
# choices jako parametr Select widgetu neumí brát callable, jen iterable, takže si pro jednoduchost můžu rovnou uložit výsledek sem...
terminy = gen_terminy()
reseni_od = forms.DateField(input_formats=[DATE_FORMAT], widget=forms.Select(choices=terminy), initial=terminy[-2])
reseni_do = forms.DateField(input_formats=[DATE_FORMAT], widget=forms.Select(choices=terminy), initial=terminy[-1])

8
seminar/templates/seminar/odevzdavatko/tabulka.html

@ -4,6 +4,14 @@
{% block content %} {% block content %}
<form method=get action=.>
{{ filtr.resitele }}
{{ filtr.problemy }}
Od: {{ filtr.reseni_od }}
Do: {{ filtr.reseni_do }}
<input type=submit value="→">
</form>
<table> <table>
<tr> <tr>
<td></td> {# Prázdná buňka v levém horním rohu #} <td></td> {# Prázdná buňka v levém horním rohu #}

54
seminar/views/odevzdavatko.py

@ -12,6 +12,7 @@ import logging
import seminar.models as m import seminar.models as m
import seminar.forms as f import seminar.forms as f
from seminar.forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm
from seminar.utils import aktivniResitele, resi_v_rocniku from seminar.utils import aktivniResitele, resi_v_rocniku
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -41,28 +42,55 @@ class TabulkaOdevzdanychReseniView(ListView):
def inicializuj_osy_tabulky(self): def inicializuj_osy_tabulky(self):
"""Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů""" """Vyrobí prvotní querysety pro sloupce a řádky, tj. seznam všech řešitelů a problémů"""
# FIXME: jméno metody není vypovídající...
# 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 # 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, ... # TODO: Prefetches, Select related, ...
self.resitele = m.Resitel.objects.all() self.resitele = m.Resitel.objects.all()
self.problemy = m.Problem.objects.all() self.problemy = m.Problem.objects.all()
self.reseni = m.Reseni.objects.all()
form = FiltrForm(self.request.GET)
if form.is_valid():
fcd = form.cleaned_data
resitele = fcd["resitele"]
problemy = fcd["problemy"]
reseni_od = fcd["reseni_od"]
reseni_do = fcd["reseni_do"]
else:
resitele = FiltrForm.get_initial_for_field(FormFiltr.resitele, "resitele")
problemy = FiltrForm.get_initial_for_field(FormFiltr.problemy, "problemy")
resitele_od = FiltrForm.get_initial_for_field(FormFiltr.resitele_od, "resitele_od")
resitele_do = FiltrForm.get_initial_for_field(FormFiltr.resitele_do, "resitele_do")
# Filtrujeme!
aktualni_rocnik = m.Nastaveni.get_solo().aktualni_rocnik # .get_solo() vrátí tu jedinou instanci
if resitele == FiltrForm.RESITELE_RELEVANTNI:
logger.warning("Někdo chtěl v tabulce jen relevantní řešitele a měl smůlu :-(")
resitele = FiltrForm.RESITELE_LETOSNI # Fall-through
elif resitele == FiltrForm.RESITELE_LETOSNI:
self.resitele = resi_v_rocniku(aktualni_rocnik)
if problemy == FiltrForm.PROBLEMY_MOJE:
org = m.Organizator.objects.get(osoba__user=self.request.user)
from django.db.models import Q
self.problemy = self.problemy.filter(Q(autor=org)|Q(garant=org)|Q(opravovatele=org), stav=m.Problem.STAV_ZADANY)
elif problemy == FiltrForm.PROBLEMY_LETOSNI:
self.problemy = self.problemy.filter(stav=m.Problem.STAV_ZADANY)
#self.problemy = list(filter(lambda problem: problem.rocnik() == aktualni_rocnik, self.problemy)) # DB HOG? # FIXME: některé problémy nemají ročník....
# 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.problemy = self.problemy.non_polymorphic()
self.reseni = self.reseni.filter(cas_doruceni__date__gte=reseni_od, cas_doruceni__date__lte=reseni_do)
def get_queryset(self): def get_queryset(self):
self.inicializuj_osy_tabulky() 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.problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic()
qs = super().get_queryset() qs = super().get_queryset()
qs = qs.filter(problem__in=self.problemy).select_related('reseni', 'problem').prefetch_related('reseni__resitele__osoba') qs = qs.filter(problem__in=self.problemy, reseni__in=self.reseni, reseni__resitele__in=self.resitele).select_related('reseni', 'problem').prefetch_related('reseni__resitele__osoba')
return qs return qs
def get_context_data(self, *args, **kwargs): 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.resitele, self.reseni a self.problemy jsou již 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.problemy = m.Problem.objects.filter(stav=m.Problem.STAV_ZADANY).non_polymorphic()
ctx = super().get_context_data(*args, **kwargs) ctx = super().get_context_data(*args, **kwargs)
ctx['problemy'] = self.problemy ctx['problemy'] = self.problemy
@ -100,6 +128,10 @@ class TabulkaOdevzdanychReseniView(ListView):
hodnoty.append(resiteluv_radek) hodnoty.append(resiteluv_radek)
ctx['radky'] = list(zip(self.resitele, hodnoty)) ctx['radky'] = list(zip(self.resitele, hodnoty))
ctx['filtr'] = FiltrForm(initial=self.request.GET)
# Pro použití hacku na automatické {{form.media}} v template:
ctx['form'] = ctx['filtr']
return ctx return ctx
# Velmi silně inspirováno zdrojáky, FIXME: Nedá se to udělat smysluplněji? # Velmi silně inspirováno zdrojáky, FIXME: Nedá se to udělat smysluplněji?

Loading…
Cancel
Save