Merged in feature/VBV-123 (pull request #216)
Reminder for Assignments Approved-by: Christian Cueni
This commit is contained in:
commit
ab7e879973
|
|
@ -8,4 +8,4 @@
|
|||
0 */1 * * * /usr/local/bin/python /app/manage.py edoniq_import_results
|
||||
|
||||
# every day at 19:30
|
||||
30 19 * * * /usr/local/bin/python /app/manage.py send_attendance_course_reminders
|
||||
30 19 * * * /usr/local/bin/python /app/manage.py send_email_reminders --type=all
|
||||
|
|
|
|||
|
|
@ -29,6 +29,30 @@ class EmailTemplate(Enum):
|
|||
"fr": "d-f88d9912e5484e55a879571463e4a166",
|
||||
}
|
||||
|
||||
ASSIGNMENT_REMINDER_CASEWORK_MEMBER = {
|
||||
"de": "d-8b84fd96213540a796c40d4344bc606f",
|
||||
"fr": "d-ae6cd87b6b574643b4fff209148c7620",
|
||||
"it": "d-1bf8b12a70324e1b91050d5fa01ed81f",
|
||||
}
|
||||
|
||||
ASSIGNMENT_REMINDER_PREP_ASSIGNMENT_MEMBER = {
|
||||
"de": "d-cb866d8c538f4ffaab923022ef7209fa",
|
||||
"fr": "d-fdc84ae0e1b7417a8ede8db4e07ee7a8",
|
||||
"it": "d-39d16586341b4559b3a3df71db3d04fb",
|
||||
}
|
||||
|
||||
ASSIGNMENT_REMINDER_EDONIQ_MEMBER = {
|
||||
"de": "d-4b26911d04834079a64ab1758ca470cc",
|
||||
"fr": "d-b9f27e3e13e44f20aa5d1a40c93da00d",
|
||||
"it": "d-1d3d854c5b3e4012ac3d33eeb3d6e7d1",
|
||||
}
|
||||
|
||||
EVALUATION_REMINDER_CASEWORK_EXPERT = {
|
||||
"de": "d-6e3dd4acc7fc4ce7a2776f5147bd32fd",
|
||||
"fr": "d-0104add90a354d7fb1fc9fecfa132d06",
|
||||
"it": "d-630e9316960647768c0a657e175436aa",
|
||||
}
|
||||
|
||||
# VBV - Geleitete Fallarbeit abgegeben
|
||||
CASEWORK_SUBMITTED = {"de": "d-599f0b35ddcd4fac99314cdf8f5446a2"}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
from datetime import timedelta
|
||||
|
||||
import structlog
|
||||
from django.utils import timezone
|
||||
|
||||
from vbv_lernwelt.assignment.models import AssignmentType
|
||||
from vbv_lernwelt.course.models import CourseSessionUser
|
||||
from vbv_lernwelt.course_session.models import (
|
||||
CourseSessionAssignment,
|
||||
CourseSessionEdoniqTest,
|
||||
)
|
||||
from vbv_lernwelt.notify.services import NotificationService
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
ASSIGNMENT_REMINDER_LEAD_TIME = timedelta(days=2)
|
||||
|
||||
|
||||
def send_assignment_reminder_notifications():
|
||||
start = timezone.now()
|
||||
end = timezone.now() + ASSIGNMENT_REMINDER_LEAD_TIME
|
||||
sent = []
|
||||
|
||||
# member notifications (CASEWORK and PREP_ASSIGNMENT)
|
||||
for assignment in CourseSessionAssignment.objects.filter(
|
||||
submission_deadline__start__lte=end,
|
||||
submission_deadline__start__gte=start,
|
||||
learning_content__assignment_type__in=[
|
||||
AssignmentType.CASEWORK.value,
|
||||
AssignmentType.PREP_ASSIGNMENT.value,
|
||||
],
|
||||
):
|
||||
for member in CourseSessionUser.objects.filter(
|
||||
course_session_id=assignment.course_session.id,
|
||||
role=CourseSessionUser.Role.MEMBER,
|
||||
):
|
||||
sent.append(
|
||||
NotificationService.send_assignment_reminder_notification_member(
|
||||
recipient=member.user, assignment=assignment
|
||||
)
|
||||
)
|
||||
|
||||
# member notifications (EDONIQ_TEST)
|
||||
for edoniq_test in CourseSessionEdoniqTest.objects.filter(
|
||||
deadline__start__lte=end,
|
||||
deadline__start__gte=start,
|
||||
):
|
||||
for member in CourseSessionUser.objects.filter(
|
||||
course_session_id=edoniq_test.course_session.id,
|
||||
role=CourseSessionUser.Role.MEMBER,
|
||||
):
|
||||
sent.append(
|
||||
NotificationService.send_edoniq_test_reminder_notification_member(
|
||||
recipient=member.user, edoniq_test=edoniq_test
|
||||
)
|
||||
)
|
||||
|
||||
# expert notifications (CASEWORK)
|
||||
for assignment in CourseSessionAssignment.objects.filter(
|
||||
evaluation_deadline__start__lte=end,
|
||||
evaluation_deadline__start__gte=start,
|
||||
learning_content__assignment_type__in=[
|
||||
AssignmentType.CASEWORK.value,
|
||||
],
|
||||
):
|
||||
for expert in CourseSessionUser.objects.filter(
|
||||
course_session_id=assignment.course_session.id,
|
||||
role=CourseSessionUser.Role.EXPERT,
|
||||
):
|
||||
sent.append(
|
||||
NotificationService.send_casework_expert_evaluation_reminder(
|
||||
recipient=expert.user, assignment=assignment
|
||||
)
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
"Sent assigment reminders",
|
||||
start_time=start.isoformat(),
|
||||
end_time=end.isoformat(),
|
||||
label="assigment_reminders",
|
||||
sent=sent,
|
||||
)
|
||||
|
||||
return {"sent": sent}
|
||||
|
|
@ -4,7 +4,6 @@ from datetime import timedelta
|
|||
import structlog
|
||||
from django.utils import timezone
|
||||
|
||||
from vbv_lernwelt.core.base import LoggedCommand
|
||||
from vbv_lernwelt.course.models import CourseSessionUser
|
||||
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
|
||||
from vbv_lernwelt.notify.services import NotificationService
|
||||
|
|
@ -14,7 +13,7 @@ logger = structlog.get_logger(__name__)
|
|||
PRESENCE_COURSE_REMINDER_LEAD_TIME = timedelta(weeks=2)
|
||||
|
||||
|
||||
def attendance_course_reminder_notification_job():
|
||||
def send_attendance_reminder_notifications():
|
||||
"""Checks if an attendance course is coming up and sends a reminder to the participants"""
|
||||
start = timezone.now()
|
||||
end = timezone.now() + PRESENCE_COURSE_REMINDER_LEAD_TIME
|
||||
|
|
@ -26,8 +25,8 @@ def attendance_course_reminder_notification_job():
|
|||
|
||||
logger.info(
|
||||
"Querying for attendance courses in specified time range",
|
||||
start_time=start,
|
||||
end_time=end,
|
||||
start_time=start.isoformat(),
|
||||
end_time=end.isoformat(),
|
||||
label="attendance_course_reminder_notification_job",
|
||||
num_attendance_courses=len(attendance_courses),
|
||||
)
|
||||
|
|
@ -36,8 +35,8 @@ def attendance_course_reminder_notification_job():
|
|||
csu = CourseSessionUser.objects.filter(course_session_id=cs_id)
|
||||
logger.info(
|
||||
"Sending attendance course reminder notification",
|
||||
start_time=start,
|
||||
end_time=end,
|
||||
start_time=start.isoformat(),
|
||||
end_time=end.isoformat(),
|
||||
label="attendance_course_reminder_notification_job",
|
||||
num_users=len(csu),
|
||||
course_session_id=cs_id,
|
||||
|
|
@ -53,17 +52,3 @@ def attendance_course_reminder_notification_job():
|
|||
label="attendance_course_reminder_notification_job",
|
||||
)
|
||||
return dict(results_counter)
|
||||
|
||||
|
||||
class Command(LoggedCommand):
|
||||
help = "Sends attendance course reminder notifications to participants"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
results = attendance_course_reminder_notification_job()
|
||||
self.job_log.json_data = results
|
||||
self.job_log.save()
|
||||
logger.info(
|
||||
"Attendance course reminder notification job finished",
|
||||
label="attendance_course_reminder_notification_job",
|
||||
results=results,
|
||||
)
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
from enum import Enum
|
||||
|
||||
import structlog
|
||||
|
||||
from vbv_lernwelt.core.base import LoggedCommand
|
||||
from vbv_lernwelt.notify.email.reminders.assigment import (
|
||||
send_assignment_reminder_notifications,
|
||||
)
|
||||
from vbv_lernwelt.notify.email.reminders.attendance import (
|
||||
send_attendance_reminder_notifications,
|
||||
)
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
class ReminderType(Enum):
|
||||
ASSIGNMENT = "assignment"
|
||||
ATTENDANCE = "attendance"
|
||||
|
||||
|
||||
ACTIONS = {
|
||||
ReminderType.ASSIGNMENT: send_assignment_reminder_notifications,
|
||||
ReminderType.ATTENDANCE: send_attendance_reminder_notifications,
|
||||
}
|
||||
|
||||
|
||||
class Command(LoggedCommand):
|
||||
help = "Sends Email Reminder Notifications"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"--type",
|
||||
choices=[t.value for t in ReminderType] + ["all"],
|
||||
required=True,
|
||||
help="Type of reminder",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
reminder_type = options["type"]
|
||||
|
||||
if reminder_type == "all":
|
||||
types = [ReminderType.ASSIGNMENT, ReminderType.ATTENDANCE]
|
||||
else:
|
||||
types = [ReminderType(reminder_type)]
|
||||
|
||||
results = {t.value: None for t in types}
|
||||
|
||||
for reminder in types:
|
||||
logger.info(f"Starting {reminder.name} reminder notification job")
|
||||
results[reminder.value] = ACTIONS[reminder]()
|
||||
logger.info(f"{reminder.name} reminder notification job finished")
|
||||
|
||||
self.job_log.json_data = results
|
||||
self.job_log.save()
|
||||
|
||||
logger.info("Reminder notification job finished")
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 3.2.20 on 2023-10-06 13:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("notify", "0004_alter_notification_notification_trigger"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="notification",
|
||||
name="notification_trigger",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("ATTENDANCE_COURSE_REMINDER", "Attendance Course Reminder"),
|
||||
("ASSIGNMENT_REMINDER", "Assignment Reminder"),
|
||||
(
|
||||
"CASEWORK_EXPERT_EVALUATION_REMINDER",
|
||||
"Casework Expert Evaluation Reminder",
|
||||
),
|
||||
("CASEWORK_SUBMITTED", "Casework Submitted"),
|
||||
("CASEWORK_EVALUATED", "Casework Evaluated"),
|
||||
("NEW_FEEDBACK", "New Feedback"),
|
||||
],
|
||||
default="",
|
||||
max_length=255,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
@ -15,6 +15,11 @@ class NotificationTrigger(models.TextChoices):
|
|||
ATTENDANCE_COURSE_REMINDER = "ATTENDANCE_COURSE_REMINDER", _(
|
||||
"Attendance Course Reminder"
|
||||
)
|
||||
ASSIGNMENT_REMINDER = "ASSIGNMENT_REMINDER", _("Assignment Reminder")
|
||||
CASEWORK_EXPERT_EVALUATION_REMINDER = (
|
||||
"CASEWORK_EXPERT_EVALUATION_REMINDER",
|
||||
_("Casework Expert Evaluation Reminder"),
|
||||
)
|
||||
CASEWORK_SUBMITTED = "CASEWORK_SUBMITTED", _("Casework Submitted")
|
||||
CASEWORK_EVALUATED = "CASEWORK_EVALUATED", _("Casework Evaluated")
|
||||
NEW_FEEDBACK = "NEW_FEEDBACK", _("New Feedback")
|
||||
|
|
|
|||
|
|
@ -6,11 +6,14 @@ import structlog
|
|||
from django.db.models import Model
|
||||
from notifications.signals import notify
|
||||
|
||||
from vbv_lernwelt.assignment.models import AssignmentType
|
||||
from vbv_lernwelt.core.models import User
|
||||
from vbv_lernwelt.course.models import CourseSession
|
||||
from vbv_lernwelt.course_session.models import CourseSessionAssignment
|
||||
from vbv_lernwelt.notify.email.email_services import (
|
||||
create_template_data_from_course_session_attendance_course,
|
||||
EmailTemplate,
|
||||
format_swiss_datetime,
|
||||
send_email,
|
||||
)
|
||||
from vbv_lernwelt.notify.models import (
|
||||
|
|
@ -21,7 +24,10 @@ from vbv_lernwelt.notify.models import (
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from vbv_lernwelt.assignment.models import AssignmentCompletion
|
||||
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
|
||||
from vbv_lernwelt.course_session.models import (
|
||||
CourseSessionAttendanceCourse,
|
||||
CourseSessionEdoniqTest,
|
||||
)
|
||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
|
@ -137,6 +143,106 @@ class NotificationService:
|
|||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def send_assignment_reminder_notification_member(
|
||||
cls,
|
||||
recipient: User,
|
||||
assignment: CourseSessionAssignment,
|
||||
):
|
||||
texts = {
|
||||
"de": "Erinnerung: Bald ist ein Abgabetermin",
|
||||
"fr": "Rappel: Une date limite approche",
|
||||
"it": "Promemoria: Una scadenza si avvicina",
|
||||
}
|
||||
|
||||
templates = {
|
||||
AssignmentType.CASEWORK: EmailTemplate.ASSIGNMENT_REMINDER_CASEWORK_MEMBER,
|
||||
AssignmentType.PREP_ASSIGNMENT: EmailTemplate.ASSIGNMENT_REMINDER_PREP_ASSIGNMENT_MEMBER,
|
||||
}
|
||||
|
||||
verb = texts.get(recipient.language, "de")
|
||||
circle = assignment.learning_content.get_parent_circle().title
|
||||
due_at = assignment.submission_deadline.start
|
||||
|
||||
return cls._send_notification(
|
||||
recipient=recipient,
|
||||
verb=verb,
|
||||
notification_category=NotificationCategory.INFORMATION,
|
||||
notification_trigger=NotificationTrigger.ASSIGNMENT_REMINDER,
|
||||
target_url=assignment.learning_content.get_frontend_url(),
|
||||
action_object=assignment,
|
||||
course_session=assignment.course_session,
|
||||
email_template=templates[
|
||||
AssignmentType(assignment.learning_content.assignment_type)
|
||||
],
|
||||
template_data={
|
||||
"circle": circle,
|
||||
"due_date": format_swiss_datetime(due_at),
|
||||
},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def send_edoniq_test_reminder_notification_member(
|
||||
cls,
|
||||
recipient: User,
|
||||
edoniq_test: CourseSessionEdoniqTest,
|
||||
):
|
||||
texts = {
|
||||
"de": "Erinnerung: Bald ist ein Abgabetermin",
|
||||
"fr": "Rappel: Une date limite approche",
|
||||
"it": "Promemoria: Una scadenza si avvicina",
|
||||
}
|
||||
|
||||
verb = texts.get(recipient.language, "de")
|
||||
circle = edoniq_test.learning_content.get_parent_circle().title
|
||||
due_at = edoniq_test.deadline.start
|
||||
|
||||
return cls._send_notification(
|
||||
recipient=recipient,
|
||||
verb=verb,
|
||||
notification_category=NotificationCategory.INFORMATION,
|
||||
notification_trigger=NotificationTrigger.ASSIGNMENT_REMINDER,
|
||||
target_url=edoniq_test.learning_content.get_frontend_url(),
|
||||
action_object=edoniq_test,
|
||||
course_session=edoniq_test.course_session,
|
||||
email_template=EmailTemplate.ASSIGNMENT_REMINDER_EDONIQ_MEMBER,
|
||||
template_data={
|
||||
"circle": circle,
|
||||
"due_date": format_swiss_datetime(due_at),
|
||||
},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def send_casework_expert_evaluation_reminder(
|
||||
cls,
|
||||
recipient: User,
|
||||
assignment: CourseSessionAssignment,
|
||||
):
|
||||
texts = {
|
||||
"de": "Erinnerung: Bald ist ein Bewertungstermin",
|
||||
"fr": "Rappel: Une date limite approche",
|
||||
"it": "Promemoria: Una scadenza si avvicina",
|
||||
}
|
||||
|
||||
verb = texts.get(recipient.language, "de")
|
||||
circle = assignment.learning_content.get_parent_circle().title
|
||||
due_at = assignment.evaluation_deadline.start
|
||||
|
||||
return cls._send_notification(
|
||||
recipient=recipient,
|
||||
verb=verb,
|
||||
notification_category=NotificationCategory.INFORMATION,
|
||||
notification_trigger=NotificationTrigger.CASEWORK_EXPERT_EVALUATION_REMINDER,
|
||||
target_url=assignment.evaluation_deadline.url_expert,
|
||||
action_object=assignment,
|
||||
course_session=assignment.course_session,
|
||||
email_template=EmailTemplate.EVALUATION_REMINDER_CASEWORK_EXPERT,
|
||||
template_data={
|
||||
"circle": circle,
|
||||
"due_date": format_swiss_datetime(due_at),
|
||||
},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _send_notification(
|
||||
cls,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,287 @@
|
|||
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.assertEquals(action_object, notification.action_object)
|
||||
self.assertEquals("ASSIGNMENT_REMINDER", notification.notification_trigger)
|
||||
self.assertEquals("INFORMATION", notification.notification_category)
|
||||
self.assertEquals(EXPECTED_MEMBER_VERB, notification.verb)
|
||||
|
||||
template_data = notification.data["template_data"]
|
||||
|
||||
self.assertEquals(
|
||||
action_object.learning_content.get_parent_circle().title,
|
||||
template_data["circle"],
|
||||
)
|
||||
|
||||
self.assertEquals(
|
||||
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.assertEquals(
|
||||
EmailTemplate.ASSIGNMENT_REMINDER_CASEWORK_MEMBER.name,
|
||||
email_template,
|
||||
)
|
||||
elif assignment_type == AssignmentType.PREP_ASSIGNMENT:
|
||||
self.assertEquals(
|
||||
EmailTemplate.ASSIGNMENT_REMINDER_PREP_ASSIGNMENT_MEMBER.name,
|
||||
email_template,
|
||||
)
|
||||
elif type(action_object) == CourseSessionEdoniqTest:
|
||||
self.assertEquals(
|
||||
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.assertEquals(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.assertEquals(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.assertEquals(1, len(Notification.objects.all()))
|
||||
|
||||
notification = Notification.objects.get(recipient__username=RECIPIENT_TRAINER)
|
||||
self.assertEquals(casework, notification.action_object)
|
||||
self.assertEquals("INFORMATION", notification.notification_category)
|
||||
self.assertEquals(EXPECTED_EXPERT_VERB, notification.verb)
|
||||
self.assertEquals(
|
||||
casework.evaluation_deadline.url_expert, notification.target_url
|
||||
)
|
||||
self.assertEquals(
|
||||
"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.assertEquals(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)
|
||||
|
|
@ -10,8 +10,8 @@ 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 CourseSessionAttendanceCourse
|
||||
from vbv_lernwelt.learnpath.models import LearningContentAttendanceCourse
|
||||
from vbv_lernwelt.notify.management.commands.send_attendance_course_reminders import (
|
||||
attendance_course_reminder_notification_job,
|
||||
from vbv_lernwelt.notify.email.reminders.attendance import (
|
||||
send_attendance_reminder_notifications,
|
||||
)
|
||||
from vbv_lernwelt.notify.models import Notification
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ class TestAttendanceCourseReminders(TestCase):
|
|||
)
|
||||
self.csac_future.due_date.save()
|
||||
|
||||
attendance_course_reminder_notification_job()
|
||||
send_attendance_reminder_notifications()
|
||||
|
||||
self.assertEquals(4, len(Notification.objects.all()))
|
||||
notification = Notification.objects.get(
|
||||
|
|
@ -3,6 +3,7 @@ env_secrets/
|
|||
env/bitbucket/Dockerfile
|
||||
env/docker_local.env
|
||||
server/vbv_lernwelt/assignment/creators/create_assignments.py
|
||||
server/vbv_lernwelt/notify/email/email_services.py
|
||||
server/vbv_lernwelt/static/
|
||||
server/vbv_lernwelt/media/
|
||||
server/vbv_lernwelt/edoniq_test/certificates/test.key
|
||||
|
|
|
|||
Loading…
Reference in New Issue