Browse Source

Merge branch 'data_migrations' into test

middleware_test
Jonas Havelka 4 years ago
parent
commit
fb01c005d9
  1. 2
      data/flat.json
  2. 26
      data/sitetree.json
  3. 7
      seminar/models.py
  4. BIN
      seminar/static/seminar/cross.png
  5. BIN
      seminar/static/seminar/plus.png
  6. 4
      seminar/templates/seminar/archiv/cislo.html
  7. 2
      seminar/templates/seminar/archiv/rocnik.html
  8. 14
      seminar/templates/seminar/odevzdavatko/detail.html
  9. 8
      seminar/templates/seminar/registrace/password_reset_email.html
  10. 1
      seminar/templates/seminar/registrace/password_reset_subject.txt
  11. 57
      seminar/utils.py
  12. 18
      seminar/views/views_all.py
  13. 12
      seminar/views/vysledkovka.py

2
data/flat.json

File diff suppressed because one or more lines are too long

26
data/sitetree.json

@ -782,5 +782,29 @@
}, },
"model": "sitetree.treeitem", "model": "sitetree.treeitem",
"pk": 40 "pk": 40
},
{
"fields": {
"access_guest": false,
"access_loggedin": false,
"access_perm_type": 1,
"access_permissions": [],
"access_restricted": false,
"alias": null,
"description": "",
"hidden": false,
"hint": "",
"inbreadcrumbs": true,
"inmenu": true,
"insitetree": true,
"parent": 1,
"sort_order": 41,
"title": "Odměny",
"tree": 1,
"url": "/o-nas/odmeny/",
"urlaspattern": false
},
"model": "sitetree.treeitem",
"pk": 41
} }
] ]

7
seminar/models.py

@ -601,7 +601,7 @@ class Cislo(SeminarModelBase):
if not self.titulka_nahled or os.path.getmtime(self.titulka_nahled.path) < os.path.getmtime(self.pdf.path): if not self.titulka_nahled or os.path.getmtime(self.titulka_nahled.path) < os.path.getmtime(self.pdf.path):
png_filename = pathlib.Path(tempfile.mkdtemp(), 'nahled.png') png_filename = pathlib.Path(tempfile.mkdtemp(), 'nahled.png')
subprocess.call([ subprocess.run([
"convert", "convert",
"-density", "300x300", "-density", "300x300",
"-geometry", "{}x{}".format(VYSKA, sirka), "-geometry", "{}x{}".format(VYSKA, sirka),
@ -609,7 +609,10 @@ class Cislo(SeminarModelBase):
"-flatten", "-flatten",
"{}[0]".format(self.pdf.path), # titulní strana "{}[0]".format(self.pdf.path), # titulní strana
png_filename png_filename
]) ],
check=True,
capture_output=True
)
with open(png_filename,'rb') as f: with open(png_filename,'rb') as f:
self.titulka_nahled.save('',f,True) self.titulka_nahled.save('',f,True)

BIN
seminar/static/seminar/cross.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

BIN
seminar/static/seminar/plus.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

4
seminar/templates/seminar/archiv/cislo.html

@ -38,8 +38,8 @@
<h2> Orgovské odkazy </h2> <h2> Orgovské odkazy </h2>
<ul> <ul>
<li><a href="obalky.pdf">Obálky (PDF)</a></li> <li><a href="obalky.pdf">Obálky (PDF)</a></li>
<li><a href="tituly.tex">Tituly (TeX)</a></li> <li><a href="tituly.tex" download>Tituly (TeX)</a></li>
<li><a href="vysledkovka.tex">Výsledkovka (TeX)</a></li> <li><a href="vysledkovka.tex" download>Výsledkovka (TeX)</a></li>
<li><a href="obalkovani">Obálkování</a></li> <li><a href="obalkovani">Obálkování</a></li>
<li><a href="odmeny/{{prevcislo.rocnik.rocnik}}.{{prevcislo.poradi}}/">Odměny</a></li> <li><a href="odmeny/{{prevcislo.rocnik.rocnik}}.{{prevcislo.poradi}}/">Odměny</a></li>
</ul> </ul>

2
seminar/templates/seminar/archiv/rocnik.html

@ -67,7 +67,7 @@
{% if vysledkovka %} {% if vysledkovka %}
{% if user.je_org %} {% if user.je_org %}
<div class='mam-org-only'> <div class='mam-org-only'>
<a href='vysledkovka.tex'>Výsledkovka ročníku (LaTeX)</a> <a href='vysledkovka.tex' download>Výsledkovka ročníku (LaTeX)</a>
</div> </div>
{% endif %} {% endif %}

14
seminar/templates/seminar/odevzdavatko/detail.html

@ -1,4 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %}
{% block content %} {% block content %}
@ -25,8 +26,9 @@ function deleteForm(prefix, btn) {
if (total >= 1){ if (total >= 1){
btn.closest('tr').remove(); btn.closest('tr').remove();
var forms = $('.hodnoceni'); var forms = $('.hodnoceni');
$('#id_' + prefix + '-TOTAL_FORMS').val(forms.length); var formCount = forms.length - 1; // There is one extra such form hidden as template!
for (var i=0, formCount=forms.length; i<formCount; i++) { $('#id_' + prefix + '-TOTAL_FORMS').val(formCount);
for (var i=0; i<formCount; i++) {
$(forms.get(i)).find(':input').each(function() { $(forms.get(i)).find(':input').each(function() {
updateElementIndex(this, prefix, i); updateElementIndex(this, prefix, i);
}); });
@ -91,21 +93,21 @@ $(document).ready(function(){
<td>{{ subform.problem }}</td> <td>{{ subform.problem }}</td>
<td>{{ subform.body }}</td> <td>{{ subform.body }}</td>
<td>{{ subform.cislo_body }}</td> <td>{{ subform.cislo_body }}</td>
<td><input type=button class="smazat_hodnoceni" value="Smazat" id="id_{{subform.prefix}}-jsremove"></td> <td><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove"><img src="{% static "seminar/cross.png" %}" alt="Smazat"></a></td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
<input type=button id="pridat_hodnoceni" value="Přidat hodnocení"> <a href="#"> <img src="{% static "seminar/plus.png" %}" id="pridat_hodnoceni" alt="Přidat hodnocení"></a> </br>
<input type=submit></form> <input type=submit value="Uložit"></form>
<table id="empty_form" style="display: none;"> <table id="empty_form" style="display: none;">
<tr class="hodnoceni"> <tr class="hodnoceni">
<td>{{ form.empty_form.problem }}</td> <td>{{ form.empty_form.problem }}</td>
<td>{{ form.empty_form.body }}</td> <td>{{ form.empty_form.body }}</td>
<td>{{ form.empty_form.cislo_body }}</td> <td>{{ form.empty_form.cislo_body }}</td>
<td><input type=button class="smazat_hodnoceni" value="Smazat" id="id_{{form.empty_form.prefix}}-jsremove"></td> <td><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove"><img src="{% static "seminar/cross.png" %}" alt="Smazat"></a></td>
</tr> </tr>
</table> </table>

8
seminar/templates/seminar/registrace/password_reset_email.html

@ -0,0 +1,8 @@
Ahoj!
Obdrželi jsme od Tebe žádost o obnovu hesla uživatele {{ user }} na webu M&M.
Pro zadání nového hesla přejdi na následující stránku:
{{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
S pozdravem,
organizátoři M&M

1
seminar/templates/seminar/registrace/password_reset_subject.txt

@ -0,0 +1 @@
Změna hesla na webu M&M

57
seminar/utils.py

@ -11,6 +11,8 @@ from django.contrib.auth.models import AnonymousUser
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from enum import Enum
import seminar.models as m import seminar.models as m
import seminar.treelib as t import seminar.treelib as t
@ -282,3 +284,58 @@ def podproblemy_v_cislu(cislo, problemy=None, hlavni_problemy=None):
podproblemy[-1].append(problem) podproblemy[-1].append(problem)
return podproblemy return podproblemy
class TypDeadline(Enum):
PredDeadline = auto()
SousDeadline = auto()
FinalDeadline = auto()
def deadline_v_rocniku(datum, rocnik):
"""Funkce pro dohledání, ke kterému deadlinu daného ročníku se datum váže.
Vrací trojici (TypDeadline, Cislo, datumDeadline: date).
V případě nevalidního volání není aktuálně chování definováno(!)
"""
cisla = m.Cislo.objects.filter(rocnik=rocnik)
deadliny = []
for c in cisla:
if c.datum_preddeadline is not None:
deadliny.append((TypDeadline.PredDeadline, c, c.datum_preddeadline))
if c.datum_deadline_soustredeni is not None:
deadliny.append((TypDeadline.SousDeadline, c, c.datum_deadline_soustredeni))
if c.datum_deadline is not None:
deadliny.append((TypDeadline.FinalDeadline, c, c.datum_deadline))
deadliny = sorted(deadliny, key=lambda x: x[2]) # podle data
for dl in deadliny:
if datum <= dl:
# První takový deadline je ten nejtěsnější
return dl
def deadline(datum):
"""Funkce pro dohledání, ke kterému deadlinu se datum váže.
Vrací trojici (TypDeadline, Cislo, datumDeadline: date).
"""
rok = datum.year
# Dva ročníky podezřelé z obsahování dat
pozdejsi_rocnik = m.Rocnik.filter(prvni_rok=rok)
drivejsi_rocnik = m.Rocnik.filter(druhy_rok=rok)
if any(
pozdejsi_rocnik.count() > 1,
drivejsi_rocnik.count() > 1,
):
raise ValueError(f"Více ročníků začíná/končí stejným rokem: {rok}")
pozdejsi_rocnik = pozdejsi_rocnik.first() if pozdejsi_rocnik.count() > 0 else None
drivejsi_rocnik = drivejsi_rocnik.first() if drivejsi_rocnik.count() > 0 else None
# Předpokládáme, že neexistuje číslo, které má deadline ale nemá finální deadline.
posledni_deadline_drivejsiho_rocniku = m.Cislo.objects.get(rocnik=drivejsi_rocnik, datum_deadline__isnull=False).datum_deadline
if datum <= posledni_deadline_drivejsiho_rocniku:
return deadline_v_rocniku(datum, drivejsi_rocnik)
else:
return deadline_v_rocniku(datum, pozdejsi_rocnik)

18
seminar/views/views_all.py

@ -4,6 +4,7 @@ from django.shortcuts import get_object_or_404, render, redirect
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, JsonResponse from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, JsonResponse
from django.urls import reverse,reverse_lazy from django.urls import reverse,reverse_lazy
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
from django.core.mail import send_mail
from django.views import generic from django.views import generic
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect
@ -1057,6 +1058,21 @@ class NahrajReseniView(LoginRequiredMixin, CreateView):
prilohy.instance = self.object prilohy.instance = self.object
prilohy.save() 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()
for prob in form.cleaned_data['problem']:
prijemci.update(prob.opravovatele.all())
prijemci.add(prob.garant)
# FIXME: Možná poslat mail i relevantním orgům nadproblémů?
# FIXME: Víc informativní obsah mailů, možná vč. příloh?
send_mail(
subject="Nové řešení k problému",
message=f"Řešitel poslal řešení...",
from_email="submitovatko@mam.mff.cuni.cz", # FIXME: Chceme to mít radši tady, nebo v nastavení?
recipient_list=list(prijemci),
)
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
@ -1223,6 +1239,8 @@ class PasswordResetView(auth_views.PasswordResetView):
# TODO: vlastní email_template_name a subject_template_name a html_email_template_name # TODO: vlastní email_template_name a subject_template_name a html_email_template_name
success_url = reverse_lazy('reset_password_done') success_url = reverse_lazy('reset_password_done')
from_email = 'login@mam.mff.cuni.cz' from_email = 'login@mam.mff.cuni.cz'
email_template_name = 'seminar/registrace/password_reset_email.html'
subject_template_name = 'seminar/registrace/password_reset_subject.txt'
class PasswordResetDoneView(auth_views.PasswordResetDoneView): class PasswordResetDoneView(auth_views.PasswordResetDoneView):
""" Poslali jsme e-mail (pokud bylo kam)). """ """ Poslali jsme e-mail (pokud bylo kam)). """

12
seminar/views/vysledkovka.py

@ -4,6 +4,8 @@ from seminar.utils import aktivniResitele, resi_v_rocniku, cisla_rocniku, hlavni
import time import time
### Výsledky ### Výsledky
ROCNIK_ZRUSENI_TEMAT = 25
def sloupec_s_poradim(setrizene_body): def sloupec_s_poradim(setrizene_body):
""" """
Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník
@ -255,7 +257,10 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None):
inst = problem.get_real_instance() inst = problem.get_real_instance()
return not(isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera)) return not(isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera))
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT:
temata_a_spol = hlavni_problemy
else:
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy))
hlavni_problemy_slovnik = {} hlavni_problemy_slovnik = {}
for hp in temata_a_spol: for hp in temata_a_spol:
@ -410,7 +415,10 @@ def vysledkovka_cisla(cislo, context=None):
return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) 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)) if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT:
temata_a_spol = hlavni_problemy
else:
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy))
# získáme body u jednotlivých témat # získáme body u jednotlivých témat
podproblemy = podproblemy_v_cislu(cislo, problemy, temata_a_spol) podproblemy = podproblemy_v_cislu(cislo, problemy, temata_a_spol)

Loading…
Cancel
Save