Repository: e-valuation/EvaP

Issue:
Filter evaluations on fast review
When finishing the review of an evaluation's text answers in fast mode, only evaluations where the evaluation period already ended should be selected for continuing the review. Currently, also evaluations that are still in evaluation are shown.


Discussion:
why? :)
Running evaluations are usually not to be reviewed to ensure the review of all text answers in an evaluation is done as a whole. This gives the evaluation team a complete overview of the course, which can be summarized for reports and feedback discussions.
If staff wants to review a specific evaluation after the change anyway, they can still open the review for this evaluation.

Resolving patch:
diff --git a/evap/staff/tools.py b/evap/staff/tools.py
--- a/evap/staff/tools.py
+++ b/evap/staff/tools.py
@@ -1,4 +1,5 @@
 import os
+from datetime import date, datetime, timedelta
 from enum import Enum
 
 from django.contrib import messages
@@ -294,8 +295,14 @@ def merge_users(main_user, other_user, preview=False):
 
 
 def find_unreviewed_evaluations(semester, excluded):
+    # as evaluations are open for an offset of hours after vote_end_datetime, the evaluations ending yesterday are also excluded during offset
+    exclude_date = date.today()
+    if datetime.now().hour < settings.EVALUATION_END_OFFSET_HOURS:
+        exclude_date -= timedelta(days=1)
+
     return semester.evaluations.exclude(pk__in=excluded) \
         .exclude(state='published') \
+        .exclude(vote_end_date__gte=exclude_date) \
         .exclude(can_publish_text_results=False) \
         .filter(contributions__textanswer_set__state=TextAnswer.State.NOT_REVIEWED) \
         .annotate(num_unreviewed_textanswers=Count("contributions__textanswer_set")) \


Test patch:
diff --git a/evap/staff/tests/test_views.py b/evap/staff/tests/test_views.py
--- a/evap/staff/tests/test_views.py
+++ b/evap/staff/tests/test_views.py
@@ -1909,7 +1909,7 @@ def setUpTestData(cls):
         cls.evaluation = baker.make(
             Evaluation,
             pk=1,
-            course=baker.make(Course, semester=semester),
+            course__semester=semester,
             participants=[student1, cls.student2],
             voters=[student1],
             state="in_evaluation"
@@ -1929,6 +1929,29 @@ def setUpTestData(cls):
         cls.answer = 'should show up'
         baker.make(TextAnswer, contribution=contribution, question=question, answer=cls.answer)
 
+        cls.evaluation2 = baker.make(
+            Evaluation,
+            course__semester=semester,
+            participants=[student1],
+            voters=[student1, cls.student2],
+            vote_start_datetime=datetime.datetime.now() - datetime.timedelta(days=5),
+            vote_end_date=datetime.date.today() - datetime.timedelta(days=4),
+            can_publish_text_results=True
+        )
+
+        contribution2 = baker.make(
+            Contribution,
+            evaluation=cls.evaluation2,
+            contributor=baker.make(UserProfile),
+            questionnaires=[questionnaire],
+        )
+        cls.text_answer = baker.make(
+            TextAnswer,
+            contribution=contribution2,
+            question=question,
+            answer='test answer text',
+        )
+
     def test_textanswers_showing_up(self):
         # in an evaluation with only one voter the view should not be available
         with run_in_staff_mode(self):
@@ -1953,6 +1976,22 @@ def test_textanswers_full_view(self):
             page = self.app.get(self.url + '?view=full', user=self.manager, status=200)
             self.assertContains(page, self.answer)
 
+    # use offset of more than 25 hours to make sure the test doesn't fail even on combined time zone change and leap second
+    @override_settings(EVALUATION_END_OFFSET_HOURS=26)
+    def test_exclude_unfinished_evaluations(self):
+        let_user_vote_for_evaluation(self.app, self.student2, self.evaluation)
+        with run_in_staff_mode(self):
+            page = self.app.get(self.url, user=self.manager, status=200)
+            # evaluation2 is finished and should show up
+            self.assertContains(page, self.evaluation2.full_name)
+
+        self.evaluation2.vote_end_date = datetime.date.today() - datetime.timedelta(days=1)
+        self.evaluation2.save()
+        with run_in_staff_mode(self):
+            page = self.app.get(self.url, user=self.manager, status=200)
+            # unfinished because still in EVALUATION_END_OFFSET_HOURS
+            self.assertNotContains(page, self.evaluation2.full_name)
+
 
 class TestEvaluationTextAnswerEditView(WebTest):
     @classmethod