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.

1262 lines
45 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 seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm
import seminar.forms as f
9 years ago
from datetime import timedelta, date, datetime
from django.utils import timezone
from itertools import groupby
from collections import OrderedDict
9 years ago
import tempfile
import subprocess
import shutil
import os
import os.path as op
9 years ago
from django.conf import settings
import unicodedata
import json
import traceback
import sys
import csv
import logging
import time
from seminar.utils import aktivniResitele, resi_v_rocniku
# ze starého modelu
#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 Problem.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
# TODO Co chceme vlastně zobrazovat na této stránce? Zatím je zde aktuální číslo, ale může tu být cokoli jiného...
class AktualniZadaniView(TreeNodeView):
def get_object(self):
nastaveni = get_object_or_404(Nastaveni)
return nastaveni.aktualni_cislo.cislonode
def get_context_data(self,**kwargs):
nastaveni = get_object_or_404(Nastaveni)
context = super().get_context_data(**kwargs)
verejne = nastaveni.aktualni_cislo.verejne()
context['verejne'] = verejne
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
def spravne_novinky(request):
"""
Vrátí správný QuerySet novinek, tedy ten, který daný uživatel smí vidět.
Tj. Organizátorům všechny, ostatním jen veřejné
"""
user = request.user
# Využíváme líné vyhodnocování QuerySetů
qs = Novinky.objects.all()
# TODO: Tohle by mělo spíš kontrolovat, že je/není někdo org, než že může do Adminu.
if not user.is_staff:
qs = qs.filter(zverejneno=True)
return qs.order_by('-datum')
def aktualni_temata(rocnik):
"""
Vrací PolymorphicQuerySet témat v daném ročníku, ke kterým se aktuálně něco odevzdat.
"""
return Tema.objects.filter(rocnik=rocnik, stav='zadany').order_by('kod')
class TitulniStranaView(generic.ListView):
template_name='seminar/titulnistrana.html'
def get_queryset(self):
return spravne_novinky(self.request)[:3]
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
# Aktuální témata
nazvy_a_odkazy_na_aktualni_temata = []
akt_temata = aktualni_temata(nastaveni.aktualni_rocnik)
for tema in akt_temata:
# FIXME: netuším, jestli funguje tema.verejne_url(), nemáme testdata na témátka - je to asi url vzhledem k ročníku
nazvy_a_odkazy_na_aktualni_temata.append({'nazev':tema.nazev,'url':tema.verejne_url()})
context['aktualni_temata'] = nazvy_a_odkazy_na_aktualni_temata
print(context)
return context
class StareNovinkyView(generic.ListView):
template_name = 'seminar/stare_novinky.html'
def get_queryset(self):
return spravne_novinky(self.request)
### 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)
cisla = Cislo.objects.filter(poradi=1)
urls ={}
for i, c in enumerate(cisla):
if c.titulka_nahled:
urls[c.rocnik] = c.titulka_nahled.url
else:
urls[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", "default.png")
context["object_list"] = urls
return context
### Výsledky
def sloupec_s_poradim(setrizene_body):
"""
Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník
vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.),
podle toho, jak jdou za sebou ve výsledkovce.
Parametr:
setrizene_body (seznam integerů): sestupně setřízená čísla
Výstup:
sloupec_s_poradim (seznam stringů)
"""
# ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím
aktualni_poradi = 1
sloupec_s_poradim = []
# seskupíme seznam všech bodů podle hodnot
for index in range(0, len(setrizene_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 setrizene_body[index] == setrizene_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(setrizene_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
def cisla_rocniku(rocnik, jen_verejne=True):
"""
Vrátí všechna čísla daného ročníku.
Parametry:
rocnik (Rocnik): ročník semináře
jen_verejne (bool): zda se mají vrátit jen veřejná, nebo všechna čísla
Vrátí:
seznam objektů typu Cislo
"""
if jen_verejne:
return rocnik.verejna_cisla()
else:
return rocnik.cisla.all()
def hlavni_problem(problem):
""" Pro daný problém vrátí jeho nejvyšší nadproblém."""
while not(problem.nadproblem == None):
problem = problem.nadproblem
return problem
def hlavni_problemy_rocniku(rocnik, jen_verejne=True):
""" Pro zadaný ročník vrátí hlavní problémy ročníku,
tj. ty, které nemají nadproblém."""
hlavni_problemy = []
for cislo in cisla_rocniku(rocnik, jen_verejne):
for problem in hlavni_problemy_cisla(cislo):
hlavni_problemy.append(problem)
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 pořadí
return hlavni_problemy
def hlavni_problemy_cisla(cislo):
""" Vrátí seznam všech problémů s body v daném čísle, které již nemají nadproblém. """
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
def body_resitelu(resitele, za, odjakziva=True):
""" Funkce počítající počty bodů pro zadané řešitele,
buď odjakživa do daného ročníku/čísla anebo za daný ročník/číslo.
Parametry:
resitele (seznam obsahující položky typu Resitel): aktivní řešitelé
za (Rocnik/Cislo): za co se mají počítat body
(generování starších výsledkovek)
odjakziva (bool): zda se mají počítat body odjakživa, nebo jen za číslo/ročník
zadané v "za"
Výstup:
slovník (Resitel.id):body
"""
resitele_id = [r.id for r in resitele]
# Zjistíme, typ objektu v parametru "za"
if isinstance(za, Rocnik):
cislo = None
rocnik = za
rok = rocnik.prvni_rok
elif isinstance(za, Cislo):
cislo = za
rocnik = None
rok = cislo.rocnik.prvni_rok
else:
assert True, "body_resitelu: za není ani číslo ani ročník."
# Kvůli rychlosti používáme sčítáme body už v databázi, viz
# https://docs.djangoproject.com/en/3.0/topics/db/aggregation/,
# sekce Filtering on annotations (protože potřebujeme filtrovat výsledky
# jen do nějakého data, abychom uměli správně nagenerovat výsledkovky i
# za historická čísla.
# Níže se vytváří dotaz na součet bodů za správně vyfiltrovaná hodnocení,
# který se použije ve výsledném dotazu.
if cislo and odjakziva: # Body se sčítají odjakživa do zadaného čísla.
# Vyfiltrujeme všechna hodnocení, která jsou buď ze starších ročníků,
# anebo ze stejného ročníku, jak je zadané číslo, tam ale sčítáme jen
# pro čísla s pořadím nejvýše stejným, jako má zadané číslo.
body_k_zapocteni = Sum('reseni__hodnoceni__body',
filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lt=rok) |
Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok,
reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) ))
elif cislo and not odjakziva: # Body se sčítají za dané číslo.
body_k_zapocteni = Sum('reseni__hodnoceni__body',
filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok,
reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) ))
elif rocnik and odjakziva: # Spočítáme body za starší ročníky až do zadaného včetně.
body_k_zapocteni = Sum('reseni__hodnoceni__body',
filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok))
elif rocnik and not odjakziva: # Spočítáme body za daný ročník.
body_k_zapocteni = Sum('reseni__hodnoceni__body',
filter= Q(reseni__hodnoceni__cislo_body__rocnik=rocnik))
else:
assert True, "body_resitelu: Neplatná kombinace za a odjakživa."
# Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů
resitele_s_body = Resitel.objects.filter(id__in=resitele_id).annotate(
body=body_k_zapocteni)
# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník
# indexovaný řešitelským id obsahující body.
# Pokud jsou body None, nahradíme za 0.
slovnik = {int(res.id) : (res.body if res.body else 0) for res in resitele_s_body}
return slovnik
class RadekVysledkovkyRocniku(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_cisla_sezn, body_rocnik, body_odjakziva, rok):
self.poradi = poradi
self.resitel = resitel
self.rocnik_resitele = resitel.rocnik(rok)
self.body_rocnik = body_rocnik
self.body_celkem_odjakziva = body_odjakziva
self.body_cisla_sezn = body_cisla_sezn
self.titul = resitel.get_titul(body_odjakziva)
def setrid_resitele_a_body(slov_resitel_body):
setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body]
setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id]
setrizene_body = [dvojice[1] for dvojice in slov_resitel_body]
return setrizeni_resitele_id, setrizeni_resitele, setrizene_body
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"
"""
## 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(resi_v_rocniku(rocnik))
cisla = cisla_rocniku(rocnik, jen_verejne)
body_cisla_slov = {}
for cislo in cisla:
# získáme body za číslo
_, cislobody = secti_body_za_cislo(cislo, aktivni_resitele)
body_cisla_slov[cislo.id] = cislobody
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně
resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele)
# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší
setrizeni_resitele_id, setrizeni_resitele, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn)
poradi = sloupec_s_poradim(setrizene_body)
# získáme body odjakživa
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik)
# vytvoříme jednotlivé sloupce výsledkovky
radky_vysledkovky = []
i = 0
for ar_id in setrizeni_resitele_id:
# seznam počtu bodů daného řešitele pro jednotlivá čísla
body_cisla_sezn = []
for cislo in cisla:
body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id])
# vytáhneme informace pro daného řešitele
radek = RadekVysledkovkyRocniku(
poradi[i], # pořadí
Resitel.objects.get(id=ar_id), # řešitel (z id)
body_cisla_sezn, # seznam bodů za čísla
setrizene_body[i], # body za ročník (spočítané výše s pořadím)
resitel_odjakzivabody_slov[ar_id], # body odjakživa
rocnik) # ročník semináře pro získání ročníku řešitele
radky_vysledkovky.append(radek)
i += 1
return radky_vysledkovky
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)
# vysledkovka = True zajistí vykreslení,
# zkontrolovat, kdy se má a nemá vykreslovat
context['vysledkovka'] = True
context['cisla_s_neverejnymi'] = cisla_rocniku(context["rocnik"], jen_verejne=False)
context['cisla'] = cisla_rocniku(context["rocnik"])
context['radky_vysledkovky'] = vysledkovka_rocniku(context["rocnik"])
context['radky_vysledkovky_s_neverejnymi'] = vysledkovka_rocniku(
context["rocnik"], jen_verejne=False)
context['hlavni_problemy_v_rocniku'] = hlavni_problemy_rocniku(context["rocnik"])
context['hlavni_problemy_v_rocniku_s_neverejnymi'] = hlavni_problemy_rocniku(context["rocnik"], jen_verejne=False)
return context
class ProblemView(generic.DetailView):
model = Problem
# Používáme funkci, protože přímo template_name neumí mít v přiřazení dost logiky. Ledaže by se to udělalo polymorfně...
def get_template_names(self, **kwargs):
# FIXME: Switch podle typu není hezký, ale nechtělo se mi to přepisovat celé. Správně by se tohle mělo řešit polymorfismem.
spravne_templaty = {
s.Uloha: "uloha",
s.Tema: "tema",
s.Konfera: "konfera",
s.Clanek: "clanek",
}
context = super().get_context_data(**kwargs)
return ['seminar/archiv/problem_' + spravne_templaty[context['object'].__class__] + '.html']
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Musí se používat context['object'], protože nevíme, jestli dostaneme úložku, téma, článek, .... a tyhle věci vyrábějí různé klíče.
if not context['object'].verejne() and not self.request.user.is_staff:
raise PermissionDenied()
if isinstance(context['object'], Clanek):
context['reseni'] = Reseni.objects.filter(problem=context['object']).select_related('resitel').order_by('resitel__prijmeni')
return context
class RadekVysledkovkyCisla(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, rok):
self.resitel = resitel
self.rocnik_resitele = resitel.rocnik(rok)
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)
def pricti_body(slovnik, resitel, body):
""" Přiřazuje danému řešiteli body do slovníku. """
# 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[resitel.id] == "":
slovnik[resitel.id] = 0
slovnik[resitel.id] += body
def secti_body_za_rocnik(za, aktivni_resitele):
""" Spočítá body za ročník (celý nebo do daného čísla),
setřídí je sestupně a vrátí jako seznam.
Parametry:
za (typu Rocnik nebo Cislo) spočítá za ročník, nebo za ročník do
daného čísla
"""
# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa)
resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False)
# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně
resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(),
key = lambda x: x[1], reverse = True)
return resitel_rocnikbody_sezn
def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None):
""" Spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata)."""
# 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ů)
if hlavni_problemy is None:
hlavni_problemy = hlavni_problemy_cisla(cislo)
hlavni_problemy_slovnik = {}
for hp in hlavni_problemy:
hlavni_problemy_slovnik[hp.id] = {}
# 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[ar.id] = ""
for hp in hlavni_problemy:
slovnik = hlavni_problemy_slovnik[hp.id]
slovnik[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[nadproblem.id]
# a mít více hodnocení
for hodn in list(reseni.hodnoceni_set.all()):
body = hodn.body
# a mít více řešitelů
for resitel in list(reseni.resitele.all()):
if resitel not in aktivni_resitele:
print("Skipping {}".format(resitel.id))
continue
pricti_body(cislobody, resitel, body)
pricti_body(nadproblem_slovnik, resitel, body)
return hlavni_problemy_slovnik, cislobody
def vysledkovka_cisla(cislo, context=None):
if context is None:
context = {}
hlavni_problemy = hlavni_problemy_cisla(cislo)
## 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(aktivniResitele(cislo))
# získáme body za číslo
hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy)
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně
resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo, aktivni_resitele)
# získáme body odjakživa
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo)
# ř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]
# spočítáme pořadí řešitelů
setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn]
poradi = sloupec_s_poradim(setrizeni_resitele_body)
# vytvoříme jednotlivé sloupce výsledkovky
radky_vysledkovky = []
i = 0
for ar_id in setrizeni_resitele_id:
# získáme seznam bodů za problémy pro daného řešitele
problemy = []
for hp in hlavni_problemy:
problemy.append(hlavni_problemy_slovnik[hp.id][ar_id])
# vytáhneme informace pro daného řešitele
radek = RadekVysledkovkyCisla(
poradi[i], # pořadí
Resitel.objects.get(id=ar_id), # řešitel (z id)
problemy, # seznam bodů za hlavní problémy čísla
cislobody[ar_id], # body za číslo
setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím)
resitel_odjakzivabody_slov[ar_id], # body odjakživa
cislo.rocnik) # ročník semináře pro zjištění ročníku řešitele
radky_vysledkovky.append(radek)
i += 1
# 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
return context
class CisloView(generic.DetailView):
# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf
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']
# vrátíme context (aktuálně obsahuje jen věci ohledně výsledkovky
return vysledkovka_cisla(cislo, context)
9 years ago
class ArchivTemataView(generic.ListView):
model = Problem
template_name = 'seminar/archiv/temata.html'
queryset = Tema.objects.filter(stav=Problem.STAV_ZADANY).select_related('rocnik').order_by('rocnik', 'kod')
def get_context_data(self, *args, **kwargs):
ctx = super().get_context_data(*args, **kwargs)
ctx['rocniky'] = OrderedDict()
for rocnik, temata in groupby(ctx['object_list'], lambda tema: tema.rocnik):
ctx['rocniky'][rocnik] = list(temata)
return ctx
9 years ago
### Generovani vysledkovky
class CisloVysledkovkaView(CisloView):
"""View vytvořené pro stránku zobrazující výsledkovku čísla v TeXu."""
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):
""" View vytvořené pro stránku zobrazující výsledkovku ročníku v TeXu."""
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
def cisloObalkyView(request, rocnik, cislo):
realne_cislo = Cislo.objects.get(poradi=cislo, rocnik__rocnik=rocnik)
return obalkyView(request, aktivniResitele(realne_cislo))
9 years ago
def obalkyView(request, resitele):
tex = render(request,'seminar/archiv/obalky.tex', {'resitele': resitele}).content
9 years ago
tempdir = tempfile.mkdtemp()
with open(tempdir+"/obalky.tex","w") as texfile:
texfile.write(tex.decode())
shutil.copy(os.path.join(settings.STATIC_ROOT, 'seminar/lisak.pdf'), tempdir)
subprocess.call(["pdflatex","obalky.tex"], cwd = tempdir)
9 years ago
with open(tempdir+"/obalky.pdf","rb") as pdffile:
response = HttpResponse(pdffile.read(), content_type='application/pdf')
shutil.rmtree(tempdir)
return response
9 years ago
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}
)
9 years ago
### Tituly
def TitulyView(request, rocnik, cislo):
""" View pro stažení makra titulů v TeXu."""
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, poradi = cislo)
asciijmena = []
jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka),
# pokud ano, vrátí se jako true
slovnik_s_body = body_resitelu(resitele, rocnik_obj)
for resitel in resitele:
resitel.titul = resitel.get_titul(slovnik_s_body[resitel.id])
jmeno = resitel.osoba.jmeno+resitel.osoba.prijmeni
# převedeme jména a příjmení řešitelů do ASCII
ascii_jmeno_bytes = unicodedata.normalize('NFKD', jmeno).encode("ascii","ignore")
# vrátí se byte string, převedeme na standardní string
ascii_jmeno_divnoznaky = str(ascii_jmeno_bytes, "utf-8", "ignore").replace(" ","")
resitel.ascii = ''.join(a for a in ascii_jmeno_divnoznaky if a.isalnum())
if resitel.ascii not in asciijmena:
asciijmena.append(resitel.ascii)
else:
jmenovci = True
return render(request, 'seminar/archiv/tituly.tex',
{'resitele': resitele,'jmenovci':jmenovci},content_type="text/plain")
### Soustredeni
class SoustredeniListView(generic.ListView):
model = Soustredeni
template_name = 'seminar/soustredeni/seznam_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 = csv.writer(response)
writer.writerow(["jmeno", "prijmeni", "rok_maturity", "telefon", "email", "ulice", "mesto", "psc","stat"])
for u in ucastnici:
o = u.osoba
writer.writerow([o.jmeno, o.prijmeni, str(u.rok_maturity), o.telefon, o.email, o.ulice, o.mesto, o.psc, o.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(osoba__pohlavi_muz=True)
zeny = Resitel.objects.filter(osoba__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.osoba.jmeno for r in muzi]),
'jmena_zen': utils.histogram([r.osoba.jmeno for r in zeny]),
})
class ResitelView(LoginRequiredMixin,generic.DetailView):
model = Resitel
template_name = 'seminar/profil/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 NahrajReseniView(LoginRequiredMixin, CreateView):
model = s.Reseni
template_name = 'seminar/profil/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/profil/login.html',
{'form': form, 'login_error': 'Neplatné jméno nebo heslo'})
else:
form = LoginForm()
return render(request, 'seminar/profil/login.html', {'form': form})
def logoutView(request):
form = LoginForm()
if request.user.is_authenticated:
logout(request)
return render(request, 'seminar/profil/login.html', {'form': form, 'login_error': 'Byli jste úspěšně odhlášeni'})
return render(request, 'seminar/profil/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/profil/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/profil/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/profil/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/profil/logout.html'
# Pavel: Vůbec nevím, proč to s _lazy funguje, ale bez toho to bylo rozbité.
next_page = reverse_lazy('titulni_strana')
class PasswordResetView(auth_views.PasswordResetView):
""" Chci resetovat heslo. """
#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'
class PasswordResetDoneView(auth_views.PasswordResetDoneView):
""" Poslali jsme e-mail (pokud bylo kam)). """
#template_name = 'seminar/password_reset_done.html'
pass
class PasswordResetConfirmView(auth_views.PasswordResetConfirmView):
""" Vymysli si heslo. """
#template_name = 'seminar/password_confirm_done.html'
success_url = reverse_lazy('reset_password_complete')
class PasswordResetCompleteView(auth_views.PasswordResetCompleteView):
""" Heslo se asi změnilo."""
#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')