diff --git a/korektury/admin.py b/korektury/admin.py index f2b0a319..5f92c542 100644 --- a/korektury/admin.py +++ b/korektury/admin.py @@ -12,7 +12,7 @@ from django.contrib import admin from reversion.admin import VersionAdmin from korektury.models import KorekturovanePDF -from django.core.mail import send_mail +from django.core.mail import EmailMessage from django.urls import reverse # Register your models here. @@ -64,6 +64,11 @@ Popis souboru: S pozdravem a korekturám zdar! Korekturovátko ''' - send_mail(predmet,text,odesilatel,[prijemce]) + EmailMessage( + subject=predmet, + body=text, + from_email=odesilatel, + to=[prijemce], + ).send() admin.site.register(KorekturovanePDF, KorekturovanePDFAdmin) diff --git a/korektury/views.py b/korektury/views.py index efeab19d..564e1331 100644 --- a/korektury/views.py +++ b/korektury/views.py @@ -8,7 +8,7 @@ from django.views import generic from django.utils.translation import ugettext as _ from django.conf import settings from django.http import HttpResponseForbidden -from django.core.mail import send_mail +from django.core.mail import EmailMessage from django.db.models import Count,Q from .models import Oprava,Komentar,KorekturovanePDF, Organizator @@ -207,7 +207,12 @@ class KorekturyView(generic.TemplateView): print("---- Konec upozornění") return - send_mail(subject, text, from_email, list(emails)) + EmailMessage( + subject=subject, + body=text, + from_email=from_email, + to=list(emails), + ).send() def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) diff --git a/make/init_local b/make/init_local index 75ee1ccf..1b2f7c1e 100755 --- a/make/init_local +++ b/make/init_local @@ -8,3 +8,4 @@ ensure_venv ./manage.py testdata ./manage.py loaddata data/* make/sync_prod_flatpages +./manage.py load_org_permissions deploy_v2/admin_org_prava.json diff --git a/mamweb/templates/base.html b/mamweb/templates/base.html index fc2ff76e..b10103e5 100644 --- a/mamweb/templates/base.html +++ b/mamweb/templates/base.html @@ -170,6 +170,22 @@ rotace_a_posun($('.container'), randomUhel()); {% endif %} + {% if april == 2023 %} + + {% endif %} {% render_block "js" %} diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index 7414b517..7cb79c21 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -2,12 +2,24 @@ {% load static %} {% load deadliny %} {% load mail %} +{% load jmena %} {% block content %} {% if edit %} + {% endif %} @@ -40,11 +52,20 @@ SouborŘešitelova poznámkaDatum {% for priloha in object.prilohy.all %} - {{ priloha.split | last }} + {{ priloha.split | last }} {{ priloha.res_poznamka }} {{ priloha.vytvoreno }} {% endfor %} +{% if edit %} {# FIXME: tohle nesouvisí s editací, ale s tím, jestli je člověk org… #} +
+ + +{% endif %} {% else %}

Žádné přílohy

{% endif %} diff --git a/odevzdavatko/templatetags/__init__.py b/odevzdavatko/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/odevzdavatko/templatetags/jmena.py b/odevzdavatko/templatetags/jmena.py new file mode 100644 index 00000000..9fe91ff5 --- /dev/null +++ b/odevzdavatko/templatetags/jmena.py @@ -0,0 +1,9 @@ +from django import template +register = template.Library() + +from personalni.utils import normalizuj_jmeno +import seminar.models as m # jen kvůli typové anotaci… + +@register.filter +def jmeno_jako_prefix(o: m.Osoba): + return normalizuj_jmeno(o).replace(' ', '_') diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 2390d27f..d3c74812 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -1,7 +1,7 @@ from django.core.exceptions import PermissionDenied from django.views.generic import ListView, DetailView, FormView from django.contrib.auth.mixins import LoginRequiredMixin -from django.core.mail import send_mail +from django.core.mail import EmailMessage from django.utils import timezone from django.views.generic import ListView, DetailView, FormView, CreateView from django.views.generic.list import MultipleObjectTemplateResponseMixin,MultipleObjectMixin @@ -449,11 +449,11 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): seznam = "problému " + str(problemy[0]) if len(problemy) == 1 else 'následujícím problémům:\n' + ', \n'.join(map(str, problemy)) seznam_do_subjectu = "problému " + str(problemy[0]) + ("" if len(problemy) == 1 else f" (a dalším { len(problemy) - 1 })") - send_mail( + EmailMessage( subject="Nové řešení k " + seznam_do_subjectu, - message=f"Řešitel{ '' if resitel.pohlavi_muz else 'ka' } { resitel } právě nahrál{'' if resitel.pohlavi_muz else 'a' } nové řešení k { seznam }.\n\nHurá do opravování: { self.object.absolute_url() }", + body=f"Řešitel{ '' if resitel.pohlavi_muz else 'ka' } { resitel } právě nahrál{'' if resitel.pohlavi_muz else 'a' } nové řešení k { seznam }.\n\nHurá do opravování: { self.object.absolute_url() }", from_email="submitovatko@mam.mff.cuni.cz", # FIXME: Chceme to mít radši tady, nebo v nastavení? - recipient_list=list(prijemci), - ) + to=list(prijemci), + ).send() return formularOKView(self.request, text='Řešení úspěšně odevzdáno') diff --git a/personalni/forms.py b/personalni/forms.py index f9d90182..3199a8a2 100644 --- a/personalni/forms.py +++ b/personalni/forms.py @@ -25,106 +25,85 @@ class TelInput(forms.TextInput): input_pattern="^[+]?[()/0-9. -]{9,}$" -class PrihlaskaForm(PasswordResetForm): - username = forms.CharField(label='Přihlašovací jméno', - max_length=256, - required=True, - help_text='Tímto jménem se následně budeš přihlašovat pro odevzdání řešení a další činnosti v semináři') +class UdajeForm(forms.Form): + username = None + err_logger = logging.getLogger('seminar.prihlaska.problem') jmeno = forms.CharField(label='Jméno', max_length=256, required=True) prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False) prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True) - pohlavi_muz = forms.ChoiceField(label='Pohlaví', - choices = ((True,'muž'),(False,'žena')), required=True) - email = forms.EmailField(label='E-mail',max_length=256, required=True) - telefon = forms.CharField(widget=TelInput(),label='Telefon',max_length=256, required=False) - datum_narozeni = forms.DateField(widget=DateInput(),label='Datum narození', required=False) + pohlavi_muz = forms.ChoiceField(label='Pohlaví', choices=((True, 'muž'), (False, 'žena')), required=True) + email = forms.EmailField(label='E-mail', max_length=256, required=True) + telefon = forms.CharField(widget=TelInput(), label='Telefon', max_length=256, required=False) + datum_narozeni = forms.DateField(widget=DateInput(), label='Datum narození', required=False) ulice = forms.CharField(label='Ulice a číslo popisné', max_length=256, required=False) mesto = forms.CharField(label='Město', max_length=256, required=False) psc = forms.CharField(label='PSČ', max_length=32, required=False) - stat = forms.ChoiceField(label='Stát', - choices = (('CZ', 'Česká Republika'), - ('SK', 'Slovenská Republika'), - ('other', 'Jiné')), - required=False) + stat = forms.ChoiceField(label='Stát', choices=(('CZ', 'Česká republika'), ('SK', 'Slovenská republika'), ('other', 'Jiné')), required=False) stat_text = forms.CharField(label='Stát', max_length=256, required=False) - skola = forms.ModelChoiceField(label="Škola", + skola = forms.ModelChoiceField( + label="Škola", queryset=Skola.objects.all(), widget=autocomplete.ModelSelect2( url='autocomplete_skola', - attrs = {'data-placeholder--id': '-1', - 'data-placeholder--text' : '---', - 'data-allow-clear': 'true'}) - ,required=False) - + attrs={ + 'data-placeholder--id': '-1', + 'data-placeholder--text': '---', + 'data-allow-clear': 'true' + } + ), + required=False, + ) + skola_nazev = forms.CharField(label='Název školy', max_length=256, required=False) skola_adresa = forms.CharField(label='Adresa školy', max_length=256, required=False) -# trida = forms.CharField(label='Třída',max_length=10, required=True) - rok_maturity = forms.IntegerField( - label='Rok maturity', - min_value=date.today().year, + label='Rok maturity', + min_value=date.today().year, max_value=date.today().year+8, - required=True) - zasilat = forms.ChoiceField(label='Kam zasílat čísla',choices = Resitel.ZASILAT_CHOICES, required=True) - zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat e-mailem upozornění na vydání nového čísla', required=False) + required=True, + ) - jak_se_dozvedeli = forms.CharField(widget=forms.Textarea({"rows": 3, "cols": 20}), label='Jak ses o M&M dozvěděl(a)? (Nechceš-li odpovídat, napiš „nechci uvést“.)', required=True) - - gdpr = forms.BooleanField(label='Souhlasím se zpracováním osobních údajů', required=True) - spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False) - - def clean_username(self): - err_logger = logging.getLogger('seminar.prihlaska.problem') - username = self.cleaned_data.get('username') - try: - User.objects.get(username=username) - msg = "Username {} exists".format(username) - err_logger.info(msg) - raise forms.ValidationError('Přihlašovací jméno je již použito') + zasilat = forms.ChoiceField(label='Kam zasílat (odměny, pozvánky, případně čísla nebo propagační materiály)', choices=[it for it in Resitel.ZASILAT_CHOICES if it[0] != Resitel.ZASILAT_NIKAM], required=True, initial=Resitel.ZASILAT_DOMU) + zasilat_cislo_papirove = forms.BooleanField(label='Chci dostávat čísla poštou (zasílání není zpoplatněno)', required=False, initial=False) + zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat e-mailem upozornění na vydání nového čísla', required=False, initial=True) + spam = forms.BooleanField(label='Souhlasím se zasíláním propagačních materiálů od MFF UK', required=False) - except ObjectDoesNotExist: - pass - return username + def clean_prezdivka_resitele(self): + prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele') + if prezdivka_resitele == '': + return prezdivka_resitele + if Resitel.objects.filter(prezdivka_resitele=prezdivka_resitele).exclude(osoba__user__username=self.username).count() > 0: + raise forms.ValidationError('Přezdívka je již použita') + return prezdivka_resitele def clean_email(self): - err_logger = logging.getLogger('seminar.prihlaska.problem') email = self.cleaned_data.get('email') try: - osoba = Osoba.objects.get(email=email) + osoba = Osoba.objects.exclude(user__username=self.username).get(email=email) msg = "Email {} exists".format(email) if osoba.user is not None: - err_logger.info(msg) + self.err_logger.info(msg) raise forms.ValidationError('E-mail je již použit') else: msg += ', but currently has no User, so allowing registration.' - err_logger.info(msg) + self.err_logger.info(msg) except ObjectDoesNotExist: pass return email - def clean_prezdivka_resitele(self): - prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele') - if prezdivka_resitele == '': - return prezdivka_resitele - if Resitel.objects.filter(prezdivka_resitele=prezdivka_resitele).count() > 0: - raise forms.ValidationError('Přezdívka je již použita') - return prezdivka_resitele - def clean_zasilat(self): zasilat = self.cleaned_data.get('zasilat') ulice = self.cleaned_data.get('ulice') if zasilat == Resitel.ZASILAT_DOMU and ulice == "": - raise forms.ValidationError('Nevyplněná adresa bydliště, nelze zasílat čísla domů.') + raise forms.ValidationError('Nevyplněná adresa bydliště, nelze zasílat domů.') return zasilat def clean(self): super().clean() - - err_logger = logging.getLogger('seminar.prihlaska.problem') data = self.cleaned_data if data.get('stat') != 'other' and data.get('stat_text') != '': @@ -140,106 +119,41 @@ class PrihlaskaForm(PasswordResetForm): self.add_error('skola_adresa',forms.ValidationError('Je nutné vyplnit adresu školy')) -class ProfileEditForm(forms.Form): - username = forms.CharField(label='Přihlašovací jméno', - max_length=256, - required=False, - disabled=True) - - jmeno = forms.CharField(label='Jméno', max_length=256, required=True) - prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False) - prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True) - pohlavi_muz = forms.ChoiceField(label='Pohlaví', - choices = ((True,'muž'),(False,'žena')), required=True) - email = forms.EmailField(label='E-mail',max_length=256, required=True) - telefon = forms.CharField(widget=TelInput(),label='Telefon',max_length=256, required=False) - datum_narozeni = forms.DateField(widget=DateInput(),label='Datum narození', required=False) - ulice = forms.CharField(label='Ulice', max_length=256, required=False) - mesto = forms.CharField(label='Město', max_length=256, required=False) - psc = forms.CharField(label='PSČ', max_length=32, required=False) - stat = forms.ChoiceField(label='Stát', - choices = (('CZ', 'Česká republika'), - ('SK', 'Slovenská republika'), - ('other', 'Jiné')), - required=False) - stat_text = forms.CharField(label='Stát', max_length=256, required=False) - - skola = forms.ModelChoiceField(label="Škola", - queryset=Skola.objects.all(), - widget=autocomplete.ModelSelect2( - url='autocomplete_skola', - attrs = {'data-placeholder--id': '-1', - 'data-placeholder--text' : '---', - 'data-allow-clear': 'true'}) - ,required=False) - - skola_nazev = forms.CharField(label='Název školy', max_length=256, required=False) - skola_adresa = forms.CharField(label='Adresa školy', max_length=256, required=False) -# trida = forms.CharField(label='Třída',max_length=10, required=True) - rok_maturity = forms.IntegerField( - label='Rok maturity', - min_value=date.today().year, - max_value=date.today().year+8, - required=True) - zasilat = forms.ChoiceField(label='Kam zasílat čísla',choices = Resitel.ZASILAT_CHOICES, required=True) - zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat email s upozorněním na vydání nového čísla', required=False) - - spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False) -# def clean_username(self): -# err_logger = logging.getLogger('seminar.prihlaska.problem') -# username = self.cleaned_data.get('username') -# try: -# User.objects.get(username=username) -# msg = "Username {} exists".format(username) -# err_logger.info(msg) -# raise forms.ValidationError('Přihlašovací jméno je již použito') -# -# except ObjectDoesNotExist: -# pass -# return username -# +class PrihlaskaForm(PasswordResetForm, UdajeForm): + username = forms.CharField( + label='Přihlašovací jméno', + max_length=256, + required=True, + help_text='Tímto jménem se následně budeš přihlašovat pro odevzdání řešení a další činnosti v semináři', + ) - def clean_prezdivka_resitele(self): - prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele') - if prezdivka_resitele == '': - return prezdivka_resitele - if Resitel.objects.filter(prezdivka_resitele=prezdivka_resitele).exclude(osoba__user__username=self.username).count() > 0: - raise forms.ValidationError('Přezdívka je již použita') - return prezdivka_resitele + jak_se_dozvedeli = forms.CharField(widget=forms.Textarea({"rows": 3, "cols": 20}), label='Jak ses o M&M dozvěděl(a)? (Nechceš-li odpovídat, napiš „nechci uvést“.)', required=True) - def clean_email(self): - err_logger = logging.getLogger('seminar.prihlaska.problem') - email = self.cleaned_data.get('email') + gdpr = forms.BooleanField(label='Souhlasím se zpracováním osobních údajů', required=True) + + def clean_username(self): + username = self.cleaned_data.get('username') try: - Osoba.objects.exclude(user__username=self.username).get(email=email) - msg = "Email {} exists (in edit)".format(email) - err_logger.info(msg) - raise forms.ValidationError('Email je již použit') + User.objects.get(username=username) + msg = "Username {} exists".format(username) + self.err_logger.info(msg) + raise forms.ValidationError('Přihlašovací jméno je již použito') except ObjectDoesNotExist: pass - return email - #def clean(self): - # super().clean() - # - # err_logger = logging.getLogger('seminar.prihlaska.problem') - - # data = self.cleaned_data - # if data.get('password') != data.get('password_check'): - # self.add_error('password_check',forms.ValidationError('Hesla se neshodují')) - # if data.get('stat') != '' and data.get('stat_text') != '': - # self.add_error('stat',forms.ValidationError('Nelze mít vybraný stát z menu a zároven zapsaný textem')) - # if data.get('skola') and (data.get('skola_nazev') or data.get('skola_adresa')): - # self.add_error('skola',forms.ValidationError('Pokud je škola v seznamu, nevypisujte ji ručně, pokud není, zrušte výběr ze seznamu (křížek vpravo)')) - # if not data.get('skola'): - # if data.get('skola_nazev')=='' and data.get('skola_adresa')=='': - # self.add_error('skola',forms.ValidationError('Je nutné vyplnit školu')) - # elif data.get('skola_nazev')=='': - # self.add_error('skola_nazev',forms.ValidationError('Je nutné vyplnit název školy')) - # elif data.get('skola_adresa')=='': - # self.add_error('skola_adresa',forms.ValidationError('Je nutné vyplnit adresu školy')) + return username + + +class ProfileEditForm(UdajeForm): + err_logger = logging.getLogger('seminar.edit.problem') + username = forms.CharField( + label='Přihlašovací jméno', + max_length=256, + required=False, + disabled=True, + ) class PoMaturiteProfileEditForm(ProfileEditForm): diff --git a/personalni/templates/personalni/udaje/edit.html b/personalni/templates/personalni/udaje/edit.html index 9091925d..085e4246 100644 --- a/personalni/templates/personalni/udaje/edit.html +++ b/personalni/templates/personalni/udaje/edit.html @@ -1,109 +1,19 @@ {% extends "base.html" %} {% load static %} -{% block script %} - -{% endblock %} - - - {% block content %}

{% block nadpis1a %} Změna osobních údajů {% endblock %}

-
- {% csrf_token %} - {{form.non_field_errors}}
+

Změnit heslo

-

- Přihlašovací údaje -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.username %} -
-

- Změnit heslo -

- -
- -

- Osobní údaje -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.prezdivka_resitele %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.pohlavi_muz%} - {% include "personalni/udaje/prihlaska_field.html" with field=form.email %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.telefon %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.datum_narozeni %} -
- -
- -

- Bydliště -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.ulice %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.mesto %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.psc %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.stat %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.stat_text id="id_li_stat_text"%} -
- -
- -

- Škola -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.skola %} - - - {% include "personalni/udaje/prihlaska_field.html" with field=form.skola_nazev id="id_li_skola_nazev" %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.skola_adresa id="id_li_skola_adresa" %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.rok_maturity %} -
Vyplň prosím celý název a adresu školy.
- -
- -

- Pošta -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat_cislo_emailem %} -
- -
- -

- Zasílání propagačních materiálů -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.spam %} -
- -
- - + + {% include "personalni/udaje/udaje.html"%} +
- + {% endblock %} diff --git a/personalni/templates/personalni/udaje/prihlaska.html b/personalni/templates/personalni/udaje/prihlaska.html index 33adba03..f26936de 100644 --- a/personalni/templates/personalni/udaje/prihlaska.html +++ b/personalni/templates/personalni/udaje/prihlaska.html @@ -5,16 +5,6 @@ {% endblock %} - - {% block content %}

{% block nadpis1a %} @@ -25,109 +15,26 @@

Tučně popsaná pole jsou povinná.

- {% csrf_token %} - {{form.non_field_errors}} - - -
-

- Přihlašovací údaje -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.username %} -{# {% include "personalni/udaje/prihlaska_field.html" with field=form.password %}#} -{# {% include "personalni/udaje/prihlaska_field.html" with field=form.password_check %}#} -
- -
- -

- Osobní údaje -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.prezdivka_resitele %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.pohlavi_muz%} - {% include "personalni/udaje/prihlaska_field.html" with field=form.email %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.telefon %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.datum_narozeni %} -
- -
- -

- Bydliště -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.ulice %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.mesto %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.psc %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.stat %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.stat_text id="id_li_stat_text"%} -
- -
- -

- Škola -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.skola %} - - - {% include "personalni/udaje/prihlaska_field.html" with field=form.skola_nazev id="id_li_skola_nazev" %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.skola_adresa id="id_li_skola_adresa" %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.rok_maturity %} -
(Prosíme, zkuste ji najít, téměř jistě ji v seznamu máme. Školy se dobře hledají podle příjmení lidí v jejich názvu, podle ulice, případně název ulice mezera město, atd. Nezadávejte slova, která se často zkracují – gymnázium, střední odborná škola, křestní jména…)
Vyplň prosím celý název a adresu školy.
- -
- -

- Pošta -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat_cislo_emailem %} -
-
- -

- GDPR -

- {% include "personalni/udaje/gdpr.html" %} - - {% include "personalni/udaje/prihlaska_field.html" with field=form.gdpr %} -
- -
- -

- Zasílání propagačních materiálů -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.spam %} -
- - - -
- -

- Ostatní -

- - {% include "personalni/udaje/prihlaska_field.html" with field=form.jak_se_dozvedeli %} -
- - - - + {% include "personalni/udaje/udaje.html" %} +

+ GDPR +

+ {% include "personalni/udaje/gdpr.html" %} + + {% include "personalni/udaje/prihlaska_field.html" with field=form.gdpr %} +
+ +
+ +

+ Ostatní +

+ + {% include "personalni/udaje/prihlaska_field.html" with field=form.jak_se_dozvedeli %} +
+ +
+ +
- - - {% endblock %} diff --git a/personalni/templates/personalni/udaje/udaje.html b/personalni/templates/personalni/udaje/udaje.html new file mode 100644 index 00000000..f39e8b47 --- /dev/null +++ b/personalni/templates/personalni/udaje/udaje.html @@ -0,0 +1,75 @@ +{% load static %} + +{% block script %} + +{% endblock %} + +{% csrf_token %} +{{form.non_field_errors}} + +
+

+ Přihlašovací údaje +

+ + {% include "personalni/udaje/prihlaska_field.html" with field=form.username %} +
+ +
+ +

+ Osobní údaje +

+ + {% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.prezdivka_resitele %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.pohlavi_muz%} + {% include "personalni/udaje/prihlaska_field.html" with field=form.email %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.telefon %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.datum_narozeni %} +
+ +
+ +

+ Škola +

+ + {% include "personalni/udaje/prihlaska_field.html" with field=form.skola %} + + + {% include "personalni/udaje/prihlaska_field.html" with field=form.skola_nazev id="id_li_skola_nazev" %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.skola_adresa id="id_li_skola_adresa" %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.rok_maturity %} +
(Prosíme, zkuste ji najít, téměř jistě ji v seznamu máme. Školy se dobře hledají podle příjmení lidí v jejich názvu, podle ulice, případně název ulice mezera město, atd. Nezadávejte slova, která se často zkracují – gymnázium, střední odborná škola, křestní jména…)
Vyplň prosím celý název a adresu školy.
+ +
+ +

+ Pošta +

+ + {% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat_cislo_emailem %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat_cislo_papirove %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.spam %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat %} +
+
+

+ Bydliště (povinné při volbě „domů“) +

+ + {% include "personalni/udaje/prihlaska_field.html" with field=form.ulice %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.mesto %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.psc %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.stat %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.stat_text id="id_li_stat_text"%} +
+ +
+ + diff --git a/personalni/utils.py b/personalni/utils.py new file mode 100644 index 00000000..0701d66a --- /dev/null +++ b/personalni/utils.py @@ -0,0 +1,11 @@ +import seminar.models as m +from various.utils import bez_diakritiky_translate +import re + +def normalizuj_jmeno(o: m.Osoba) -> str: + # FIXME: Možná není potřeba vázat na model? + cele_jmeno = f'{o.jmeno} {o.prijmeni}' + cele_jmeno = cele_jmeno.translate(bez_diakritiky_translate) + cele_jmeno = re.sub(r'[^a-zA-Z- ]', '', cele_jmeno) + return cele_jmeno + diff --git a/personalni/views.py b/personalni/views.py index a52f7f5f..a45aee52 100644 --- a/personalni/views.py +++ b/personalni/views.py @@ -165,6 +165,7 @@ def resitelEditView(request): resitel_edit.rok_maturity = fcd['rok_maturity'] resitel_edit.zasilat = fcd['zasilat'] resitel_edit.zasilat_cislo_emailem = fcd['zasilat_cislo_emailem'] + resitel_edit.zasilat_cislo_papirove = fcd['zasilat_cislo_papirove'] if fcd.get('skola'): resitel_edit.skola = fcd['skola'] else: @@ -264,10 +265,11 @@ def prihlaskaView(request): err_logger.warning(f'Zaregistrovala se osoba s kolizním jménem. ID osob: {[o.id for o in kolize]}') r = s.Resitel( - prezdivka_resitele=fcd['prezdivka_resitele'], + prezdivka_resitele=fcd['prezdivka_resitele'] if fcd['prezdivka_resitele'] != "" else None, rok_maturity = fcd['rok_maturity'], zasilat = fcd['zasilat'], - zasilat_cislo_emailem = fcd['zasilat_cislo_emailem'] + zasilat_cislo_emailem = fcd['zasilat_cislo_emailem'], + zasilat_cislo_papirove = fcd['zasilat_cislo_papirove'], ) if fcd.get('skola'): @@ -284,7 +286,7 @@ def prihlaskaView(request): except m.Resitel.DoesNotExist: # Stejný trik: orig_resitel = r - resitel_attrs = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem'] + resitel_attrs = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem', 'zasilat_cislo_papirove'] for attr in resitel_attrs: new = getattr(r, attr) old = getattr(orig_resitel, attr) @@ -345,6 +347,7 @@ def dataResiteluCsvResponse(queryset, columns=None, with_header=True): 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem', + 'zasilat_cislo_papirove', 'osoba__datum_registrace', 'osoba__datum_souhlasu_udaje', 'osoba__datum_souhlasu_zasilani', diff --git a/seminar/migrations/0112_prijemce_zasilat_cislo_emailem.py b/seminar/migrations/0112_prijemce_zasilat_cislo_emailem.py new file mode 100644 index 00000000..8efaf925 --- /dev/null +++ b/seminar/migrations/0112_prijemce_zasilat_cislo_emailem.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2023-04-17 18:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0111_nikam2nezasilat_papirove'), + ] + + operations = [ + migrations.AddField( + model_name='prijemce', + name='zasilat_cislo_emailem', + field=models.BooleanField(default=False, help_text='True pokud chce příjemce dostávat číslo emailem', verbose_name='zasílat číslo emailem'), + ), + ] diff --git a/seminar/migrations/0113_resitel_zasilat_cislo_papirove.py b/seminar/migrations/0113_resitel_zasilat_cislo_papirove.py new file mode 100644 index 00000000..9539f328 --- /dev/null +++ b/seminar/migrations/0113_resitel_zasilat_cislo_papirove.py @@ -0,0 +1,42 @@ +# Generated by Django 2.2.28 on 2023-03-13 22:02 + +from django.db import migrations, models + +ZASILAT_DOMU = 'domu' +ZASILAT_DO_SKOLY = 'do_skoly' +ZASILAT_NIKAM = 'nikam' + + +def default_zasilat_papirove(apps, schema_editor): + Resitel = apps.get_model('seminar', 'Resitel') + + for resitel in Resitel.objects.all(): + resitel.zasilat_cislo_papirove = resitel.zasilat != ZASILAT_NIKAM + if resitel.zasilat == ZASILAT_NIKAM: + resitel.zasilat = ZASILAT_DOMU if resitel.osoba.ulice else ZASILAT_DO_SKOLY + resitel.save() + + +def vrat_nikam(apps, schema_editor): + Resitel = apps.get_model('seminar', 'Resitel') + + for resitel in Resitel.objects.all(): + if not resitel.zasilat_cislo_papirove: + resitel.zasilat = ZASILAT_NIKAM + resitel.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0112_prijemce_zasilat_cislo_emailem'), + ] + + operations = [ + migrations.AddField( + model_name='resitel', + name='zasilat_cislo_papirove', + field=models.BooleanField(default=True, help_text='True pokud chce řešitel dostávat číslo papírově', verbose_name='zasílat číslo papírově'), + ), + migrations.RunPython(default_zasilat_papirove, vrat_nikam), + ] diff --git a/seminar/models/personalni.py b/seminar/models/personalni.py index 9ac85e51..61313e87 100644 --- a/seminar/models/personalni.py +++ b/seminar/models/personalni.py @@ -192,6 +192,8 @@ class Prijemce(SeminarModelBase): help_text='Které osobě či na jakou adresu se mají zasílat čísla', on_delete=models.CASCADE) + zasilat_cislo_emailem = models.BooleanField('zasílat číslo emailem', help_text='True pokud chce příjemce dostávat číslo emailem', default=False) + # FIXME: možná chceme něco jako vazbu na osobu XOR školu a počet kusů k zaslání # FIXME: a možná taky posílání na mail a možná taky přes něj chceme posílat i řešitelům @@ -236,6 +238,8 @@ class Resitel(SeminarModelBase): zasilat_cislo_emailem = models.BooleanField('zasílat číslo emailem', help_text='True pokud chce řešitel dostávat číslo emailem', default=False) + zasilat_cislo_papirove = models.BooleanField('zasílat číslo papírově', help_text='True pokud chce řešitel dostávat číslo papírově', default=True) + poznamka = models.TextField('neveřejná poznámka', blank=True, help_text='Neveřejná poznámka k řešiteli (plain text)') @@ -402,6 +406,7 @@ class Organizator(SeminarModelBase): editable=False ) + # Ne, date to nebude. SQLite: invalid literal for int() with base 10: b'17 23:00:00' organizuje_od = models.DateTimeField('Organizuje od', blank=True, null=True) organizuje_do = models.DateTimeField('Organizuje do', blank=True, null=True) diff --git a/seminar/models/tvorba.py b/seminar/models/tvorba.py index f2c10e3e..fdc7e434 100644 --- a/seminar/models/tvorba.py +++ b/seminar/models/tvorba.py @@ -265,6 +265,7 @@ class Cislo(SeminarModelBase): poslat_z_mailu = 'zadani@mam.mff.cuni.cz' predmet = 'Vyšlo číslo {}'.format(self.kod()) + # TODO Možná nechceme všem psát „Ahoj“, např. příjemcům… text_mailu = 'Ahoj,\n' \ 'na adrese {} najdete nejnovější číslo.\n' \ 'Vaše M&M\n'.format(odkaz) @@ -288,9 +289,14 @@ class Cislo(SeminarModelBase): email.send() - posli(text_mailu, resitele_vsichni.filter(zasilat=pm.Resitel.ZASILAT_NIKAM)) - posli(text_mailu + 'P. S. Také by vám brzy měla přijít papírová verze. Připomínáme, že pokud papírovou verzi čísla nevyužijete, můžete v https://mam.mff.cuni.cz/resitel/osobni-udaje/ zaškrtnout, abychom vám ji neposílali. Děkujeme. (Čísla vždy můžete nalézt v našem archivu a dál vám budou chodit e-mailem.)\n', - resitele_vsichni.exclude(zasilat=pm.Resitel.ZASILAT_NIKAM)) + paticka = "---\nK odběru těchto e-mailů jste se přihlásili na stránkách https://mam.matfyz.cz. Z odběru se lze odhlásit na https://mam.matfyz.cz/resitel/osobni-udaje/" + + posli(text_mailu + paticka, resitele_vsichni.filter(zasilat=pm.Resitel.zasilat_cislo_papirove)) + posli(text_mailu + 'P. S. Brzy budeme též rozesílat papírovou verzi čísla. Připomínáme, že pokud papírovou verzi čísla nevyužijete, můžete v https://mam.mff.cuni.cz/resitel/osobni-udaje/ zaškrtnout, abychom vám ji neposílali. Čísla vždy můžete nalézt v našem archivu a dál vám budou chodit e-mailem. Děkujeme.\n' + paticka, + resitele_vsichni.exclude(zasilat=pm.Resitel.zasilat_cislo_papirove)) + + paticka_prijemce = "---\nPokud tyto e-maily nechcete nadále dostávat, prosíme, ozvěte se nám na mam@matfyz.cz." + posli(text_mailu + paticka_prijemce, pm.Prijemce.objects.filter(zasilat_cislo_emailem=True)) def save(self, *args, **kwargs): super().save(*args, **kwargs) diff --git a/seminar/templates/seminar/archiv/obalky.tex b/seminar/templates/seminar/archiv/obalky.tex index ba18d318..9ae62121 100644 --- a/seminar/templates/seminar/archiv/obalky.tex +++ b/seminar/templates/seminar/archiv/obalky.tex @@ -100,6 +100,7 @@ {% with o=r.osoba %} {% with s=r.skola %} {% spaceless %} + {% if r.zasilat_cislo_papirove %} {% if r.zasilat == "do_skoly" %} {% if o.stat == "CZ" %} \obalka{{o.jmeno|sloz}}{{o.prijmeni|sloz}}{{s.nazev|sloz}}{{s.ulice|sloz}}{{s.psc|sloz}}{{s.mesto|sloz}}{{''|sloz}} @@ -115,6 +116,7 @@ {% endif %} {% else %} {% endif %} + {% endif %} {% endspaceless %} {% endwith %} {% endwith %} diff --git a/seminar/utils.py b/seminar/utils.py index e7d52529..e10920b8 100644 --- a/seminar/utils.py +++ b/seminar/utils.py @@ -265,7 +265,7 @@ def merge_resitele(cilovy, zdrojovy): # Postup: # Sjednotit / upravit informace cílového řešitele print('Upravuji data modelu') - fieldy_shoda = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem'] + fieldy_shoda = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem', 'zasilat_cislo_papirove'] for f in fieldy_shoda: zf = getattr(zdrojovy, f) diff --git a/various/mail_prefixer.py b/various/mail_prefixer.py index 14d4386b..2af0b3f7 100644 --- a/various/mail_prefixer.py +++ b/various/mail_prefixer.py @@ -7,6 +7,11 @@ Used to distinguish testing emails from production ones.""" from django.core.mail.backends.smtp import EmailBackend as DjangoSMTPBackend from django.conf import settings +def omezovatko_poctu_mailu(maily:list, maximum:int) -> str: + if len(maily) <= maximum: return str(maily) + # Aspoň zhruba simulujeme tisk pole… + return '[' + ", ".join(f"'{mail}'" for mail in maily[:maximum - 1]) + f', … ({len(maily)} e-mailů) ]' + class PrefixingMailBackend(DjangoSMTPBackend): # method _send is not probably meant to be monkey_patched, so we patch send_messages instead. def send_messages(self, messages): @@ -16,10 +21,13 @@ class PrefixingMailBackend(DjangoSMTPBackend): if message.from_email != settings.SERVER_EMAIL: message.subject = prefix + ' ' + message.subject + to = omezovatko_poctu_mailu(message.to, 3) + cc = omezovatko_poctu_mailu(message.cc, 3) + bcc = omezovatko_poctu_mailu(message.bcc, 3) message.body = f"""Bylo by posláno na e-maily: - To: {message.to} - Cc: {message.cc} - Bcc: {message.bcc} + To: {to} + Cc: {cc} + Bcc: {bcc} """+ "\n\n" + message.body message.to = settings.TESTOVACI_EMAILOVA_KONFERENCE message.cc = [] diff --git a/various/utils.py b/various/utils.py new file mode 100644 index 00000000..5905b2f6 --- /dev/null +++ b/various/utils.py @@ -0,0 +1,33 @@ +bez_diakritiky = ({} + # FIXME: funguje jen pro český a slovenský text, jinak jsou špatně + # transliterace. Potenciální řešení: + # https://stackoverflow.com/questions/517923/what-is-the-best-way-to-remove-accents-normalize-in-a-python-unicode-string + # (ale přidává to další závislosti…) + + # Tisknutelné ASCII + | {chr(a): chr(a) for a in range(32, 126+1)} + + # České, slovenské a blízké diakritiky a divnoznaky + | { x: 'a' for x in 'áÁäÄ'} + | { x: 'c' for x in 'čČ'} + | { x: 'd' for x in 'ďĎ'} + | { x: 'e' for x in 'éÉěĚëË'} + | { x: 'i' for x in 'íÍ'} + | { x: 'l' for x in 'ľĽĺĹ'} + | { x: 'n' for x in 'ňŇ'} + | { x: 'o' for x in 'óÓöÖôÔ'} + | { x: 'r' for x in 'řŘŕŔ'} + | { x: 's' for x in 'šŠßẞ'} + | { x: 't' for x in 'ťŤ'} + | { x: 'u' for x in 'úÚůŮ'} + | { x: 'y' for x in 'ýÝ'} + | { x: 'z' for x in 'žŽ'} + ) + +# Tabulka pro str.translate +class _bez_diakritiky_translate: + def __getitem__(self, it): + return ord(bez_diakritiky.get(chr(it), None)) +bez_diakritiky_translate = _bez_diakritiky_translate() + +# TODO: testy?