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.
 
 
 
 
 
 

1446 lines
50 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
import time
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
# 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
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)
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é už 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=cislo) ))
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 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(Resitel.objects.filter(
rok_maturity__gte=rocnik.druhy_rok()))
# TODO: zkusit hodnoceni__rocnik...
#.filter(hodnoceni_set__rocnik__eq=cislo_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 = [dvojice[0] for dvojice in resitel_rocnikbody_sezn]
setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id]
setrizene_body = [dvojice[1] for dvojice in 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(rocnik, aktivni_resitele):
""" Spočítá body za ročník, setřídí je sestupně a vrátí jako seznam."""
# 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, rocnik, 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()):
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(Resitel.objects.filter(
rok_maturity__gte=cislo.rocnik.druhy_rok()))
# TODO: zkusit hodnoceni__rocnik...
#.filter(hodnoceni_set__rocnik__eq=cislo_rocnik)
# 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.rocnik, 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):
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)
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):
"""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
### 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
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'
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')
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')