Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds shortcut button to add exam evaluation #2050

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix
  • Loading branch information
Frederic Sadrieh committed Jun 3, 2024
commit 74bda64ba09aacd8786264c37c79f1bbd755c3e2
23 changes: 21 additions & 2 deletions evap/evaluation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from django.core.exceptions import ValidationError
from django.core.mail import EmailMultiAlternatives
from django.db import IntegrityError, models, transaction
from django.db.models import CheckConstraint, Count, F, Manager, OuterRef, Q, Subquery, Value
from django.db.models import CheckConstraint, Count, F, Manager, OuterRef, Q, Subquery, Value, query
from django.db.models.functions import Coalesce, Lower, NullIf, TruncDate
from django.dispatch import Signal, receiver
from django.template import Context, Template
Expand Down Expand Up @@ -447,7 +447,26 @@ class State:
def has_exam(self):
return self.course.evaluations.filter(name_de="Klausur", name_en="Exam").exists()

def make_exam_evaluation(self, exam_date: date, participants, eval_contributions):
def make_exam_evaluation(
FSadrieh marked this conversation as resolved.
Show resolved Hide resolved
self,
exam_date: datetime,
evaluation_end_date: datetime,
FSadrieh marked this conversation as resolved.
Show resolved Hide resolved
participants: query.QuerySet["UserProfile"],
FSadrieh marked this conversation as resolved.
Show resolved Hide resolved
eval_contributions: query.QuerySet["Contribution"],
FSadrieh marked this conversation as resolved.
Show resolved Hide resolved
):
self.weight = 9
self.vote_end_date = evaluation_end_date
self.save()
exam_evaluation = Evaluation(course=self.course, name_de="Klausur", name_en="Exam", weight=1, is_rewarded=False)
exam_evaluation.set_exam_evaluation_attributes(exam_date, participants, eval_contributions)
exam_evaluation.save()

def set_exam_evaluation_attributes(
FSadrieh marked this conversation as resolved.
Show resolved Hide resolved
self,
exam_date: date,
participants: query.QuerySet["UserProfile"],
eval_contributions: query.QuerySet["Contribution"],
):
self.vote_start_datetime = datetime.combine(exam_date + timedelta(days=1), time(8, 0))
self.vote_end_date = exam_date + timedelta(days=3)
self.save()
Expand Down
45 changes: 30 additions & 15 deletions evap/staff/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1926,50 +1926,54 @@ def test_evaluation_copy(self):

class TestEvaluationExamCreation(WebTestStaffMode):
csrf_checks = False
url = reverse("staff:create_exam_evaluation")

@classmethod
def setUpTestData(cls):
cls.manager = make_manager()
cls.semester = baker.make(Semester)
cls.course = baker.make(Course, semester=cls.semester)
cls.course = baker.make(Course)
vote_start_datetime = datetime.datetime.now() - datetime.timedelta(days=50)
cls.evaluation = baker.make(Evaluation, course=cls.course, vote_start_datetime=vote_start_datetime)
cls.contributions = baker.make(
Contribution, evaluation=cls.evaluation, _fill_optional=["contributor"], _quantity=3, _bulk_create=True
)
cls.questionnaire = baker.make(Questionnaire, pk=83)
cls.url = reverse("staff:create_exam_evaluation")
cls.exam_date = (datetime.date.today()) + datetime.timedelta(days=10)
cls.exam_datetime = (datetime.date.today()) + datetime.timedelta(days=10)
cls.semester_overview_url = reverse("staff:semester_view", args=[cls.course.semester.pk])
# Default the evaluation does not have any participants, so we need to add some
cls.evaluation.participants.set(baker.make(UserProfile, _quantity=3))

def test_create_exam_evaluation(self):
self.app.post(
self.url,
user=self.manager,
status=200,
params={"evaluation_id": self.evaluation.pk, "exam_date": self.exam_date},
params={"evaluation_id": self.evaluation.pk, "exam_date": self.exam_datetime},
)
self.assertEqual(Evaluation.objects.count(), 2)
exam_evaluation = Evaluation.objects.exclude(pk=self.evaluation.pk).get()
self.assertEqual(exam_evaluation.contributions.count(), self.evaluation.contributions.count())
self.assertEqual(
exam_evaluation.vote_start_datetime,
datetime.datetime.combine(self.exam_date + datetime.timedelta(days=1), datetime.time(8, 0)),
datetime.datetime.combine(self.exam_datetime + datetime.timedelta(days=1), datetime.time(8, 0)),
)
self.assertEqual(exam_evaluation.vote_end_date, self.exam_date + datetime.timedelta(days=3))
self.assertEqual(exam_evaluation.vote_end_date, self.exam_datetime + datetime.timedelta(days=3))
self.assertEqual(exam_evaluation.name_de, "Klausur")
self.assertEqual(exam_evaluation.name_en, "Exam")
self.assertEqual(exam_evaluation.course, self.evaluation.course)
self.assertEqual(exam_evaluation.participants.count(), self.evaluation.participants.count())
self.assertQuerySetEqual(exam_evaluation.participants.all(), self.evaluation.participants.all())
self.assertEqual(exam_evaluation.weight, 1)
FSadrieh marked this conversation as resolved.
Show resolved Hide resolved

evaluation = Evaluation.objects.get(pk=self.evaluation.pk)
self.assertEqual(evaluation.weight, 9)

def test_exam_evaluation_for_single_result(self):
self.evaluation.is_single_result = True
self.evaluation.save()
self.app.post(
self.url,
user=self.manager,
status=400,
params={"evaluation_id": self.evaluation.pk, "exam_date": self.exam_date},
params={"evaluation_id": self.evaluation.pk, "exam_date": self.exam_datetime},
)
self.assertFalse(self.evaluation.has_exam)

Expand All @@ -1980,21 +1984,32 @@ def test_exam_evaluation_for_already_existing_exam_evaluation(self):
self.url,
user=self.manager,
status=400,
params={"evaluation_id": self.evaluation.pk, "exam_date": self.exam_date},
params={"evaluation_id": self.evaluation.pk, "exam_date": self.exam_datetime},
)

def test_exam_evaluation_with_wrong_date(self):
self.evaluation.vote_start_datetime = datetime.datetime.now() + datetime.timedelta(days=100)
self.evaluation.vote_end_date = datetime.date.today() + datetime.timedelta(days=150)
self.evaluation.save()
self.app.get("", user=self.manager) # Needed to not get a last login database update

self.app.post(
self.url,
with assert_no_database_modifications():
self.app.post(
self.url,
user=self.manager,
status=200,
params={"evaluation_id": self.evaluation.pk, "exam_date": self.exam_datetime},
)

page = self.app.get(
self.semester_overview_url,
user=self.manager,
status=200,
params={"evaluation_id": self.evaluation.pk, "exam_date": self.exam_date},
params={"semester_id": self.evaluation.course.semester.pk},
)
self.assertContains(
page, "The exam date is before the start date of the main evaluation. No exam evaluation was created."
)
self.assertFalse(self.evaluation.has_exam)


class TestCourseCopyView(WebTestStaffMode):
Expand Down
31 changes: 15 additions & 16 deletions evap/staff/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
from django.forms import BaseForm, formset_factory
from django.forms.models import inlineformset_factory, modelformset_factory
from django.http import Http404, HttpRequest, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render
from django.shortcuts import _get_queryset, get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy
from django.utils.datastructures import MultiValueDictKeyError
from django.utils.html import format_html
from django.utils.translation import get_language
from django.utils.translation import gettext as _
Expand Down Expand Up @@ -1055,34 +1056,32 @@ def course_copy(request, course_id):
@manager_required
@transaction.atomic
FSadrieh marked this conversation as resolved.
Show resolved Hide resolved
def create_exam_evaluation(request):
evaluation = get_object_from_dict_pk_entry_or_logged_40x(Evaluation, request.POST, "evaluation_id")
exam_date = request.POST.get("exam_date")
exam_date = datetime.combine(datetime.strptime(exam_date, "%Y-%m-%d"), datetime.min.time())

query_set = _get_queryset(Evaluation)
try:
evaluation = query_set.get(pk=request.POST["evaluation_id"])
except MultiValueDictKeyError:
return HttpResponseBadRequest()
if evaluation.is_single_result:
raise SuspiciousOperation("Creating an exam evaluation for a single result evaluation is not allowed")

if evaluation.has_exam:
raise SuspiciousOperation("An exam evaluation already exists for this course")
exam_datetime = request.POST.get("exam_date")

evaluation_end_date = exam_date - timedelta(days=1)
exam_datetime = datetime.combine(datetime.strptime(exam_datetime, "%Y-%m-%d"), datetime.min.time())

evaluation_end_date = exam_datetime - timedelta(days=1)
if evaluation.vote_start_datetime > evaluation_end_date:
messages.error(
request, _("The exam date is before the start date of the main evaluation. No exam evaluation created.")
request, _("The exam date is before the start date of the main evaluation. No exam evaluation was created.")
)
return HttpResponse()

evaluation.weight = 9
evaluation.vote_end_date = evaluation_end_date
evaluation.save()

exam_evaluation = Evaluation(
course=evaluation.course, name_de="Klausur", name_en="Exam", weight=1, is_rewarded=False
)
exam_evaluation.make_exam_evaluation(
exam_date=exam_date,
evaluation.make_exam_evaluation(
exam_date=exam_datetime,
participants=evaluation.participants.all(),
eval_contributions=evaluation.contributions.exclude(contributor=None),
evaluation_end_date=evaluation_end_date,
)
messages.success(request, _("Successfully created exam evaluation."))
return HttpResponse() # 200 OK
Expand Down
Loading