import re from datetime import datetime from typing import Dict from django.test import TestCase from django.utils import timezone from freezegun import freeze_time from vbv_lernwelt.assignment.models import AssignmentType from vbv_lernwelt.core.constants import TEST_COURSE_SESSION_BERN_ID from vbv_lernwelt.core.create_default_users import create_default_users from vbv_lernwelt.course.creators.test_course import create_test_course from vbv_lernwelt.course.models import CourseSession from vbv_lernwelt.course_session.models import ( CourseSessionAssignment, CourseSessionEdoniqTest, ) from vbv_lernwelt.learnpath.models import ( LearningContentAssignment, LearningContentEdoniqTest, ) from vbv_lernwelt.notify.email.email_services import EmailTemplate from vbv_lernwelt.notify.email.reminders.assigment import ( send_assignment_reminder_notifications, ) from vbv_lernwelt.notify.models import Notification EXPECTED_MEMBER_VERB = "Erinnerung: Bald ist ein Abgabetermin" EXPECTED_EXPERT_VERB = "Erinnerung: Bald ist ein Bewertungstermin" RECIPIENT_TRAINER = "test-trainer1@example.com" RECIPIENT_STUDENTS = [ "test-student1@example.com", "test-student2@example.com", "test-student3@example.com", ] ASSIGNMENT_TYPE_LEARNING_CONTENT_LOOKUP: Dict[AssignmentType, str] = { AssignmentType.CONDITION_ACCEPTANCE: "test-lehrgang-lp-circle-fahrzeug-lc-redlichkeitserklärung", AssignmentType.PREP_ASSIGNMENT: "test-lehrgang-lp-circle-fahrzeug-lc-fahrzeug-mein-erstes-auto", AssignmentType.REFLECTION: "test-lehrgang-lp-circle-fahrzeug-lc-reflexion", AssignmentType.CASEWORK: "test-lehrgang-lp-circle-fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice", } def create_assignment( assignment_type: AssignmentType, submission_deadline=None, evaluation_deadline=None, ): assignment = CourseSessionAssignment.objects.create( course_session=CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID), learning_content=LearningContentAssignment.objects.get( slug=ASSIGNMENT_TYPE_LEARNING_CONTENT_LOOKUP[assignment_type] ), ) assert ( AssignmentType(assignment.learning_content.assignment_type) == assignment_type ) if submission_deadline: assignment.submission_deadline.start = submission_deadline assignment.submission_deadline.end = None assignment.submission_deadline.save() if evaluation_deadline: assignment.evaluation_deadline.start = evaluation_deadline assignment.evaluation_deadline.end = None assignment.evaluation_deadline.save() return assignment def create_edoniq_test_assignment(deadline_start): edoniq_test = CourseSessionEdoniqTest.objects.create( course_session=CourseSession.objects.get( id=TEST_COURSE_SESSION_BERN_ID, ), learning_content=LearningContentEdoniqTest.objects.get( slug="test-lehrgang-lp-circle-fahrzeug-lc-wissens-und-verständnisfragen" ), ) edoniq_test.deadline.start = deadline_start edoniq_test.deadline.end = None edoniq_test.deadline.save() return edoniq_test class TestAssignmentCourseRemindersTest(TestCase): def setUp(self): create_default_users() create_test_course(with_sessions=True) CourseSessionAssignment.objects.all().delete() CourseSessionEdoniqTest.objects.all().delete() Notification.objects.all().delete() def _assert_member_assignment_notifications( self, action_object, expected_recipients ): for expected_recipient in expected_recipients: notification = Notification.objects.get( recipient__username=expected_recipient ) self.assertEqual(action_object, notification.action_object) self.assertEqual("ASSIGNMENT_REMINDER", notification.notification_trigger) self.assertEqual("INFORMATION", notification.notification_category) self.assertEqual(EXPECTED_MEMBER_VERB, notification.verb) template_data = notification.data["template_data"] self.assertEqual( action_object.learning_content.get_parent_circle().title, template_data["circle"], ) self.assertEqual( action_object.learning_content.get_frontend_url(), notification.target_url, ) match = re.fullmatch( r"\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}", template_data["due_date"] ) self.assertIsNotNone( match, f"due_date format is incorrect: {template_data['due_date']}" ) email_template = notification.data["email_template"] # make sure we have the correct email template if type(action_object) == CourseSessionAssignment: assignment_type = AssignmentType( action_object.learning_content.assignment_type ) if assignment_type == AssignmentType.CASEWORK: self.assertEqual( EmailTemplate.ASSIGNMENT_REMINDER_CASEWORK_MEMBER.name, email_template, ) elif assignment_type == AssignmentType.PREP_ASSIGNMENT: self.assertEqual( EmailTemplate.ASSIGNMENT_REMINDER_PREP_ASSIGNMENT_MEMBER.name, email_template, ) elif type(action_object) == CourseSessionEdoniqTest: self.assertEqual( EmailTemplate.ASSIGNMENT_REMINDER_EDONIQ_MEMBER.name, email_template, ) @freeze_time("2023-01-01") def test_notification_edoniq(self): # GIVEN should_be_sent = create_edoniq_test_assignment( deadline_start=timezone.make_aware(datetime(2023, 1, 2)) ) # ...too early create_edoniq_test_assignment( deadline_start=timezone.make_aware(datetime(2023, 1, 4)) ) # ...too late create_edoniq_test_assignment( deadline_start=timezone.make_aware(datetime(2022, 1, 1)) ) # WHEN send_assignment_reminder_notifications() # THEN self.assertEqual(3, len(Notification.objects.all())) self._assert_member_assignment_notifications( action_object=should_be_sent, expected_recipients=RECIPIENT_STUDENTS, ) with self.assertRaises(Notification.DoesNotExist): Notification.objects.get(recipient__username=RECIPIENT_TRAINER) @freeze_time("2023-01-01") def test_notification_casework_for_members(self): # GIVEN casework = create_assignment( assignment_type=AssignmentType.CASEWORK, # has a submission deadline within range -> member notification submission_deadline=timezone.make_aware(datetime(2023, 1, 2)), # but no evaluation deadline within range -> no expert notification evaluation_deadline=timezone.make_aware(datetime(2023, 2, 2)), ) # ...too early create_assignment( assignment_type=AssignmentType.CASEWORK, submission_deadline=timezone.make_aware(datetime(2023, 1, 4)), evaluation_deadline=timezone.make_aware(datetime(2023, 2, 2)), ) # ...too late create_assignment( assignment_type=AssignmentType.CASEWORK, submission_deadline=timezone.make_aware(datetime(2022, 1, 1)), evaluation_deadline=timezone.make_aware(datetime(2022, 2, 2)), ) # WHEN send_assignment_reminder_notifications() # THEN self.assertEqual(3, len(Notification.objects.all())) self._assert_member_assignment_notifications( action_object=casework, expected_recipients=RECIPIENT_STUDENTS, ) with self.assertRaises(Notification.DoesNotExist): Notification.objects.get(recipient__username=RECIPIENT_TRAINER) @freeze_time("2023-01-01") def test_notification_casework_for_experts(self): # GIVEN casework = create_assignment( assignment_type=AssignmentType.CASEWORK, submission_deadline=timezone.make_aware(datetime(2022, 12, 12)), evaluation_deadline=timezone.make_aware(datetime(2023, 1, 2)), ) # WHEN send_assignment_reminder_notifications() # THEN self.assertEqual(1, len(Notification.objects.all())) notification = Notification.objects.get(recipient__username=RECIPIENT_TRAINER) self.assertEqual(casework, notification.action_object) self.assertEqual("INFORMATION", notification.notification_category) self.assertEqual(EXPECTED_EXPERT_VERB, notification.verb) self.assertEqual( casework.evaluation_deadline.url_expert, notification.target_url ) self.assertEqual( "CASEWORK_EXPERT_EVALUATION_REMINDER", notification.notification_trigger ) @freeze_time("2023-01-01") def test_notification_prep_assignment(self): # GIVEN prep_assignment = create_assignment( assignment_type=AssignmentType.PREP_ASSIGNMENT, submission_deadline=timezone.make_aware(datetime(2023, 1, 2)), evaluation_deadline=None, ) # ...too early create_assignment( assignment_type=AssignmentType.PREP_ASSIGNMENT, submission_deadline=timezone.make_aware(datetime(2023, 1, 4)), evaluation_deadline=None, ) # ...too late create_assignment( assignment_type=AssignmentType.PREP_ASSIGNMENT, submission_deadline=timezone.make_aware(datetime(2022, 1, 1)), evaluation_deadline=None, ) # WHEN send_assignment_reminder_notifications() # THEN self.assertEqual(3, len(Notification.objects.all())) self._assert_member_assignment_notifications( action_object=prep_assignment, expected_recipients=RECIPIENT_STUDENTS, ) with self.assertRaises(Notification.DoesNotExist): Notification.objects.get(recipient__username=RECIPIENT_TRAINER)