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.
 
 
 
 
 
 

1297 lines
43 KiB

from django.shortcuts import get_object_or_404, render, redirect
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.views.generic.base import TemplateView
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, Permission
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import transaction
from django.core import serializers
from django.core.exceptions import PermissionDenied
from django.forms.models import model_to_dict
import seminar.models as s
import seminar.models as m
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
import seminar.templatetags.treenodes as tnltt
import seminar.views.views_rest as vr
from seminar.views.vysledkovka import vysledkovka_rocniku, vysledkovka_cisla
from datetime import timedelta, date, datetime, MAXYEAR
from django.utils import timezone
from itertools import groupby
from collections import OrderedDict
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
from seminar.utils import aktivniResitele, resi_v_rocniku, hlavni_problemy_rocniku, cisla_rocniku, hlavni_problemy_cisla
# 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 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,parent=None, index=None):
self.node = anode
self.sernode = vr.TreeNodeSerializer(anode)
self.children = []
self.parent = parent
self.tema_in_path = False
self.index = index
if parent:
self.tema_in_path = parent.tema_in_path
if isinstance(anode, m.TemaVCisleNode):
self.tema_in_path = True
def add_edit_options(self):
self.deletable = tnltt.deletable(self)
self.editable_siblings = tnltt.editableSiblings(self)
self.editable_children = tnltt.editableChildren(self)
self.text_only_subtree = tnltt.textOnlySubtree(self)
self.can_podvesit_za = tnltt.canPodvesitZa(self)
self.can_podvesit_pred = tnltt.canPodvesitPred(self)
self.appendable_children = tnltt.appendableChildren(self)
print("appChld",self.appendable_children)
if self.parent:
self.appendable_siblings = tnltt.appendableChildren(self.parent)
else:
self.appendable_siblings = []
@classmethod
def public_above(cls, anode):
""" Returns output of verejne for closest Rocnik, Cislo or Problem above.
(All of them have method verejne.)"""
parent = anode # chceme začít už od konkrétního node včetně
while True:
rocnik = isinstance(parent, s.RocnikNode)
cislo = isinstance(parent, s.CisloNode)
uloha = (isinstance(parent, s.UlohaVzorakNode) or
isinstance(parent, s.UlohaZadaniNode))
tema = isinstance(parent, s.TemaVCisleNode)
if (rocnik or cislo or uloha or tema) or parent==None:
break
else:
parent = treelib.get_parent(parent)
if rocnik:
return parent.rocnik.verejne()
elif cislo:
return parent.cislo.verejne()
elif uloha:
return parent.uloha.verejne()
elif tema:
return parent.tema.verejne()
elif None:
print("Existuje TreeNode, který není pod číslem, ročníkem, úlohou"
"ani tématem. {}".format(anode))
return False
@classmethod
def all_public_children(cls, anode):
for ch in treelib.all_children(anode):
if TNLData.public_above(ch):
yield ch
else:
continue
@classmethod
def from_treenode(cls, anode, user, parent=None, index=None):
if TNLData.public_above(anode) or user.has_perm('auth.org'):
out = cls(anode,parent,index)
else:
raise PermissionDenied()
if user.has_perm('auth.org'):
enum_children = enumerate(treelib.all_children(anode))
else:
enum_children = enumerate(TNLData.all_public_children(anode))
for (idx,ch) in enum_children:
outitem = cls.from_treenode(ch, user, out, idx)
out.children.append(outitem)
out.add_edit_options()
return out
@classmethod
def from_tnldata_list(cls, tnllist):
"""Vyrobíme virtuální TNL, který nemá obsah, ale má za potomky všechna zadaná TNLData"""
result = cls(None)
for idx, tnl in enumerate(tnllist):
result.children.append(tnl)
tnl.parent = result
tnl.index = idx
result.add_edit_options()
return result
@classmethod
def filter_treenode(cls, treenode, predicate):
tnll = cls._filter_treenode_recursive(treenode, predicate) # TreeNodeList List :-)
return TNLData.from_tnldata_list(tnll)
@classmethod
def _filter_treenode_recursive(cls, treenode, predicate):
if predicate(treenode):
return [cls.from_treenode(treenode)]
else:
found = []
for tn in all_children(treenode):
result = cls.filter_treenode(tn, predicate)
# Result by v tuhle chvíli měl být seznam TNLDat odpovídající treenodům, jež matchnuly predikát.
for tnl in result:
found.append(tnl)
return found
def to_json(self):
#self.node = anode
#self.children = []
#self.parent = parent
#self.tema_in_path = False
#self.index = index
out = {}
out['node'] = self.sernode.data
out['children'] = [n.to_json() for n in self.children]
out['tema_in_path'] = self.tema_in_path
out['index'] = self.index
out['deletable'] = self.deletable
out['editable_siblings'] = self.editable_siblings
out['editable_children'] = self.editable_children
out['text_only_subtree'] = self.text_only_subtree
out['can_podvesit_za'] = self.can_podvesit_za
out['can_podvesit_pod'] = self.can_podvesit_pred
out['appendable_children'] = self.appendable_children
out['appendable_siblings'] = self.appendable_siblings
return out
def __repr__(self):
return("TNL({})".format(self.node))
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'] = TNLData.from_treenode(self.object,self.request.user)
return context
class TreeNodeJSONView(generic.DetailView):
model = s.TreeNode
def get(self,request,*args, **kwargs):
self.object = self.get_object()
data = TNLData.from_treenode(self.object,self.request.user).to_json()
return JsonResponse(data)
class TreeNodePridatView(generic.View):
type_from_str = {
'rocnikNode': m.RocnikNode,
'cisloNode': m.CisloNode,
'castNode': m.CastNode,
'textNode': m.TextNode,
'temaVCisleNode': m.TemaVCisleNode,
'reseniNode': m.ReseniNode,
'ulohaZadaniNode': m.UlohaZadaniNode,
'ulohaVzorakNode': m.UlohaVzorakNode,
'pohadkaNode': m.PohadkaNode,
'orgText': m.OrgTextNode,
}
def post(self, request, *args, **kwargs):
######## FIXME: ROZEPSANE, NEFUNGUJE, DOPSAT !!!!!! ###########
node = s.TreeNode.objects.get(pk=self.kwargs['pk'])
kam = self.kwargs['kam']
co = self.kwargs['co']
typ = self.type_from_str[co]
raise NotImplementedError('Neni to dopsane, dopis to!')
if kam not in ('pred','syn','za'):
raise ValidationError('Přidat lze pouze před nebo za node nebo jako syna')
if co == m.TextNode:
new_obj = m.Text()
new_obj.save()
elif co == m.CastNode:
new_obj = m.CastNode()
new_obj.nadpis = request.POST.get('pridat-castNode-{}-{}'.format(node.id,kam))
new_obj.save()
elif co == m.ReseniNode:
new_obj = m
pass
elif co == m.UlohaZadaniNode:
pass
elif co == m.UlohaReseniNode:
pass
else:
new_obj = None
if kam == 'pred':
pass
if kam == 'syn':
if typ == m.TextNode:
text_obj = m.Text()
text_obj.save()
node = treelib.create_child(node,typ,text=text_obj)
else:
node = treelib.create_child(node,typ)
if kam == 'za':
if typ == m.TextNode:
text_obj = m.Text()
text_obj.save()
node = treelib.create_node_after(node,typ,text=text_obj)
else:
node = treelib.create_node_after(node,typ)
return redirect(node.get_admin_url())
class TreeNodeSmazatView(generic.base.View):
def post(self, request, *args, **kwargs):
node = s.TreeNode.objects.get(pk=self.kwargs['pk'])
if node.first_child:
raise NotImplementedError('Mazání TreeNode se syny není zatím podporováno!')
treelib.disconnect_node(node)
node.delete()
return redirect(request.headers.get('referer'))
class TreeNodeOdvesitPrycView(generic.base.View):
def post(self, request, *args, **kwargs):
node = s.TreeNode.objects.get(pk=self.kwargs['pk'])
treelib.disconnect_node(node)
node.root = None
node.save()
return redirect(request.headers.get('referer'))
class TreeNodePodvesitView(generic.base.View):
def post(self, request, *args, **kwargs):
node = s.TreeNode.objects.get(pk=self.kwargs['pk'])
kam = self.kwargs['kam']
if kam == 'pred':
treelib.lower_node(node)
elif kam == 'za':
raise NotImplementedError('Podvěsit za není zatím podporováno')
return redirect(request.headers.get('referer'))
class TreeNodeProhoditView(generic.base.View):
def post(self, request, *args, **kwargs):
node = s.TreeNode.objects.get(pk=self.kwargs['pk'])
treelib.swap_succ(node)
return redirect(request.headers.get('referer'))
#FIXME ve formulari predat puvodni url a vratit redirect na ni
class SirotcinecView(generic.ListView):
model = s.TreeNode
template_name = 'seminar/orphanage.html'
def get_queryset(self):
return s.TreeNode.objects.not_instance_of(s.RocnikNode).filter(root=None,prev=None,succ=None,father_of_first=None)
# FIXME pouzit Django REST Framework
class TextWebView(generic.DetailView):
model = s.Text
def get(self,request,*args, **kwargs):
self.object = self.get_object()
return JsonResponse(model_to_dict(self.object,exclude='do_cisla'))
class ProblemView(generic.DetailView):
model = s.Problem
# Zkopírujeme template_name od TreeNodeView, protože jsme prakticky jen trošku upravený TreeNodeView
template_name = TreeNodeView.template_name
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = self.request.user
# Teď potřebujeme doplnit tnldata do kontextu.
# Ošklivý type switch, hezčí by bylo udělat to polymorfni. FIXME.
if False:
# Hezčí formátování zbytku :-P
pass
elif isinstance(self.object, s.Clanek) or isinstance(self.object, s.Konfera):
# Tyhle Problémy mají ŘešeníNode
context['tnldata'] = TNLData.from_treenode(self.object.reseninode,user)
elif isinstance(self.object, s.Uloha):
# FIXME: Teď vždycky zobrazujeme i vzorák! Možná by bylo hezčí/lepší mít to stejně jako pro Téma: procházet jen dosažitelné z Ročníku / čísla / whatever
tnl_zadani = TNLData.from_treenode(self.object.ulohazadaninode,user)
tnl_vzorak = TNLData.from_treenode(self.object.ulohavzoraknode,user)
context['tnldata'] = TNLData.from_tnldata_list([tnl_zadani, tnl_vzorak])
elif isinstance(self.object, s.Tema):
rocniknode = self.object.rocnik.rocniknode
context['tnldata'] = TNLData.filter_treenode(rocniknode, lambda x: isinstance(x, s.TemaVCisleNode))
else:
raise ValueError("Obecný problém nejde zobrazit.")
return context
#class AktualniZadaniView(generic.TemplateView):
# template_name = 'seminar/treenode.html'
# 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()
return render(request, 'seminar/zadani/AktualniZadani.html',
{'nastaveni': nastaveni,
'verejne': verejne,
},
)
def ZadaniTemataView(request):
nastaveni = get_object_or_404(Nastaveni)
verejne = nastaveni.aktualni_cislo.verejne()
akt_rocnik = nastaveni.aktualni_cislo.rocnik
temata = s.Tema.objects.filter(rocnik=akt_rocnik, stav='zadany')
return render(request, 'seminar/tematka/rozcestnik.html',
{
'tematka': temata,
'verejne': verejne,
},
)
# 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()
if not user.je_org():
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ě dá 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)
deadline_soustredeni = (nastaveni.aktualni_cislo.datum_deadline_soustredeni, "soustredeni")
preddeadline = (nastaveni.aktualni_cislo.datum_preddeadline, "preddeadline")
deadline = (nastaveni.aktualni_cislo.datum_deadline, "deadline")
try:
nejblizsi_deadline = sorted(filter(lambda dl: dl[0] is not None and dl[0] >= date.today(), [deadline_soustredeni, preddeadline, deadline]))[0]
if nejblizsi_deadline[0] == deadline_soustredeni[0]:
nejblizsi_deadline = deadline_soustredeni
except IndexError:
nejblizsi_deadline = (None, None) # neni zadna aktualni deadline
if nejblizsi_deadline[0] is not None:
context['nejblizsi_deadline'] = datetime.combine(nejblizsi_deadline[0], datetime.max.time())
else:
context['nejblizsi_deadline'] = None
context['typ_deadline'] = nejblizsi_deadline[1]
# 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
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()
return get_object_or_404(queryset,rocnik=self.kwargs.get('rocnik'))
def get_context_data(self, **kwargs):
start = time.time()
context = super(RocnikView, self).get_context_data(**kwargs)
# vysledkovka = True zajistí vykreslení,
# zkontrolovat, kdy se má a nemá vykreslovat
context['vysledkovka'] = True
if self.request.user.je_org():
context['cisla_s_neverejnymi'] = cisla_rocniku(context["rocnik"], jen_verejne=False)
context['radky_vysledkovky_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False)
context['hlavni_problemy_v_rocniku_s_neverejnymi'] = hlavni_problemy_rocniku(context["rocnik"], jen_verejne=False)
context['cisla'] = cisla_rocniku(context["rocnik"])
context['radky_vysledkovky'] = vysledkovka_rocniku(context["rocnik"])
context['hlavni_problemy_v_rocniku'] = hlavni_problemy_rocniku(context["rocnik"])
end = time.time()
print("Kontext:", end-start)
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.je_org():
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 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']
context['prevcislo'] = Cislo.objects.filter((Q(rocnik__lt=self.object.rocnik) | Q(poradi__lt=self.object.poradi))&Q(rocnik__lte=self.object.rocnik)).first()
# 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('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
class OdmenyView(generic.TemplateView):
template_name = 'seminar/archiv/odmeny.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
fromcislo = Cislo.objects.get(rocnik=self.kwargs.get('frocnik'), poradi=self.kwargs.get('fcislo'))
tocislo = Cislo.objects.get(rocnik=self.kwargs.get('trocnik'), poradi=self.kwargs.get('tcislo'))
resitele = aktivniResitele(tocislo)
frombody = body_resitelu(resitele, fromcislo)
tobody = body_resitelu(resitele, tocislo)
outlist = []
for (aid, tbody) in tobody.items():
fbody = frombody.get(aid,0)
resitel = Resitel.objects.get(pk=aid)
ftitul = resitel.get_titul(fbody)
ttitul = resitel.get_titul(tbody)
if ftitul != ttitul:
outlist.append({'jmeno': resitel.osoba.plne_jmeno(), 'ftitul': ftitul, 'ttitul': ttitul})
context['zmeny'] = outlist
return context
### 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))
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.decode())
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}
)
### Orgostránky
class OrgoRozcestnikView(TemplateView):
''' Zobrazí organizátorský rozcestník.'''
template_name = 'seminar/orgorozcestnik.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['posledni_soustredeni'] = Soustredeni.objects.order_by('-datum_konce').first()
nastaveni = Nastaveni.objects.first()
aktualni_rocnik = nastaveni.aktualni_rocnik
context['posledni_cislo_url'] = nastaveni.aktualni_cislo.verejne_url()
# TODO možná chceme odkazovat na právě rozpracované číslo, a ne to poslední vydané
# pokud nechceme haluzit kód (= poradi) dalšího čísla, bude asi potřeba jít
# přes treenody (a dát si přitom pozor na MezicisloNode)
u = self.request.user
os = s.Osoba.objects.get(user=u)
organizator = s.Organizator.objects.get(osoba=os)
temata_garant = s.Tema.objects.filter(garant=organizator,
rocnik=aktualni_rocnik)
#FIXME: přidat opravovatel, stav='STAV_ZADANY'
ulohy_garant = s.Uloha.objects.filter(garant=organizator,
cislo_zadani__rocnik=aktualni_rocnik)
clanky_garant = s.Clanek.objects.filter(garant=organizator,
cislo__rocnik=aktualni_rocnik)
context['temata'] = temata_garant
context['ulohy'] = ulohy_garant
context['clanky'] = clanky_garant
context['organizator'] = organizator
return context
#content_type = 'text/plain; charset=UTF8'
#XXX
### 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
def group_by_rocnik(clanky):
''' Vezme zadaný seznam článků a seskupí je podle ročníku.
Vrátí seznam seznamů článků ze stejného ročníku.'''
if len(clanky) == 0:
return clanky
clanky.order_by('cislo__rocnik__rocnik')
skupiny_clanku = []
skupina = []
rocnik = clanky.first().cislo.rocnik.rocnik # první ročník
for clanek in clanky:
if clanek.cislo.rocnik.rocnik == rocnik:
skupina.append(clanek)
else:
skupiny_clanku.append(skupina)
skupina = []
skupina.append(clanek)
rocnik = clanek.cislo.rocnik.rocnik
skupiny_clanku.append(skupina)
return skupiny_clanku
# FIXME: clanky jsou vsechny, pokud budou i neresitelske, tak se take zobrazi
# FIXME: Původně tu byl kód přímo v těle třídy, což rozbíjelo migrace. Opravil jsem, ale vůbec nevím, jestli to funguje.
class ClankyResitelView(generic.ListView):
model = Problem
template_name = 'seminar/clanky/resitelske_clanky.html'
# FIXME: QuerySet není pole!
def get_queryset(self):
clanky = Clanek.objects.filter(stav=Problem.STAV_VYRESENY).select_related('cislo__rocnik').order_by('-cislo__rocnik__rocnik')
queryset = []
skupiny_clanku = group_by_rocnik(clanky)
for skupina in skupiny_clanku:
skupina.sort(key=lambda clanek: clanek.kod_v_rocniku())
for clanek in skupina:
queryset.append(clanek)
return queryset
# 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
# pro přidání políčka do formuláře je potřeba
# - mít v modelu tu položku, kterou chci upravovat
# - přidat do views (prihlaskaView, resitelEditView)
# - přidat do forms
# - includovat do html
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 prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data):
msg = "{}, form_hash:{}".format(msg,hash(frozenset(form_data.items)))
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í objektů Osoba a Resitel patřících k aktuálně přihlášenému uživateli
u = request.user
osoba_edit = Osoba.objects.get(user=u)
if hasattr(osoba_edit,'resitel'):
resitel_edit = osoba_edit.resitel
else:
resitel_edit = None
user_edit = osoba_edit.user
## Vytvoření slovníku, kterým předvyplním formulář
prefill_1=model_to_dict(user_edit)
if resitel_edit:
prefill_2=model_to_dict(resitel_edit)
prefill_1.update(prefill_2)
prefill_3=model_to_dict(osoba_edit)
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'])
if resitel_edit:
## Změny v řešiteli
resitel_edit.skola = fcd['skola']
resitel_edit.rok_maturity = fcd['rok_maturity']
resitel_edit.zasilat = fcd['zasilat']
resitel_edit.zasilat_cislo_emailem = fcd['zasilat_cislo_emailem']
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 formularOKView(request)
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(frozenset(fcd.items()))
form_logger.info(str(fcd) + str(form_hash)) # TODO možná logovat jinak
with transaction.atomic():
u = User.objects.create_user(
username=fcd['username'],
password=fcd['password'],
email = fcd['email'])
u.save()
resitel_perm = Permission.objects.filter(codename__exact='resitel').first()
u.user_permissions.add(resitel_perm)
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 + str(form_hash))
o.save()
o.user = u
o.save()
r = Resitel(
rok_maturity = fcd['rok_maturity'],
zasilat = fcd['zasilat'],
zasilat_cislo_emailem = fcd['zasilat_cislo_emailem']
)
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 + str(form_hash))
r.save()
return formularOKView(request)
# 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('profil')
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')
class VueTestView(generic.TemplateView):
template_name = 'seminar/vuetest.html'
class NahrajObrazekKTreeNoduView(LoginRequiredMixin, CreateView):
model = s.Obrazek
form_class = f.NahrajObrazekKTreeNoduForm
def get_initial(self):
initial = super().get_initial()
initial['na_web'] = self.request.FILES['upload']
return initial
def form_valid(self,form):
print(self.request.headers)
print(self.request.headers['Textid'])
print(form.instance)
print(form)
self.object = form.save(commit=False)
print(self.object.na_web)
self.object.text = m.Text.objects.get(pk=int(self.request.headers['Textid']))
self.object.save()
return JsonResponse({"url":self.object.na_web.url})
# Jen hloupé rozhazovátko
def profilView(request):
user = request.user
if user.has_perm('auth.org'):
return OrgoRozcestnikView.as_view()(request)
if user.has_perm('auth.resitel'):
return ResitelView.as_view()(request)
else:
return LoginView.as_view()(request)
# Interní, nemá se nikdy objevit v urls (jinak to účastníci vytrolí)
def formularOKView(request):
template_name = 'seminar/formular_ok.html'
odkazy = [
# (Text, odkaz)
('Vrátit se na titulní stránku', reverse('titulni_strana')),
('Zobrazit aktuální zadání', reverse('seminar_aktualni_zadani')),
]
context = {
'odkazy': odkazy,
}
return render(request, template_name, context)
#------------------ Jak řešit - možná má být udělané úplně jinak
class JakResitView(generic.ListView):
template_name = 'seminar/jak-resit.html'
def get_queryset(self):
return None