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 @@