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.
961 lines
35 KiB
961 lines
35 KiB
# coding:utf-8
|
|
|
|
from django.shortcuts import get_object_or_404, render
|
|
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, JsonResponse
|
|
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,HttpResponseBadRequest
|
|
from django.db.models import Q
|
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
|
from django.contrib.auth import authenticate, login
|
|
|
|
from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Prispevek
|
|
from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
|
|
from . import utils
|
|
from .unicodecsv import UnicodeWriter
|
|
|
|
from datetime import timedelta, date, datetime
|
|
from itertools import groupby
|
|
import tempfile
|
|
import subprocess
|
|
import shutil
|
|
import os
|
|
import os.path as op
|
|
from django.conf import settings
|
|
import unicodedata
|
|
import json
|
|
import traceback
|
|
import sys
|
|
import csv
|
|
|
|
|
|
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)
|
|
temata = verejna_temata(nastaveni.aktualni_rocnik)
|
|
for t in temata:
|
|
if request.user.is_staff:
|
|
t.prispevky = t.prispevek_set.filter(problem=t)
|
|
else:
|
|
t.prispevky = t.prispevek_set.filter(problem=t, zverejnit=True)
|
|
return render(request, 'seminar/zadani/Temata.html',
|
|
{
|
|
'temata': temata,
|
|
}
|
|
)
|
|
|
|
|
|
def ZadaniAktualniVysledkovkaView(request):
|
|
nastaveni = get_object_or_404(Nastaveni)
|
|
# Aktualni verejna vysledkovka
|
|
vysledkovka = vysledkovka_rocniku(nastaveni.aktualni_rocnik)
|
|
# kdyz neni verejna vysledkovka, tak zobraz starou
|
|
if not vysledkovka:
|
|
try:
|
|
minuly_rocnik = Rocnik.objects.get(
|
|
prvni_rok=(nastaveni.aktualni_rocnik.prvni_rok-1))
|
|
vysledkovka = vysledkovka_rocniku(minuly_rocnik)
|
|
except ObjectDoesNotExist:
|
|
pass
|
|
# vysledkovka s neverejnyma vysledkama
|
|
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)
|
|
|
|
# zjisteni spravneho terminu
|
|
if nastaveni.aktualni_cislo.datum_deadline_soustredeni:
|
|
cas_deadline_soustredeni = nastaveni.aktualni_cislo.\
|
|
datum_deadline_soustredeni
|
|
if (datetime.now().date() <= cas_deadline_soustredeni):
|
|
cas_deadline = cas_deadline_soustredeni
|
|
deadline_soustredeni = True
|
|
else:
|
|
cas_deadline = nastaveni.aktualni_cislo.datum_deadline
|
|
deadline_soustredeni = False
|
|
else:
|
|
cas_deadline = nastaveni.aktualni_cislo.datum_deadline
|
|
deadline_soustredeni = False
|
|
|
|
# Pokud neni zverejnene cislo nezverejnuj odpocet
|
|
if nastaveni.aktualni_cislo.verejne():
|
|
# pokus se zjistit termin odeslani a pokud neni zadany,
|
|
# nezverejnuj odpocet
|
|
context['deadline_soustredeni'] = deadline_soustredeni
|
|
try:
|
|
context['dead'] = datetime.combine(cas_deadline,
|
|
datetime.max.time())
|
|
context['ted'] = datetime.now()
|
|
except:
|
|
context['dead'] = None
|
|
else:
|
|
context['dead'] = None
|
|
context['deadline_soustredeni'] = deadline_soustredeni
|
|
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
|
|
def aktivniOrganizatori(rok=date.today().year):
|
|
return Organizator.objects.exclude(
|
|
organizuje_do_roku__isnull=False,
|
|
organizuje_do_roku__lt=rok
|
|
).order_by('user__first_name')
|
|
|
|
|
|
class CojemamOrganizatoriView(generic.ListView):
|
|
model = Organizator
|
|
template_name = 'seminar/cojemam/organizatori.html'
|
|
queryset = aktivniOrganizatori()
|
|
|
|
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.exclude(
|
|
id__in=aktivniOrganizatori()).order_by('-organizuje_do_roku')
|
|
|
|
### Archiv
|
|
|
|
|
|
class ArchivView(generic.ListView):
|
|
model = Rocnik
|
|
template_name='seminar/archiv/cisla.html'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(ArchivView, self).get_context_data(**kwargs)
|
|
|
|
vyska = 297 # px
|
|
sirka = 210 # px
|
|
|
|
cisla = Cislo.objects.filter(verejne_db=True)[:10]
|
|
|
|
png_dir = op.join(settings.MEDIA_ROOT, "cislo", "png")
|
|
|
|
# seznam [(url obrázku, číslo)]
|
|
urls = []
|
|
|
|
for i, c in enumerate(cisla):
|
|
if not c.pdf:
|
|
continue
|
|
filename = os.path.split(c.pdf.file.name)[1].split(".")[0]
|
|
png_filename = "{}-{}px.png".format(filename, vyska)
|
|
|
|
# Pokud obrázek neexistuje nebo není aktuální, vytvoř jej
|
|
png_path = op.join(png_dir, png_filename)
|
|
if not op.exists(png_path) or \
|
|
op.getmtime(png_path) < op.getmtime(c.pdf.path):
|
|
|
|
subprocess.call([
|
|
"convert",
|
|
"-density", "180x180",
|
|
"-geometry", "{}x{}".format(vyska, vyska),
|
|
"-background", "white",
|
|
"-flatten",
|
|
"-rotate", str(90 * i),
|
|
"{}[0]".format(c.pdf.path), # titulní strana
|
|
png_path
|
|
])
|
|
|
|
urls.append(
|
|
(op.join(settings.MEDIA_URL, "cislo", "png", png_filename), c)
|
|
)
|
|
vyska, sirka = sirka, vyska / 2
|
|
|
|
tags = []
|
|
|
|
def spirala(urls, tags, idx):
|
|
"""Rekurzivně prochází urls a generuje strom elementů do tags"""
|
|
if idx >= len(urls):
|
|
return
|
|
|
|
img_url, cislo = urls[idx]
|
|
tags.append(
|
|
"<div style='top:{}%;left:{}%;width:{}%;height:{}%;'>"
|
|
.format(
|
|
50 if idx % 4 == 2 else 0,
|
|
50 if idx % 4 == 1 else 0,
|
|
50 if idx % 2 == 1 else 100,
|
|
50 if idx > 0 and idx % 2 == 0 else 100
|
|
)
|
|
)
|
|
tags.append("<a href='{}' title='{}'>".format(
|
|
cislo.verejne_url(), cislo.kod()
|
|
))
|
|
tags.append(
|
|
"<img src='{}' style='top:{}%;left:{}%;width:{}%;height:{}%;'>"
|
|
.format(
|
|
img_url,
|
|
50 if idx % 4 == 3 else 0,
|
|
50 if idx % 4 == 2 else 0,
|
|
50 if idx % 2 == 0 else 100,
|
|
50 if idx % 2 == 1 else 100
|
|
)
|
|
)
|
|
tags.append("</a>")
|
|
spirala(urls, tags, idx + 1)
|
|
tags.append("</div>")
|
|
spirala(urls, tags, 0)
|
|
|
|
context["nahledy"] = "\n".join(tags)
|
|
return context
|
|
|
|
|
|
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.rocnik = rocnik.rocnik
|
|
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 PrispevekView(generic.DetailView):
|
|
model = Prispevek
|
|
template_name = 'seminar/archiv/prispevek.html'
|
|
|
|
# Vlastni ziskavani objektu z databaze podle prispevku
|
|
# pokud je prispevek neverejny zobrazi se jenom orgum
|
|
def get_object(self, queryset=None):
|
|
if queryset is None:
|
|
queryset = self.get_queryset()
|
|
problem_arg = self.kwargs.get('pk')
|
|
prispevek_arg = self.kwargs.get('prispevek')
|
|
queryset = queryset.filter(pk=prispevek_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})
|
|
|
|
if self.request.user.is_staff or obj.zverejnit:
|
|
return obj
|
|
else:
|
|
raise Http404()
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(PrispevekView, self).get_context_data(**kwargs)
|
|
# snaho o ziskani titulu
|
|
titul = ''
|
|
try:
|
|
resitel = context['prispevek'].reseni.resitel
|
|
cislo = context['prispevek'].reseni.cislo_body
|
|
body = VysledkyKCisluOdjakziva.objects.get(resitel=resitel,
|
|
cislo=cislo).body
|
|
titul = resitel.get_titul(body)
|
|
except:
|
|
pass
|
|
context['titul'] = titul
|
|
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)
|
|
v.titulmakro = unicodedata.normalize('NFKD',v.resitel.jmeno+v.resitel.prijmeni).encode("ascii","ignore").replace(" ","")
|
|
|
|
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:
|
|
if r.body is None or vysledky_resitele[r.resitel.id].body_ulohy[problem_index[r.problem.id]] is None:
|
|
vysledky_resitele[r.resitel.id].body_ulohy[problem_index[r.problem.id]] = None
|
|
elif vysledky_resitele[r.resitel.id].body_ulohy[problem_index[r.problem.id]] == '':
|
|
vysledky_resitele[r.resitel.id].body_ulohy[problem_index[r.problem.id]] = r.body
|
|
else:
|
|
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').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.get(rocnik = rocnik)
|
|
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.get(rocnik = rocnik)
|
|
loni.rocnik = Rocnik.objects.get(rocnik = int(rocnik)-1)
|
|
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__in = letos.cisla)
|
|
else:
|
|
problemy = Problem.objects.filter(Q(cislo_zadani__in = letos.cisla)|Q(cislo_zadani__in = loni.cisla))
|
|
resitele = aktualni_resitele.filter(reseni__in = Reseni.objects.filter(problem__in=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:
|
|
texfile.write(tex)
|
|
shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.pdf'),tempdir)
|
|
subprocess.call(["pdflatex","obalky.tex"],cwd = tempdir)
|
|
|
|
with open(tempdir+"/obalky.pdf","rb") as pdffile:
|
|
response = HttpResponse(pdffile.read(),content_type='application/pdf')
|
|
shutil.rmtree(tempdir)
|
|
return response
|
|
|
|
|
|
def obalkovaniView(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__prijmeni',
|
|
'resitel__jmeno',
|
|
'problem__typ',
|
|
'problem__kod'
|
|
)
|
|
)
|
|
|
|
problemy = sorted(set(r.problem for r in reseni), key=lambda p: (p.typ, p.kod))
|
|
return render(
|
|
request,
|
|
'seminar/archiv/cislo_obalkovani.html',
|
|
{'cislo': cislo, 'problemy': problemy, 'reseni': reseni}
|
|
)
|
|
|
|
### Tituly
|
|
|
|
# TODO udelat neco jako get_objects_or_404
|
|
def TitulyView(request, rocnik, cislo):
|
|
rocnik_obj = Rocnik.objects.get(rocnik = rocnik)
|
|
resitele = Resitel.objects.filter(rok_maturity__gte = rocnik_obj.prvni_rok)
|
|
cislo_obj = Cislo.objects.get(rocnik = rocnik_obj, cislo = cislo)
|
|
|
|
asciijmena = []
|
|
broken = False
|
|
|
|
for resitel in resitele:
|
|
try:
|
|
vys = VysledkyKCisluOdjakziva.objects.get(resitel = resitel, cislo = cislo_obj)
|
|
body = vys.body
|
|
except ObjectDoesNotExist:
|
|
body = 0
|
|
resitel.titul = resitel.get_titul(body)
|
|
resitel.ascii = unicodedata.normalize('NFKD',resitel.jmeno+resitel.prijmeni).encode("ascii","ignore").replace(" ","")
|
|
if resitel.ascii not in asciijmena:
|
|
asciijmena.append(resitel.ascii)
|
|
else:
|
|
broken = True
|
|
|
|
return render(request, 'seminar/archiv/tituly.tex',{'resitele': resitele,'broken':broken},content_type="text/plain")
|
|
|
|
|
|
### 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 = get_object_or_404(Soustredeni,id = soustredeni)
|
|
return obalkyView(request,soustredeni.ucastnici.all())
|
|
|
|
|
|
class SoustredeniUcastniciBaseView(generic.ListView):
|
|
model = Soustredeni_Ucastnici
|
|
|
|
def get_queryset(self):
|
|
soustredeni = get_object_or_404(
|
|
Soustredeni,
|
|
pk=self.kwargs["soustredeni"]
|
|
)
|
|
return Soustredeni_Ucastnici.objects.filter(
|
|
soustredeni=soustredeni).select_related('resitel')
|
|
|
|
|
|
class SoustredeniMailyUcastnikuView(SoustredeniUcastniciBaseView):
|
|
"""Seznam e-mailů řešitelů oddělených čárkami"""
|
|
model = Soustredeni_Ucastnici
|
|
template_name = 'seminar/soustredeni/maily_ucastniku.txt'
|
|
|
|
|
|
class SoustredeniUcastniciView(SoustredeniUcastniciBaseView):
|
|
"""HTML tabulka účastníků pro tisk"""
|
|
model = Soustredeni_Ucastnici
|
|
template_name = 'seminar/soustredeni/seznam_ucastniku.html'
|
|
|
|
|
|
def soustredeniStvrzenkyExportView(request,soustredeni,first_num):
|
|
first_num = int(first_num)
|
|
soustredeni = get_object_or_404(Soustredeni,id = soustredeni)
|
|
ucastnici = Resitel.objects.filter(soustredeni=soustredeni)
|
|
for (idx,u) in enumerate(ucastnici):
|
|
u.cislo_stvrzenky = first_num+idx;
|
|
tex = render(request,'seminar/soustredeni/ucastnici.tex', {'ucastnici': ucastnici, 'datum':soustredeni.datum_zacatku }).content
|
|
|
|
tempdir = tempfile.mkdtemp()
|
|
with open(tempdir+"/ucastnici.tex","w") as texfile:
|
|
# Pokud TeX chce ISO Latin, tak se da encode nastavit
|
|
texfile.write(tex.decode("utf-8").encode("utf-8"))
|
|
shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/stvrzenka.sty'),tempdir)
|
|
shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/stvrzenky.tex'),tempdir)
|
|
subprocess.call(["cslatex","stvrzenky.tex"],cwd = tempdir)
|
|
subprocess.call(["dvipdf","stvrzenky.dvi"],cwd = tempdir)
|
|
|
|
with open(tempdir+"/stvrzenky.pdf","rb") as pdffile:
|
|
response = HttpResponse(pdffile.read(),content_type='application/pdf')
|
|
shutil.rmtree(tempdir)
|
|
return response
|
|
|
|
|
|
def soustredeniUcastniciExportView(request,soustredeni):
|
|
soustredeni = get_object_or_404(Soustredeni,id = soustredeni)
|
|
ucastnici = Resitel.objects.filter(soustredeni=soustredeni)
|
|
response = HttpResponse(content_type='text/csv')
|
|
response['Content-Disposition'] = 'attachment; filename="ucastnici.csv"'
|
|
|
|
writer = UnicodeWriter(response)
|
|
writer.writerow(["jmeno", "prijmeni", "rok_maturity", "telefon", "email", "ulice", "mesto", "psc","stat"])
|
|
for u in ucastnici:
|
|
writer.writerow([u.jmeno, u.prijmeni, str(u.rok_maturity), u.telefon, u.email, u.ulice, u.mesto, u.psc, u.stat.name])
|
|
return response
|
|
|
|
|
|
### Č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').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').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]),
|
|
})
|
|
|
|
|
|
|
|
@ensure_csrf_cookie
|
|
def LoginView(request):
|
|
"""Pro přihlášení při nahrávání z texu"""
|
|
q = request.POST
|
|
# nastavení cookie csrftoken
|
|
if not q:
|
|
return JsonResponse({"ok": 1})
|
|
|
|
if "username" in q:
|
|
username = q["username"]
|
|
password = q["password"]
|
|
user = authenticate(username=username, password=password)
|
|
if user is not None and user.is_staff:
|
|
login(request, user)
|
|
return JsonResponse({"ok": 1})
|
|
else:
|
|
return JsonResponse({"error": "Neplatné uživatelské jméno nebo heslo"})
|
|
|
|
|
|
@ensure_csrf_cookie
|
|
def texUploadView(request):
|
|
|
|
def uloz_soubory(files, rocnik, cislo):
|
|
for filename, f in files:
|
|
path = os.path.join(
|
|
settings.MEDIA_ROOT,
|
|
settings.CISLO_IMG_DIR,
|
|
rocnik,
|
|
cislo,
|
|
filename
|
|
)
|
|
|
|
adresar = os.path.dirname(path)
|
|
if not os.path.exists(adresar):
|
|
os.makedirs(adresar)
|
|
|
|
with open(path, "wb+") as fout:
|
|
for chunk in f.chunks():
|
|
fout.write(chunk)
|
|
|
|
q = request.POST
|
|
# nastavení cookie csrftoken
|
|
if not q:
|
|
return JsonResponse({"ok": 1})
|
|
|
|
# Odchytíme všechny výjimky a traceback pošleme v odpovědi
|
|
try:
|
|
meta = json.loads(q["meta"])
|
|
html = q["html"]
|
|
|
|
if meta["typ"] in ["uloha", "serial", "reseni", "tema"]:
|
|
|
|
# Uložíme soubory
|
|
if meta["typ"] != "reseni":
|
|
c = meta["cislo"]
|
|
else:
|
|
# Řešení má nastavené číslo svojí úlohy, ale obrázky jsou
|
|
# ukládány do čísla, kde řešení vyšlo
|
|
c = meta["cislo_reseni"]
|
|
|
|
# Zjistíme typ ukládaného problému
|
|
typy = {
|
|
"uloha": Problem.TYP_ULOHA,
|
|
"serial": Problem.TYP_SERIAL,
|
|
"reseni": Problem.TYP_ULOHA,
|
|
"tema": Problem.TYP_TEMA,
|
|
}
|
|
problem_typ = typy[meta["typ"]]
|
|
|
|
# Pokud už problém existuje, vytáhneme jej z db a upravíme
|
|
# Pokud neexistuje, vytvoříme jej jedině pokud je to vynucené
|
|
|
|
# Pokud ročník/číslo ještě neexistuje, vyhodí to výjimku ->
|
|
# číslo/ročník se musí založit ručně v adminu
|
|
rocnik = Rocnik.objects.get(rocnik=meta["rocnik"])
|
|
cislo = Cislo.objects.get(rocnik=rocnik, cislo=meta["cislo"])
|
|
|
|
existujici = Problem.objects.filter(
|
|
typ=problem_typ,
|
|
stav=Problem.STAV_ZADANY,
|
|
cislo_zadani=cislo,
|
|
kod=meta["kod"]
|
|
)
|
|
|
|
problem = None
|
|
if existujici:
|
|
problem = existujici[0]
|
|
elif "vytvor" in q:
|
|
# vytvoříme nový
|
|
problem = Problem(
|
|
typ=problem_typ,
|
|
stav=Problem.STAV_ZADANY,
|
|
kod=meta["kod"],
|
|
cislo_zadani=cislo
|
|
)
|
|
else:
|
|
return JsonResponse({
|
|
"error": "Problém neexistuje: {} {}.{} kód {}".format(
|
|
meta["typ"], meta["rocnik"], meta["cislo"], meta["kod"]
|
|
)
|
|
})
|
|
|
|
uloz_soubory(request.FILES.items(), meta["rocnik"], c)
|
|
|
|
if meta["typ"] == "reseni":
|
|
problem.text_reseni = html
|
|
|
|
# Pokud ročník/číslo ještě neexistuje, vyhodí to výjimku ->
|
|
# číslo/ročník se musí založit ručně v adminu
|
|
problem.cislo_reseni = Cislo.objects.get(
|
|
rocnik=rocnik,
|
|
cislo=meta["cislo_reseni"]
|
|
)
|
|
# při nahrávání řešení už původní zadání atd. neměníme
|
|
else:
|
|
problem.text_zadani = html
|
|
problem.nazev = meta["nazev"]
|
|
if meta["typ"] != "tema":
|
|
problem.body = meta["body"]
|
|
|
|
problem.save()
|
|
cislo.faze = cislo.FAZE_NAHRANO
|
|
cislo.save()
|
|
|
|
# Vrátíme id dané úlohy, aby se k ní dala případně připojit pohádka
|
|
return JsonResponse({"db_id": problem.id})
|
|
|
|
elif meta["typ"] == "pohadka":
|
|
uloha = Problem.objects.get(typ=Problem.TYP_ULOHA, pk=meta["uloha"])
|
|
|
|
# Pokud už příslušná pohádka existuje, jen ji upravíme
|
|
existujici = Pohadka.objects.filter(uloha=uloha, pred=meta["pred"])
|
|
pohadka = None
|
|
if existujici:
|
|
pohadka = existujici[0]
|
|
else:
|
|
pohadka = Pohadka(uloha=uloha, pred=meta["pred"])
|
|
pohadka.text = q["html"]
|
|
pohadka.save()
|
|
|
|
return JsonResponse({"db_id": pohadka.id})
|
|
|
|
except Exception, e:
|
|
# Pošleme zpátky traceback, ať uživatel ví, v čem je problém
|
|
tb = "".join(traceback.format_exception(type(e), e, sys.exc_info()[2]))
|
|
return JsonResponse({"error": tb})
|
|
|
|
|
|
def texDownloadView(request, rocnik, cislo):
|
|
"""View posílající JSON se zadanými a řešenými problémy pro založení čísla
|
|
"""
|
|
cislo = Cislo.objects.get(rocnik__rocnik=rocnik, cislo=cislo)
|
|
if cislo.faze == cislo.FAZE_NAHRANO:
|
|
# obsah byl nahrán z TeXu na web, už je příliš složitý
|
|
return JsonResponse(
|
|
{"error": "Obsah čísla už byl nahrán z TeXu na web."}
|
|
)
|
|
|
|
zadane = Problem.objects.filter(
|
|
cislo_zadani=cislo,
|
|
stav=Problem.STAV_ZADANY
|
|
)
|
|
resene = Problem.objects.filter(
|
|
cislo_reseni=cislo,
|
|
stav=Problem.STAV_ZADANY,
|
|
typ=Problem.TYP_ULOHA
|
|
)
|
|
pred_pohadky = Pohadka.objects.filter(uloha__cislo_zadani=cislo, pred=True)
|
|
po_pohadky = Pohadka.objects.filter(uloha__cislo_zadani=cislo, pred=False)
|
|
|
|
response = {
|
|
"zadane": [
|
|
{
|
|
"nazev": p.nazev,
|
|
"typ": p.typ,
|
|
"kod": p.kod,
|
|
"body": p.body,
|
|
"zadani": p.text_zadani,
|
|
"pred_pohadky": [x.text for x in pred_pohadky.filter(uloha=p)],
|
|
"po_pohadky": [x.text for x in po_pohadky.filter(uloha=p)],
|
|
} for p in zadane
|
|
],
|
|
"resene": [
|
|
{
|
|
"nazev": p.nazev,
|
|
"typ": p.typ,
|
|
"kod": p.kod,
|
|
"body": p.body,
|
|
"zadani": p.text_zadani,
|
|
"reseni": p.text_reseni,
|
|
"cislo_zadani": p.cislo_zadani.cislo,
|
|
} for p in resene
|
|
],
|
|
}
|
|
|
|
cislo.faze = Cislo.FAZE_TEX
|
|
cislo.save()
|
|
return JsonResponse(response)
|
|
|
|
|
|
# Ceka na autocomplete v3
|
|
# class OrganizatorAutocomplete(autocomplete.Select2QuerySetView):
|
|
# def get_queryset(self):
|
|
# if not self.request.user.is_authenticated():
|
|
# return Organizator.objects.none()
|
|
#
|
|
# qs = aktivniOrganizatori()
|
|
#
|
|
# if self.q:
|
|
# if self.q[0] == "!":
|
|
# qs = Organizator.objects.all()
|
|
# query = self.q[1:]
|
|
# else:
|
|
# query = self.q
|
|
# qs = qs.filter(
|
|
# Q(prezdivka__isstartswith=query)|
|
|
# Q(user__first_name__isstartswith=query)|
|
|
# Q(user__last_name__isstartswith=query))
|
|
#
|
|
# return qs
|
|
|