diff --git a/src/feedback/admin.py b/src/feedback/admin.py index 7573f7c3..1f24e8be 100644 --- a/src/feedback/admin.py +++ b/src/feedback/admin.py @@ -9,7 +9,7 @@ from feedback.models import Person, Veranstaltung, Semester, \ Mailvorlage, Kommentar, Tutor, BarcodeScanner, BarcodeScannEvent, BarcodeAllowedState, \ EmailEndung, Fragebogen2020, FragebogenUE2020, Ergebnis2020, Fragebogen2016, FragebogenUE2016, Ergebnis2016, \ - Fragebogen2025, FragebogenUE2025, Ergebnis2025 + Fragebogen2025, FragebogenUE2025, Ergebnis2025, FragebogenSE2025, ErgebnisSE2025 from feedback.models.base import Log, Fachgebiet, FachgebietEmail @@ -258,6 +258,8 @@ class FragebogenAdmin(admin.ModelAdmin): admin.site.register(Person, PersonAdmin) admin.site.register(Veranstaltung, VeranstaltungAdmin) admin.site.register(Semester, SemesterAdmin) +admin.site.register(FragebogenSE2025, FragebogenAdmin) +admin.site.register(ErgebnisSE2025, FragebogenAdmin) admin.site.register(Fragebogen2025, FragebogenAdmin) admin.site.register(FragebogenUE2025, FragebogenAdmin) admin.site.register(Ergebnis2025, FragebogenAdmin) diff --git a/src/feedback/migrations/0058_ergebnisse2025_fragebogense2025.py b/src/feedback/migrations/0058_ergebnisse2025_fragebogense2025.py new file mode 100644 index 00000000..ab842401 --- /dev/null +++ b/src/feedback/migrations/0058_ergebnisse2025_fragebogense2025.py @@ -0,0 +1,106 @@ +# Generated by Django 5.2.7 on 2025-11-22 23:20 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('feedback', '0057_rename_v_7_5_ergebnis2025_v_8_1_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='ErgebnisSE2025', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('anzahl', models.PositiveIntegerField()), + ('s_didaktik', models.FloatField(blank=True, null=True)), + ('s_didaktik_count', models.PositiveIntegerField(default=0)), + ('s_organisation', models.FloatField(blank=True, null=True)), + ('s_organisation_count', models.PositiveIntegerField(default=0)), + ('s_praxisbezug_motivation', models.FloatField(blank=True, null=True)), + ('s_praxisbezug_motivation_count', models.PositiveIntegerField(default=0)), + ('s_digitale_lehre', models.FloatField(blank=True, null=True)), + ('s_digitale_lehre_count', models.PositiveIntegerField(default=0)), + ('s_9_5', models.FloatField(blank=True, null=True)), + ('s_9_5_count', models.PositiveIntegerField(default=0)), + ('s_feedbackpreis', models.FloatField(blank=True, null=True)), + ('s_feedbackpreis_count', models.PositiveIntegerField(default=0)), + ('gesamt', models.FloatField(blank=True, null=True)), + ('gesamt_count', models.PositiveIntegerField(default=0)), + ('veranstaltung', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='feedback.veranstaltung')), + ], + options={ + 'verbose_name': 'Seminarergebnis 2025', + 'verbose_name_plural': 'Seminarergebnisse 2025', + 'ordering': ['veranstaltung'], + }, + ), + migrations.CreateModel( + name='FragebogenSE2025', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('fach', models.CharField(blank=True, choices=[('inf', 'Informatik'), ('math', 'Mathematik'), ('ce', 'Computational Engineering'), ('ist', 'Informationssystemtechnik'), ('etit', 'Elektrotechnik'), ('psyit', 'Psychologie in IT'), ('winf', 'Wirtschaftsinformatik'), ('sonst', 'etwas anderes')], max_length=5)), + ('abschluss', models.CharField(blank=True, choices=[('bsc', 'Bachelor'), ('msc', 'Master'), ('dipl', 'Diplom'), ('lehr', 'Lehramt'), ('sonst', 'anderer Abschluss')], max_length=5)), + ('semester', models.CharField(blank=True, choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '>=10')], max_length=4)), + ('geschlecht', models.CharField(blank=True, choices=[('w', 'weiblich'), ('m', 'männlich'), ('s', 'sonstiges')], max_length=1)), + ('studienberechtigung', models.CharField(blank=True, choices=[('d', 'Deutschland'), ('o', 'anderes Land')], max_length=1)), + ('male_veranstaltung_gehoert', models.CharField(blank=True, choices=[('1', '1'), ('2', '2'), ('3', '3'), ('4', '<=4')], max_length=1)), + ('s_wie_oft_besucht', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_besuch_ueberschneidung', models.CharField(blank=True, choices=[('j', 'ja'), ('n', 'nein')], max_length=1)), + ('s_besuch_qualitaet', models.CharField(blank=True, choices=[('j', 'ja'), ('n', 'nein')], max_length=1)), + ('s_besuch_verhaeltnisse', models.CharField(blank=True, choices=[('j', 'ja'), ('n', 'nein')], max_length=1)), + ('s_besuch_privat', models.CharField(blank=True, choices=[('j', 'ja'), ('n', 'nein')], max_length=1)), + ('s_besuch_elearning', models.CharField(blank=True, choices=[('j', 'ja'), ('n', 'nein')], max_length=1)), + ('s_besuch_zufrueh', models.CharField(blank=True, choices=[('j', 'ja'), ('n', 'nein')], max_length=1)), + ('s_besuch_sonstiges', models.CharField(blank=True, choices=[('j', 'ja'), ('n', 'nein')], max_length=1)), + ('s_3_1', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_3_2', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_3_3', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_3_4', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_3_6', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_3_7', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_3_8', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_3_9', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_3_10', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_4_1', models.CharField(blank=True, choices=[('j', 'ja'), ('n', 'nein')], max_length=1)), + ('s_4_2', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_4_3', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_4_4', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_4_5', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_4_6', models.CharField(blank=True, choices=[('0', '0'), ('1', '0.5'), ('2', '1'), ('3', '2'), ('4', '3'), ('5', '4'), ('6', '5'), ('7', '>=5')], max_length=1)), + ('s_5_1', models.CharField(blank=True, choices=[('j', 'ja'), ('n', 'nein')], max_length=1)), + ('s_5_2', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_5_3', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_5_4', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_5_5', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_5_6', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_5_7', models.CharField(blank=True, choices=[('0', '0'), ('1', '0.5'), ('2', '1'), ('3', '2'), ('4', '3'), ('5', '4'), ('6', '5'), ('7', '>=5')], max_length=1)), + ('s_6_1', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_6_2', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_6_3', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_6_4', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_6_5', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_6_6', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_6_7', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_6_8', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_6_9', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_6_10', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_7_1', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_7_2', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_9_1', models.CharField(blank=True, choices=[('h', 'zu hoch'), ('n', 'zu niedrig')], max_length=1)), + ('s_9_2', models.CharField(blank=True, choices=[('h', 'zu hoch'), ('n', 'zu niedrig')], max_length=1)), + ('s_9_3', models.CharField(blank=True, choices=[('h', 'zu hoch'), ('n', 'zu niedrig')], max_length=1)), + ('s_9_4', models.PositiveSmallIntegerField(blank=True, null=True)), + ('s_9_5', models.PositiveSmallIntegerField(blank=True, null=True)), + ('veranstaltung', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='feedback.veranstaltung')), + ], + options={ + 'verbose_name': 'Seminarfragebogen 2025', + 'verbose_name_plural': 'Seminarfragebögen 2025', + 'ordering': ['semester', 'veranstaltung'], + }, + ), + ] diff --git a/src/feedback/migrations/0059_fragebogense2025_s_3_5.py b/src/feedback/migrations/0059_fragebogense2025_s_3_5.py new file mode 100644 index 00000000..76d23fe8 --- /dev/null +++ b/src/feedback/migrations/0059_fragebogense2025_s_3_5.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2025-11-30 23:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('feedback', '0058_ergebnisse2025_fragebogense2025'), + ] + + operations = [ + migrations.AddField( + model_name='fragebogense2025', + name='s_3_5', + field=models.PositiveSmallIntegerField(blank=True, null=True), + ), + ] diff --git a/src/feedback/migrations/0060_alter_fragebogense2025_s_9_1_and_more.py b/src/feedback/migrations/0060_alter_fragebogense2025_s_9_1_and_more.py new file mode 100644 index 00000000..8c7b480e --- /dev/null +++ b/src/feedback/migrations/0060_alter_fragebogense2025_s_9_1_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.7 on 2025-12-01 00:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('feedback', '0059_fragebogense2025_s_3_5'), + ] + + operations = [ + migrations.AlterField( + model_name='fragebogense2025', + name='s_9_1', + field=models.PositiveSmallIntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name='fragebogense2025', + name='s_9_2', + field=models.PositiveSmallIntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name='fragebogense2025', + name='s_9_3', + field=models.PositiveSmallIntegerField(blank=True, null=True), + ), + ] diff --git a/src/feedback/models/__init__.py b/src/feedback/models/__init__.py index a763013c..ed25bae8 100644 --- a/src/feedback/models/__init__.py +++ b/src/feedback/models/__init__.py @@ -14,13 +14,21 @@ from feedback.models.fragebogenUE2020 import FragebogenUE2020 from feedback.models.fragebogen2025 import Fragebogen2025, Ergebnis2025 from feedback.models.fragebogenUE2025 import FragebogenUE2025 +from feedback.models.fragebogenSE2025 import FragebogenSE2025, ErgebnisSE2025 from django.core.exceptions import ObjectDoesNotExist from django.db.models import Q -def get_model(model, semester): - mod = '%s.fragebogen%s' % (__name__, semester.fragebogen) +def get_model(model, semester, is_seminar=False): + if not is_seminar : + mod = '%s.fragebogen%s' % (__name__, semester.fragebogen) + else : + mod = '%s.fragebogenSE%s' % (__name__, semester.fragebogen) + + if is_seminar : + model = model + "SE" + cls = '%s%s' % (model, semester.fragebogen) module = __import__(mod, fromlist=(cls,)) return getattr(module, cls) @@ -33,6 +41,15 @@ def get_model_string(model, semester): return getattr(module, cls) +def semester_has_seminar_model(semester: Semester) : + """ + check if given semester has a seminar model + """ + SEMINAR_YEARS_LIST = ["2025",] + + return True if semester.fragebogen in SEMINAR_YEARS_LIST else False + + def long_not_ordert(): """Alle Veranstaltungen die schon länger nicht mehr evaluiert wurden""" # suche nach allen Veranstaltungen aus dem aktellen Semester bei denen diff --git a/src/feedback/models/fragebogen2025.py b/src/feedback/models/fragebogen2025.py index 8ea20fc1..251568d3 100644 --- a/src/feedback/models/fragebogen2025.py +++ b/src/feedback/models/fragebogen2025.py @@ -173,12 +173,12 @@ class Ergebnis2025(Ergebnis): [ '3.2 Die Vorlesungsmaterialien (Folien, Skripte, Tafelanschrieb, Lehrbücher,e-Learning, etc.) haben das Lernen wirkungsvoll unterstützt.', '3.6 Der Lehrende war auch außerhalb der Veranstaltung ansprechbar.', - '3.8 Die Vorlesung motivierte dazu, sich außerhalb der Veranstaltungselbstständig mit den behandelten Themen auseinander zu setzen.', # not in fragebogen? '4.4 Die Vorlesung war inhaltlich gut strukturiert, ein roter Faden war erkennbar.', '4.6 Der Stoff wurde anhand von Beispielen verdeutlicht.', '4.7 Der Bezug zwischen Theorie und praktischem Arbeiten / praktischen Anwendungen wurde hergestellt.', '5.2 Die Lehrkraft hat Kompliziertes verständlich dargelegt.', '5.4 Die Lernziele der Veranstaltung sind mir klar geworden.', + '5.5 Die Vorlesung motivierte dazu, sich außerhalb der Veranstaltungselbstständig mit den behandelten Themen auseinander zu setzen.', '6.1 Der Lehrende regte gezielt zur eigenen Mitarbeit / zum Mitdenken in der Veranstaltung an.', '6.2 Die (Zwischen-)Fragen der Studierenden wurden angemessen beantwortet.', '6.3 Die Lehrkraft zeigte sich gut vorbereitet.', diff --git a/src/feedback/models/fragebogenSE2025.py b/src/feedback/models/fragebogenSE2025.py new file mode 100644 index 00000000..d85dc55f --- /dev/null +++ b/src/feedback/models/fragebogenSE2025.py @@ -0,0 +1,192 @@ + +# coding=utf-8 + +from django.db import models +from feedback.models import Fragebogen, Ergebnis + + +class FragebogenSE2025(Fragebogen): + fach = models.CharField(max_length=5, choices=Fragebogen.FACH_CHOICES, blank=True) + abschluss = models.CharField(max_length=5, choices=Fragebogen.ABSCHLUSS_CHOICES, blank=True) + semester = models.CharField(max_length=4, choices=Fragebogen.SEMESTER_CHOICES16, blank=True) + geschlecht = models.CharField(max_length=1, choices=Fragebogen.GESCHLECHT_CHOICES, blank=True) + studienberechtigung = models.CharField(max_length=1, choices=Fragebogen.STUDIENBERECHTIGUNG_CHOICES, blank=True) + male_veranstaltung_gehoert = models.CharField(max_length=1, choices=Fragebogen.VERANSTALTUNG_GEHOERT, blank=True) + + s_wie_oft_besucht = models.PositiveSmallIntegerField(blank=True, null=True) + s_besuch_ueberschneidung = models.CharField(max_length=1, choices=Fragebogen.BOOLEAN_CHOICES, blank=True) + s_besuch_qualitaet = models.CharField(max_length=1, choices=Fragebogen.BOOLEAN_CHOICES, blank=True) + s_besuch_verhaeltnisse = models.CharField(max_length=1, choices=Fragebogen.BOOLEAN_CHOICES, blank=True) + s_besuch_privat = models.CharField(max_length=1, choices=Fragebogen.BOOLEAN_CHOICES, blank=True) + s_besuch_elearning = models.CharField(max_length=1, choices=Fragebogen.BOOLEAN_CHOICES, blank=True) + s_besuch_zufrueh = models.CharField(max_length=1, choices=Fragebogen.BOOLEAN_CHOICES, blank=True) + s_besuch_sonstiges = models.CharField(max_length=1, choices=Fragebogen.BOOLEAN_CHOICES, blank=True) + + s_3_1 = models.PositiveSmallIntegerField(blank=True, null=True) + s_3_2 = models.PositiveSmallIntegerField(blank=True, null=True) + s_3_3 = models.PositiveSmallIntegerField(blank=True, null=True) + s_3_4 = models.PositiveSmallIntegerField(blank=True, null=True) + s_3_6 = models.PositiveSmallIntegerField(blank=True, null=True) + s_3_5 = models.PositiveSmallIntegerField(blank=True, null=True) + s_3_7 = models.PositiveSmallIntegerField(blank=True, null=True) + s_3_8 = models.PositiveSmallIntegerField(blank=True, null=True) + s_3_9 = models.PositiveSmallIntegerField(blank=True, null=True) + s_3_10 = models.PositiveSmallIntegerField(blank=True, null=True) + + s_4_1 = models.CharField(max_length=1, choices=Fragebogen.BOOLEAN_CHOICES, blank=True) + s_4_2 = models.PositiveSmallIntegerField(blank=True, null=True) + s_4_3 = models.PositiveSmallIntegerField(blank=True, null=True) + s_4_4 = models.PositiveSmallIntegerField(blank=True, null=True) + s_4_5 = models.PositiveSmallIntegerField(blank=True, null=True) + s_4_6 = models.CharField(max_length=1, choices=Fragebogen.STUNDEN_NACHBEARBEITUNG, blank=True) + + s_5_1 = models.CharField(max_length=1, choices=Fragebogen.BOOLEAN_CHOICES, blank=True) + s_5_2 = models.PositiveSmallIntegerField(blank=True, null=True) + s_5_3 = models.PositiveSmallIntegerField(blank=True, null=True) + s_5_4 = models.PositiveSmallIntegerField(blank=True, null=True) + s_5_5 = models.PositiveSmallIntegerField(blank=True, null=True) + s_5_6 = models.PositiveSmallIntegerField(blank=True, null=True) + s_5_7 = models.CharField(max_length=1, choices=Fragebogen.STUNDEN_NACHBEARBEITUNG, blank=True) + + s_6_1 = models.PositiveSmallIntegerField(blank=True, null=True) + s_6_2 = models.PositiveSmallIntegerField(blank=True, null=True) + s_6_3 = models.PositiveSmallIntegerField(blank=True, null=True) + s_6_4 = models.PositiveSmallIntegerField(blank=True, null=True) + s_6_5 = models.PositiveSmallIntegerField(blank=True, null=True) + s_6_6 = models.PositiveSmallIntegerField(blank=True, null=True) + s_6_7 = models.PositiveSmallIntegerField(blank=True, null=True) + s_6_8 = models.PositiveSmallIntegerField(blank=True, null=True) + s_6_9 = models.PositiveSmallIntegerField(blank=True, null=True) + s_6_10 = models.PositiveSmallIntegerField(blank=True, null=True) + + s_7_1 = models.PositiveSmallIntegerField(blank=True, null=True) + s_7_2 = models.PositiveSmallIntegerField(blank=True, null=True) + + s_9_1 = models.PositiveSmallIntegerField(blank=True, null=True) + s_9_2 = models.PositiveSmallIntegerField(blank=True, null=True) + s_9_3 = models.PositiveSmallIntegerField(blank=True, null=True) + s_9_4 = models.PositiveSmallIntegerField(blank=True, null=True) + s_9_5 = models.PositiveSmallIntegerField(blank=True, null=True) + + # after adding 'prize recommendation' question complete this + # s_9_ = models.CharField(max_length=1, choices=Fragebogen.BOOLEAN_CHOICES, blank=True) + + class Meta: + verbose_name = 'Seminarfragebogen 2025' + verbose_name_plural = 'Seminarfragebögen 2025' + ordering = ['semester', 'veranstaltung'] + app_label = 'feedback' + + +class ErgebnisSE2025(Ergebnis): + parts = [ + [ + 's_9_5', 'Seminar: Gesamtnote', + [ + '9.5 Welche Gesamtnote gibst du dem Seminar?', + ] + ], + [ + 's_didaktik', 'Seminar: Didaktik', + [ + '3.4 Das Seminar war eine gute Mischung aus Wissensvermittlung und Diskussion.', + '3.8 Es wurde ausreichend konstruktives Feedback gegeben.', + '3.9 Das Seminar hat mich dazu motiviert, mich weiter mit dem Thema zu beschäftigen.', + '5.3 Der Umfang der Ausarbeitungen war angemessen.', + '5.4 Während der Erstellung der Ausarbeitung gab es ausreichende Unterstützung durch die Veranstalter*innen.', + '6.3 Die Lehrkraft hat bei Problemen kompetente Unterstützung bieten können.', + '6.4 Die Lehrkraft war engagiert.', + '6.5 Die Lehrkraft ist auf studentische Fragen und Beiträge angemessen eingegangen.', + '6.6 Die Lehrkraft hat die Diskussionen gut geleitet. ', + '6.7 Die Lehrkraft regte uns gezielt zum Mitdenken und eigener Mitarbeit an.', + '6.9 Die Lehrkraft hat die Veranstaltung gut und angemessen betreut.', + ] + ], + [ + 's_organisation', 'Seminar: Organisation', + [ + '3.1 Das Seminar war inhaltlich gut strukturiert.', + '3.2 Die Organisation des Seminars war gut.', + '3.5 Für die Diskussionen der Themen war genug Zeit.', + '3.7 Es wurden ausreichend Materialien zur Verfügung gestellt.', + '4.2 Die Kriterien (Inhalt, Aufbau, Präsentation) für ein gutes Referat wurden verdeutlicht.', + '4.4 Referate waren eine sinnvolle Weise, um den Teilnehmer*innen die Inhalte zu vermitteln.', + '4.5 Die Zeitpunkte für die Referate waren gut vereinbar mit anderen Veranstaltungen und Klausurterminen.', + '5.2 Die Kriterien für eine gute Ausarbeitung wurden verdeutlicht.', + '5.6 Die Zeitpunkte für die Abgabe von Ausarbeitungen waren gut vereinbar mit anderen Veranstaltungen und Klausurterminen.', + '6.1 Die Lehrkraft hat eine gute Einführung in die Thematik gegeben.', + ] + ], + [ + 's_praxisbezug_motivation', 'Seminar: Praxisbezug und Motivation', + [ + '3.10 Durch das Seminar konnte ich meine Vortragstechnik verfeinern.', + '5.5 Durch das Anfertigen der Ausarbeitungen habe ich einen umfassenden Einblick in das Thema erhalten.', + ] + ], + [ + 's_digitale_lehre', 'Seminar: Digitale Lehre', + [ + '6.8 Die Lehrkraft setzte verfügbare Medien sinnvoll ein.', + '7.1 Ich habe ausreichend Informationen zur Nutzung des digitalen Lehr-/Lernangebots von dem/der Lehrenden erhalten.', + '7.2 Mir ist es technisch möglich, in vollem Umfang an der Veranstaltung teilzunehmen.', + ] + ], + ] + + hidden_parts = [ + [ + 's_feedbackpreis', 'Feedbackpreis: Bestes Seminar', + [ + '3.1 Das Seminar war inhaltlich gut strukturiert.', + '3.7 Es wurden ausreichend Materialien zur Verfügung gestellt.', + '3.8 Es wurde ausreichend konstruktives Feedback gegeben.', + '3.9 Das Seminar hat mich dazu motiviert, mich weiter mit dem Thema zu beschäftigen.', + '4.2 Die Kriterien (Inhalt, Aufbau, Präsentation) für ein gutes Referat wurden verdeutlicht.', + '5.4 Während der Erstellung der Ausarbeitung gab es ausreichende Unterstützung durch die Veranstalter*innen.', + '6.3 Die Lehrkraft hat bei Problemen kompetente Unterstützung bieten können.', + '6.4 Die Lehrkraft war engagiert.', + '6.7 Die Lehrkraft regte uns gezielt zum Mitdenken und eigener Mitarbeit an.', + '6.8 Die Lehrkraft setzte verfügbare Medien sinnvoll ein.', + '9.5 Welche Gesamtnote gibst du dem Seminar?', + ] + ] + ] + + weight = {} # adjust weight for ranking + + + # TODO: decimal statt float benutzen + s_didaktik = models.FloatField(blank=True, null=True) + s_didaktik_count = models.PositiveIntegerField(default=0) + s_didaktik_parts = ['s_3_4', 's_3_8', 's_3_9', 's_5_3', 's_5_4', 's_6_3', 's_6_4', 's_6_5', 's_6_6', 's_6_7', 's_6_7', 's_6_9',] + + s_organisation = models.FloatField(blank=True, null=True) + s_organisation_count = models.PositiveIntegerField(default=0) + s_organisation_parts = ['s_3_1', 's_3_2', 's_3_5', 's_3_7', 's_4_2', 's_4_4', 's_4_5', 's_5_2', 's_5_6', 's_6_1',] + + s_praxisbezug_motivation = models.FloatField(blank=True, null=True) + s_praxisbezug_motivation_count = models.PositiveIntegerField(default=0) + s_praxisbezug_motivation_parts = ['s_3_10', 's_5_5',] + + s_digitale_lehre = models.FloatField(blank=True, null=True) + s_digitale_lehre_count = models.PositiveIntegerField(default=0) + s_digitale_lehre_parts = ['s_6_8', 's_7_1', 's_7_2',] + + s_9_5 = models.FloatField(blank=True, null=True) + s_9_5_count = models.PositiveIntegerField(default=0) + + s_feedbackpreis = models.FloatField(blank=True, null=True) + s_feedbackpreis_count = models.PositiveIntegerField(default=0) + s_feedbackpreis_parts = [ + 's_3_1', 's_3_7', 's_3_8', 's_3_9', 's_4_2', 's_5_4', 's_6_3', 's_6_4', 's_6_7', 's_6_8', 's_9_5', + ] + + gesamt = models.FloatField(blank=True, null=True) + gesamt_count = models.PositiveIntegerField(default=0) + + class Meta: + verbose_name = 'Seminarergebnis 2025' + verbose_name_plural = 'Seminarergebnisse 2025' + ordering = ['veranstaltung'] + app_label = 'feedback' diff --git a/src/feedback/parser/ergebnisse/parserSE2025.py b/src/feedback/parser/ergebnisse/parserSE2025.py new file mode 100644 index 00000000..8c1aa480 --- /dev/null +++ b/src/feedback/parser/ergebnisse/parserSE2025.py @@ -0,0 +1,74 @@ + +# coding = utf-8 + +from feedback.models import FragebogenSE2025 +from feedback.parser.ergebnisse.parser import Parser + + +class ParserSE2025(Parser): + @classmethod + def create_fragebogen(cls, veranst, frageb): + FragebogenSE2025.objects.create( + veranstaltung=veranst, + fach=cls.parse_fach_16(frageb[1]), + abschluss=cls.parse_abschluss_16(frageb[3]), + semester=cls.parse_semester_16(frageb[4]), + geschlecht=cls.parse_geschlecht_16(frageb[5]), + studienberechtigung=cls.parse_studienberechtigung(frageb[6]), + male_veranstaltung_gehoert=cls.parse_veranstaltung_gehoert(frageb[7]), + + s_wie_oft_besucht=cls.parse_int(frageb[8]), + s_besuch_ueberschneidung=cls.parse_boolean(frageb[9]), + s_besuch_qualitaet=cls.parse_boolean(frageb[10]), + s_besuch_verhaeltnisse=cls.parse_boolean(frageb[11]), + s_besuch_privat=cls.parse_boolean(frageb[12]), + s_besuch_elearning=cls.parse_boolean(frageb[13]), + s_besuch_zufrueh=cls.parse_boolean(frageb[14]), + s_besuch_sonstiges=cls.parse_boolean(frageb[15]), + + s_3_1=cls.parse_int(frageb[17]), + s_3_2=cls.parse_int(frageb[18]), + s_3_3=cls.parse_int(frageb[19]), + s_3_4=cls.parse_int(frageb[20]), + s_3_5=cls.parse_int(frageb[21]), + s_3_6=cls.parse_int(frageb[22]), + s_3_7=cls.parse_int(frageb[23]), + s_3_8=cls.parse_int(frageb[24]), + s_3_9=cls.parse_int(frageb[25]), + s_3_10=cls.parse_int(frageb[26]), + + s_4_1=cls.parse_boolean(frageb[27]), + s_4_2=cls.parse_int(frageb[28]), + s_4_3=cls.parse_int(frageb[29]), + s_4_4=cls.parse_int(frageb[30]), + s_4_5=cls.parse_int(frageb[31]), + s_4_6=cls.parse_extrazeit(frageb[32]), + + s_5_1=cls.parse_boolean(frageb[33]), + s_5_2=cls.parse_int(frageb[34]), + s_5_3=cls.parse_int(frageb[35]), + s_5_4=cls.parse_int(frageb[36]), + s_5_5=cls.parse_int(frageb[37]), + s_5_6=cls.parse_int(frageb[38]), + s_5_7=cls.parse_extrazeit(frageb[39]), + + s_6_1=cls.parse_int(frageb[40]), + s_6_2=cls.parse_int(frageb[41]), + s_6_3=cls.parse_int(frageb[42]), + s_6_4=cls.parse_int(frageb[43]), + s_6_5=cls.parse_int(frageb[44]), + s_6_6=cls.parse_int(frageb[45]), + s_6_7=cls.parse_int(frageb[46]), + s_6_8=cls.parse_int(frageb[47]), + s_6_9=cls.parse_int(frageb[48]), + s_6_10=cls.parse_int(frageb[49]), + + s_7_1=cls.parse_int(frageb[50]), + s_7_2=cls.parse_int(frageb[51]), + + s_9_1=cls.parse_int(frageb[56]), + s_9_2=cls.parse_int(frageb[56]), + s_9_3=cls.parse_int(frageb[56]), + s_9_4=cls.parse_int(frageb[56]), + s_9_5=cls.parse_int(frageb[57]), + ) diff --git a/src/feedback/urls.py b/src/feedback/urls.py index 9acabf1c..390ce91e 100644 --- a/src/feedback/urls.py +++ b/src/feedback/urls.py @@ -25,6 +25,7 @@ # öffentliche Views urlpatterns += [ re_path(r'^ergebnisse/(?P\d+)/$', feedback.views.public.veranstaltung, name='public-veranstaltung'), + re_path(r'^ergebnisse/(?P\d+)/(?Pseminar)/$', feedback.views.public.veranstaltung, name='public-veranstaltung'), re_path(r'^ergebnisse/$', feedback.views.public.index, name='public-results'), ] @@ -92,4 +93,4 @@ # angezeigt werden. Im Server-Betrieb kümmert sich Apache darum. urlpatterns += [ re_path(r'^d120de/(?P.*)$', feedback.views.redirect, {'redirect_to': 'http://www.d120.de/d120de/'}), - ] \ No newline at end of file + ] diff --git a/src/feedback/views/intern/__init__.py b/src/feedback/views/intern/__init__.py index f5997981..70128408 100644 --- a/src/feedback/views/intern/__init__.py +++ b/src/feedback/views/intern/__init__.py @@ -29,7 +29,7 @@ from feedback.forms import UploadTANCSV, SendOrPDF, EMailTemplates from feedback.parser.ergebnisse import parse_ergebnisse from feedback.views import public -from feedback.models import Veranstaltung, Semester, Mailvorlage, get_model, long_not_ordert, \ +from feedback.models import Veranstaltung, Semester, Mailvorlage, get_model, long_not_ordert, semester_has_seminar_model, \ FachgebietEmail, Tutor from feedback.models.fragebogenUE2016 import FragebogenUE2016 from feedback.models.fragebogenUE2020 import FragebogenUE2020 @@ -477,6 +477,11 @@ def import_ergebnisse(request): warnings, errors, vcount, fbcount = parse_ergebnisse(semester, TextIOWrapper(request.FILES['file'].file, encoding='UTF-8'), f'UE{semester.fragebogen}') + elif 'typ' in request.POST and request.POST['typ'] == 'seminar': + warnings, errors, vcount, fbcount = parse_ergebnisse(semester, + TextIOWrapper(request.FILES['file'].file, encoding='UTF-8'), + f'SE{semester.fragebogen}') + else: warnings, errors, vcount, fbcount = parse_ergebnisse(semester, TextIOWrapper(request.FILES['file'].file, encoding='UTF-8')) if fbcount: @@ -526,6 +531,7 @@ def sync_ergebnisse(request): erg = FragebogenUE2020.objects.filter(veranstaltung=v) else: erg = FragebogenUE2025.objects.filter(veranstaltung=v) + if len(fbs): found_something = True data = {'veranstaltung': v, 'anzahl': len(fbs)} @@ -549,6 +555,27 @@ def sync_ergebnisse(request): data[part[0]] = result data[part[0] + '_count'] = count ergebnis.objects.create(**data) + + # results seminar + if semester_has_seminar_model(semester) : + fragebogen_se = get_model('Fragebogen', semester, is_seminar=True) + ergebnis_se = get_model('Ergebnis', semester, is_seminar=True) + + ergebnis_se.objects.filter(veranstaltung__semester=semester).delete() + + for v in Veranstaltung.objects.filter(semester=semester) : + fbs = fragebogen_se.objects.filter(veranstaltung=v) + + if len(fbs) : + found_something = True + data = {'veranstaltung': v, 'anzahl': len(fbs)} + + for part in ergebnis_se.parts + ergebnis_se.hidden_parts : + result, count = tools.get_average(ergebnis_se, fbs, part[0]) + data[part[0]] = result + data[part[0] + '_count'] = count + ergebnis_se.objects.create(**data) + if not found_something: messages.warning(request, 'Für das %s liegen keine Ergebnisse vor.' % semester) diff --git a/src/feedback/views/public.py b/src/feedback/views/public.py index da1becfa..3b0bd4be 100644 --- a/src/feedback/views/public.py +++ b/src/feedback/views/public.py @@ -4,7 +4,7 @@ from django.shortcuts import get_object_or_404, render from django.views.decorators.http import require_safe from feedback.views.public_class_view import barcodedrop -from feedback.models import Semester, get_model, Veranstaltung, Kommentar +from feedback.models import Semester, get_model, Veranstaltung, Kommentar, semester_has_seminar_model from django.conf import settings @@ -46,6 +46,7 @@ def index(request): data['parts'] = Ergebnis.parts data['include_hidden'] = False + # Sortierung try: parts = [part[0] for part in data['parts']] @@ -72,11 +73,62 @@ def index(request): data['order_num'] = 0 data['ergebnisse'] = list(ergebnisse.order_by('veranstaltung__name')) + + if not semester_has_seminar_model(data['semester']) : + data['show_seminar'] = False + else : + data['show_seminar'] = True + + # Seminar Ergebnisse einlesen + Ergebnis_SE = get_model('Ergebnis', data['semester'], is_seminar=True) + ergebnisse_se = Ergebnis_SE.objects.filter(veranstaltung__semester=data['semester']) + + if ergebnisse_se.exists() : + data['se_exists'] = True + else : + data['se_exists'] = False + + # anzuzeigende Informationen auswählen + if request.user.is_superuser or settings.DEBUG == True: + data['parts_se'] = Ergebnis_SE.parts + Ergebnis_SE.hidden_parts + data['include_hidden_se'] = True + else: + data['parts_se'] = Ergebnis_SE.parts + data['include_hidden_se'] = False + + + # Seminar Sortierung + try: + parts_se = [part[0] for part in data['parts_se']] + order_se = request.GET['order_se'] + if not order_se in parts_se: + raise KeyError + data['order_se'] = order_se + data['order_num_se'] = parts_se.index(order_se) + 1 + data['ergebnisse_se'] = list(ergebnisse_se.order_by(order_se)) + + # Veranstaltung mit zu kleinen Teilnehmerzahlen bei aktuellem Kriterium nach hinten sortieren + tail = [] + for e in data['ergebnisse_se']: + count = getattr(e, order_se + '_count') + if count < settings.THRESH_SHOW: + tail.append(e) + + for e in tail: + data['ergebnisse_se'].remove(e) + data['ergebnisse_se'].extend(tail) + + except KeyError: + data['order_se'] = 'alpha' + data['order_num_se'] = 0 + data['ergebnisse_se'] = list(ergebnisse_se.order_by('veranstaltung__name')) + + return render(request, 'public/index.html', data) @require_safe -def veranstaltung(request, vid=None): +def veranstaltung(request, vid=None, seminar=False): # Zugangskontrolle if request.user.is_superuser or settings.DEBUG == True: authfilter = {} @@ -92,7 +144,7 @@ def veranstaltung(request, vid=None): if data['v'].semester.sichtbarkeit != 'ALL': data['restricted'] = True - Ergebnis = get_model('Ergebnis', veranstaltung.semester) + Ergebnis = get_model('Ergebnis', veranstaltung.semester, is_seminar=seminar) if veranstaltung.typ == 'v': parts = Ergebnis.parts_vl else: diff --git a/src/templates/intern/import_ergebnisse.html b/src/templates/intern/import_ergebnisse.html index 861cc0d4..fe2e172d 100644 --- a/src/templates/intern/import_ergebnisse.html +++ b/src/templates/intern/import_ergebnisse.html @@ -16,14 +16,21 @@

{% translate "Ergebnisse aus EvaSys importieren" %}


- +

- + +

+ +

+ + +
{{ form }} +

diff --git a/src/templates/public/index.html b/src/templates/public/index.html index 353d02ad..e5d2a7bc 100644 --- a/src/templates/public/index.html +++ b/src/templates/public/index.html @@ -22,6 +22,8 @@

{% blocktranslate %}Feedback-Ergebnisse ({{ semester }}){% endblocktranslate

{% translate "Hinweis: Die Spalten zum Feedbackpreis sind nur für Administratoren sichtbar." %}

{% endif %} +

{% translate "Vorlesung und Übung" %}

+
{% translate "alphabetisch" %} @@ -53,6 +55,7 @@

{% blocktranslate %}Feedback-Ergebnisse ({{ semester }}){% endblocktranslate
+ {% for e in ergebnisse %} @@ -73,6 +76,70 @@

{% blocktranslate %}Feedback-Ergebnisse ({{ semester }}){% endblocktranslate

+{% if show_seminar %} +
+

{% translate "Seminar" %}

+ + {% if se_exists %} +
+ + {% for p in parts_se %} +
+ {% endfor %} +
+ + {% for p, p_pretty, z in parts_se reversed %} +
+ + {% for p, p_pretty, z in parts_se %} {% if forloop.parentloop.counter < forloop.revcounter %}
+
+ {% else %} +
+ {% endif %} {% endfor %} +
+ {% endfor %} + +
+
+ {% for p, p_pretty, z in parts_se %} +
+ {% endfor %} +
+ +
+ + + + + {% for e in ergebnisse_se %} + + + + {% if include_hidden_se %} {% for val, count in e.all_values %} + + {% endfor %} {% else %} {% for val, count in e.values %} + + {% endfor %} {% endif %} + + {% endfor %} + +
{{ e.veranstaltung.name }} + {% if count >= thresh_show %}{{ val|floatformat:1 }}{% endif %} + + {% if count >= thresh_show %}{{ val|floatformat:1 }}{% endif %} +
+ {% else %} + {% translate 'Kein Seminarergebnis ist verfügbar.' %} + {% endif %} +{% endif %} + +
+ +

{% translate "Darstellung der Noten" %}

  • {% blocktranslate %}Durchschnittsnoten werden entsprechend gekennzeichnet, falls weniger als {{ thresh_valid }} Antworten einflossen.{% endblocktranslate %}
  • @@ -81,6 +148,7 @@

    {% translate "Darstellung der Noten" %}

    {% translate "Aufschlüsselung der Kategorien" %}

    {% translate "Hier sind die Fragen aufgelistet, die in die einzelnen Noten einfließen." %}

    + {% for p, p_pretty, questions in parts %}

    {{ p_pretty }}

      @@ -88,4 +156,17 @@

      {{ p_pretty }}

    • {{ q }}
    • {% endfor %}
    -{% endfor %} {% endblock %} +{% endfor %} + +{% if show_seminar %} + {% for p, p_pretty, questions in parts_se %} +

    {{ p_pretty }}

    +
      + {% for q in questions %} +
    • {{ q }}
    • + {% endfor %} +
    + {% endfor %} +{% endif %} + +{% endblock %}