Web M&M
https://mam.matfyz.cz
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
466 lines
18 KiB
466 lines
18 KiB
# coding:utf-8
|
|
|
|
from django.shortcuts import get_object_or_404, render
|
|
from django.http import HttpResponseRedirect
|
|
from django.http import HttpResponse
|
|
from django.core.urlresolvers import reverse
|
|
from django.views import generic
|
|
from django.utils.translation import ugettext as _
|
|
from django.http import Http404
|
|
from django.db.models import Q
|
|
|
|
from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici
|
|
from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
|
|
from . import utils
|
|
|
|
from datetime import timedelta, date, datetime
|
|
from itertools import groupby
|
|
import tempfile
|
|
import subprocess
|
|
import shutil
|
|
import os
|
|
from django.conf import settings
|
|
import unicodedata
|
|
|
|
def verejna_temata(rocnik):
|
|
"""Vrací queryset zveřejněných témat v daném ročníku.
|
|
"""
|
|
return Problem.objects.filter(typ=Problem.TYP_TEMA, cislo_zadani__rocnik=rocnik, cislo_zadani__verejne_db=True).order_by('kod')
|
|
|
|
|
|
def AktualniZadaniView(request):
|
|
nastaveni = get_object_or_404(Nastaveni)
|
|
problemy = Problem.objects.filter(cislo_zadani=nastaveni.aktualni_cislo).filter(stav = 'zadany')
|
|
ulohy = problemy.filter(typ = 'uloha').order_by('kod')
|
|
serialy = problemy.filter(typ = 'serial').order_by('kod')
|
|
jednorazove_problemy = [ulohy, serialy]
|
|
return render(request, 'seminar/zadani/AktualniZadani.html',
|
|
{'nastaveni': nastaveni,
|
|
'jednorazove_problemy': jednorazove_problemy,
|
|
'temata': verejna_temata(nastaveni.aktualni_rocnik),
|
|
},
|
|
)
|
|
|
|
def ZadaniTemataView(request):
|
|
nastaveni = get_object_or_404(Nastaveni)
|
|
return render(request, 'seminar/zadani/Temata.html',
|
|
{
|
|
'temata': verejna_temata(nastaveni.aktualni_rocnik)
|
|
}
|
|
)
|
|
|
|
def ZadaniAktualniVysledkovkaView(request):
|
|
nastaveni = get_object_or_404(Nastaveni)
|
|
vysledkovka = vysledkovka_rocniku(nastaveni.aktualni_rocnik)
|
|
vysledkovka_s_neverejnymi = vysledkovka_rocniku(nastaveni.aktualni_rocnik, jen_verejne=False)
|
|
return render(request, 'seminar/zadani/AktualniVysledkovka.html',
|
|
{
|
|
'nastaveni': nastaveni,
|
|
'vysledkovka': vysledkovka,
|
|
'vysledkovka_s_neverejnymi': vysledkovka_s_neverejnymi,
|
|
}
|
|
)
|
|
|
|
|
|
### Titulni strana
|
|
|
|
class TitulniStranaView(generic.ListView):
|
|
model = Novinky
|
|
template_name='seminar/titulnistrana.html'
|
|
queryset = Novinky.objects.order_by('-datum')[:5]
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(TitulniStranaView, self).get_context_data(**kwargs)
|
|
nastaveni = get_object_or_404(Nastaveni)
|
|
cas_deadline = nastaveni.aktualni_cislo.datum_deadline
|
|
try:
|
|
rozdil_casu = datetime.combine(cas_deadline, datetime.max.time()) \
|
|
- datetime.now()
|
|
context['cas_do_konce_dni'] = rozdil_casu.days
|
|
context['cas_do_konce_hodin'] = rozdil_casu.seconds / 3600
|
|
context['cas_do_konce_minut'] = (rozdil_casu.seconds / 60) % 60
|
|
context['cas_do_konce_sekund'] = rozdil_casu.seconds % 60
|
|
context['dead'] = datetime.combine(cas_deadline, datetime.max.time())
|
|
context['ted'] = datetime.now()
|
|
except:
|
|
context['dead'] = None
|
|
return context
|
|
|
|
class StareNovinkyView(generic.ListView):
|
|
model = Novinky
|
|
template_name = 'seminar/stare_novinky.html'
|
|
queryset = Novinky.objects.filter(zverejneno=True).order_by('-datum')
|
|
|
|
### Co je M&M
|
|
|
|
## Organizatori
|
|
|
|
class CojemamOrganizatoriView(generic.ListView):
|
|
model = Organizator
|
|
template_name='seminar/cojemam/organizatori.html'
|
|
queryset = Organizator.objects.exclude(organizuje_do_roku__isnull=False, organizuje_do_roku__lt=date.today().year).order_by('user__first_name')
|
|
def get_context_data(self, **kwargs):
|
|
context = super(CojemamOrganizatoriView, self).get_context_data(**kwargs)
|
|
context['aktivni'] = True
|
|
return context
|
|
|
|
class CojemamOrganizatoriStariView(generic.ListView):
|
|
model = Organizator
|
|
template_name='seminar/cojemam/organizatori.html'
|
|
queryset = Organizator.objects.filter(organizuje_do_roku__isnull=False, organizuje_do_roku__lt=date.today().year).order_by('-organizuje_do_roku')
|
|
|
|
### Archiv
|
|
|
|
class CislaView(generic.ListView):
|
|
model = Rocnik
|
|
template_name='seminar/archiv/cisla.html'
|
|
|
|
|
|
def sloupec_s_poradim(vysledky):
|
|
# počet řešitelů ve výsledkovce nad aktuálním
|
|
lepsich_resitelu = 0
|
|
|
|
poradi_l = []
|
|
# projdeme skupiny řešitelů se stejným počtem bodů
|
|
for skupina in (list(x) for _, x in groupby(vysledky, lambda x: x.body)):
|
|
|
|
# připravíme si obsahy buněk ve sloupci pořadí pro skupinu
|
|
if len(skupina) == 1:
|
|
poradi_l += ["{}.".format(lepsich_resitelu + 1)]
|
|
# je-li účastníků se stejným počtem bodů víc, pořadí (rozsah X.-Y.) je jen u prvního
|
|
else:
|
|
poradi_l += [u"{}. – {}.".format(lepsich_resitelu + 1, lepsich_resitelu + len(skupina))] + [""] * (len(skupina)-1)
|
|
lepsich_resitelu += len(skupina)
|
|
#pomlcka je opravdu pomlcka v unicode!!dulezite pro vysledkovku v TeXu
|
|
|
|
return poradi_l
|
|
|
|
|
|
def vysledkovka_rocniku(rocnik, jen_verejne=True):
|
|
"""Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve
|
|
formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html"
|
|
"""
|
|
|
|
#vyberu vsechny vysledky z rocniku
|
|
cisla_v_rocniku = VysledkyKCisluZaRocnik.objects.filter(cislo__rocnik=rocnik).order_by('cislo')
|
|
if jen_verejne:
|
|
cisla_v_rocniku = cisla_v_rocniku.filter(cislo__verejna_vysledkovka=True)
|
|
|
|
#pokud žádné nejsou, výsledkovka se nezobrazí
|
|
if not cisla_v_rocniku:
|
|
return None
|
|
|
|
#vybere vsechny vysledky z posledniho (verejneho) cisla a setridi sestupne dle bodu
|
|
vysledky = list(cisla_v_rocniku.filter(cislo = cisla_v_rocniku[0].cislo).order_by('-body', 'resitel__prijmeni', 'resitel__jmeno').select_related('resitel'))
|
|
|
|
class Vysledkovka:
|
|
def __init__(self):
|
|
self.radky = []
|
|
self.cisla = []
|
|
|
|
vysledkovka = Vysledkovka()
|
|
vysledkovka.cisla = (rocnik.verejne_vysledkovky_cisla() if jen_verejne else rocnik.cisla.all().order_by('cislo'))
|
|
|
|
# doplníme některé údaje do řádků výsledkovky pro řešitele ve skupině
|
|
for poradi, v in zip(sloupec_s_poradim(vysledky), vysledky):
|
|
v.poradi = poradi
|
|
v.resitel.rocnik = v.resitel.rocnik(rocnik)
|
|
|
|
verejne_vysl_odjakziva = VysledkyKCisluOdjakziva.objects.filter(cislo__rocnik=rocnik, cislo=cisla_v_rocniku[0].cislo)
|
|
if jen_verejne:
|
|
verejne_vysl_odjakziva = verejne_vysl_odjakziva.filter(cislo__verejna_vysledkovka=True)
|
|
|
|
v.body_odjakziva = verejne_vysl_odjakziva.filter(resitel = v.resitel)[0].body
|
|
v.titul = v.resitel.get_titul(v.body_odjakziva)
|
|
v.body_rocnik = v.body
|
|
v.body_cisla = []
|
|
|
|
#pokud pro dany rok a cislo nema resitel vysledky, ma defaultne 0
|
|
for cis in vysledkovka.cisla:
|
|
if not jen_verejne or cis.verejna_vysledkovka:
|
|
#seznam vysledku se spravnym rocnikem a cislem pro resitele
|
|
#zobrazim jen je-li vysledkovka verejna
|
|
body_za_cislo = VysledkyZaCislo.objects.filter(cislo__rocnik=rocnik).filter(cislo = cis).filter(resitel = v.resitel)
|
|
if body_za_cislo:
|
|
#neprazdne vysledky by mely obsahovat prave jeden vysledek
|
|
v.body_cisla.append(body_za_cislo[0].body)
|
|
else:
|
|
#resitel nema za cislo body
|
|
v.body_cisla.append(0)
|
|
|
|
vysledkovka.radky.append(v)
|
|
|
|
return vysledkovka
|
|
|
|
|
|
class RocnikView(generic.DetailView):
|
|
model = Rocnik
|
|
template_name = 'seminar/archiv/rocnik.html'
|
|
|
|
# Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik)
|
|
def get_object(self, queryset=None):
|
|
if queryset is None:
|
|
queryset = self.get_queryset()
|
|
rocnik_arg = self.kwargs.get('rocnik')
|
|
queryset = queryset.filter(rocnik=rocnik_arg)
|
|
|
|
try:
|
|
obj = queryset.get()
|
|
except queryset.model.DoesNotExist:
|
|
raise Http404(_("No %(verbose_name)s found matching the query") %
|
|
{'verbose_name': queryset.model._meta.verbose_name})
|
|
return obj
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(RocnikView, self).get_context_data(**kwargs)
|
|
|
|
context['vysledkovka'] = vysledkovka_rocniku(context["rocnik"])
|
|
context['vysledkovka_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False)
|
|
context['temata_v_rocniku'] = verejna_temata(context["rocnik"])
|
|
|
|
return context
|
|
|
|
|
|
class ProblemView(generic.DetailView):
|
|
model = Problem
|
|
|
|
def _je_clanek(self, problem):
|
|
return problem.typ in [Problem.TYP_ORG_CLANEK, Problem.TYP_RES_CLANEK]
|
|
|
|
def get_template_names(self, **kwargs):
|
|
context = super(ProblemView, self).get_context_data(**kwargs)
|
|
return ['seminar/archiv/problem_' + ('clanek.html' if self._je_clanek(context['problem']) else 'uloha_tema.html')]
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(ProblemView, self).get_context_data(**kwargs)
|
|
if context['problem'].typ == Problem.TYP_RES_CLANEK:
|
|
context['reseni'] = Reseni.objects.filter(problem=context['problem']).select_related('resitel').order_by('resitel__prijmeni')
|
|
return context
|
|
|
|
|
|
class RadekVysledkovky(object):
|
|
pass
|
|
|
|
class CisloView(generic.DetailView):
|
|
model = Cislo
|
|
template_name = 'seminar/archiv/cislo.html'
|
|
|
|
# Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik)
|
|
def get_object(self, queryset=None):
|
|
if queryset is None:
|
|
queryset = self.get_queryset()
|
|
rocnik_arg = self.kwargs.get('rocnik')
|
|
cislo_arg = self.kwargs.get('cislo')
|
|
queryset = queryset.filter(rocnik__rocnik=rocnik_arg, cislo=cislo_arg)
|
|
|
|
try:
|
|
obj = queryset.get()
|
|
except queryset.model.DoesNotExist:
|
|
raise Http404(_("No %(verbose_name)s found matching the query") %
|
|
{'verbose_name': queryset.model._meta.verbose_name})
|
|
return obj
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(CisloView, self).get_context_data(**kwargs)
|
|
|
|
vysledky = VysledkyKCisluZaRocnik.objects.filter(cislo = context['cislo']).order_by('-body', 'resitel__prijmeni', 'resitel__jmeno')
|
|
reseni = Reseni.objects.filter(cislo_body = context['cislo']).select_related("resitel")
|
|
|
|
# typy úloh, které se mají zobrazovat u čísla, tj. těch, které byly v čísle skutečně zadány
|
|
typy_skutecne_zadanych = [Problem.TYP_ULOHA, Problem.TYP_SERIAL, Problem.TYP_ORG_CLANEK]
|
|
v_cisle_zadane = Problem.objects.filter(cislo_zadani=context['cislo']).filter(typ__in=typy_skutecne_zadanych).order_by('kod')
|
|
|
|
resene_problemy = Problem.objects.filter(cislo_reseni=context['cislo']).filter(typ__in=typy_skutecne_zadanych).order_by('cislo_zadani__cislo', 'kod')
|
|
|
|
poradi_typu = {
|
|
Problem.TYP_ULOHA: 1,
|
|
Problem.TYP_SERIAL: 2,
|
|
Problem.TYP_ORG_CLANEK: 3,
|
|
Problem.TYP_TEMA: 4,
|
|
Problem.TYP_RES_CLANEK: 5
|
|
}
|
|
problemy = sorted(set(r.problem for r in reseni), key=lambda x:(poradi_typu[x.typ], x.kod_v_rocniku()))
|
|
#setridi problemy podle typu a poradi zadani
|
|
problem_index = {}
|
|
for i in range(len(problemy)):
|
|
problem_index[problemy[i].id] = i
|
|
#umoznuje zjistit index podle id problemu
|
|
|
|
vysledky_resitele = {}
|
|
vysledkovka = []
|
|
|
|
# doplníme některé údaje do řádků výsledkovky pro řešitele ve skupině
|
|
for poradi, v in zip(sloupec_s_poradim(vysledky), vysledky):
|
|
v.poradi = poradi
|
|
v.body_celkem_rocnik = v.body
|
|
v.body_celkem_odjakziva = VysledkyKCisluOdjakziva.objects.get(resitel=v.resitel, cislo=context['cislo']).body
|
|
|
|
# je tady '', aby se nezobrazovala 0, pokud se řešitel o řešení úlohy ani nepokusil
|
|
v.body_ulohy = [''] * len(problemy)
|
|
|
|
v.titul = v.resitel.get_titul(v.body_celkem_odjakziva)
|
|
|
|
body_cislo_q = VysledkyZaCislo.objects.filter(resitel=v.resitel, cislo=context['cislo'])
|
|
v.body_cislo = body_cislo_q[0].body if body_cislo_q else 0
|
|
|
|
vysledkovka.append(v)
|
|
|
|
# připravíme si odkaz na řádek, abychom do něj mohli doplnit body za jednotlivé úlohy
|
|
vysledky_resitele[v.resitel.id] = v
|
|
|
|
# za každé řešení doplníme k příslušnému řešiteli a úloze body
|
|
for r in reseni:
|
|
vysledky_resitele[r.resitel.id].body_ulohy[problem_index[r.problem.id]] = r.body
|
|
|
|
context['vysledkovka'] = vysledkovka
|
|
context['problemy'] = problemy
|
|
context['v_cisle_zadane'] = v_cisle_zadane
|
|
context['resene_problemy'] = resene_problemy
|
|
return context
|
|
|
|
class ArchivTemataView(generic.ListView):
|
|
model = Problem
|
|
template_name = 'seminar/archiv/temata.html'
|
|
queryset = Problem.objects.filter(typ=Problem.TYP_TEMA, stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod')
|
|
|
|
### Generovani vysledkovky
|
|
|
|
class CisloVysledkovkaView(CisloView):
|
|
model = Cislo
|
|
template_name = 'seminar/archiv/cislo_vysledkovka.tex'
|
|
#content_type = 'application/x-tex; charset=UTF8'
|
|
#umozni rovnou stahnout TeXovsky dokument
|
|
content_type = 'text/plain; charset=UTF8'
|
|
#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani
|
|
|
|
class RocnikVysledkovkaView(RocnikView):
|
|
model = Rocnik
|
|
template_name = 'seminar/archiv/rocnik_vysledkovka.tex'
|
|
#content_type = 'application/x-tex; charset=UTF8'
|
|
#umozni rovnou stahnout TeXovsky dokument
|
|
content_type = 'text/plain; charset=UTF8'
|
|
#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani
|
|
|
|
### Generovani obalek
|
|
class CisloObalkyStruct:
|
|
rocnik = None
|
|
cisla = None
|
|
|
|
|
|
# Vraci QuerySet aktualnich resitelu = nekdy neco poslali, ale jeste neodmaturovali
|
|
def aktualniResitele(rocnik):
|
|
letos = Rocnik.objects.filter(rocnik = rocnik).first()
|
|
return Resitel.objects.filter(rok_maturity__gt = letos.prvni_rok)
|
|
# # ALERT: pokud nekdo nema vypleny rok maturity, tak neni aktualni, protoze Karel Tesar a jini
|
|
# return Resitel.objects.filter(Q(rok_maturity__gt = letos.prvni_rok)|Q(rok_maturity = None))
|
|
|
|
# Vraci QuerySet aktivnich resitelu =
|
|
# jeste neodmaturovali &&
|
|
# (pokud je aktualni cislo mensi nez 3, pak (letos || loni) neco poslali
|
|
# jinak letos neco poslali)
|
|
def aktivniResitele(rocnik,cislo):
|
|
letos = CisloObalkyStruct()
|
|
loni = CisloObalkyStruct()
|
|
|
|
aktualni_resitele = aktualniResitele(rocnik)
|
|
|
|
letos.rocnik = Rocnik.objects.filter(rocnik = rocnik).first()
|
|
loni.rocnik = Rocnik.objects.filter(rocnik = int(rocnik)-1).first()
|
|
letos.cisla = Cislo.objects.filter(rocnik=letos.rocnik,cislo__lte = cislo)
|
|
loni.cisla = Cislo.objects.filter(rocnik=loni.rocnik)
|
|
if int(cislo) > 3:
|
|
problemy = Problem.objects.filter(cislo_zadani = letos.cisla)
|
|
else:
|
|
problemy = Problem.objects.filter(Q(cislo_zadani = letos.cisla)|Q(cislo_zadani=loni.cisla))
|
|
resitele = aktualni_resitele.filter(reseni = Reseni.objects.filter(problem=problemy)).distinct()
|
|
|
|
return resitele
|
|
|
|
|
|
def cisloObalkyView(request,rocnik,cislo):
|
|
return obalkyView(request,aktivniResitele(rocnik,cislo))
|
|
|
|
|
|
def obalkyView(request,resitele):
|
|
tex = render(request,'seminar/archiv/obalky.tex', {'resitele': resitele}).content
|
|
|
|
tempdir = tempfile.mkdtemp()
|
|
with open(tempdir+"/obalky.tex","w") as texfile:
|
|
# Pokud TeX chce ISO Latin, tak se da encode nastavit
|
|
texfile.write(tex.decode("utf-8").encode("iso-8859-2"))
|
|
shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.eps'),tempdir)
|
|
subprocess.call(["csplain","obalky.tex"],cwd = tempdir)
|
|
subprocess.call(["dvipdf","obalky.dvi"],cwd = tempdir)
|
|
|
|
with open(tempdir+"/obalky.pdf","rb") as pdffile:
|
|
response = HttpResponse(pdffile.read(),content_type='application/pdf')
|
|
shutil.rmtree(tempdir)
|
|
return response
|
|
|
|
### Tituly
|
|
|
|
# TODO udelat neco jako get_objects_or_404
|
|
def TitulyView(request, rocnik, cislo):
|
|
rocnik_obj = Rocnik.objects.filter(rocnik = rocnik).first()
|
|
resitele = Resitel.objects.filter(rok_maturity__gte = rocnik_obj.prvni_rok)
|
|
cislo_obj = Cislo.objects.filter(rocnik = rocnik_obj, cislo = cislo).first()
|
|
|
|
for resitel in resitele:
|
|
vys = VysledkyKCisluOdjakziva.objects.filter(resitel = resitel, cislo = cislo_obj).first()
|
|
if vys == None:
|
|
body = 0
|
|
else:
|
|
body = vys.body
|
|
resitel.titul = resitel.get_titul(body)
|
|
resitel.ascii = unicodedata.normalize('NFKD',resitel.jmeno+resitel.prijmeni).encode("ascii","ignore").replace(" ","")
|
|
|
|
return render(request, 'seminar/archiv/tituly.tex',{'resitele': resitele})
|
|
|
|
|
|
### Soustredeni
|
|
|
|
class SoustredeniListView(generic.ListView):
|
|
model = Soustredeni
|
|
template_name = 'seminar/soustredeni/seznam_soustredeni.html'
|
|
|
|
class SoustredeniView(generic.DetailView):
|
|
model = Soustredeni
|
|
template_name = 'seminar/archiv/soustredeni.html'
|
|
|
|
def soustredeniObalkyView(request,soustredeni):
|
|
soustredeni = Soustredeni.objects.filter(id = soustredeni)[0]
|
|
return obalkyView(request,soustredeni.ucastnici.all())
|
|
|
|
|
|
### Články
|
|
|
|
class ClankyResitelView(generic.ListView):
|
|
model = Problem
|
|
template_name = 'seminar/clanky/resitelske_clanky.html'
|
|
queryset = Problem.objects.filter(typ=Problem.TYP_RES_CLANEK, stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod')
|
|
|
|
class ClankyOrganizatorView(generic.ListView):
|
|
model = Problem
|
|
template_name = 'seminar/clanky/organizatorske_clanky.html'
|
|
queryset = Problem.objects.filter(typ=Problem.TYP_ORG_CLANEK, stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod')
|
|
|
|
|
|
### Status
|
|
|
|
def StavDatabazeView(request):
|
|
# nastaveni = Nastaveni.objects.get()
|
|
problemy = utils.seznam_problemu()
|
|
muzi = Resitel.objects.filter(pohlavi_muz=True)
|
|
zeny = Resitel.objects.filter(pohlavi_muz=False)
|
|
return render(request, 'seminar/stav_databaze.html',
|
|
{
|
|
# 'nastaveni': nastaveni,
|
|
'problemy': problemy,
|
|
|
|
'resitele': Resitel.objects.all(),
|
|
'muzi': muzi,
|
|
'zeny': zeny,
|
|
'jmena_muzu': utils.histogram([r.jmeno for r in muzi]),
|
|
'jmena_zen': utils.histogram([r.jmeno for r in zeny]),
|
|
})
|
|
|