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.
1497 lines
51 KiB
1497 lines
51 KiB
# coding:utf-8
|
|
|
|
from django.shortcuts import get_object_or_404, render
|
|
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, JsonResponse
|
|
from django.urls import reverse,reverse_lazy
|
|
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,HttpResponseRedirect
|
|
from django.db.models import Q, Sum, Count
|
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
|
from django.views.generic.edit import FormView, CreateView
|
|
from django.contrib.auth import authenticate, login, get_user_model, logout
|
|
from django.contrib.auth import views as auth_views
|
|
from django.contrib.auth.models import User
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.db import transaction
|
|
|
|
import seminar.models as s
|
|
from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Tema, Clanek, Osoba, Skola # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci
|
|
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
|
|
from seminar import utils,treelib
|
|
from .unicodecsv import UnicodeWriter
|
|
from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm
|
|
import seminar.forms as f
|
|
|
|
from datetime import timedelta, date, datetime
|
|
from django.utils import timezone
|
|
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
|
|
import logging
|
|
|
|
|
|
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 temata_v_rocniku(rocnik):
|
|
return Problem.objects.filter(typ=Problem.TYP_TEMA, rocnik=rocnik)
|
|
|
|
def get_problemy_k_tematu(tema):
|
|
return Problemy.objects.filter(nadproblem = tema)
|
|
|
|
|
|
class VlozBodyView(generic.ListView):
|
|
template_name = 'seminar/org/vloz_body.html'
|
|
|
|
def get_queryset(self):
|
|
self.tema = get_object_or_404(Problem,id=self.kwargs['tema'])
|
|
print(self.tema)
|
|
self.problemy = Problem.objects.filter(nadproblem = self.tema)
|
|
print(self.problemy)
|
|
self.reseni = Reseni.objects.filter(problem__in=self.problemy)
|
|
print(self.reseni)
|
|
return self.reseni
|
|
|
|
|
|
class ObalkovaniView(generic.ListView):
|
|
template_name = 'seminar/org/obalkovani.html'
|
|
|
|
def get_queryset(self):
|
|
rocnik = get_object_or_404(Rocnik,rocnik=self.kwargs['rocnik'])
|
|
cislo = get_object_or_404(Cislo,rocnik=rocnik,poradi=self.kwargs['cislo'])
|
|
self.cislo = cislo
|
|
self.hodnoceni = s.Hodnoceni.objects.filter(cislo_body=cislo)
|
|
self.reseni = Reseni.objects.filter(hodnoceni__in = self.hodnoceni).annotate(Sum('hodnoceni__body')).annotate(Count('hodnoceni')).order_by('resitele__osoba')
|
|
return self.reseni
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(ObalkovaniView, self).get_context_data(**kwargs)
|
|
print(self.cislo)
|
|
context['cislo'] = self.cislo
|
|
return context
|
|
|
|
class TNLData(object):
|
|
def __init__(self,anode):
|
|
self.node = anode
|
|
self.children = []
|
|
|
|
|
|
def treenode_strom_na_seznamy(node):
|
|
out = TNLData(node)
|
|
for ch in treelib.all_children(node):
|
|
outitem = treenode_strom_na_seznamy(ch)
|
|
out.children.append(outitem)
|
|
return out
|
|
|
|
class TreeNodeView(generic.DetailView):
|
|
model = s.TreeNode
|
|
template_name = 'seminar/treenode.html'
|
|
|
|
def get_context_data(self,**kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['tnldata'] = treenode_strom_na_seznamy(self.object)
|
|
return context
|
|
|
|
|
|
#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 TematkoView(request, rocnik, tematko):
|
|
# nastaveni = s.Nastaveni.objects.first()
|
|
# rocnik_object = s.Rocnik.objects.filter(rocnik=rocnik)
|
|
# tematko_object = s.Tema.objects.filter(rocnik=rocnik_object[0], kod=tematko)
|
|
# seznam = vytahniZLesaSeznam(tematko_object[0], nastaveni.aktualni_rocnik().rocniknode)
|
|
# for node, depth in seznam:
|
|
# if node.isinstance(node, s.KonferaNode):
|
|
# raise Exception("Not implemented yet")
|
|
# if node.isinstance(node, s.PohadkaNode): # Mohu ignorovat, má pod sebou
|
|
# pass
|
|
#
|
|
# return render(request, 'seminar/tematka/toaletak.html', {})
|
|
#
|
|
#
|
|
#def TemataRozcestnikView(request):
|
|
# print("=============================================")
|
|
# nastaveni = s.Nastaveni.objects.first()
|
|
# tematka_objects = s.Tema.objects.filter(rocnik=nastaveni.aktualni_rocnik())
|
|
# tematka = [] #List tematka obsahuje pro kazde tematko object a list vsech TemaVCisleNodu - implementované pomocí slovníku
|
|
# for tematko_object in tematka_objects:
|
|
# print("AKTUALNI TEMATKO")
|
|
# print(tematko_object.id)
|
|
# odkazy = vytahniZLesaSeznam(tematko_object, nastaveni.aktualni_rocnik().rocniknode, pouze_zajimave = True) #Odkazy jsou tuply (node, depth) v listu
|
|
# print(odkazy)
|
|
# cisla = [] # List tuplů (nazev cisla, list odkazů)
|
|
# vcisle = []
|
|
# cislo = None
|
|
# for odkaz in odkazy:
|
|
# if odkaz[1] == 0:
|
|
# if cislo != None:
|
|
# cisla.append((cislo, vcisle))
|
|
# cislo = (odkaz[0].getOdkazStr(), odkaz[0].getOdkaz())
|
|
# vcisle = []
|
|
# else:
|
|
# print(odkaz[0].getOdkaz())
|
|
# vcisle.append((odkaz[0].getOdkazStr(), odkaz[0].getOdkaz()))
|
|
# if cislo != None:
|
|
# cisla.append((cislo, vcisle))
|
|
#
|
|
# print(cisla)
|
|
# tematka.append({
|
|
# "kod" : tematko_object.kod,
|
|
# "nazev" : tematko_object.nazev,
|
|
# "abstrakt" : tematko_object.abstrakt,
|
|
# "obrazek": tematko_object.obrazek,
|
|
# "cisla" : cisla
|
|
# })
|
|
# return render(request, 'seminar/tematka/rozcestnik.html', {"tematka": tematka, "rocnik" : nastaveni.aktualni_rocnik().rocnik})
|
|
#
|
|
|
|
#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(datum=timezone.now()):
|
|
return Organizator.objects.exclude(
|
|
organizuje_do__isnull=False,
|
|
organizuje_do__lt=datum
|
|
).order_by('osoba__jmeno')
|
|
|
|
|
|
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')
|
|
|
|
### 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
|
|
|
|
# nejnovějších 10 zveřejněných čísel
|
|
cisla = Cislo.objects.filter(verejne_db=True)[:10]
|
|
|
|
# op = os.path, udělá z argumentů cestu
|
|
png_dir = op.join(settings.MEDIA_ROOT, "cislo", "png")
|
|
|
|
# seznam [(url obrázku, číslo)]
|
|
urls = []
|
|
|
|
# c je číslo, i je pořadí čísla
|
|
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
|
|
|
|
### Výsledky
|
|
|
|
# ze setřízeného(!) seznamu všech bodů vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.)
|
|
def sloupec_s_poradim(seznam_s_body):
|
|
aktualni_poradi = 1
|
|
sloupec_s_poradim = []
|
|
|
|
# seskupíme seznam všech bodů podle hodnot
|
|
for index in range(0, len(seznam_s_body)):
|
|
# pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah a chceme
|
|
# vypsat už jen prázdné místo, než dojdeme na správný řádek
|
|
if (index + 1) < aktualni_poradi:
|
|
sloupec_s_poradim.append("")
|
|
continue
|
|
velikost_skupiny = 0
|
|
# zjistíme počet po sobě jdoucích stejných hodnot
|
|
while seznam_s_body[index] == seznam_s_body[index + velikost_skupiny]:
|
|
velikost_skupiny = velikost_skupiny + 1
|
|
# na konci musíme ošetřit přetečení seznamu
|
|
if (index + velikost_skupiny) > len(seznam_s_body) - 1:
|
|
break
|
|
# pokud je velikost skupiny 1, vypíšu pořadí
|
|
if velikost_skupiny == 1:
|
|
sloupec_s_poradim.append("{}.".format(aktualni_poradi))
|
|
# pokud je skupina větší, vypíšu rozsah
|
|
else:
|
|
sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi,
|
|
aktualni_poradi+velikost_skupiny-1))
|
|
# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno
|
|
aktualni_poradi = aktualni_poradi + velikost_skupiny
|
|
return sloupec_s_poradim
|
|
|
|
## spočítá součet bodů získaných daným řešitelem za zadaný problém a všechny jeho podproblémy
|
|
#def __soucet_resitele_problemu(problem, resitel, cislo, soucet):
|
|
# # sečteme body za daný problém přes všechna řešení daného problému
|
|
# # od daného řešitele
|
|
# reseni_resitele = s.Reseni_Resitele.objects.filter(resitele=resitel)
|
|
# hodnoceni_resitele = problem.hodnoceni.filter(reseni__in=reseni_resitele,
|
|
# cislo_body=cislo)
|
|
# # XXX chyba na řádku výše - řešení může mít více řešitelů, asi chceme contains
|
|
# # nebo in
|
|
# for r in hodnoceni_resitele:
|
|
# soucet += r.body
|
|
#
|
|
# # a přičteme k tomu hodnocení všech podproblémů
|
|
# for p in problem.podproblem.all():
|
|
# # i přes jméno by to měla být množina jeho podproblémů
|
|
# soucet += __soucet_resitele_problemu(p, resitel, soucet)
|
|
# return soucet
|
|
|
|
## spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele
|
|
#def body_resitele_problemu_v_cisle(problem, resitel, cislo):
|
|
# # probably FIXED: nezohledňuje číslo, do kterého se body počítají
|
|
# return __soucet_resitele_problemu(problem, resitel, cislo, 0)
|
|
|
|
# pro daný problém vrátí jeho nejvyšší nadproblém
|
|
def hlavni_problem(problem):
|
|
while not(problem.nadproblem == None):
|
|
problem = problem.nadproblem
|
|
return problem
|
|
|
|
# vrátí list všech problémů s body v daném čísle, které již nemají nadproblém
|
|
def hlavni_problemy_cisla(cislo):
|
|
hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all() # hodnocení, která se vážou k danému číslu
|
|
|
|
reseni = [h.reseni for h in hodnoceni]
|
|
problemy = [h.problem for h in hodnoceni]
|
|
problemy_set = set(problemy) # chceme každý problém unikátně,
|
|
problemy = (list(problemy_set)) # převedení na množinu a zpět to zaručí
|
|
|
|
# hlavní problémy čísla
|
|
# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém)
|
|
hlavni_problemy = []
|
|
for p in problemy:
|
|
hlavni_problemy.append(hlavni_problem(p))
|
|
|
|
# zunikátnění
|
|
hlavni_problemy_set = set(hlavni_problemy)
|
|
hlavni_problemy = list(hlavni_problemy_set)
|
|
hlavni_problemy.sort(key=lambda k: k.kod_v_rocniku()) # setřídit podle t1, t2, c3, ...
|
|
|
|
return hlavni_problemy
|
|
|
|
# vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník
|
|
# POZOR! Aktuálně počítá jen za posledních 10 let od zadaného ročníku
|
|
def body_resitelu_odjakziva(rocnik, resitele):
|
|
body_odjakziva = {}
|
|
|
|
for r in resitele:
|
|
body_odjakziva[str(r.id)] = 0
|
|
# # Body za posledních 10 let je dobrá aproximace pro naše potřeby (výsledkovka
|
|
# # s aktivními řešiteli)
|
|
#
|
|
# body_pred_roky = []
|
|
# for i in range(0, 10):
|
|
# body_pred_roky.append(body_resitelu_za_rocnik(rocnik-i, resitele))
|
|
#
|
|
# for r in resitele:
|
|
# for i in range(0,10):
|
|
# body_odjakziva[str(r.id)] += body_pred_roky[i][str(r.id)]
|
|
|
|
|
|
# Nasledující řešení je sice správné, ale moc pomalé:
|
|
for res in Reseni.objects.prefetch_related('resitele', 'hodnoceni_set').all():
|
|
for r in res.resitele.all():
|
|
# daný řešitel nemusí být v naší podmnožině
|
|
if r not in resitele: continue
|
|
|
|
for hodn in res.hodnoceni_set.all():
|
|
pricti_body(body_odjakziva, r, hodn.body)
|
|
return body_odjakziva
|
|
|
|
# vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník
|
|
def body_resitelu_za_rocnik(rocnik, aktivni_resitele):
|
|
body_za_rocnik = {}
|
|
# inicializujeme na 0 pro všechny aktivní řešitele
|
|
for ar in aktivni_resitele:
|
|
body_za_rocnik[str(ar.id)] = 0
|
|
|
|
# spočítáme body řešitelům přes všechna řešení s hodnocením v daném ročníku
|
|
reseni = Reseni.objects.prefetch_related('resitele', 'hodnoceni_set').filter(hodnoceni__cislo_body__rocnik=rocnik)
|
|
for res in reseni:
|
|
for resitel in res.resitele.all():
|
|
for hodn in res.hodnoceni_set.all():
|
|
pricti_body(body_za_rocnik, resitel, hodn.body)
|
|
return body_za_rocnik
|
|
|
|
#def body_resitele_odjakziva(resitel):
|
|
# body = 0
|
|
# resitelova_hodnoceni = Hodnoceni.objects.select_related('body').all().filter(reseni_resitele=resitel)
|
|
# # TODO: v radku nahore chceme _in nebo _contains
|
|
# for hodnoceni in resitelova_hodnoceni:
|
|
# body = body + hodnoceni.body
|
|
# return body
|
|
|
|
# spočítá součet všech bodů řešitele za dané číslo
|
|
#def body_resitele_v_cisle(resitel, cislo):
|
|
# hlavni_problemy = hlavni_problemy_cisla(cislo)
|
|
# body_resitele = 0
|
|
# for h in hlavni_problemy:
|
|
# body_resitele = body_resitele + body_resitele_problemu_v_cisle(h, resitel, cislo)
|
|
# # TODO: je rozdíl mezi odevzdanou úlohou za 0 a tím, když řešitel nic neodevzdal
|
|
# # řešit přes kontrolu velikosti množiny řešení daného problému do daného čísla?
|
|
# # Tady to ale nevadí, tady se počítá součet za číslo.
|
|
# return body_resitele
|
|
|
|
# spočítá součet všech bodů řešitele za daný rok (nebo jen do daného čísla včetně)
|
|
#def body_resitele_v_rocniku(resitel, rocnik, do_cisla=None):
|
|
# # pokud do_cisla=None, tak do posledního čísla v ročníku
|
|
# # do_cisla je objekt Cislo
|
|
# cisla = rocnik.cisla.all() # funkce vrátí pole objektů
|
|
# # Cislo už lexikograficky setřízené, viz models
|
|
# body = 0
|
|
# for cislo in cisla:
|
|
# if cislo.poradi == do_cisla.poradi: break
|
|
# # druhá část zaručuje, že máme výsledky do daného čísla včetně
|
|
# body = body + body_resitele_v_cisle(resitel, cislo)
|
|
# return body
|
|
|
|
# TODO: předělat na nový model
|
|
#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].poradi).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].poradi)
|
|
# 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"])
|
|
# FIXME: opravit vylistování témat v ročníku
|
|
|
|
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):
|
|
"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
|
|
Umožňuje snazší práci v templatu (lepší, než seznam)."""
|
|
|
|
def __init__(self, poradi, resitel, body_problemy_sezn,
|
|
body_cislo, body_rocnik, body_odjakziva):
|
|
self.resitel = resitel
|
|
self.body_cislo = body_cislo
|
|
self.body_rocnik = body_rocnik
|
|
self.body_celkem_odjakziva = body_odjakziva
|
|
self.poradi = poradi
|
|
self.body_problemy_sezn = body_problemy_sezn
|
|
self.titul = resitel.get_titul(body_odjakziva)
|
|
|
|
|
|
# přiřazuje danému řešiteli body do slovníku
|
|
def pricti_body(slovnik, resitel, body):
|
|
# testujeme na None (""), pokud je to první řešení
|
|
# daného řešitele, předěláme na 0
|
|
# (v dalším kroku přičteme reálný počet bodů),
|
|
# rozlišujeme tím mezi 0 a neodevzdaným řešením
|
|
if slovnik[str(resitel.id)] == "":
|
|
slovnik[str(resitel.id)] = 0
|
|
|
|
slovnik[str(resitel.id)] += body
|
|
|
|
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')
|
|
poradi_arg = self.kwargs.get('cislo')
|
|
queryset = queryset.filter(rocnik__rocnik=rocnik_arg, poradi=poradi_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)
|
|
|
|
cislo = context['cislo']
|
|
hlavni_problemy = hlavni_problemy_cisla(cislo)
|
|
# TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé
|
|
# pro každý hlavní problém zavedeme slovník s body za daný hlavní problém
|
|
# pro jednotlivé řešitele (slovník slovníků hlavních problémů)
|
|
hlavni_problemy_slovnik = {}
|
|
for hp in hlavni_problemy:
|
|
hlavni_problemy_slovnik[str(hp.id)] = {}
|
|
|
|
## TODO dostat pro tyto problémy součet v daném čísle pro daného řešitele
|
|
## TODO možná chytřeji vybírat aktivní řešitele
|
|
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají
|
|
# u alespoň jedné hodnoty něco jiného než NULL
|
|
aktivni_resitele = list(Resitel.objects.filter(
|
|
rok_maturity__gte=cislo.rocnik.druhy_rok()))
|
|
# TODO: zkusit hodnoceni__rocnik...
|
|
#.filter(hodnoceni_set__rocnik__eq=cislo_rocnik)
|
|
# zakládání prázdných záznamů pro řešitele
|
|
cislobody = {}
|
|
for ar in aktivni_resitele:
|
|
# řešitele převedeme na řetězec pomocí unikátního id
|
|
cislobody[str(ar.id)] = ""
|
|
for hp in hlavni_problemy:
|
|
slovnik = hlavni_problemy_slovnik[str(hp.id)]
|
|
slovnik[str(ar.id)] = ""
|
|
|
|
# vezmeme všechna řešení s body do daného čísla
|
|
reseni_do_cisla = Reseni.objects.prefetch_related('problem', 'resitele', 'hodnoceni_set').filter(hodnoceni__cislo_body=cislo)
|
|
|
|
# projdeme všechna řešení do čísla a přičteme body každému řešiteli do celkových
|
|
# bodů i do bodů za problém
|
|
for reseni in reseni_do_cisla:
|
|
|
|
# řešení může řešit více problémů
|
|
for prob in list(reseni.problem.all()):
|
|
nadproblem = hlavni_problem(prob)
|
|
nadproblem_slovnik = hlavni_problemy_slovnik[str(nadproblem.id)]
|
|
|
|
# a více hodnocení
|
|
for hodn in list(reseni.hodnoceni_set.all()):
|
|
body = hodn.body
|
|
|
|
# a více řešitelů
|
|
for resitel in list(reseni.resitele.all()):
|
|
pricti_body(cislobody, resitel, body)
|
|
pricti_body(nadproblem_slovnik, resitel, body)
|
|
|
|
# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně
|
|
resitel_rocnikbody_slov = body_resitelu_za_rocnik(cislo.rocnik, aktivni_resitele)
|
|
resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(),
|
|
key = lambda x: x[1], reverse = True)
|
|
|
|
# získáme body odjakživa
|
|
resitel_odjakzivabody_slov = body_resitelu_odjakziva(cislo.rocnik.druhy_rok(),
|
|
aktivni_resitele)
|
|
|
|
# řešitelé setřídění podle bodů za číslo sestupně
|
|
setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn]
|
|
setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id]
|
|
|
|
# vytvoříme jednotlivé sloupce výsledkovky
|
|
radky_vysledkovky = []
|
|
odjakziva_body = []
|
|
rocnik_body = []
|
|
cislo_body = []
|
|
hlavni_problemy_body = []
|
|
for ar_id in setrizeni_resitele_id:
|
|
# vytáhneme ze slovníků body pro daného řešitele
|
|
odjakziva_body.append(resitel_odjakzivabody_slov[ar_id])
|
|
rocnik_body.append(resitel_rocnikbody_slov[ar_id])
|
|
cislo_body.append(cislobody[ar_id])
|
|
problemy = []
|
|
for hp in hlavni_problemy:
|
|
problemy.append(hlavni_problemy_slovnik[str(hp.id)][ar_id])
|
|
hlavni_problemy_body.append(problemy)
|
|
print("{}: body za problémy - {}, číslobody - {}, ročníkbody - {}, odjakživabody - ".format(ar_id, problemy, cislobody[ar_id], resitel_rocnikbody_slov[ar_id]))
|
|
# pořadí určíme pomocí funkce, které dáme celkové body za ročník vzestupně
|
|
poradi = sloupec_s_poradim(rocnik_body)
|
|
|
|
radky_vysledkovky = []
|
|
for i in range(0, len(setrizeni_resitele_id)):
|
|
radek = RadekVysledkovky(poradi[i], setrizeni_resitele[i],
|
|
hlavni_problemy_body[i], cislo_body[i], rocnik_body[i],
|
|
odjakziva_body[i])
|
|
radky_vysledkovky.append(radek)
|
|
print("Přikládám {}-tý řádek.".format(i))
|
|
|
|
print("Následuje předávání do kontextu.")
|
|
# vytahané informace předáváme do kontextu
|
|
context['cislo'] = cislo
|
|
context['radky_vysledkovky'] = radky_vysledkovky
|
|
context['problemy'] = hlavni_problemy
|
|
# context['v_cisle_zadane'] = TODO
|
|
# context['resene_problemy'] = resene_problemy
|
|
print("Předávám kontext.")
|
|
return context
|
|
|
|
# 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)):
|
|
# #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
|
|
|
|
class ArchivTemataView(generic.ListView):
|
|
model = Problem
|
|
template_name = 'seminar/archiv/temata.html'
|
|
queryset = Tema.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod')
|
|
|
|
### Generovani vysledkovky
|
|
|
|
#class CisloVysledkovkaView(CisloView):i
|
|
# poradi | titul. jmeno prijmeni | ulohy | za cislo | celkem | odjakziva
|
|
#
|
|
#
|
|
#
|
|
# 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 oldObalkovaniView(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
|
|
# FIXME: prepsat, aby nepouzivalo VysledkyK...
|
|
#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 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
|
|
|
|
# FIXME: clanky jsou vsechny, pokud budou i neresitelske, tak se take zobrazi
|
|
class ClankyResitelView(generic.ListView):
|
|
model = Problem
|
|
template_name = 'seminar/clanky/resitelske_clanky.html'
|
|
queryset = Clanek.objects.filter(stav=Problem.STAV_ZADANY).select_related('cislo_zadani__rocnik').order_by('-cislo_zadani__rocnik__rocnik', 'kod')
|
|
|
|
# FIXME: pokud chceme orgoclanky, tak nejak zavest do modelu a podle toho odkomentovat a upravit
|
|
#class ClankyOrganizatorView(generic.ListView)<F12>:
|
|
# model = Problem
|
|
# template_name = 'seminar/clanky/organizatorske_clanky.html'
|
|
# queryset = Problem.objects.filter(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 TeXUploadLoginView(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 as 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.poradi,
|
|
} for p in resene
|
|
],
|
|
}
|
|
|
|
cislo.faze = Cislo.FAZE_TEX
|
|
cislo.save()
|
|
return JsonResponse(response)
|
|
|
|
class ResitelView(LoginRequiredMixin,generic.DetailView):
|
|
model = Resitel
|
|
template_name = 'seminar/resitel.html'
|
|
|
|
def get_object(self, queryset=None):
|
|
print(self.request.user)
|
|
return Resitel.objects.get(osoba__user=self.request.user)
|
|
|
|
## Formulare
|
|
class AddSolutionView(LoginRequiredMixin, FormView):
|
|
template_name = 'seminar/org/vloz_reseni.html'
|
|
form_class = f.VlozReseniForm
|
|
success_url = '/'
|
|
|
|
class SubmitSolutionView(LoginRequiredMixin, CreateView):
|
|
model = s.Reseni
|
|
template_name = 'seminar/nahraj_reseni.html'
|
|
form_class = f.NahrajReseniForm
|
|
success_url = '/'
|
|
|
|
def get_context_data(self,**kwargs):
|
|
data = super().get_context_data(**kwargs)
|
|
if self.request.POST:
|
|
data['prilohy'] = f.ReseniSPrilohamiFormSet(self.request.POST,self.request.FILES)
|
|
else:
|
|
data['prilohy'] = f.ReseniSPrilohamiFormSet()
|
|
return data
|
|
|
|
# FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni
|
|
# Inspirace: https://stackoverflow.com/questions/41599809/using-a-django-filefield-in-an-inline-formset
|
|
def form_valid(self,form):
|
|
context = self.get_context_data()
|
|
prilohy = context['prilohy']
|
|
if not prilohy.is_valid():
|
|
return super().form_invalid(form)
|
|
with transaction.atomic():
|
|
self.object = form.save()
|
|
self.object.resitele.add(Resitel.objects.get(osoba__user = self.request.user))
|
|
self.object.cas_doruceni = timezone.now()
|
|
self.object.forma = s.Reseni.FORMA_UPLOAD
|
|
self.object.save()
|
|
|
|
prilohy.instance = self.object
|
|
prilohy.save()
|
|
|
|
return HttpResponseRedirect(self.get_success_url())
|
|
|
|
|
|
|
|
|
|
def resetPasswordView(request):
|
|
pass
|
|
|
|
def loginView(request):
|
|
if request.method == 'POST':
|
|
form = LoginForm(request.POST)
|
|
if form.is_valid():
|
|
user = authenticate(request,
|
|
username=form.cleaned_data['username'],
|
|
password=form.cleaned_data['password'])
|
|
print(form.cleaned_data)
|
|
if user is not None:
|
|
login(request,user)
|
|
return HttpResponseRedirect('/')
|
|
else:
|
|
return render(request,
|
|
'seminar/login.html',
|
|
{'form': form, 'login_error': 'Neplatné jméno nebo heslo'})
|
|
|
|
else:
|
|
form = LoginForm()
|
|
return render(request, 'seminar/login.html', {'form': form})
|
|
|
|
def logoutView(request):
|
|
form = LoginForm()
|
|
if request.user.is_authenticated:
|
|
logout(request)
|
|
return render(request, 'seminar/login.html', {'form': form, 'login_error': 'Byli jste úspěšně odhlášeni'})
|
|
return render(request, 'seminar/login.html', {'form': form})
|
|
|
|
|
|
def prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data):
|
|
msg = "{}, form_hash:{}".format(msg,hash(form_data))
|
|
logger.warn(msg)
|
|
gdpr_logger.warn(msg+", form:{}".format(form_data))
|
|
|
|
from django.forms.models import model_to_dict
|
|
def resitelEditView(request):
|
|
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
|
## Načtení objektu Osoba a Resitel, patrici k aktuálně přihlášenému uživately
|
|
u = request.user
|
|
osoba_edit = Osoba.objects.get(user=u)
|
|
resitel_edit = osoba_edit.resitel
|
|
user_edit = osoba_edit.user
|
|
## Vytvoření slovníku, kterým předvyplním formulář
|
|
prefill_1=model_to_dict(user_edit)
|
|
prefill_2=model_to_dict(resitel_edit)
|
|
prefill_3=model_to_dict(osoba_edit)
|
|
prefill_1.update(prefill_2)
|
|
prefill_1.update(prefill_3)
|
|
form = ProfileEditForm(initial=prefill_1)
|
|
## Změna údajů a jejich uložení
|
|
if request.method == 'POST':
|
|
form = ProfileEditForm(request.POST)
|
|
if form.is_valid():
|
|
## Změny v osobě
|
|
fcd = form.cleaned_data
|
|
osoba_edit.jmeno = fcd['jmeno']
|
|
osoba_edit.prijmeni = fcd['prijmeni']
|
|
osoba_edit.pohlavi_muz = fcd['pohlavi_muz']
|
|
osoba_edit.email = fcd['email']
|
|
osoba_edit.telefon = fcd['telefon']
|
|
osoba_edit.ulice = fcd['ulice']
|
|
osoba_edit.mesto = fcd['mesto']
|
|
osoba_edit.psc = fcd['psc']
|
|
## Změny v osobě s podmínkami
|
|
if fcd.get('spam',False):
|
|
osoba_edit.datum_souhlasu_zasilani = date.today()
|
|
if fcd.get('stat','') in ('CZ','SK'):
|
|
osoba_edit.stat = fcd['stat']
|
|
else:
|
|
## Neznámá země
|
|
msg = "Unknown country {}".format(fcd['stat_text'])
|
|
|
|
## Změny v řešiteli
|
|
resitel_edit.skola = fcd['skola']
|
|
resitel_edit.rok_maturity = fcd['rok_maturity']
|
|
resitel_edit.zasilat = fcd['zasilat']
|
|
if fcd.get('skola'):
|
|
resitel_edit.skola = fcd['skola']
|
|
else:
|
|
# Unknown school - log it
|
|
msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa'])
|
|
resitel_edit.save()
|
|
osoba_edit.save()
|
|
return HttpResponseRedirect('/thanks/')
|
|
else:
|
|
## Stránka před odeslaním formuláře = předvyplněný formulář
|
|
return render(request, 'seminar/edit.html', {'form': form})
|
|
|
|
def prihlaskaView(request):
|
|
generic_logger = logging.getLogger('seminar.prihlaska')
|
|
err_logger = logging.getLogger('seminar.prihlaska.problem')
|
|
form_logger = logging.getLogger('seminar.prihlaska.form')
|
|
if request.method == 'POST':
|
|
form = PrihlaskaForm(request.POST)
|
|
# TODO vyresit, co se bude v jakych situacich zobrazovat
|
|
if form.is_valid():
|
|
generic_logger.info("Form valid")
|
|
fcd = form.cleaned_data
|
|
form_hash = hash(fcd)
|
|
form_logger.info(fcd,form_hash=form_hash)
|
|
|
|
with transaction.atomic():
|
|
u = User.objects.create_user(
|
|
username=fcd['username'],
|
|
password=fcd['password'],
|
|
email = fcd['email'])
|
|
u.save()
|
|
|
|
o = Osoba(
|
|
jmeno = fcd['jmeno'],
|
|
prijmeni = fcd['prijmeni'],
|
|
pohlavi_muz = fcd['pohlavi_muz'],
|
|
email = fcd['email'],
|
|
telefon = fcd.get('telefon',''),
|
|
datum_narozeni = fcd.get('datum_narozeni',None),
|
|
datum_souhlasu_udaje = date.today(),
|
|
datum_registrace = date.today(),
|
|
ulice = fcd.get('ulice',''),
|
|
mesto = fcd.get('mesto',''),
|
|
psc = fcd.get('psc',''),
|
|
poznamka = str(fcd)
|
|
)
|
|
if fcd.get('spam',False):
|
|
o.datum_souhlasu_zasilani = date.today()
|
|
if fcd.get('stat','') in ('CZ','SK'):
|
|
o.stat = fcd['stat']
|
|
else:
|
|
# Unknown country - log it
|
|
msg = "Unknown country {}".format(fcd['stat_text'])
|
|
err_logger.warn(msg,form_hash=form_hash)
|
|
|
|
o.save()
|
|
o.user = u
|
|
o.save()
|
|
|
|
r = Resitel(
|
|
rok_maturity = fcd['rok_maturity'],
|
|
zasilat = fcd['zasilat']
|
|
)
|
|
|
|
r.save()
|
|
r.osoba = o
|
|
if fcd.get('skola'):
|
|
r.skola = fcd['skola']
|
|
else:
|
|
# Unknown school - log it
|
|
msg = "Unknown school {}, {}".format(fcd['skola_nazev'],fcd['skola_adresa'])
|
|
err_logger.warn(msg,form_hash=form_hash)
|
|
r.save()
|
|
|
|
|
|
return HttpResponseRedirect('/thanks/')
|
|
|
|
# if a GET (or any other method) we'll create a blank form
|
|
else:
|
|
form = PrihlaskaForm()
|
|
|
|
return render(request, 'seminar/prihlaska.html', {'form': form})
|
|
|
|
|
|
|
|
|
|
|
|
# FIXME: Tohle asi vlastně vůbec nepatří do aplikace 'seminar'
|
|
class LoginView(auth_views.LoginView):
|
|
# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL
|
|
template_name = 'seminar/login.html'
|
|
|
|
# Přesměrovací URL má být v kontextu:
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx['next'] = reverse('titulni_strana')
|
|
return ctx
|
|
|
|
class LogoutView(auth_views.LogoutView):
|
|
# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL
|
|
template_name = 'seminar/logout.html'
|
|
# Pavel: Vůbec nevím, proč to s _lazy funguje, ale bez toho to bylo rozbité.
|
|
next_page = reverse_lazy('titulni_strana')
|
|
|
|
# "Chci resetovat heslo"
|
|
class PasswordResetView(auth_views.PasswordResetView):
|
|
#template_name = 'seminar/password_reset.html'
|
|
# TODO: vlastní email_template_name a subject_template_name a html_email_template_name
|
|
success_url = reverse_lazy('reset_password_done')
|
|
from_email = 'login@mam.mff.cuni.cz'
|
|
|
|
# "Poslali jsme e-mail (pokud bylo kam))"
|
|
class PasswordResetDoneView(auth_views.PasswordResetDoneView):
|
|
#template_name = 'seminar/password_reset_done.html'
|
|
pass
|
|
|
|
# "Vymysli si heslo"
|
|
class PasswordResetConfirmView(auth_views.PasswordResetConfirmView):
|
|
#template_name = 'seminar/password_confirm_done.html'
|
|
success_url = reverse_lazy('reset_password_complete')
|
|
|
|
# "Heslo se asi změnilo."
|
|
class PasswordResetCompleteView(auth_views.PasswordResetCompleteView):
|
|
#template_name = 'seminar/password_complete_done.html'
|
|
pass
|
|
|
|
class PasswordChangeView(auth_views.PasswordChangeView):
|
|
#template_name = 'seminar/password_change.html'
|
|
success_url = reverse_lazy('titulni_strana')
|
|
|