|
|
@ -1,10 +1,12 @@ |
|
|
|
|
|
|
|
|
|
|
|
from django.contrib.auth.tokens import PasswordResetTokenGenerator |
|
|
|
from django.contrib.sites.shortcuts import get_current_site |
|
|
|
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.core.mail import send_mail |
|
|
|
from django.utils.encoding import force_bytes |
|
|
|
from django.utils.http import urlsafe_base64_encode |
|
|
|
from django.views import generic |
|
|
|
from django.utils.translation import ugettext as _ |
|
|
|
from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect |
|
|
@ -79,7 +81,7 @@ class ObalkovaniView(generic.ListView): |
|
|
|
def get_context_data(self, **kwargs): |
|
|
|
context = super(ObalkovaniView, self).get_context_data(**kwargs) |
|
|
|
print(self.cislo) |
|
|
|
context['cislo'] = self.cislo |
|
|
|
context['cislo'] = self.cislo |
|
|
|
return context |
|
|
|
|
|
|
|
class TNLData(object): |
|
|
@ -97,7 +99,7 @@ class TNLData(object): |
|
|
|
self.tema_in_path = True |
|
|
|
|
|
|
|
def add_edit_options(self): |
|
|
|
self.deletable = tnltt.deletable(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) |
|
|
@ -117,7 +119,7 @@ class TNLData(object): |
|
|
|
while True: |
|
|
|
rocnik = isinstance(parent, s.RocnikNode) |
|
|
|
cislo = isinstance(parent, s.CisloNode) |
|
|
|
uloha = (isinstance(parent, s.UlohaVzorakNode) or |
|
|
|
uloha = (isinstance(parent, s.UlohaVzorakNode) or |
|
|
|
isinstance(parent, s.UlohaZadaniNode)) |
|
|
|
tema = isinstance(parent, s.TemaVCisleNode) |
|
|
|
|
|
|
@ -137,7 +139,7 @@ class TNLData(object): |
|
|
|
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): |
|
|
@ -156,14 +158,14 @@ class TNLData(object): |
|
|
|
if user.has_perm('auth.org'): |
|
|
|
enum_children = enumerate(treelib.all_children(anode)) |
|
|
|
else: |
|
|
|
enum_children = enumerate(TNLData.all_public_children(anode)) |
|
|
|
|
|
|
|
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""" |
|
|
@ -192,7 +194,7 @@ class TNLData(object): |
|
|
|
for tnl in result: |
|
|
|
found.append(tnl) |
|
|
|
return found |
|
|
|
|
|
|
|
|
|
|
|
def to_json(self): |
|
|
|
#self.node = anode |
|
|
|
#self.children = [] |
|
|
@ -264,7 +266,7 @@ class TreeNodePridatView(generic.View): |
|
|
|
|
|
|
|
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() |
|
|
@ -303,7 +305,7 @@ class TreeNodePridatView(generic.View): |
|
|
|
node = treelib.create_node_after(node,typ) |
|
|
|
|
|
|
|
return redirect(node.get_admin_url()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TreeNodeSmazatView(generic.base.View): |
|
|
|
def post(self, request, *args, **kwargs): |
|
|
@ -343,7 +345,7 @@ class TreeNodeProhoditView(generic.base.View): |
|
|
|
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) |
|
|
|
|
|
|
@ -548,7 +550,7 @@ class TitulniStranaView(generic.ListView): |
|
|
|
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") |
|
|
@ -564,7 +566,7 @@ class TitulniStranaView(generic.ListView): |
|
|
|
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 |
|
|
@ -637,10 +639,10 @@ class ArchivView(generic.ListView): |
|
|
|
urls[c.rocnik] = c.titulka_nahled.url |
|
|
|
|
|
|
|
context["object_list"] = urls |
|
|
|
|
|
|
|
|
|
|
|
return context |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -762,7 +764,7 @@ class OdmenyView(generic.TemplateView): |
|
|
|
outlist.append({'jmeno': resitel.osoba.plne_jmeno(), 'ftitul': ftitul, 'ttitul': ttitul}) |
|
|
|
context['zmeny'] = outlist |
|
|
|
return context |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -852,7 +854,7 @@ class OrgoRozcestnikView(TemplateView): |
|
|
|
os = s.Osoba.objects.get(user=u) |
|
|
|
organizator = s.Organizator.objects.get(osoba=os) |
|
|
|
#FIXME: přidat stav='STAV_ZADANY' |
|
|
|
temata = s.Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), |
|
|
|
temata = s.Tema.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), |
|
|
|
rocnik=aktualni_rocnik).distinct() |
|
|
|
ulohy = s.Uloha.objects.filter(Q(garant=organizator) | Q(autor=organizator) | Q(opravovatele__in=[organizator]), |
|
|
|
cislo_zadani__rocnik=aktualni_rocnik).distinct() |
|
|
@ -877,7 +879,7 @@ def TitulyView(request, rocnik, cislo): |
|
|
|
cislo_obj = Cislo.objects.get(rocnik = rocnik_obj, poradi = cislo) |
|
|
|
|
|
|
|
asciijmena = [] |
|
|
|
jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka), |
|
|
|
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) |
|
|
|
|
|
|
@ -893,7 +895,7 @@ def TitulyView(request, rocnik, cislo): |
|
|
|
asciijmena.append(resitel.ascii) |
|
|
|
else: |
|
|
|
jmenovci = True |
|
|
|
|
|
|
|
|
|
|
|
return render(request, 'seminar/archiv/tituly.tex', |
|
|
|
{'resitele': resitele,'jmenovci':jmenovci},content_type="text/plain") |
|
|
|
|
|
|
@ -966,7 +968,7 @@ def group_by_rocnik(clanky): |
|
|
|
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. |
|
|
@ -1055,9 +1057,9 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): |
|
|
|
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) |
|
|
|
data['prilohy'] = f.ReseniSPrilohamiFormSet(self.request.POST,self.request.FILES) |
|
|
|
else: |
|
|
|
data['prilohy'] = f.ReseniSPrilohamiFormSet() |
|
|
|
data['prilohy'] = f.ReseniSPrilohamiFormSet() |
|
|
|
return data |
|
|
|
|
|
|
|
# FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni |
|
|
@ -1070,13 +1072,13 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): |
|
|
|
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.cas_doruceni = timezone.now() |
|
|
|
self.object.forma = s.Reseni.FORMA_UPLOAD |
|
|
|
self.object.save() |
|
|
|
|
|
|
|
prilohy.instance = self.object |
|
|
|
prilohy.save() |
|
|
|
|
|
|
|
|
|
|
|
# Pošleme mail opravovatelům a garantovi |
|
|
|
# FIXME: Nechat spočítat databázi? Je to pár dotazů (pravděpodobně), takže to za to možná nestojí |
|
|
|
prijemci = set() |
|
|
@ -1099,7 +1101,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): |
|
|
|
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)) |
|
|
|
gdpr_logger.warn(msg+", form:{}".format(form_data)) |
|
|
|
|
|
|
|
from django.forms.models import model_to_dict |
|
|
|
def resitelEditView(request): |
|
|
@ -1185,11 +1187,10 @@ def prihlaskaView(request): |
|
|
|
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() |
|
|
@ -1229,7 +1230,7 @@ def prihlaskaView(request): |
|
|
|
zasilat = fcd['zasilat'], |
|
|
|
zasilat_cislo_emailem = fcd['zasilat_cislo_emailem'] |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
r.save() |
|
|
|
r.osoba = o |
|
|
|
if fcd.get('skola'): |
|
|
@ -1240,11 +1241,19 @@ def prihlaskaView(request): |
|
|
|
err_logger.warn(msg + str(form_hash)) |
|
|
|
r.save() |
|
|
|
|
|
|
|
send_mail( |
|
|
|
uid = urlsafe_base64_encode(force_bytes(u.pk)) |
|
|
|
token = PasswordResetTokenGenerator().make_token(u) |
|
|
|
url = "https://%s%s" % ( |
|
|
|
str(get_current_site(request)), |
|
|
|
str(reverse_lazy("password_reset_confirm", args=[uid, token])) |
|
|
|
) |
|
|
|
|
|
|
|
u.email_user( |
|
|
|
subject="Registrace na M&M", |
|
|
|
message=f"Tento e-mail byl právě zaregistrován na mam.matfyz.cz.", # TODO: Dovymyslet text. |
|
|
|
message=f"Tento e-mail byl právě zaregistrován na mam.matfyz.cz. Nové heslo si nastavíš na: " + url, |
|
|
|
# TODO: templates/seminar/registrace a django/contrib/auth/forms.py říkají, jak na to lépe |
|
|
|
# TODO: Dovymyslet text. |
|
|
|
from_email="registrace@mam.mff.cuni.cz", # FIXME: Chceme to mít radši tady, nebo v nastavení? |
|
|
|
recipient_list=list([fcd['email']]), |
|
|
|
) |
|
|
|
|
|
|
|
return formularOKView(request) |
|
|
@ -1319,7 +1328,7 @@ class NahrajObrazekKTreeNoduView(LoginRequiredMixin, CreateView): |
|
|
|
|
|
|
|
return JsonResponse({"url":self.object.na_web.url}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Jen hloupé rozhazovátko |
|
|
|
def profilView(request): |
|
|
@ -1345,10 +1354,10 @@ def formularOKView(request): |
|
|
|
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 |
|
|
|
|
|
|
|