Compare commits

..

No commits in common. "e889620ddb0ef150c982b7973118c053ebf0b586" and "36124fa69ee2d933f308d9df3aff88a7f6ab1631" have entirely different histories.

5 changed files with 29 additions and 56 deletions

View file

@ -1245,7 +1245,6 @@ div.gdpr {
.dosla_reseni tr th, .dosla_reseni tr td { .dosla_reseni tr th, .dosla_reseni tr td {
padding: 1px 10px 1px 10px; padding: 1px 10px 1px 10px;
border-collapse: collapse; border-collapse: collapse;
min-width: 8em; /*Nastřeleno, aby se řádky s řešeními nezalamovaly. Teoreticky není potřeba pro th, ale whatever.*/
} }
.dosla_reseni tr:nth-child(even) { .dosla_reseni tr:nth-child(even) {

View file

@ -238,7 +238,6 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form):
'reseni_od': terminy[-2] if rocnik is None else terminy[0], 'reseni_od': terminy[-2] if rocnik is None else terminy[0],
'reseni_do': terminy[-1], 'reseni_do': terminy[-1],
'neobodovane': False, 'neobodovane': False,
'barvicky': True,
} }
return initial return initial
@ -263,4 +262,3 @@ class OdevzdavatkoTabulkaFiltrForm(forms.Form):
reseni_od = forms.DateField(input_formats=[DATE_FORMAT]) reseni_od = forms.DateField(input_formats=[DATE_FORMAT])
reseni_do = forms.DateField(input_formats=[DATE_FORMAT]) reseni_do = forms.DateField(input_formats=[DATE_FORMAT])
neobodovane = forms.BooleanField(required=False) neobodovane = forms.BooleanField(required=False)
barvicky = forms.BooleanField(required=False)

View file

@ -1,7 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load utils %} {# Možná by mohlo být někde výš v hierarchii templatů... #} {% load utils %} {# Možná by mohlo být někde výš v hierarchii templatů... #}
{% load barvy_reseni %}
{% block content %} {% block content %}
@ -12,7 +11,6 @@
Od data (vyjma): {{ filtr.reseni_od }} Od data (vyjma): {{ filtr.reseni_od }}
Do data (včetně): {{ filtr.reseni_do }} Do data (včetně): {{ filtr.reseni_do }}
<span title="Jen neobodovaná řešení">🔨?</span> {{ filtr.neobodovane }} <span title="Jen neobodovaná řešení">🔨?</span> {{ filtr.neobodovane }}
<span title="Obarvit shodná řešení shodně">🎨?</span> {{ filtr.barvicky }}
<input type=submit value="→"> <input type=submit value="→">
</form> </form>
@ -38,15 +36,12 @@ Do data (včetně): {{ filtr.reseni_do }}
{# TODO: Chceme mít view i na řešení konkrétního řešitele ke všem problémům? #} {# TODO: Chceme mít view i na řešení konkrétního řešitele ke všem problémům? #}
{{ resitel }} {{ resitel }}
</td> </td>
{% for soucet,bunka in hodnoty %} {% for hodn in hodnoty %}
<td> <td>
{% for reseni,hodnoceni in bunka %} {% if hodn %}
<a {% if barvicky %} style="color: {{reseni|barva_reseni}};" {% endif %} href="{% url 'odevzdavatko_detail_reseni' pk=reseni.id %}"> <a href="{% url 'odevzdavatko_reseni_resitele_k_problemu' problem=hodn.problem_id resitel=hodn.resitel_id %}">
{{reseni.cas_doruceni | date:"j. n."}} ({{ hodnoceni.body|default_if_none:"🔨"}} b) {{ hodn.pocet_reseni }} řeš.<br>{{ hodn.body }} b<br>{{ hodn.posledni_odevzdani|kratke_datum|default_if_none:"Nikdy"|default:"???"}}
</a><br> </a>
{% endfor %}
{% if bunka|length > 1 %}
<b>Σ: {{soucet}} b</b>
{% endif %} {% endif %}
</td> </td>
{% endfor %} {% endfor %}

View file

@ -1,15 +0,0 @@
from django import template
register = template.Library()
from functools import cache
import seminar.models as m
@register.filter
@cache
def barva_reseni(r: m.Reseni):
"""Vrátí nějakou barvu pro daný problém, ve tvaru '#RRGGBB'
Efektivně hešujeme do barev."""
#TODO: ne všechny barvy jsou dobře rozlišitelné a vidět…
return f'#{hash(str(r.id)) & 0xffffff:06x}'

View file

@ -13,7 +13,6 @@ from django.db.models import Q
from dataclasses import dataclass from dataclasses import dataclass
import datetime import datetime
from decimal import Decimal
from itertools import groupby from itertools import groupby
import logging import logging
@ -38,6 +37,14 @@ logger = logging.getLogger(__name__)
# Taky se může hodit: # Taky se může hodit:
# - Tabulka všech řešitelů x všech problémů? # - 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): class TabulkaOdevzdanychReseniView(ListView):
template_name = 'odevzdavatko/tabulka.html' template_name = 'odevzdavatko/tabulka.html'
model = m.Hodnoceni model = m.Hodnoceni
@ -63,7 +70,6 @@ class TabulkaOdevzdanychReseniView(ListView):
reseni_od = fcd["reseni_od"] reseni_od = fcd["reseni_od"]
reseni_do = fcd["reseni_do"] reseni_do = fcd["reseni_do"]
jen_neobodovane = fcd["neobodovane"] jen_neobodovane = fcd["neobodovane"]
self.barvicky = fcd["barvicky"]
else: else:
initial = FiltrForm.gen_initial(self.aktualni_rocnik) initial = FiltrForm.gen_initial(self.aktualni_rocnik)
resitele = initial['resitele'] resitele = initial['resitele']
@ -71,7 +77,6 @@ class TabulkaOdevzdanychReseniView(ListView):
reseni_od = initial['reseni_od'][0] reseni_od = initial['reseni_od'][0]
reseni_do = initial['reseni_do'][0] reseni_do = initial['reseni_do'][0]
jen_neobodovane = initial["neobodovane"] jen_neobodovane = initial["neobodovane"]
self.barvicky = initial["barvicky"]
# Chceme jen letošní problémy # Chceme jen letošní problémy
@ -115,45 +120,42 @@ class TabulkaOdevzdanychReseniView(ListView):
return qs return qs
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
# TODO: refactor asi. Přepisoval jsem to jen syntakticky, nejspíš půlka kódu přestala dávat smysl…
# self.resitele, self.reseni a self.problemy jsou již nastavené # self.resitele, self.reseni a self.problemy jsou již nastavené
ctx = super().get_context_data(*args, **kwargs) ctx = super().get_context_data(*args, **kwargs)
ctx['problemy'] = self.problemy ctx['problemy'] = self.problemy
ctx['resitele'] = self.resitele ctx['resitele'] = self.resitele
tabulka: dict[m.Problem, dict[m.Resitel, list[tuple[m.Reseni, m.Hodnoceni]]]] = dict() tabulka = dict()
soucty: dict[m.Problem, dict[m.Resitel, Decimal]] = dict()
def pridej_reseni(resitel, hodnoceni): def pridej_reseni(problem, resitel, body, cas):
problem = hodnoceni.problem
body = hodnoceni.body
cas = hodnoceni.reseni.cas_doruceni
reseni = hodnoceni.reseni
if problem not in tabulka: if problem not in tabulka:
tabulka[problem] = dict() tabulka[problem] = dict()
soucty[problem] = dict()
if resitel not in tabulka[problem]: if resitel not in tabulka[problem]:
tabulka[problem][resitel] = [(reseni, hodnoceni)] tabulka[problem][resitel] = SouhrnReseni(pocet_reseni=1, posledni_odevzdani=cas, body=body)
soucty[problem][resitel] = hodnoceni.body or 0 # Neobodované neřešíme
else: else:
tabulka[problem][resitel].append((reseni, hodnoceni)) tabulka[problem][resitel].posledni_odevzdani = max(tabulka[problem][resitel].posledni_odevzdani, cas)
soucty[problem][resitel] += hodnoceni.body or 0 # Neobodované neřešíme # Zvětšení počtu bodů o aktuální počet, pokud se tam někde nevyskytuje None pak je součet taky None ("Pozor, nezadané body")
tabulka[problem][resitel].body = tabulka[problem][resitel].body + body if body is not None and tabulka[problem][resitel].body is not None else None
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 hodnoceni in self.get_queryset():
for resitel in hodnoceni.reseni.resitele.all(): for resitel in hodnoceni.reseni.resitele.all():
pridej_reseni(resitel, hodnoceni) pridej_reseni(hodnoceni.problem, resitel, hodnoceni.body, hodnoceni.reseni.cas_doruceni)
hodnoty: list[list[tuple[Decimal,list[tuple[m.Reseni, m.Hodnoceni]]]]] = [] # Seznam řádků výsledné tabulky podle self.resitele, v každém řádku buňky v pořadí podle self.problemy + jejich součty, v každé buňce seznam řešení k danému řešiteli a problému. hodnoty = []
resitele_do_tabulky: list[m.Resitel] = [] resitele_do_tabulky = []
for resitel in self.resitele: for resitel in self.resitele:
dostal_body = False dostal_body = False
resiteluv_radek: list[tuple[Decimal,list[tuple[m.Reseni, m.Hodnoceni]]]] = [] # podle pořadí v self.problemy resiteluv_radek = []
for problem in self.problemy: for problem in self.problemy:
if problem in tabulka and resitel in tabulka[problem]: if problem in tabulka and resitel in tabulka[problem]:
resiteluv_radek.append((soucty[problem][resitel], tabulka[problem][resitel])) resiteluv_radek.append(tabulka[problem][resitel])
dostal_body = True dostal_body = True
else: else:
resiteluv_radek.append((Decimal(0),[])) resiteluv_radek.append(None)
if self.chteni_resitele != FiltrForm.RESITELE_RELEVANTNI or dostal_body: if self.chteni_resitele != FiltrForm.RESITELE_RELEVANTNI or dostal_body:
hodnoty.append(resiteluv_radek) hodnoty.append(resiteluv_radek)
resitele_do_tabulky.append(resitel) resitele_do_tabulky.append(resitel)
@ -163,7 +165,6 @@ class TabulkaOdevzdanychReseniView(ListView):
ctx['form'] = ctx['filtr'] ctx['form'] = ctx['filtr']
# Pro maximum v přesměrovátku ročníků # Pro maximum v přesměrovátku ročníků
ctx['aktualni_rocnik'] = m.Nastaveni.get_solo().aktualni_rocnik ctx['aktualni_rocnik'] = m.Nastaveni.get_solo().aktualni_rocnik
ctx['barvicky'] = self.barvicky
if 'rocnik' in self.kwargs: if 'rocnik' in self.kwargs:
ctx['rocnik'] = self.kwargs['rocnik'] ctx['rocnik'] = self.kwargs['rocnik']
else: else:
@ -173,11 +174,6 @@ class TabulkaOdevzdanychReseniView(ListView):
# 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?
class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixin, View): class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixin, View):
"""Rozskok mezi více řešeními téhož problému od téhož řešitele.
Asi bude zastaralý v okamžiku, kdy se tenhle komentář nasadí na produkci :-)
V případě, že takové řešení existuje jen jedno, tak na něj přesměruje."""
model = m.Reseni model = m.Reseni
template_name = 'odevzdavatko/seznam.html' template_name = 'odevzdavatko/seznam.html'