From 6ea212cdf8159e42600dc04fc38204500fb23bed Mon Sep 17 00:00:00 2001 From: Karel Balej Date: Tue, 3 Dec 2024 20:01:19 +0100 Subject: [PATCH 1/3] =?UTF-8?q?odevzdavatko:=20odes=C3=ADl=C3=A1n=C3=AD=20?= =?UTF-8?q?emailu=20=C5=99e=C5=A1iteli=20p=C5=99i=20zm=C4=9Bn=C4=9B=20zp?= =?UTF-8?q?=C4=9Btn=C3=A9=20vazby?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Toto se rozbíjí, když dojde ke smazání hodnocení v pořadí dříve, než nějaké hodnocení s neprázdnou zpětnou vazbou, neboť řádky formsetu jsou přečíslovány a pak špatně spárovány s původními hodnotami, takže se nesprávně detekuje změna. --- odevzdavatko/models.py | 3 ++ .../templates/odevzdavatko/detail.html | 2 +- odevzdavatko/views.py | 48 +++++++++++++------ personalni/forms.py | 2 + .../migrations/0018_resitel_upozorneni.py | 18 +++++++ personalni/models.py | 2 + .../templates/personalni/udaje/udaje.html | 1 + personalni/views.py | 1 + 8 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 personalni/migrations/0018_resitel_upozorneni.py diff --git a/odevzdavatko/models.py b/odevzdavatko/models.py index 3b510fe7..6666fcb1 100644 --- a/odevzdavatko/models.py +++ b/odevzdavatko/models.py @@ -65,6 +65,9 @@ class Reseni(SeminarModelBase): def absolute_url(self): return "https://" + str(get_current_site(None)) + self.verejne_url() + def resitel_url(self): + return f'https://{get_current_site(None)}{reverse_lazy("odevzdavatko_resitel_reseni", args=[self.id])}' + # má OneToOneField s: # Konfera diff --git a/odevzdavatko/templates/odevzdavatko/detail.html b/odevzdavatko/templates/odevzdavatko/detail.html index 616ec8e0..4d2b780a 100644 --- a/odevzdavatko/templates/odevzdavatko/detail.html +++ b/odevzdavatko/templates/odevzdavatko/detail.html @@ -191,7 +191,7 @@ Sloupce:
  • Pokud nemáš důvod, deadline neměň. Sloupeček s deadlinem znamená, do kterého deadlinu se započítají body (nemusí se shodovat s deadlinem řešení).
  • -
  • Poslední sloupec je na zpětnou vazbu řešiteli, tedy (na rozdíl od Neveřejné poznámky, která je určena pro synchronizaci orgů) ji uvidí řešitelé. Zatím jen pasivně (nechodí e-mail). Pohled řešitele si můžete prohlédnout zde. Pokud chcete z nějakého důvodu napsat řešitelům e-mail, klikněte na „Poslat mail všem řešitelům“.
  • +
  • Poslední sloupec je na zpětnou vazbu řešiteli, tedy (na rozdíl od Neveřejné poznámky, která je určena pro synchronizaci orgů) ji uvidí řešitelé. Změníte-li u nějakého hodnocení toto políčko, řešitel bude upozorněn emailem, pokud si tuto možnost nevypl ve svém profilu. Pohled řešitele si můžete prohlédnout zde. Pokud chcete z nějakého důvodu napsat řešitelům e-mail, klikněte na „Poslat mail všem řešitelům“.
  • Další poznámky diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 2a213a2c..b5c3d54d 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -222,6 +222,17 @@ class ReseniProblemuView(MultipleObjectTemplateResponseMixin, MultipleObjectMixi ctx["problem_id"] = self.kwargs['problem'] return ctx +HODNOCENI_INITIAL_DATA = [ + "problem", + "body", + "body_celkem", + "body_neprepocitane", + "body_neprepocitane_celkem", + "body_max", + "body_neprepocitane_max", + "deadline_body", + "feedback", +] ## XXX: https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#avoid-anything-more-complex class DetailReseniView(DetailView): """ Náhled na řešení. Editace je v :py:class:`EditReseniView`. """ @@ -232,18 +243,7 @@ class DetailReseniView(DetailView): self.reseni = get_object_or_404(Reseni, id=self.kwargs['pk']) result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet for hodn in Hodnoceni.objects.filter(reseni=self.reseni): - seznam_atributu = [ - "problem", - "body", - "body_celkem", - "body_neprepocitane", - "body_neprepocitane_celkem", - "body_max", - "body_neprepocitane_max", - "deadline_body", - "feedback", - ] - result.append({attr: getattr(hodn, attr) for attr in seznam_atributu}) + result.append({attr: getattr(hodn, attr) for attr in HODNOCENI_INITIAL_DATA}) return result def get_context_data(self, **kw): @@ -291,9 +291,9 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): reseni = get_object_or_404(Reseni, pk=pk) success_url = reverse('odevzdavatko_detail_reseni', kwargs={'pk': pk}) - # FIXME: Použit initial i tady a nebastlit hodnocení tak nízkoúrovňově - # Also: https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#django.forms.ModelForm - formset = f.OhodnoceniReseniFormSet(request.POST) + formset = f.OhodnoceniReseniFormSet(request.POST, initial=[ + {k: getattr(h, k) for k in HODNOCENI_INITIAL_DATA} for h in Hodnoceni.objects.filter(reseni=reseni) + ]) poznamka_form = f.PoznamkaReseniForm(request.POST, instance=reseni) # TODO: Napsat validaci formuláře a formsetu if not (formset.is_valid() and poznamka_form.is_valid()): @@ -309,7 +309,9 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): qs.delete() # Vyrobíme nová podle formsetu + notifikace = False for form in formset: + notifikace |= 'feedback' in form.changed_data data_for_hodnoceni = form.cleaned_data data_for_body = data_for_hodnoceni.copy() del(data_for_hodnoceni["body_celkem"]) @@ -330,6 +332,22 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): hodnoceni.body = -0.1 hodnoceni.save() + adresati = reseni.resitele.filter(upozorneni=True).values_list('osoba__email', flat=True) + if notifikace and adresati: + email = EmailMessage( + subject='Změna hodnocení odevzdaného řešení', + body=f"""Milá řešitelko, milý řešiteli, + +došlo ke změně zpětné vazby k Tebou odevzdanému řešení. Zobrazit si ji můžeš na {reseni.resitel_url()}. + +Tvoji organizátoři M&M +--- +Nechceš-li tato upozornění dostávat, můžeš si to nastavit ve svém profilu.""", + from_email='odevzdavatko@mam.mff.cuni.cz', + bcc=adresati, + ) + email.send() + return redirect(success_url) diff --git a/personalni/forms.py b/personalni/forms.py index ae08a8c9..2f6c79c8 100644 --- a/personalni/forms.py +++ b/personalni/forms.py @@ -71,6 +71,8 @@ class UdajeForm(forms.Form): 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) + upozorneni = forms.BooleanField(label='Chci dostávat emailová upozornění na změnu zpětné vazby k mým řešením', required=False, initial=True) + def clean_prezdivka_resitele(self): prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele') if prezdivka_resitele == '': diff --git a/personalni/migrations/0018_resitel_upozorneni.py b/personalni/migrations/0018_resitel_upozorneni.py new file mode 100644 index 00000000..64aede5f --- /dev/null +++ b/personalni/migrations/0018_resitel_upozorneni.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.16 on 2024-12-03 19:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('personalni', '0017_odstrel_treenode_post'), + ] + + operations = [ + migrations.AddField( + model_name='resitel', + name='upozorneni', + field=models.BooleanField(default=True, verbose_name='zasílat upozornění na změnu zpětné vazby k řešení emailem'), + ), + ] diff --git a/personalni/models.py b/personalni/models.py index 636b132e..930716a7 100644 --- a/personalni/models.py +++ b/personalni/models.py @@ -250,6 +250,8 @@ class Resitel(SeminarModelBase): poznamka = models.TextField('neveřejná poznámka', blank=True, help_text='Neveřejná poznámka k řešiteli (plain text)') + upozorneni = models.BooleanField('zasílat upozornění na změnu zpětné vazby k řešení emailem', default=True) + def export_row(self): "Slovnik pro pouziti v AESOP exportu" diff --git a/personalni/templates/personalni/udaje/udaje.html b/personalni/templates/personalni/udaje/udaje.html index 894ddaf9..5ed30b74 100644 --- a/personalni/templates/personalni/udaje/udaje.html +++ b/personalni/templates/personalni/udaje/udaje.html @@ -51,6 +51,7 @@ {% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat_cislo_emailem %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.upozorneni %} {% 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 %} diff --git a/personalni/views.py b/personalni/views.py index 7c95325c..38f9bc57 100644 --- a/personalni/views.py +++ b/personalni/views.py @@ -230,6 +230,7 @@ def resitelEditView(request): resitel_edit.zasilat = fcd['zasilat'] resitel_edit.zasilat_cislo_emailem = fcd['zasilat_cislo_emailem'] resitel_edit.zasilat_cislo_papirove = fcd['zasilat_cislo_papirove'] + resitel_edit.upozorneni = fcd['upozorneni'] if fcd.get('skola'): resitel_edit.skola = fcd['skola'] else: From 1bee36d9b6a1d9c6eccbeb2a824e03cd2628bd8b Mon Sep 17 00:00:00 2001 From: Karel Balej Date: Tue, 14 Jan 2025 21:00:33 +0100 Subject: [PATCH 2/3] =?UTF-8?q?personalni:=20p=C5=99ejmenov=C3=A1n=C3=AD?= =?UTF-8?q?=20sloupce=20pro=20upozorn=C4=9Bn=C3=AD=20na=20zp=C4=9Btnou=20v?= =?UTF-8?q?azbu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odevzdavatko/views.py | 2 +- personalni/forms.py | 2 +- ...eni_resitel_upozornovat_na_opravy_reseni.py | 18 ++++++++++++++++++ personalni/models.py | 2 +- .../templates/personalni/udaje/udaje.html | 2 +- personalni/views.py | 2 +- 6 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 personalni/migrations/0019_rename_upozorneni_resitel_upozornovat_na_opravy_reseni.py diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index b5c3d54d..255f48a9 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -332,7 +332,7 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): hodnoceni.body = -0.1 hodnoceni.save() - adresati = reseni.resitele.filter(upozorneni=True).values_list('osoba__email', flat=True) + adresati = reseni.resitele.filter(upozornovat_na_opravy_reseni=True).values_list('osoba__email', flat=True) if notifikace and adresati: email = EmailMessage( subject='Změna hodnocení odevzdaného řešení', diff --git a/personalni/forms.py b/personalni/forms.py index 2f6c79c8..39e1b6ab 100644 --- a/personalni/forms.py +++ b/personalni/forms.py @@ -71,7 +71,7 @@ class UdajeForm(forms.Form): 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) - upozorneni = forms.BooleanField(label='Chci dostávat emailová upozornění na změnu zpětné vazby k mým řešením', required=False, initial=True) + upozornovat_na_opravy_reseni = forms.BooleanField(label='Chci dostávat emailová upozornění na změnu zpětné vazby k mým řešením', required=False, initial=True) def clean_prezdivka_resitele(self): prezdivka_resitele = self.cleaned_data.get('prezdivka_resitele') diff --git a/personalni/migrations/0019_rename_upozorneni_resitel_upozornovat_na_opravy_reseni.py b/personalni/migrations/0019_rename_upozorneni_resitel_upozornovat_na_opravy_reseni.py new file mode 100644 index 00000000..e5a4caaf --- /dev/null +++ b/personalni/migrations/0019_rename_upozorneni_resitel_upozornovat_na_opravy_reseni.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.18 on 2025-01-14 19:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('personalni', '0018_resitel_upozorneni'), + ] + + operations = [ + migrations.RenameField( + model_name='resitel', + old_name='upozorneni', + new_name='upozornovat_na_opravy_reseni', + ), + ] diff --git a/personalni/models.py b/personalni/models.py index 930716a7..e04aca0b 100644 --- a/personalni/models.py +++ b/personalni/models.py @@ -250,7 +250,7 @@ class Resitel(SeminarModelBase): poznamka = models.TextField('neveřejná poznámka', blank=True, help_text='Neveřejná poznámka k řešiteli (plain text)') - upozorneni = models.BooleanField('zasílat upozornění na změnu zpětné vazby k řešení emailem', default=True) + upozornovat_na_opravy_reseni = models.BooleanField('zasílat upozornění na změnu zpětné vazby k řešení emailem', default=True) def export_row(self): diff --git a/personalni/templates/personalni/udaje/udaje.html b/personalni/templates/personalni/udaje/udaje.html index 5ed30b74..83bce161 100644 --- a/personalni/templates/personalni/udaje/udaje.html +++ b/personalni/templates/personalni/udaje/udaje.html @@ -51,7 +51,7 @@
    {% include "personalni/udaje/prihlaska_field.html" with field=form.zasilat_cislo_emailem %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.upozorneni %} + {% include "personalni/udaje/prihlaska_field.html" with field=form.upozornovat_na_opravy_reseni %} {% 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 %} diff --git a/personalni/views.py b/personalni/views.py index 38f9bc57..c71ce418 100644 --- a/personalni/views.py +++ b/personalni/views.py @@ -230,7 +230,7 @@ def resitelEditView(request): resitel_edit.zasilat = fcd['zasilat'] resitel_edit.zasilat_cislo_emailem = fcd['zasilat_cislo_emailem'] resitel_edit.zasilat_cislo_papirove = fcd['zasilat_cislo_papirove'] - resitel_edit.upozorneni = fcd['upozorneni'] + resitel_edit.upozornovat_na_opravy_reseni = fcd['upozornovat_na_opravy_reseni'] if fcd.get('skola'): resitel_edit.skola = fcd['skola'] else: From 071c66ee1091be11efebebd129916b170d4ea87e Mon Sep 17 00:00:00 2001 From: Karel Balej Date: Wed, 15 Jan 2025 18:14:39 +0100 Subject: [PATCH 3/3] =?UTF-8?q?personalni:=20zm=C4=9Bna=20nastaven=C3=AD?= =?UTF-8?q?=20upozor=C5=88ov=C3=A1n=C3=AD=20u=20existuj=C3=ADc=C3=ADch=20?= =?UTF-8?q?=C5=99e=C5=A1itel=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- personalni/migrations/0018_resitel_upozorneni.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/personalni/migrations/0018_resitel_upozorneni.py b/personalni/migrations/0018_resitel_upozorneni.py index 64aede5f..1b5b7280 100644 --- a/personalni/migrations/0018_resitel_upozorneni.py +++ b/personalni/migrations/0018_resitel_upozorneni.py @@ -2,6 +2,9 @@ from django.db import migrations, models +def vypnuti_upozorneni_na_opravy_reseni(apps, schema_editor): + Resitel = apps.get_model('personalni', 'Resitel') + Resitel.objects.update(upozorneni=False) class Migration(migrations.Migration): @@ -15,4 +18,5 @@ class Migration(migrations.Migration): name='upozorneni', field=models.BooleanField(default=True, verbose_name='zasílat upozornění na změnu zpětné vazby k řešení emailem'), ), + migrations.RunPython(vypnuti_upozorneni_na_opravy_reseni), ]