mamweb/seminar/views.py

494 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# coding:utf-8
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden
from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
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)
verejne = nastaveni.aktualni_cislo.verejne()
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),
'verejne': verejne,
},
)
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
# Pokud neni zverejnene cislo nezverejnuj odpocet
if nastaveni.aktualni_cislo.verejne():
# pokus se zjistit termin odeslani a pokud neni zadany,
# nezverejnuj odpocet
try:
context['dead'] = datetime.combine(cas_deadline, datetime.max.time())
context['ted'] = datetime.now()
except:
context['dead'] = None
else:
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 not context['problem'].verejne() and not self.request.user.is_staff:
raise PermissionDenied()
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
v.resitel.rocnik = v.resitel.rocnik(v.cislo.rocnik)
# 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
def resitelUlohaView(request,rocnik,cislo):
rocnik = Rocnik.objects.get(rocnik=rocnik)
cislo = Cislo.objects.get(rocnik=rocnik,cislo=cislo)
reseni = Reseni.objects.filter(cislo_body=cislo).order_by('resitel')
# TODO: Nasledujici 4 radky jsou fuj. Znate neco lepsiho?
resitele = list(set([r.resitel for r in reseni]))
resitele.sort(key=lambda r: (r.prijmeni,r.jmeno))
ulohy = list(set([r.problem for r in reseni]))
ulohy.sort(key=lambda u: (u.typ,u.kod))
kdoco = []
for r in resitele:
res_ulohy = [r.jmeno+" "+r.prijmeni]
for u in ulohy:
try:
rsni = reseni.get(resitel=r,problem=u)
res_ulohy.append(rsni.body)
except ObjectDoesNotExist:
res_ulohy.append("")
kdoco.append(res_ulohy)
return render(request, 'seminar/archiv/resitel_uloha.html',{'ulohy':ulohy,'kdoco':kdoco})
### 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]),
})