# 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 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 from dal import autocomplete import seminar.models as s from .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 . import utils from .unicodecsv import UnicodeWriter from .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 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, } ) # TODO Napsat tuto funkci znovu rekurzivně podle Jethrorad. Potom se podívat, jak lehce se dá modifikovat pro Rozcestník. Pokud lehce, rozšířit ji. Pokud složitě - použít tuhle def vytahniZLesaSeznam(tematko, koren, pouze_zajimave=False): returnVal = [] stack = [] stack.append((koren.first_child, 0, False)) #Tuple of node, depth and relevance while len(stack) > 0: wn, wd, wr = stack.pop() if wn.succ != None: stack.append((wn.succ, wd, wr)) if isinstance(wn, s.TemaVCisleNode): print("TEMA") print(wn.tema.id) print(tematko.id) if wn.tema.id == tematko.id: returnVal.append((posledni_cislo, 0)) print("PRIDANO") wr = True wd = 1 if wn.srolovatelne: tagOpen = s.Text(na_web = "Otevírací srolovací tag") tagOpenNode = s.TextNode(text = tagOpen) tagClose = s.Text(na_web = "Zavírací srolovací tag") tagCloseNode = s.TextNode(text = tagClose) stack.append((tagCloseNode, wd, True)) if wn.first_child != None: stack.append((wn.first_child, wd + 1, wr)) if isinstance(wn, s.CisloNode): posledni_cislo = wn print(wn) if wr: print("ZAJIMAVE") if pouze_zajimave: if not wn.zajimave: continue returnVal.append((wn, wd)) return returnVal 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 cisla = Cislo.objects.filter(verejne_db=True)[:10] png_dir = op.join(settings.MEDIA_ROOT, "cislo", "png") # seznam [(url obrázku, číslo)] urls = [] for i, c in enumerate(cisla): if not c.pdf: continue filename = os.path.split(c.pdf.file.name)[1].split(".")[0] png_filename = "{}-{}px.png".format(filename, vyska) # Pokud obrázek neexistuje nebo není aktuální, vytvoř jej png_path = op.join(png_dir, png_filename) if not op.exists(png_path) or \ op.getmtime(png_path) < op.getmtime(c.pdf.path): subprocess.call([ "convert", "-density", "180x180", "-geometry", "{}x{}".format(vyska, vyska), "-background", "white", "-flatten", "-rotate", str(90 * i), "{}[0]".format(c.pdf.path), # titulní strana png_path ]) urls.append( (op.join(settings.MEDIA_URL, "cislo", "png", png_filename), c) ) vyska, sirka = sirka, vyska / 2 tags = [] def spirala(urls, tags, idx): """Rekurzivně prochází urls a generuje strom elementů do tags""" if idx >= len(urls): return img_url, cislo = urls[idx] tags.append( "
" .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("".format( cislo.verejne_url(), cislo.kod() )) tags.append( "" .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("") spirala(urls, tags, idx + 1) tags.append("
") 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 = Reseni.objects.filter(resitele__in=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) # 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: while not(p.nadproblem == None): p = p.nadproblem hlavni_problemy.append(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_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 #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 VysledkyResitele(object): """Pro daného řešitele ukládá počet bodů za jednotlivé úlohy a celkový počet bodů za konkrétní ročník do daného čísla a za dané číslo.""" def __init__(self, resitel, cislo, rocnik): self.resitel = resitel self.cislo = cislo self.body_cislo = body_resitele_v_cisle(resitel, cislo) self.body = [] self.rocnik = rocnik self.body_rocnik = body_resitele_v_rocniku(resitel, rocnik, cislo) self.body_celkem_odjakziva = resitel.vsechny_body() self.poradi = 0 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) ## TODO upravit dle nového modelu cislo = context['cislo'] hlavni_problemy = hlavni_problemy_cisla(cislo) ## 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 ## chceme letos něco poslal aktivni_resitele = Resitel.objects.filter( rok_maturity__gte=cislo.rocnik.druhy_rok()) # TODO: zkusit hodnoceni__rocnik... #.filter(hodnoceni_set__rocnik__eq=cislo_rocnik) radky_vysledkovky = [] for ar in aktivni_resitele: # získáme výsledky řešitele - součty přes číslo a ročník vr = VysledkyResitele(ar, cislo, cislo.rocnik) for hp in hlavni_problemy: vr.body.append( body_resitele_problemu_v_cisle(hp, ar, cislo)) radky_vysledkovky.append(vr) # setřídíme řádky výsledkovky/objekty VysledkyResitele podle bodů radky_vysledkovky.sort(key=lambda vr: vr.body_rocnik, reverse=True) # generujeme sloupec s pořadím pomocí stejně zvané funkce pocty_bodu = [rv.body_rocnik for rv in radky_vysledkovky] sloupec_poradi = sloupec_s_poradim(pocty_bodu) # každému řádku výsledkovky přidáme jeho pořadí i = 0 for rv in radky_vysledkovky: rv.poradi = sloupec_poradi[i] i = 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 #XXX testovat #XXX opravit to, že se nezobrazují body za jednotlivé úlohy 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): # 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 = '/' 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}) class SkolaAutocomplete(autocomplete.Select2QuerySetView): def get_queryset(self): # Don't forget to filter out results depending on the visitor ! qs = Skola.objects.all() if self.q: qs = qs.filter( Q(nazev__istartswith=self.q)| Q(kratky_nazev__istartswith=self.q)| Q(ulice__istartswith=self.q)| Q(mesto__istartswith=self.q)) return qs class LoginRequiredAjaxMixin(object): def dispatch(self, request, *args, **kwargs): #if request.is_ajax() and not request.user.is_authenticated: # Pokud to otevřu jako stránku, tak se omezení neuplatní, takže to asi nechceme if not request.user.is_authenticated: return JsonResponse(data={'results': [], 'pagination': {}}, status=401) return super(LoginRequiredAjaxMixin, self).dispatch(request, *args, **kwargs) class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetView): def get_queryset(self): qs = Resitel.objects.all() if self.q: qs = qs.filter( Q(osoba__jmeno__startswith=self.q)| Q(osoba__prijmeni__startswith=self.q)| Q(osoba__prezdivka__startswith=self.q) ) return qs # Ceka na autocomplete v3 # class OrganizatorAutocomplete(autocomplete.Select2QuerySetView): # def get_queryset(self): # if not self.request.user.is_authenticated(): # return Organizator.objects.none() # # qs = aktivniOrganizatori() # # if self.q: # if self.q[0] == "!": # qs = Organizator.objects.all() # query = self.q[1:] # else: # query = self.q # qs = qs.filter( # Q(prezdivka__isstartswith=query)| # Q(user__first_name__isstartswith=query)| # Q(user__last_name__isstartswith=query)) # # return qs # 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')