Compare commits

...

3 Commits

Author SHA1 Message Date
Karel Balej 1dfeeaa9d1 odevzdavatko: odesílání upozornění na změnu zpětné vazby 2 years ago
Karel Balej c8b2d07605 odevzdavatko: zjednodušení editace hodnocení řešení 2 years ago
Karel Balej 288c8ad459 odevzdavatko: zjednodušení DetailReseniView 2 years ago
  1. 22
      odevzdavatko/forms.py
  2. 1
      odevzdavatko/templates/odevzdavatko/detail.html
  3. 64
      odevzdavatko/views.py
  4. 1
      personalni/forms.py
  5. 9
      personalni/templates/personalni/udaje/edit.html
  6. 1
      personalni/views.py
  7. 18
      seminar/migrations/0110_resitel_upozorneni.py
  8. 2
      seminar/models/personalni.py

22
odevzdavatko/forms.py

@ -1,7 +1,6 @@
from django import forms from django import forms
from dal import autocomplete from dal import autocomplete
from django.forms import formset_factory from django.forms.models import modelformset_factory, inlineformset_factory
from django.forms.models import inlineformset_factory
from django.utils import timezone from django.utils import timezone
from seminar.models import Resitel from seminar.models import Resitel
@ -85,19 +84,14 @@ ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni,
) )
class JednoHodnoceniForm(forms.ModelForm): HodnoceniFormSet = modelformset_factory(
class Meta: m.Hodnoceni,
model = m.Hodnoceni fields=('id', 'problem', 'body', 'deadline_body', 'feedback',),
fields = ('problem', 'body', 'deadline_body', 'feedback',) extra=0,
widgets = { widgets={
'problem': autocomplete.ModelSelect2( 'problem': autocomplete.ModelSelect2(url='autocomplete_problem',),
url='autocomplete_problem',
),
'feedback': forms.Textarea(attrs={'rows': 1, 'cols': 30, 'class': 'feedback'}), 'feedback': forms.Textarea(attrs={'rows': 1, 'cols': 30, 'class': 'feedback'}),
} },
OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm,
extra = 0,
) )
class PoznamkaReseniForm(forms.ModelForm): class PoznamkaReseniForm(forms.ModelForm):

1
odevzdavatko/templates/odevzdavatko/detail.html

@ -112,6 +112,7 @@ $(document).ready(function(){
<td class="has_smazat_hodnoceni"><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove" title="Smazat hodnocení"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td> <td class="has_smazat_hodnoceni"><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove" title="Smazat hodnocení"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td>
</tr> </tr>
</tbody> </tbody>
{{ subform.id }}
{% endfor %} {% endfor %}
</table> </table>

64
odevzdavatko/views.py

@ -1,7 +1,7 @@
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.views.generic import ListView, DetailView, FormView from django.views.generic import ListView, DetailView, FormView
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.mail import send_mail from django.core.mail import send_mail, EmailMessage
from django.utils import timezone from django.utils import timezone
from django.views.generic import ListView, DetailView, FormView, CreateView from django.views.generic import ListView, DetailView, FormView, CreateView
from django.views.generic.list import MultipleObjectTemplateResponseMixin,MultipleObjectMixin from django.views.generic.list import MultipleObjectTemplateResponseMixin,MultipleObjectMixin
@ -219,58 +219,46 @@ class DetailReseniView(DetailView):
model = m.Reseni model = m.Reseni
template_name = 'odevzdavatko/detail.html' template_name = 'odevzdavatko/detail.html'
def aktualni_hodnoceni(self):
self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk'])
result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet
for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni):
result.append({
"problem": hodn.problem,
"body": hodn.body,
"deadline_body": hodn.deadline_body,
"feedback": hodn.feedback,
})
return result
def get_context_data(self, **kw): def get_context_data(self, **kw):
reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk'])
ctx = super().get_context_data(**kw) ctx = super().get_context_data(**kw)
ctx['form'] = f.OhodnoceniReseniFormSet( ctx['form'] = f.HodnoceniFormSet(queryset=m.Hodnoceni.objects.filter(reseni=reseni))
initial = self.aktualni_hodnoceni() ctx['poznamka_form'] = f.PoznamkaReseniForm(instance=reseni)
)
ctx['poznamka_form'] = f.PoznamkaReseniForm(instance=self.reseni)
return ctx return ctx
def hodnoceniReseniView(request, pk, *args, **kwargs): def hodnoceniReseniView(request, pk, *args, **kwargs):
reseni = get_object_or_404(m.Reseni, pk=pk) reseni = get_object_or_404(m.Reseni, pk=pk)
template_name = 'odevzdavatko/detail.html' template_name = 'odevzdavatko/detail.html'
form_class = f.OhodnoceniReseniFormSet form_class = f.HodnoceniFormSet
success_url = reverse('odevzdavatko_detail_reseni', kwargs={'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ě hodnoceni_formset = f.HodnoceniFormSet(request.POST, queryset=m.Hodnoceni.objects.filter(reseni=reseni))
# Also: https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#django.forms.ModelForm
formset = f.OhodnoceniReseniFormSet(request.POST)
poznamka_form = f.PoznamkaReseniForm(request.POST, instance=reseni) poznamka_form = f.PoznamkaReseniForm(request.POST, instance=reseni)
# TODO: Napsat validaci formuláře a formsetu # TODO: Napsat validaci formuláře a formsetu
if not (formset.is_valid() and poznamka_form.is_valid()): if not (hodnoceni_formset.is_valid() and poznamka_form.is_valid()):
raise ValueError(formset.errors, poznamka_form.errors) raise ValueError(hodnoceni_formset.errors, poznamka_form.errors)
with transaction.atomic(): with transaction.atomic():
# Poznámka je jednoduchá na zpracování: if poznamka_form.has_changed(): poznamka_form.save()
poznamka_form.save() if hodnoceni_formset.has_changed(): hodnoceni_formset.save()
# Smažeme všechna dosavadní hodnocení tohoto řešení zmeny = []
qs = m.Hodnoceni.objects.filter(reseni=reseni) for hodnoceni_form in hodnoceni_formset:
logger.info(f"Will delete {qs.count()} objects: {qs}") if 'feedback' in hodnoceni_form.changed_data:
qs.delete() zmeny.append(hodnoceni_form.instance)
# Vyrobíme nová podle formsetu if zmeny:
for form in formset: adresati = reseni.resitele.filter(upozorneni=True).values_list('osoba__email', flat=True)
hodnoceni = m.Hodnoceni( if adresati:
reseni=reseni, email = EmailMessage(
**form.cleaned_data, subject='Změna zpětné vazby k Tvému řešení',
body='\n\n'.join([f'{hodnoceni.problem}\n{hodnoceni.feedback}' for hodnoceni in zmeny]) + f'\n\nNechceš-li toto upozornění dostávat, můžeš jej vypnout v nastavení svého účtu.\n\nTvoji organizátoři M&M',
from_email='odevzdavatko@mam.mff.cuni.cz',
to=adresati,
reply_to=[request.user.email] or None,
) )
logger.info(f"Creating Hodnoceni: {hodnoceni}") email.send()
hodnoceni.save()
return redirect(success_url) return redirect(success_url)

1
personalni/forms.py

@ -177,6 +177,7 @@ class ProfileEditForm(forms.Form):
zasilat_cislo_emailem = forms.BooleanField(label='Chci dostávat email s upozorněním na vydání nového čísla', required=False) 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) spam = forms.BooleanField(label='Souhlasím se zasíláním materiálů od MFF UK', required=False)
upozorneni = forms.BooleanField(label='Chci dostat email, když opravovatel přidá zpětnou vazbu k mému řešení', required=False)
# def clean_username(self): # def clean_username(self):
# err_logger = logging.getLogger('seminar.prihlaska.problem') # err_logger = logging.getLogger('seminar.prihlaska.problem')
# username = self.cleaned_data.get('username') # username = self.cleaned_data.get('username')

9
personalni/templates/personalni/udaje/edit.html

@ -97,6 +97,15 @@
{% include "personalni/udaje/prihlaska_field.html" with field=form.spam %} {% include "personalni/udaje/prihlaska_field.html" with field=form.spam %}
</table> </table>
<hr>
<h4>
Zasílání upozornění na zpětnou vazbu k řešení
</h4>
<table class="form">
{% include "personalni/udaje/prihlaska_field.html" with field=form.upozorneni %}
</table>
<hr> <hr>
<input type="submit" value="Změnit"> <input type="submit" value="Změnit">

1
personalni/views.py

@ -164,6 +164,7 @@ def resitelEditView(request):
resitel_edit.rok_maturity = fcd['rok_maturity'] resitel_edit.rok_maturity = fcd['rok_maturity']
resitel_edit.zasilat = fcd['zasilat'] resitel_edit.zasilat = fcd['zasilat']
resitel_edit.zasilat_cislo_emailem = fcd['zasilat_cislo_emailem'] resitel_edit.zasilat_cislo_emailem = fcd['zasilat_cislo_emailem']
resitel_edit.upozorneni = fcd['upozorneni']
if fcd.get('skola'): if fcd.get('skola'):
resitel_edit.skola = fcd['skola'] resitel_edit.skola = fcd['skola']
else: else:

18
seminar/migrations/0110_resitel_upozorneni.py

@ -0,0 +1,18 @@
# Generated by Django 3.2.16 on 2022-12-19 22:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('seminar', '0109_hodnoceni_feedback'),
]
operations = [
migrations.AddField(
model_name='resitel',
name='upozorneni',
field=models.BooleanField(default=True),
),
]

2
seminar/models/personalni.py

@ -237,6 +237,8 @@ class Resitel(SeminarModelBase):
poznamka = models.TextField('neveřejná poznámka', blank=True, poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k řešiteli (plain text)') help_text='Neveřejná poznámka k řešiteli (plain text)')
upozorneni = models.BooleanField(default=True)
def export_row(self): def export_row(self):
"Slovnik pro pouziti v AESOP exportu" "Slovnik pro pouziti v AESOP exportu"

Loading…
Cancel
Save