internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: e-valuation/EvaP

Issue:
Fix create course form
When creating a new course the form is not rendered correctly because `course.can_staff_edit` is not defined in this situation.



Discussion:

Resolving patch:
diff --git a/evap/contributor/views.py b/evap/contributor/views.py
--- a/evap/contributor/views.py
+++ b/evap/contributor/views.py
@@ -66,7 +66,7 @@ def course_view(request, course_id):
         for name, field in cform.fields.items():
             field.disabled = True
 
-    template_data = dict(form=form, formset=formset, course=course, edit=False, responsible=course.responsible_contributor.username)
+    template_data = dict(form=form, formset=formset, course=course, editable=False, responsible=course.responsible_contributor.username)
     return render(request, "contributor_course_form.html", template_data)
 
 
@@ -104,7 +104,7 @@ def course_edit(request, course_id):
         return redirect('contributor:index')
     else:
         sort_formset(request, formset)
-        template_data = dict(form=course_form, formset=formset, course=course, edit=True, responsible=course.responsible_contributor.username)
+        template_data = dict(form=course_form, formset=formset, course=course, editable=True, responsible=course.responsible_contributor.username)
         return render(request, "contributor_course_form.html", template_data)
 
 
diff --git a/evap/staff/views.py b/evap/staff/views.py
--- a/evap/staff/views.py
+++ b/evap/staff/views.py
@@ -440,7 +440,7 @@ def course_create(request, semester_id):
         messages.success(request, _("Successfully created course."))
         return redirect('staff:semester_view', semester_id)
     else:
-        return render(request, "staff_course_form.html", dict(semester=semester, form=form, formset=formset, staff=True))
+        return render(request, "staff_course_form.html", dict(semester=semester, form=form, formset=formset, staff=True, editable=True, state=""))
 
 
 @staff_required
@@ -478,6 +478,7 @@ def helper_course_edit(request, semester, course):
 
     form = CourseForm(request.POST or None, instance=course)
     formset = InlineContributionFormset(request.POST or None, instance=course, form_kwargs={'course': course})
+    editable = course.can_staff_edit
 
     operation = request.POST.get('operation')
 
@@ -504,7 +505,7 @@ def helper_course_edit(request, semester, course):
         return custom_redirect('staff:semester_view', semester.id)
     else:
         sort_formset(request, formset)
-        template_data = dict(semester=semester, course=course, form=form, formset=formset, staff=True, state=course.state)
+        template_data = dict(semester=semester, form=form, formset=formset, staff=True, state=course.state, editable=editable)
         return render(request, "staff_course_form.html", template_data)
 
 


Test patch:
Repository: e-valuation/EvaP

Issue:
Create semester form
The form for creating a semester should not have an `is archived` checkbox.



Discussion:

Resolving patch:
diff --git a/evap/staff/forms.py b/evap/staff/forms.py
--- a/evap/staff/forms.py
+++ b/evap/staff/forms.py
@@ -40,7 +40,7 @@ class UserBulkDeleteForm(forms.Form, BootstrapMixin):
 class SemesterForm(forms.ModelForm, BootstrapMixin):
     class Meta:
         model = Semester
-        fields = "__all__"
+        fields = ("name_de", "name_en")
 
 
 class DegreeForm(forms.ModelForm, BootstrapMixin):


Test patch:
Repository: e-valuation/EvaP

Issue:
Deal with update.sh
update.sh is out of date. We can either
- remove it
- update it
- replace it with something else

having a script would be pretty cool to document what needs to be done when updating a production server.

maybe this can go into a management command.

an idea for additional automation would be a git post-commit-hook that checks out the release branch and updates everything when someone pushes to the production server via ssh. logs of the update could be sent via email to the admins.

Deal with update.sh
update.sh is out of date. We can either
- remove it
- update it
- replace it with something else

having a script would be pretty cool to document what needs to be done when updating a production server.

maybe this can go into a management command.

an idea for additional automation would be a git post-commit-hook that checks out the release branch and updates everything when someone pushes to the production server via ssh. logs of the update could be sent via email to the admins.



Discussion:

Resolving patch:
diff --git a/evap/evaluation/management/commands/refresh_results_cache.py b/evap/evaluation/management/commands/refresh_results_cache.py
--- a/evap/evaluation/management/commands/refresh_results_cache.py
+++ b/evap/evaluation/management/commands/refresh_results_cache.py
@@ -1,4 +1,5 @@
 from django.core.management.base import BaseCommand
+from django.core.serializers.base import ProgressBar
 from django.core.cache import cache
 
 from evap.evaluation.models import Course
@@ -12,9 +13,15 @@ class Command(BaseCommand):
     def handle(self, *args, **options):
         self.stdout.write("Clearing cache...")
         cache.clear()
+        total_count = Course.objects.count()
 
         self.stdout.write("Calculating results for all courses...")
-        for course in Course.objects.all():
+
+        self.stdout.ending = None
+        progress_bar = ProgressBar(self.stdout, total_count)
+
+        for counter, course in enumerate(Course.objects.all()):
+            progress_bar.update(counter + 1)
             calculate_results(course)
 
-        self.stdout.write("Done with updating cache.")
+        self.stdout.write("Done with updating cache.\n")


Test patch:
Repository: e-valuation/EvaP

Issue:
Remove passwords from error messages
EvaP sends error messages to the admins when a server error occurs. Post data sent to the page where the error occurred will be included in the message, but passwords must not be included in these messages.
This currently happens e.g. when a user is logging in who has two accounts like described in #791.

Remove passwords from error messages
EvaP sends error messages to the admins when a server error occurs. Post data sent to the page where the error occurred will be included in the message, but passwords must not be included in these messages.
This currently happens e.g. when a user is logging in who has two accounts like described in #791.



Discussion:
for the record, this already happened once two years ago here https://github.com/fsr-itse/EvaP/issues/237 and apparently was not solved completely.

for the record, this already happened once two years ago here https://github.com/fsr-itse/EvaP/issues/237 and apparently was not solved completely.


Resolving patch:
diff --git a/evap/evaluation/views.py b/evap/evaluation/views.py
--- a/evap/evaluation/views.py
+++ b/evap/evaluation/views.py
@@ -10,6 +10,7 @@
 from django.utils.translation import ugettext as _
 from django.core.urlresolvers import resolve, Resolver404
 from django.views.decorators.http import require_POST
+from django.views.decorators.debug import sensitive_post_parameters
 
 from evap.evaluation.forms import NewKeyForm, LoginUsernameForm
 from evap.evaluation.models import UserProfile, FaqSection, EmailTemplate, Semester
@@ -17,6 +18,7 @@
 logger = logging.getLogger(__name__)
 
 
+@sensitive_post_parameters("password")
 def index(request):
     """Main entry page into EvaP providing all the login options available. The username/password
        login is thought to be used for internal users, e.g. by connecting to a LDAP directory.


Test patch:
Repository: e-valuation/EvaP

Issue:
Adding multiple questions at once does not work
When trying to add multiple questions to a questionnaire by adding forms to the formset and saving it only the last entry is saved in the database.



Discussion:

Resolving patch:
diff --git a/evap/staff/views.py b/evap/staff/views.py
--- a/evap/staff/views.py
+++ b/evap/staff/views.py
@@ -866,10 +866,10 @@ def questionnaire_copy(request, questionnaire_id):
             messages.success(request, _("Successfully created questionnaire."))
             return redirect('staff:questionnaire_index')
         else:
-            return render(request, "staff_questionnaire_form.html", dict(form=form, formset=formset))
+            return render(request, "staff_questionnaire_form.html", dict(form=form, formset=formset, editable=True))
     else:
         form, formset = get_identical_form_and_formset(copied_questionnaire)
-        return render(request, "staff_questionnaire_form.html", dict(form=form, formset=formset))
+        return render(request, "staff_questionnaire_form.html", dict(form=form, formset=formset, editable=True))
 
 
 @staff_required
@@ -913,10 +913,10 @@ def questionnaire_new_version(request, questionnaire_id):
                 else:
                     raise IntegrityError
         except IntegrityError:
-            return render(request, "staff_questionnaire_form.html", dict(form=form, formset=formset))
+            return render(request, "staff_questionnaire_form.html", dict(form=form, formset=formset, editable=True))
     else:
         form, formset = get_identical_form_and_formset(old_questionnaire)
-        return render(request, "staff_questionnaire_form.html", dict(form=form, formset=formset))
+        return render(request, "staff_questionnaire_form.html", dict(form=form, formset=formset, editable=True))
 
 
 @require_POST


Test patch:
Repository: e-valuation/EvaP

Issue:
+x on update.sh, earlier apache restart
update_production.sh is missing the x bit, also because of the cache clearing the apache is restarted 2min after the code has changed.



Discussion:
apache reload shouldnt happen before `compress` is run, otherwise accessing the compressed files will fail, as the code wants to access new files, but they are not there yet.


Resolving patch:
diff --git a/evap/evaluation/management/commands/refresh_results_cache.py b/evap/evaluation/management/commands/refresh_results_cache.py
--- a/evap/evaluation/management/commands/refresh_results_cache.py
+++ b/evap/evaluation/management/commands/refresh_results_cache.py
@@ -24,4 +24,4 @@ def handle(self, *args, **options):
             progress_bar.update(counter + 1)
             calculate_results(course)
 
-        self.stdout.write("Done with updating cache.\n")
+        self.stdout.write("Results cache has been refreshed.\n")


Test patch:
diff --git a/evap/evaluation/tests/test_commands.py b/evap/evaluation/tests/test_commands.py
--- a/evap/evaluation/tests/test_commands.py
+++ b/evap/evaluation/tests/test_commands.py
@@ -3,7 +3,7 @@
 from unittest.mock import patch
 
 from django.conf import settings
-from django.utils.six import StringIO
+from io import StringIO
 from django.core import management, mail
 from django.test import TestCase
 from django.test.utils import override_settings
diff --git a/evap/evaluation/tests/test_misc.py b/evap/evaluation/tests/test_misc.py
--- a/evap/evaluation/tests/test_misc.py
+++ b/evap/evaluation/tests/test_misc.py
@@ -1,4 +1,5 @@
 import os.path
+from io import StringIO
 
 from django.conf import settings
 from django.contrib.auth.models import Group
@@ -59,3 +60,15 @@ def load_test_data(self):
             call_command("loaddata", "test_data", verbosity=0)
         except Exception:
             self.fail("Test data failed to load.")
+
+
+class TestMissingMigrations(TestCase):
+    def test_for_missing_migrations(self):
+        output = StringIO()
+        try:
+            call_command('makemigrations', interactive=False, dry_run=True, exit_code=True, stdout=output)
+        except SystemExit as e:
+            # The exit code will be 1 when there are no missing migrations
+            self.assertEqual(str(e), '1')
+        else:
+            self.fail("There are missing migrations:\n %s" % output.getvalue())
Repository: e-valuation/EvaP

Issue:
Revoke course approval
It must be possible to revoke the approval of a course and to move it back to state `new`.



Discussion:

Resolving patch:
diff --git a/evap/evaluation/models.py b/evap/evaluation/models.py
--- a/evap/evaluation/models.py
+++ b/evap/evaluation/models.py
@@ -343,7 +343,7 @@ def editor_approve(self):
     def staff_approve(self):
         pass
 
-    @transition(field=state, source='prepared', target='new')
+    @transition(field=state, source=['prepared', 'approved'], target='new')
     def revert_to_new(self):
         pass
 


Test patch:
diff --git a/evap/evaluation/tests/test_coverage.py b/evap/evaluation/tests/test_coverage.py
--- a/evap/evaluation/tests/test_coverage.py
+++ b/evap/evaluation/tests/test_coverage.py
@@ -175,9 +175,12 @@ def helper_semester_state_views(self, course_ids, old_state, new_state, operatio
     def test_semester_publish(self):
         self.helper_semester_state_views([7], "reviewed", "published", "publish")
 
-    def test_semester_reset(self):
+    def test_semester_reset_1(self):
         self.helper_semester_state_views([2], "prepared", "new", "revertToNew")
 
+    def test_semester_reset_2(self):
+        self.helper_semester_state_views([4], "approved", "new", "revertToNew")
+
     def test_semester_approve_1(self):
         self.helper_semester_state_views([1], "new", "approved", "approve")
Repository: e-valuation/EvaP

Issue:
Rework all the page titles
e.g. when opening the results of multiple courses in tabs, my browser labels the tabs "Results - EvaP". Putting the course name in there would help. Same for staff course edit.



Discussion:

Resolving patch:
diff --git a/evap/staff/views.py b/evap/staff/views.py
--- a/evap/staff/views.py
+++ b/evap/staff/views.py
@@ -569,7 +569,7 @@ def helper_course_edit(request, semester, course):
         return custom_redirect('staff:semester_view', semester.id)
     else:
         sort_formset(request, formset)
-        template_data = dict(semester=semester, form=form, formset=formset, staff=True, state=course.state, editable=editable)
+        template_data = dict(course=course, semester=semester, form=form, formset=formset, staff=True, state=course.state, editable=editable)
         return render(request, "staff_course_form.html", template_data)
 
 


Test patch:
Repository: e-valuation/EvaP

Issue:
Fix formset for creating questionnaire
When creating a new questionnaire, the formset does not have an `add another` link and the remove checkbox is not replaced by a `remove` link.

Fix formset for creating questionnaire
When creating a new questionnaire, the formset does not have an `add another` link and the remove checkbox is not replaced by a `remove` link.



Discussion:

Resolving patch:
diff --git a/evap/staff/views.py b/evap/staff/views.py
--- a/evap/staff/views.py
+++ b/evap/staff/views.py
@@ -783,7 +783,7 @@ def questionnaire_create(request):
         messages.success(request, _("Successfully created questionnaire."))
         return redirect('staff:questionnaire_index')
     else:
-        return render(request, "staff_questionnaire_form.html", dict(form=form, formset=formset))
+        return render(request, "staff_questionnaire_form.html", dict(form=form, formset=formset, editable=True))
 
 
 def make_questionnaire_edit_forms(request, questionnaire, editable):


Test patch:
Repository: e-valuation/EvaP

Issue:
Votes might get counted twice
`student vote` checks whether a user can vote and raises a `PermissionDenied` if not.
but if the post request with the votes for a course is sent twice immediately after each other this check can in both cases return `True` if the vote was not saved before the second request is handled. then, the votes would be saved twice.



Discussion:

Resolving patch:
diff --git a/evap/evaluation/models.py b/evap/evaluation/models.py
--- a/evap/evaluation/models.py
+++ b/evap/evaluation/models.py
@@ -509,9 +509,6 @@ def is_archived(self):
     def is_archiveable(self):
         return not self.is_archived and self.state in ["new", "published"]
 
-    def was_evaluated(self, request):
-        self.course_evaluated.send(sender=self.__class__, request=request, semester=self.semester)
-
     @property
     def final_grade_documents(self):
         from evap.grades.models import GradeDocument
diff --git a/evap/student/views.py b/evap/student/views.py
--- a/evap/student/views.py
+++ b/evap/student/views.py
@@ -1,7 +1,7 @@
 from collections import OrderedDict
 
 from django.contrib import messages
-from django.core.exceptions import PermissionDenied
+from django.core.exceptions import PermissionDenied, SuspiciousOperation
 from django.db import transaction
 from django.shortcuts import get_object_or_404, redirect, render
 from django.utils.translation import ugettext as _
@@ -83,6 +83,13 @@ def vote(request, course_id):
 
     # all forms are valid, begin vote operation
     with transaction.atomic():
+        # add user to course.voters
+        # not using course.voters.add(request.user) since it fails silently when done twice.
+        # manually inserting like this gives us the 'created' return value and ensures at the database level that nobody votes twice.
+        __, created = course.voters.through.objects.get_or_create(userprofile_id=request.user.pk, course_id=course.pk)
+        if not created:  # vote already got recorded, bail out
+            raise SuspiciousOperation("A second vote has been received shortly after the first one.")
+
         for contribution, form_group in form_groups.items():
             for questionnaire_form in form_group:
                 questionnaire = questionnaire_form.questionnaire
@@ -102,10 +109,7 @@ def vote(request, course_id):
                             answer_counter.add_vote()
                             answer_counter.save()
 
-        # remember that the user voted already
-        course.voters.add(request.user)
-
-        course.was_evaluated(request)
+        course.course_evaluated.send(sender=Course, request=request, semester=course.semester)
 
     messages.success(request, _("Your vote was recorded."))
     return redirect('student:index')


Test patch:
Repository: e-valuation/EvaP

Issue:
User merge: user selection shows only first name and last name
When selecting users for merging, their first and last name is shown. this breaks if both have the same first and last name, in which case the staff user cannot know which one should be the main or other user. a possible solution would be to additionally show the username.

User merge: user selection shows only first name and last name
When selecting users for merging, their first and last name is shown. this breaks if both have the same first and last name, in which case the staff user cannot know which one should be the main or other user. a possible solution would be to additionally show the username.



Discussion:

Resolving patch:
diff --git a/evap/evaluation/models.py b/evap/evaluation/models.py
--- a/evap/evaluation/models.py
+++ b/evap/evaluation/models.py
@@ -900,6 +900,13 @@ def full_name(self):
         else:
             return self.username
 
+    @property
+    def full_name_with_username(self):
+        name = self.full_name
+        if self.username not in name:
+            name += " (" + self.username + ")"
+        return name
+
     def __str__(self):
         return self.full_name
 
diff --git a/evap/staff/forms.py b/evap/staff/forms.py
--- a/evap/staff/forms.py
+++ b/evap/staff/forms.py
@@ -501,9 +501,14 @@ def save(self, *args, **kw):
             self.instance.groups.remove(grade_user_group)
 
 
+class UserModelChoiceField(forms.ModelChoiceField):
+    def label_from_instance(self, obj):
+        return obj.full_name_with_username
+
+
 class UserMergeSelectionForm(forms.Form):
-    main_user = forms.ModelChoiceField(UserProfile.objects.all())
-    other_user = forms.ModelChoiceField(UserProfile.objects.all())
+    main_user = UserModelChoiceField(UserProfile.objects.all())
+    other_user = UserModelChoiceField(UserProfile.objects.all())
 
 
 class LotteryForm(forms.Form):


Test patch:
Repository: e-valuation/EvaP

Issue:
User create view crashes
likely dd42a3cf895fefb8d4b2abc1c46ea8eb825446ae broke it. `context_processors.auth` sets the template variable `user` to the currently logged in user, so the variable rename in that commit was faulty.

does not happen in production because `DEBUG=False` eats template errors.

Wrong labels on user creation page
When creating a new user, the four labels `Staff`, `Grade publisher`, `Contributor`, `Responsible` are shown above the form although none of these should be displayed.



Discussion:

Resolving patch:
diff --git a/evap/contributor/views.py b/evap/contributor/views.py
--- a/evap/contributor/views.py
+++ b/evap/contributor/views.py
@@ -44,7 +44,7 @@ def settings_edit(request):
         messages.success(request, _("Successfully updated your settings."))
         return redirect('contributor:index')
     else:
-        return render(request, "contributor_settings.html", dict(form=form, user=user))
+        return render(request, "contributor_settings.html", dict(form=form))
 
 
 @editor_or_delegate_required
diff --git a/evap/staff/views.py b/evap/staff/views.py
--- a/evap/staff/views.py
+++ b/evap/staff/views.py
@@ -1052,7 +1052,7 @@ def user_edit(request, user_id):
         messages.success(request, _("Successfully updated user."))
         return redirect('staff:user_index')
     else:
-        return render(request, "staff_user_form.html", dict(form=form, user=user, courses_contributing_to=courses_contributing_to))
+        return render(request, "staff_user_form.html", dict(form=form, courses_contributing_to=courses_contributing_to))
 
 
 @require_POST


Test patch: