Kateřina Č
4 years ago
19 changed files with 1342 additions and 47 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@ |
|||
,anet,erebus,25.03.2020 22:21,file:///home/anet/.config/libreoffice/4; |
@ -0,0 +1,7 @@ |
|||
from rest_framework.permissions import BasePermission |
|||
|
|||
class AllowWrite(BasePermission): |
|||
|
|||
def has_permission(self, request, view): |
|||
return request.user.has_perm('auth.org') |
|||
|
@ -0,0 +1,97 @@ |
|||
{% extends "seminar/archiv/base_cisla.html" %} |
|||
|
|||
{# {% block content %} |
|||
<div> |
|||
|
|||
<h1> |
|||
{% block nadpis1a %}{% block nadpis1b %} |
|||
Číslo {{ cislo }} |
|||
{% endblock %}{% endblock %} |
|||
</h1> |
|||
|
|||
{% if cislo.pdf %} |
|||
<p><a href='{{ cislo.pdf.url }}'>Číslo v pdf</a> |
|||
{% endif %} |
|||
<p><a href='{{ cislo.rocnik.verejne_url }}'>Ročník {{ cislo.rocnik }}</a> |
|||
|
|||
{% if v_cisle_zadane %} |
|||
<h2>Zadané problémy</h2> |
|||
<ul> |
|||
{% for p in v_cisle_zadane %} |
|||
<li{% if user.is_staff and not cislo.verejne %} class='mam-org-only'{% endif %}> |
|||
{% if user.is_staff or cislo.verejne %} |
|||
<a href='{{ p.verejne_url }}'>{% endif %}{{ p.kod_v_rocniku }} {{ p.nazev }} {{ p.body_v_zavorce }}{% if user.is_staff or cislo.verejne %}</a>{% endif %} |
|||
{% endfor %} |
|||
</ul> |
|||
{% endif %} |
|||
|
|||
{% if resene_problemy %} |
|||
<h2>Řešené problémy</h2> |
|||
<ul> |
|||
{% for p in resene_problemy %} |
|||
<li{% if user.is_staff and not cislo.verejne %} class='mam-org-only'{% endif %}> |
|||
{% if user.is_staff or cislo.verejne %} |
|||
<a href='{{ p.verejne_url }}'>{% endif %}{{ p.kod_v_rocniku }} {{ p.nazev }} {{ p.body_v_zavorce }}{% if user.is_staff or cislo.verejne %}</a>{% endif %} |
|||
{% endfor %} |
|||
</ul> |
|||
{% endif %} |
|||
|
|||
{% if user.is_staff %} |
|||
<div class="mam-org-only"> |
|||
<h2> Orgovské odkazy </h2> |
|||
<ul> |
|||
<li><a href="obalky.pdf">Obálky (PDF)</a></li> |
|||
<li><a href="tituly.tex">Tituly (TeX)</a></li> |
|||
<li><a href="vysledkovka.tex">Výsledkovka (TeX)</a></li> |
|||
<li><a href="obalkovani">Obálkování</a></li> |
|||
</ul> |
|||
</div> |
|||
{% endif %} |
|||
|
|||
{% if cislo.verejna_vysledkovka %} |
|||
<h2>Výsledkovka</h2> |
|||
{% else %} |
|||
{% if user.is_staff %} |
|||
<div class='mam-org-only'> |
|||
<h2>Výsledkovka (neveřejná)</h2> |
|||
{% endif %} |
|||
{% endif %} |
|||
|
|||
{% if cislo.verejna_vysledkovka or user.is_staff %} |
|||
<table class='vysledkovka'> |
|||
<tr class='border-b'> |
|||
<th class='border-r'># |
|||
<th class='border-r'>Jméno #} |
|||
{# problémy by měly být veřejné, když je veřejná výsledkovka #} |
|||
{# {% for p in problemy %} |
|||
<th class='border-r'><a href="{{ p.verejne_url }}">{{ p.kod_v_rocniku }}</a> |
|||
{% endfor %} |
|||
<th class='border-r'>Za číslo</sup> |
|||
<th class='border-r'>Za ročník |
|||
<th class='border-r'>Odjakživa |
|||
{% for rv in radky_vysledkovky %} |
|||
<tr> |
|||
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %} |
|||
<th class='border-r'> |
|||
{% if rv.resitel.titul != "" %} |
|||
{{ rv.resitel.titul }}<sup>MM</sup> |
|||
{% endif %} |
|||
{{ rv.resitel.osoba.plne_jmeno }} |
|||
{% for b in rv.hlavni_problemy_body %} |
|||
<td class='border-r'>{{ b }} |
|||
{% endfor %} |
|||
<td class='border-r'>{{ rv.body_cislo }} |
|||
<td class='border-r'><b>{{ rv.body_rocnik }}</b> |
|||
<td class='border-r'>{{ rv.body_celkem_odjakziva }} |
|||
</tr> |
|||
{% endfor %} |
|||
</table> |
|||
{% endif %} |
|||
|
|||
{% if not cislo.verejna_vysledkovka and user.is_staff %} |
|||
</div> |
|||
{% endif %} |
|||
|
|||
</div> |
|||
{% endblock content %} #} |
|||
|
@ -0,0 +1,19 @@ |
|||
{% extends "seminar/archiv/problem.html" %} |
|||
|
|||
{% block problem %} |
|||
<h1> |
|||
{% block nadpis1a %}{% block nadpis1b %} |
|||
{{ problem.nazev_typu }} {{ problem.kod_v_rocniku }}: {{ problem.nazev }} |
|||
{% endblock %}{% endblock %} |
|||
</h1> |
|||
|
|||
<h2>Zadání</h2> |
|||
{{ problem.text_zadani |safe }} |
|||
{% if problem.text_reseni %} |
|||
<h2>Řešení</h2> |
|||
{{ problem.text_reseni |safe }} |
|||
{% endif %} |
|||
|
|||
{# TODO vysledkovka tematu #} |
|||
|
|||
{% endblock %} |
@ -0,0 +1,23 @@ |
|||
{% extends "seminar/archiv/problem.html" %} |
|||
|
|||
{% block problem %} |
|||
<h1> |
|||
{% block nadpis1a %}{% block nadpis1b %} |
|||
{{ problem.nazev_typu }} {{ problem.kod_v_rocniku }}: {{ problem.nazev }} {{ problem.body_v_zavorce }} |
|||
{% endblock %}{% endblock %} |
|||
</h1> |
|||
{% if problem.cislo_zadani %} |
|||
<p>Zadáno v čísle <a href='{{ problem.cislo_zadani.verejne_url }}'>{{ problem.cislo_zadani.kod }}</a>. |
|||
{% endif %} |
|||
{% if problem.cislo_reseni %} |
|||
<p>Řešeno v čísle <a href='{{ problem.cislo_reseni.verejne_url }}'>{{ problem.cislo_reseni.kod }}</a>. |
|||
{% endif %} |
|||
|
|||
<h2>Zadání</h2> |
|||
{{ problem.text_zadani |safe }} |
|||
{% if problem.text_reseni %} |
|||
<h2>Řešení</h2> |
|||
{{ problem.text_reseni |safe }} |
|||
{% endif %} |
|||
|
|||
{% endblock %} |
@ -0,0 +1,47 @@ |
|||
{% extends "base.html" %} |
|||
|
|||
{% block content %} |
|||
|
|||
<p>Řešené problémy: {{ object.problem.all | join:", " }}</p> |
|||
|
|||
<p>Řešitelé: {{ object.resitele.all | join:", " }}</p> |
|||
|
|||
{# https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.get_FOO_display #} |
|||
<p>Forma: {{ object.get_forma_display }}, doručeno {{ object.cas_doruceni }}</p> |
|||
|
|||
{# Soubory: #} |
|||
<h3>Přílohy:</h3> |
|||
{% if object.prilohy.all %} |
|||
<table> |
|||
<tr><th>Soubor</th><th>Řešitelova poznámka</th><th>Datum</th></tr> |
|||
{% for priloha in object.prilohy.all %} |
|||
<tr> |
|||
<td><a href="{{ priloha.soubor.url }}" download>{{ priloha.split | last }}</a></td> |
|||
<td>{{ priloha.res_poznamka }}</td> |
|||
<td>{{ priloha.vytvoreno }}</td></tr> |
|||
{# TODO: Orgo-poznámka, ideálně jako formulář #} |
|||
{% endfor %} |
|||
</table> |
|||
{% else %} |
|||
<p>Žádné přílohy</p> |
|||
{% endif %} |
|||
|
|||
{# Hodnocení: #} |
|||
{# FIXME: Udělat jako formulář #} |
|||
<h3>Hodnocení:</h3> |
|||
{% if object.hodnoceni_set.all %} |
|||
<table> |
|||
<tr><th>Problém</th><th>Body</th><th>Číslo pro body</th></tr> |
|||
{% for h in object.hodnoceni_set.all %} |
|||
<tr> |
|||
<td>{{ h.problem }}</a></td> |
|||
<td>{{ h.body }}</td> |
|||
<td>{{ h.cislo_body }}</td></tr> |
|||
{% endfor %} |
|||
</table> |
|||
{% else %} |
|||
<p>Ještě nebylo hodnoceno</p> |
|||
{% endif %} |
|||
|
|||
|
|||
{% endblock %} |
@ -0,0 +1,11 @@ |
|||
{% extends "base.html" %} |
|||
|
|||
{% block content %} |
|||
|
|||
<ul> |
|||
{% for obj in object_list %} |
|||
<li><a href="{% url 'odevzdavatko_detail_reseni' pk=obj.id %}">{{ obj }}</a> ({{ obj.get_forma_display }} {{ obj.cas_doruceni }}) |
|||
{% endfor %} |
|||
</ul> |
|||
|
|||
{% endblock %} |
@ -0,0 +1,36 @@ |
|||
{% extends "base.html" %} |
|||
|
|||
{% load utils %} {# Možná by mohlo být někde výš v hierarchii templatů... #} |
|||
|
|||
{% block content %} |
|||
|
|||
<table> |
|||
<tr> |
|||
<td></td> {# Prázdná buňka v levém horním rohu #} |
|||
{% for p in problemy %} |
|||
<th> |
|||
{# TODO: Přehled řešení k problému, odkázaný odsud? #} |
|||
{{ p }} |
|||
</th> |
|||
{% endfor %} |
|||
</tr> |
|||
{% for resitel,hodnoty in radky%} |
|||
<tr> |
|||
<td> |
|||
{# TODO: Chceme mít view i na řešení konkrétního řešitele ke všem problémům? #} |
|||
{{ resitel }} |
|||
</td> |
|||
{% for hodn in hodnoty %} |
|||
<td> |
|||
{% if hodn %} |
|||
<a href="{% url 'odevzdavatko_reseni_resitele_k_problemu' problem=hodn.problem_id resitel=hodn.resitel_id %}"> |
|||
{{ hodn.pocet_reseni }} řeš.<br>{{ hodn.body }} b<br>{{ hodn.posledni_odevzdani|kratke_datum|default_if_none:"Nikdy"|default:"???"}} |
|||
</a> |
|||
{% endif %} |
|||
</td> |
|||
{% endfor %} |
|||
</tr> |
|||
{% endfor %} |
|||
</table> |
|||
|
|||
{% endblock %} |
@ -0,0 +1,27 @@ |
|||
from django import template |
|||
from datetime import datetime, timedelta |
|||
from pytz import timezone |
|||
from mamweb.settings import TIME_ZONE |
|||
import logging |
|||
register = template.Library() |
|||
|
|||
logger = logging.getLogger(__name__) |
|||
|
|||
@register.filter(name='kratke_datum', expects_localtime=True) |
|||
def kratke_datum(dt): |
|||
# None dává None, ne-datum dává False, aby se daly použít filtry typu "default". |
|||
if dt is None: |
|||
return None |
|||
if not isinstance(dt, datetime): |
|||
logger.warning(f"Špatné volání filtru {__name__}: {dt}") |
|||
return False |
|||
naive_now = datetime.now() |
|||
tz = timezone(TIME_ZONE) |
|||
now = tz.localize(naive_now) |
|||
delta = now - dt |
|||
if delta <= timedelta(days=1): |
|||
return dt.strftime("%k:%M") |
|||
if delta <= timedelta(days=365): # Timedelta neumí vyjádřit 1 rok |
|||
return dt.strftime("%d. %m.") |
|||
return dt.strftime("%d. %m. %Y") |
|||
|
@ -1,3 +1,4 @@ |
|||
from .views_all import * |
|||
from .autocomplete import * |
|||
from .views_rest import * |
|||
from .odevzdavatko import * |
|||
|
@ -0,0 +1,129 @@ |
|||
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 |
Loading…
Reference in new issue