• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

d120 / pyfeedback / 25237229969

01 May 2026 11:14PM UTC coverage: 88.833% (-0.8%) from 89.664%
25237229969

push

github

4-dash
minor fiexs

2991 of 3367 relevant lines covered (88.83%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.02
/src/feedback/forms.py
1
# coding=utf-8
2

3
from typing import Any
1✔
4
from django import forms
1✔
5
from django.forms import widgets
1✔
6

7
from feedback.models import Person, Veranstaltung, Kommentar, BarcodeScannEvent, EmailChange
1✔
8
from django.core.exceptions import ValidationError
1✔
9
from django.utils.translation import gettext_lazy as _
1✔
10

11
from feedback.models import Semester, Mailvorlage
1✔
12

13

14
class BestellWizardForm(forms.ModelForm):
1✔
15
    required_css_class = "required"
1✔
16

17

18
class VeranstaltungAnzahlForm(BestellWizardForm) :
1✔
19
    """Defines form for number of orders"""
20

21
    class Meta:
1✔
22
        model = Veranstaltung
1✔
23
        fields = ("anzahl",)
1✔
24
    
25
    def __init__(self, *args, **kwargs) :
1✔
26
        vollerhebung = kwargs.pop('vollerhebung', False)
1✔
27

28
        super(VeranstaltungAnzahlForm, self).__init__(*args, **kwargs)
1✔
29

30
        self.fields["anzahl"] = forms.IntegerField(label=_("Anzahl der Bestellungen"), min_value=0)
1✔
31
        
32
        if not vollerhebung:
1✔
33
            self.fields["anzahl"].help_text = _("Bitte geben Sie die Anzahl an Teilnehmenden an. \
1✔
34
                                                Auf der nächsten Seite haben Sie die Wahl, \
35
                                                ob Sie Ihre Veranstaltung evaluieren möchten.")
36
        else :
37
            self.fields["anzahl"].help_text = _("Bitte geben Sie die Anzahl an Teilnehmenden an.")
1✔
38

39
class VeranstaltungEvaluationForm(BestellWizardForm):
1✔
40
    """Definiert die Form für den 1. Schritt des Wizards"""
41

42
    class Meta:
1✔
43
        model = Veranstaltung
1✔
44
        fields = ("evaluieren",)
1✔
45
        widgets = {"evaluieren": forms.RadioSelect()}
1✔
46

47
    def __init__(self, *args, **kwargs):
1✔
48
        hide_field = kwargs.pop('hide_eval_field', False)
1✔
49
        no_eval = kwargs.pop('no_eval', False)
1✔
50

51
        super(VeranstaltungEvaluationForm, self).__init__(
1✔
52
            *args, **dict(kwargs, initial={"evaluieren": not no_eval})
53
        )
54

55
        self.fields['evaluieren'].required = True
1✔
56

57
        if not no_eval and hide_field :
1✔
58
            # with vollerhebung and correct anzahl hide field so no option not to evaluate
59
            self.fields['evaluieren'].widget = forms.HiddenInput()
1✔
60
            # this prevents user from changing hidden input data in inspect mode of browser
61
            self.fields['evaluieren'].disabled = True
1✔
62

63
        elif no_eval and hide_field :
1✔
64
            # when anzahl less than MIN_BESTELLUNG_ANZAHL hide
65
            self.fields['evaluieren'].widget = forms.HiddenInput()
1✔
66
            # this prevents user from changing hidden input data in inspect mode of browser
67
            self.fields['evaluieren'].disabled = True
1✔
68

69
class VeranstaltungBasisdatenForm(BestellWizardForm):
1✔
70
    """Definiert die Form für den 2. Schritt des Wizards."""
71

72
    def __init__(self, *args, **kwargs):
1✔
73
        veranstalter_queryset = kwargs.pop("all_veranstalter", None)
1✔
74

75
        super(VeranstaltungBasisdatenForm, self).__init__(*args, **kwargs)
1✔
76

77
        # Schränke QuerySet nur auf den Veranstalter ein
78
        self.fields["ergebnis_empfaenger"].queryset = veranstalter_queryset
1✔
79

80
        self.fields["auswertungstermin"] = forms.DateField(
1✔
81
            label=_("Auswertungstermin"),
82
            help_text=_("Zu diesem Termin werden die Ergebnisse versandt. Nach diesem Datum können keine Evaluationsbögen mehr abgegeben werden und die digitale Evaluation geschlossen."),
83
            widget=forms.DateInput(attrs={"type": "date", "value": Semester.current().standard_ergebnisversand}),
84
        )
85

86
        # Lösche die Auswahl ob es eine Übung gibt wenn es keine Vorlesung ist
87
        vltypes = ["vu", "v"]
1✔
88
        if kwargs["instance"].typ not in vltypes:
1✔
89
            del self.fields["typ"]
×
90
        else:
91
            choices = []
1✔
92
            for cur in self.fields["typ"].choices:
1✔
93
                if cur[0] in vltypes:
1✔
94
                    choices.append(cur)
1✔
95

96
            self.fields["typ"].choices = choices
1✔
97

98
        # Wenn Evaluation oder Vollerhebung, dann sind alle anderen Felder notwendig
99
        for k, field in list(self.fields.items()):
1✔
100
            if k not in ["auswertungstermin"]:
1✔
101
                field.required = True
1✔
102

103
    class Meta:
1✔
104
        model = Veranstaltung
1✔
105
        fields = (
1✔
106
            "typ",
107
            "sprache",
108
            "ergebnis_empfaenger",
109
            "auswertungstermin",
110
        )
111
        widgets = {"ergebnis_empfaenger": forms.CheckboxSelectMultiple}
1✔
112
    
113
    def clean(self) -> dict[str, Any]:
1✔
114
        cleaned_data = super().clean()
1✔
115
        
116
        # "digitale_eval" and "verantwortlich" removed from user interface
117
        cleaned_data["digitale_eval"] = True
1✔
118

119
        if "ergebnis_empfaenger" in cleaned_data :
1✔
120
            # because it does not validate yet, django doesn't notice that "ergebnis_empfaenger" does not exist and this causes error
121
            cleaned_data["verantwortlich"] = cleaned_data["ergebnis_empfaenger"][0]
1✔
122

123

124
class VeranstaltungDigitaleEvaluationForm(BestellWizardForm):
1✔
125
    class Meta:
1✔
126
        model = Veranstaltung
1✔
127
        fields = ("digitale_eval_type", )
1✔
128

129
class VeranstaltungFreieFragen(BestellWizardForm):
1✔
130
    """Definiert die Form für den 5. Schritt des Wizards."""
131

132
    class Meta:
1✔
133
        model = Veranstaltung
1✔
134
        fields = ("freiefrage1", "freiefrage2")
1✔
135

136
class VeranstaltungVeroeffentlichung(BestellWizardForm):
1✔
137
    """Definiert die Form für den 7. Schritt des Wizards."""
138

139
    class Meta:
1✔
140
        model = Veranstaltung
1✔
141
        fields = ("veroeffentlichen",)
1✔
142

143

144
class UploadFileForm(forms.Form):
1✔
145
    """Definiert die Form für den XML Import."""
146
    file = forms.FileField(label=_("Datei"))
1✔
147

148

149
class PersonForm(forms.ModelForm):
1✔
150
    """Definiert die Form für die Bearbeitung von Personen."""
151

152
    class Meta:
1✔
153
        model = Person
1✔
154
        fields = ("geschlecht", "email")
1✔
155

156
    def clean(self):
1✔
157
        geschlecht = self.cleaned_data.get("geschlecht")
1✔
158
        email = self.cleaned_data.get("email")
1✔
159

160
        if not geschlecht or not email:
1✔
161
            raise forms.ValidationError(_("Das Feld für die Anrede oder Email ist leer."))
1✔
162

163

164
class PersonUpdateForm(forms.ModelForm):
1✔
165
    """Definiert die Form für die Nachpflege von Personendaten"""
166

167
    class Meta:
1✔
168
        model = Person
1✔
169
        fields = ("anschrift", "fachgebiet")
1✔
170

171

172
class KommentarModelForm(forms.ModelForm):
1✔
173
    """Definiert die Form für Kommentare."""
174

175
    def __init__(self, *args, **kwargs):
1✔
176
        veranst = kwargs.pop("veranstaltung", None)
1✔
177

178
        if veranst is None:
1✔
179
            raise KeyError(
1✔
180
                "This form needs an veranstaltung=... parameter to function properly."
181
            )
182

183
        super(KommentarModelForm, self).__init__(*args, **kwargs)
1✔
184
        self.fields["autor"].queryset = veranst.veranstalter.all()
1✔
185

186
    class Meta:
1✔
187
        model = Kommentar
1✔
188
        exclude = ("veranstaltung",)
1✔
189

190

191
CLOSE_ORDER_CHOICES = (("ja", _("Ja")), ("nein", _("Nein")))
1✔
192

193

194
class CloseOrderForm(forms.Form):
1✔
195
    """Definiert die Form für das Beenden der Bestellphase"""
196
    auswahl = forms.ChoiceField(choices=CLOSE_ORDER_CHOICES, label=_("Auswahl"))
1✔
197

198

199
class CreateBarcodeScannEventForm(forms.ModelForm):
1✔
200
    """Definiert die Form für einen Barcodescan-Event"""
201
    scanner_token = forms.CharField()
1✔
202

203
    class Meta:
1✔
204
        model = BarcodeScannEvent
1✔
205
        fields = ["barcode", "scanner"]
1✔
206

207
    def clean(self):
1✔
208
        super(CreateBarcodeScannEventForm, self).clean()
×
209
        cd = self.cleaned_data
×
210

211
        if cd["scanner"].token != cd["scanner_token"]:
×
212
            raise ValidationError(
×
213
                ValidationError("Token dose not match", code="tokendmatch")
214
            )
215
        else:
216
            barcode_decoded = Veranstaltung.decode_barcode(cd["barcode"])
×
217
            cd["veranstaltung"] = barcode_decoded["veranstaltung"]
×
218

219
            if barcode_decoded["tutorgroup"] >= 1:
×
220
                cd["tutorgroup"] = barcode_decoded["tutorgroup"]
×
221

222
        return cd
×
223

224

225
class UploadTANCSV(forms.Form):
1✔
226
    csv = forms.FileField(label=_('CSV Datei aus Evasys'), help_text=_('Im Evasysseitenmenü unter dem Punkt "Teilnahmeübersicht" generierbar.'))
1✔
227

228
class SendOrPDF(forms.Form):
1✔
229
    choice = forms.ChoiceField(choices=(('mail', _('Versende TANs per E-Mail'),),), label=_('Verarbeitungsart'))
1✔
230

231
class EMailTemplates(forms.Form):
1✔
232
    losungstemplate = forms.ModelChoiceField(Mailvorlage.objects.all(), 
1✔
233
    required=False, help_text=_('Hier wird eine E-Mail an alle Veranstalter*innen ohne Anhang versendet. Es werden die selben Ersetzungen wie beim Standardmailsystem unterstützt und zusätzlich das Feld {{ losung }}.'))
234
    tantemplate = forms.ModelChoiceField(Mailvorlage.objects.all(), required=False, help_text=_('Hier wird die gewählte Vorlage an alle Veranstalter*innen mit einer CSV Datei versendet. Es werden die selben Ersetzungen wie beim Standardmailsystem unterstützt.'))
1✔
235

236

237
class EMailChangeRequestForm(forms.ModelForm) :
1✔
238
    class Meta :
1✔
239
        model = EmailChange
1✔
240
        fields = ["old_email"]
1✔
241

242

243

244
class EMailChangeForm(forms.ModelForm):
1✔
245
    def __init__(self, *args, **kwargs):
1✔
246
        super().__init__(*args, **kwargs)
1✔
247

248
        if self.instance.pk:
1✔
249
            matching_people = Person.objects.filter(email__iexact=self.instance.old_email.strip())
1✔
250
            count = matching_people.count()
1✔
251

252
            if count == 1:
1✔
253
                self.fields["person_list_to_change"].widget = forms.MultipleHiddenInput()
1✔
254
            else :
255
                self.fields["person_list_to_change"].widget = forms.CheckboxSelectMultiple()
×
256
                self.fields["person_list_to_change"].queryset = matching_people
×
257
                self.fields["person_list_to_change"].required = True
×
258

259
        # double check new email
260
        self.fields["new_email_check"] = forms.EmailField(
1✔
261
            label=_("E-Mail nochmal eingeben :"),
262
            widget=forms.EmailInput()
263
        )
264

265

266
    def clean(self):
1✔
267
        super(EMailChangeForm, self).clean()
1✔
268
        email = self.cleaned_data.get("new_email")
1✔
269
        email_check = self.cleaned_data.get("new_email_check")
1✔
270

271
        if email and email_check and email != email_check :
1✔
272
            self.add_error('new_email_check', _("E-Mail-Adressen stimmen nicht überein."))
×
273

274

275
        person_list = self.cleaned_data.get("person_list_to_change")
1✔
276
        new_cleaned_data = self.cleaned_data
1✔
277
        
278
        if not person_list :
1✔
279
            new_cleaned_data["person_list_to_change"] = Person.objects.filter(email__iexact=self.instance.old_email.strip())
1✔
280

281
        return new_cleaned_data
1✔
282
    
283
    
284
    class Meta:
1✔
285
        model = EmailChange
1✔
286
        fields = ["new_email", "person_list_to_change"]
1✔
287

288

289
class EMailChangeValidateForm(forms.Form):
1✔
290
    otp = forms.CharField(max_length=32, label="OTP")
1✔
291

292
    def __init__(self, *args, **kwargs):
1✔
293
        self.email_change = kwargs.pop("instance")
1✔
294
        super().__init__(*args, **kwargs)
1✔
295

296
    def clean_otp(self):
1✔
297
        otp = self.cleaned_data["otp"]
×
298

299
        if not self.email_change.verify_otp(otp):
×
300
            raise forms.ValidationError("Invalid or expired OTP")
×
301

302
        return otp
×
303

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc