1743 lines
60 KiB
Python
1743 lines
60 KiB
Python
|
||
|
||
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 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
|
||
|
||
# ze starého modelu
|
||
#def verejna_temata(rocnik):
|
||
# """
|
||
# Vrací queryset zveřejněných témat v daném ročníku.
|
||
# """
|
||
# return Problem.objects.filter(typ=Problem.TYP_TEMA, cislo_zadani__rocnik=rocnik, cislo_zadani__verejne_db=True).order_by('kod')
|
||
#
|
||
#def temata_v_rocniku(rocnik):
|
||
# return Problem.objects.filter(typ=Problem.TYP_TEMA, rocnik=rocnik)
|
||
|
||
def get_problemy_k_tematu(tema):
|
||
return Problem.objects.filter(nadproblem = tema)
|
||
|
||
|
||
class VlozBodyView(generic.ListView):
|
||
template_name = 'seminar/org/vloz_body.html'
|
||
|
||
def get_queryset(self):
|
||
self.tema = get_object_or_404(Problem,id=self.kwargs['tema'])
|
||
print(self.tema)
|
||
self.problemy = Problem.objects.filter(nadproblem = self.tema)
|
||
print(self.problemy)
|
||
self.reseni = Reseni.objects.filter(problem__in=self.problemy)
|
||
print(self.reseni)
|
||
return self.reseni
|
||
|
||
|
||
class ObalkovaniView(generic.ListView):
|
||
template_name = 'seminar/org/obalkovani.html'
|
||
|
||
def get_queryset(self):
|
||
rocnik = get_object_or_404(Rocnik,rocnik=self.kwargs['rocnik'])
|
||
cislo = get_object_or_404(Cislo,rocnik=rocnik,poradi=self.kwargs['cislo'])
|
||
self.cislo = cislo
|
||
self.hodnoceni = s.Hodnoceni.objects.filter(cislo_body=cislo)
|
||
self.reseni = Reseni.objects.filter(hodnoceni__in = self.hodnoceni).annotate(Sum('hodnoceni__body')).annotate(Count('hodnoceni')).order_by('resitele__osoba')
|
||
return self.reseni
|
||
|
||
def get_context_data(self, **kwargs):
|
||
context = super(ObalkovaniView, self).get_context_data(**kwargs)
|
||
print(self.cislo)
|
||
context['cislo'] = self.cislo
|
||
return context
|
||
|
||
class TNLData(object):
|
||
def __init__(self,anode,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()
|
||
# 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)
|
||
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]
|
||
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
|
||
|
||
### 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__rocnik__prvni_rok=rok,
|
||
reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) ))
|
||
elif rocnik and odjakziva: # Spočítáme body za starší ročníky až do zadaného včetně.
|
||
body_k_zapocteni = Sum('reseni__hodnoceni__body',
|
||
filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok))
|
||
elif rocnik and not odjakziva: # Spočítáme body za daný ročník.
|
||
body_k_zapocteni = Sum('reseni__hodnoceni__body',
|
||
filter= Q(reseni__hodnoceni__cislo_body__rocnik=rocnik))
|
||
else:
|
||
assert True, "body_resitelu: Neplatná kombinace za a odjakživa."
|
||
|
||
# Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů
|
||
resitele_s_body = Resitel.objects.filter(id__in=resitele_id).annotate(
|
||
body=body_k_zapocteni)
|
||
# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník
|
||
# indexovaný řešitelským id obsahující body.
|
||
# Pokud jsou body None, nahradíme za 0.
|
||
slovnik = {int(res.id) : (res.body if res.body else 0) for res in resitele_s_body}
|
||
return slovnik
|
||
|
||
class RadekVysledkovkyRocniku(object):
|
||
""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
|
||
Umožňuje snazší práci v templatu (lepší, než seznam)."""
|
||
|
||
def __init__(self, poradi, resitel, body_cisla_sezn, body_rocnik, body_odjakziva, rok):
|
||
self.poradi = poradi
|
||
self.resitel = resitel
|
||
self.rocnik_resitele = resitel.rocnik(rok)
|
||
self.body_rocnik = body_rocnik
|
||
self.body_celkem_odjakziva = body_odjakziva
|
||
self.body_cisla_sezn = body_cisla_sezn
|
||
self.titul = resitel.get_titul(body_odjakziva)
|
||
|
||
def setrid_resitele_a_body(slov_resitel_body):
|
||
setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body]
|
||
setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id]
|
||
setrizene_body = [dvojice[1] for dvojice in slov_resitel_body]
|
||
return setrizeni_resitele_id, setrizeni_resitele, setrizene_body
|
||
|
||
def vysledkovka_rocniku(rocnik, jen_verejne=True):
|
||
""" Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve
|
||
formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html"
|
||
"""
|
||
|
||
## TODO možná chytřeji vybírat aktivní řešitele
|
||
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají
|
||
# u alespoň jedné hodnoty něco jiného než NULL
|
||
aktivni_resitele = list(resi_v_rocniku(rocnik))
|
||
cisla = cisla_rocniku(rocnik, jen_verejne)
|
||
body_cisla_slov = {}
|
||
for cislo in cisla:
|
||
# získáme body za číslo
|
||
_, cislobody = secti_body_za_cislo(cislo, aktivni_resitele)
|
||
body_cisla_slov[cislo.id] = cislobody
|
||
|
||
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně
|
||
resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele)
|
||
|
||
# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší
|
||
setrizeni_resitele_id, setrizeni_resitele, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn)
|
||
poradi = sloupec_s_poradim(setrizene_body)
|
||
|
||
# získáme body odjakživa
|
||
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik)
|
||
|
||
# vytvoříme jednotlivé sloupce výsledkovky
|
||
radky_vysledkovky = []
|
||
i = 0
|
||
for ar_id in setrizeni_resitele_id:
|
||
# seznam počtu bodů daného řešitele pro jednotlivá čísla
|
||
body_cisla_sezn = []
|
||
for cislo in cisla:
|
||
body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id])
|
||
|
||
# vytáhneme informace pro daného řešitele
|
||
radek = RadekVysledkovkyRocniku(
|
||
poradi[i], # pořadí
|
||
Resitel.objects.get(id=ar_id), # řešitel (z id)
|
||
body_cisla_sezn, # seznam bodů za čísla
|
||
setrizene_body[i], # body za ročník (spočítané výše s pořadím)
|
||
resitel_odjakzivabody_slov[ar_id], # body odjakživa
|
||
rocnik) # ročník semináře pro získání ročníku řešitele
|
||
radky_vysledkovky.append(radek)
|
||
i += 1
|
||
|
||
return radky_vysledkovky
|
||
|
||
|
||
class RocnikView(generic.DetailView):
|
||
model = Rocnik
|
||
template_name = 'seminar/archiv/rocnik.html'
|
||
|
||
# Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik)
|
||
def get_object(self, queryset=None):
|
||
if queryset is None:
|
||
queryset = self.get_queryset()
|
||
rocnik_arg = self.kwargs.get('rocnik')
|
||
queryset = queryset.filter(rocnik=rocnik_arg)
|
||
|
||
try:
|
||
obj = queryset.get()
|
||
except queryset.model.DoesNotExist:
|
||
raise Http404(_("No %(verbose_name)s found matching the query") %
|
||
{'verbose_name': queryset.model._meta.verbose_name})
|
||
return obj
|
||
|
||
def get_context_data(self, **kwargs):
|
||
context = super(RocnikView, self).get_context_data(**kwargs)
|
||
|
||
# vysledkovka = True zajistí vykreslení,
|
||
# zkontrolovat, kdy se má a nemá vykreslovat
|
||
context['vysledkovka'] = True
|
||
context['cisla_s_neverejnymi'] = cisla_rocniku(context["rocnik"], jen_verejne=False)
|
||
context['cisla'] = cisla_rocniku(context["rocnik"])
|
||
context['radky_vysledkovky'] = vysledkovka_rocniku(context["rocnik"])
|
||
context['radky_vysledkovky_s_neverejnymi'] = vysledkovka_rocniku(
|
||
context["rocnik"], jen_verejne=False)
|
||
context['hlavni_problemy_v_rocniku'] = hlavni_problemy_rocniku(context["rocnik"])
|
||
context['hlavni_problemy_v_rocniku_s_neverejnymi'] = hlavni_problemy_rocniku(context["rocnik"], jen_verejne=False)
|
||
|
||
return context
|
||
|
||
|
||
class ProblemView(generic.DetailView):
|
||
model = Problem
|
||
|
||
# Používáme funkci, protože přímo template_name neumí mít v přiřazení dost logiky. Ledaže by se to udělalo polymorfně...
|
||
def get_template_names(self, **kwargs):
|
||
# FIXME: Switch podle typu není hezký, ale nechtělo se mi to přepisovat celé. Správně by se tohle mělo řešit polymorfismem.
|
||
spravne_templaty = {
|
||
s.Uloha: "uloha",
|
||
s.Tema: "tema",
|
||
s.Konfera: "konfera",
|
||
s.Clanek: "clanek",
|
||
}
|
||
context = super().get_context_data(**kwargs)
|
||
return ['seminar/archiv/problem_' + spravne_templaty[context['object'].__class__] + '.html']
|
||
|
||
def get_context_data(self, **kwargs):
|
||
context = super().get_context_data(**kwargs)
|
||
# Musí se používat context['object'], protože nevíme, jestli dostaneme úložku, téma, článek, .... a tyhle věci vyrábějí různé klíče.
|
||
if not context['object'].verejne() and not self.request.user.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 RadekVysledkovkyCisla(object):
|
||
"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
|
||
Umožňuje snazší práci v templatu (lepší, než seznam)."""
|
||
|
||
def __init__(self, poradi, resitel, body_problemy_sezn,
|
||
body_cislo, body_rocnik, body_odjakziva, rok):
|
||
self.resitel = resitel
|
||
self.rocnik_resitele = resitel.rocnik(rok)
|
||
self.body_cislo = body_cislo
|
||
self.body_rocnik = body_rocnik
|
||
self.body_celkem_odjakziva = body_odjakziva
|
||
self.poradi = poradi
|
||
self.body_problemy_sezn = body_problemy_sezn
|
||
self.titul = resitel.get_titul(body_odjakziva)
|
||
|
||
|
||
def pricti_body(slovnik, resitel, body):
|
||
""" Přiřazuje danému řešiteli body do slovníku. """
|
||
# testujeme na None (""), pokud je to první řešení
|
||
# daného řešitele, předěláme na 0
|
||
# (v dalším kroku přičteme reálný počet bodů),
|
||
# rozlišujeme tím mezi 0 a neodevzdaným řešením
|
||
if slovnik[resitel.id] == "":
|
||
slovnik[resitel.id] = 0
|
||
|
||
slovnik[resitel.id] += body
|
||
|
||
def secti_body_za_rocnik(za, aktivni_resitele):
|
||
""" Spočítá body za ročník (celý nebo do daného čísla),
|
||
setřídí je sestupně a vrátí jako seznam.
|
||
Parametry:
|
||
za (typu Rocnik nebo Cislo) spočítá za ročník, nebo za ročník až do
|
||
daného čísla
|
||
"""
|
||
# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa)
|
||
resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False)
|
||
# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně
|
||
resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(),
|
||
key = lambda x: x[1], reverse = True)
|
||
return resitel_rocnikbody_sezn
|
||
|
||
def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None):
|
||
""" Spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata)."""
|
||
# TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé
|
||
# pro každý hlavní problém zavedeme slovník s body za daný hlavní problém
|
||
# pro jednotlivé řešitele (slovník slovníků hlavních problémů)
|
||
if hlavni_problemy is None:
|
||
hlavni_problemy = hlavni_problemy_cisla(cislo)
|
||
|
||
def ne_clanek_ne_konfera(problem):
|
||
return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera))
|
||
|
||
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy))
|
||
|
||
def cosi(problem):
|
||
return problem.id
|
||
|
||
hlavni_problemy_slovnik = {}
|
||
for hp in temata_a_spol:
|
||
hlavni_problemy_slovnik[hp.id] = {}
|
||
|
||
hlavni_problemy_slovnik[-1] = {}
|
||
|
||
# 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 temata_a_spol:
|
||
slovnik = hlavni_problemy_slovnik[hp.id]
|
||
slovnik[ar.id] = ""
|
||
|
||
hlavni_problemy_slovnik[-1][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)
|
||
if ne_clanek_ne_konfera(nadproblem):
|
||
nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id]
|
||
else:
|
||
nadproblem_slovnik = hlavni_problemy_slovnik[-1]
|
||
|
||
# a mít více hodnocení
|
||
for hodn in list(reseni.hodnoceni_set.all()):
|
||
body = hodn.body
|
||
|
||
# a mít více řešitelů
|
||
for resitel in list(reseni.resitele.all()):
|
||
if resitel not in aktivni_resitele:
|
||
print("Skipping {}".format(resitel.id))
|
||
continue
|
||
pricti_body(cislobody, resitel, body)
|
||
pricti_body(nadproblem_slovnik, resitel, body)
|
||
return hlavni_problemy_slovnik, cislobody
|
||
|
||
def vysledkovka_cisla(cislo, context=None):
|
||
if context is None:
|
||
context = {}
|
||
|
||
hlavni_problemy = hlavni_problemy_cisla(cislo)
|
||
## TODO možná chytřeji vybírat aktivní řešitele
|
||
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají
|
||
# u alespoň jedné hodnoty něco jiného než NULL
|
||
aktivni_resitele = list(aktivniResitele(cislo))
|
||
|
||
# získáme body za číslo
|
||
hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy)
|
||
|
||
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně
|
||
resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo, aktivni_resitele)
|
||
|
||
# získáme body odjakživa
|
||
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo)
|
||
|
||
# řešitelé setřídění podle bodů za číslo sestupně
|
||
setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn]
|
||
setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id]
|
||
|
||
# spočítáme pořadí řešitelů
|
||
setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn]
|
||
poradi = sloupec_s_poradim(setrizeni_resitele_body)
|
||
|
||
# vytvoříme jednotlivé sloupce výsledkovky
|
||
radky_vysledkovky = []
|
||
i = 0
|
||
|
||
def ne_clanek_ne_konfera(problem):
|
||
return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera))
|
||
|
||
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy))
|
||
|
||
# def not_empty(value):
|
||
# return value != ''
|
||
#
|
||
# je_nejake_ostatni = any(filter(not_empty, hlavni_problemy_slovnik[-1].values())) > 0
|
||
|
||
je_nejake_ostatni = len(hlavni_problemy) - len(temata_a_spol) > 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 temata_a_spol:
|
||
problemy.append(hlavni_problemy_slovnik[hp.id][ar_id])
|
||
if je_nejake_ostatni:
|
||
problemy.append(hlavni_problemy_slovnik[-1][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'] = temata_a_spol
|
||
context['ostatni'] = je_nejake_ostatni
|
||
#context['v_cisle_zadane'] = TODO
|
||
#context['resene_problemy'] = resene_problemy
|
||
return context
|
||
|
||
class CisloView(generic.DetailView):
|
||
# FIXME zobrazování témátek a vůbec, teď je tam jen odkaz na číslo v pdf
|
||
model = Cislo
|
||
template_name = 'seminar/archiv/cislo.html'
|
||
|
||
# Vlastni ziskavani objektu z databaze podle (Rocnik.rocnik)
|
||
def get_object(self, queryset=None):
|
||
if queryset is None:
|
||
queryset = self.get_queryset()
|
||
rocnik_arg = self.kwargs.get('rocnik')
|
||
poradi_arg = self.kwargs.get('cislo')
|
||
queryset = queryset.filter(rocnik__rocnik=rocnik_arg, poradi=poradi_arg)
|
||
|
||
try:
|
||
obj = queryset.get()
|
||
except queryset.model.DoesNotExist:
|
||
raise Http404(_("No %(verbose_name)s found matching the query") %
|
||
{'verbose_name': queryset.model._meta.verbose_name})
|
||
return obj
|
||
|
||
def get_context_data(self, **kwargs):
|
||
context = super(CisloView, self).get_context_data(**kwargs)
|
||
|
||
cislo = context['cislo']
|
||
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_ZADANY).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 resetPasswordView(request):
|
||
pass
|
||
|
||
def loginView(request):
|
||
if request.method == 'POST':
|
||
form = LoginForm(request.POST)
|
||
if form.is_valid():
|
||
user = authenticate(request,
|
||
username=form.cleaned_data['username'],
|
||
password=form.cleaned_data['password'])
|
||
print(form.cleaned_data)
|
||
if user is not None:
|
||
login(request,user)
|
||
return HttpResponseRedirect('/')
|
||
else:
|
||
return render(request,
|
||
'seminar/profil/login.html',
|
||
{'form': form, 'login_error': 'Neplatné jméno nebo heslo'})
|
||
|
||
else:
|
||
form = LoginForm()
|
||
return render(request, 'seminar/profil/login.html', {'form': form})
|
||
|
||
def logoutView(request):
|
||
form = LoginForm()
|
||
if request.user.is_authenticated:
|
||
logout(request)
|
||
return render(request, 'seminar/profil/login.html', {'form': form, 'login_error': 'Byli jste úspěšně odhlášeni'})
|
||
return render(request, 'seminar/profil/login.html', {'form': form})
|
||
|
||
|
||
def prihlaska_log_gdpr_safe(logger, gdpr_logger, msg, form_data):
|
||
msg = "{}, form_hash:{}".format(msg,hash(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)
|
||
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']
|
||
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('titulni_strana')
|
||
return ctx
|
||
|
||
class LogoutView(auth_views.LogoutView):
|
||
# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL
|
||
template_name = 'seminar/profil/logout.html'
|
||
# Pavel: Vůbec nevím, proč to s _lazy funguje, ale bez toho to bylo rozbité.
|
||
next_page = reverse_lazy('titulni_strana')
|
||
|
||
class PasswordResetView(auth_views.PasswordResetView):
|
||
""" Chci resetovat heslo. """
|
||
#template_name = 'seminar/password_reset.html'
|
||
# TODO: vlastní email_template_name a subject_template_name a html_email_template_name
|
||
success_url = reverse_lazy('reset_password_done')
|
||
from_email = 'login@mam.mff.cuni.cz'
|
||
|
||
class PasswordResetDoneView(auth_views.PasswordResetDoneView):
|
||
""" Poslali jsme e-mail (pokud bylo kam)). """
|
||
#template_name = 'seminar/password_reset_done.html'
|
||
pass
|
||
|
||
class PasswordResetConfirmView(auth_views.PasswordResetConfirmView):
|
||
""" Vymysli si heslo. """
|
||
#template_name = 'seminar/password_confirm_done.html'
|
||
success_url = reverse_lazy('reset_password_complete')
|
||
|
||
class PasswordResetCompleteView(auth_views.PasswordResetCompleteView):
|
||
""" Heslo se asi změnilo."""
|
||
#template_name = 'seminar/password_complete_done.html'
|
||
pass
|
||
|
||
class PasswordChangeView(auth_views.PasswordChangeView):
|
||
#template_name = 'seminar/password_change.html'
|
||
success_url = reverse_lazy('titulni_strana')
|
||
|
||
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
|