diff --git a/enrollments-events/app/events/emails/__init__.py b/enrollments-events/app/events/emails/__init__.py
index e69de29..fc0d6f3 100644
--- a/enrollments-events/app/events/emails/__init__.py
+++ b/enrollments-events/app/events/emails/__init__.py
@@ -0,0 +1,102 @@
+from typing import TYPE_CHECKING, TypedDict
+
+from aws_lambda_powertools import Logger
+from layercake.dateutils import now
+from layercake.dynamodb import DynamoDBPersistenceLayer
+from layercake.email_ import Message
+from layercake.strutils import first_word, truncate_str
+
+from . import (
+ reminder_access_period_before_30_days,
+ reminder_cert_expiration_before_30_days,
+ reminder_no_access_after_3_days,
+ reminder_no_activity_after_7_days,
+)
+
+if TYPE_CHECKING:
+ from mypy_boto3_sesv2 import SESV2Client
+else:
+ SESV2Client = object
+
+Event = TypedDict('Event', {'id': str, 'sk': str})
+
+TEMPLATES = {
+ 'reminder_access_period_before_30_days': {
+ 'subject': reminder_access_period_before_30_days.SUBJECT,
+ 'message': reminder_access_period_before_30_days.MESSAGE,
+ },
+ 'reminder_cert_expiration_before_30_days': {
+ 'subject': reminder_cert_expiration_before_30_days.SUBJECT,
+ 'message': reminder_cert_expiration_before_30_days.MESSAGE,
+ },
+ 'reminder_no_access_after_3_days': {
+ 'subject': reminder_no_access_after_3_days.SUBJECT,
+ 'message': reminder_no_access_after_3_days.MESSAGE,
+ },
+ 'reminder_no_activity_after_7_days': {
+ 'subject': reminder_no_activity_after_7_days.SUBJECT,
+ 'message': reminder_no_activity_after_7_days.MESSAGE,
+ },
+}
+
+logger = Logger(__name__)
+
+
+def send_email(
+ to: tuple[str, str],
+ subject: str,
+ message: str,
+ context: dict = {},
+ *,
+ sender: tuple[str, str],
+ event: Event,
+ sesv2_client: SESV2Client,
+ dynamodb_persistence_layer: DynamoDBPersistenceLayer,
+) -> bool:
+ now_ = now()
+ name, _ = to
+ event_name = event['sk']
+ emailmsg = Message(
+ from_=sender,
+ to=to,
+ subject=subject.format(
+ course=truncate_str(context['course']),
+ ),
+ )
+ emailmsg.add_alternative(
+ message.format(
+ first_name=first_word(name),
+ course=context['course'],
+ )
+ )
+
+ try:
+ sesv2_client.send_email(
+ Content={
+ 'Raw': {
+ 'Data': emailmsg.as_bytes(),
+ },
+ }
+ )
+ dynamodb_persistence_layer.put_item(
+ item={
+ 'id': event['id'],
+ 'sk': f'{event_name}#EXECUTED',
+ 'created_at': now_,
+ }
+ )
+ logger.info('Email sent')
+ except Exception as exc:
+ logger.exception(exc)
+
+ dynamodb_persistence_layer.put_item(
+ item={
+ 'id': event['id'],
+ 'sk': f'{event_name}#FAILED',
+ 'created_at': now_,
+ }
+ )
+
+ return False
+ else:
+ return True
diff --git a/enrollments-events/app/events/emails/email_.py b/enrollments-events/app/events/emails/email_.py
deleted file mode 100644
index 17f980f..0000000
--- a/enrollments-events/app/events/emails/email_.py
+++ /dev/null
@@ -1,76 +0,0 @@
-from typing import TYPE_CHECKING, TypedDict
-
-from aws_lambda_powertools import Logger
-from layercake.dateutils import now
-from layercake.dynamodb import DynamoDBPersistenceLayer
-from layercake.email_ import Message
-from layercake.strutils import first_word, truncate_str
-
-logger = Logger(__name__)
-
-if TYPE_CHECKING:
- from mypy_boto3_sesv2 import SESV2Client
-else:
- SESV2Client = object
-
-Event = TypedDict('Event', {'id': str, 'sk': str})
-
-
-def send_email(
- to: tuple[str, str],
- subject: str,
- message: str,
- context: dict = {},
- *,
- sender: tuple[str, str],
- event: Event,
- sesv2_client: SESV2Client,
- dynamodb_persistence_layer: DynamoDBPersistenceLayer,
-) -> bool:
- now_ = now()
- name, _ = to
- event_name = event['sk']
- emailmsg = Message(
- from_=sender,
- to=to,
- subject=subject.format(
- course=truncate_str(context['course']),
- ),
- )
- emailmsg.add_alternative(
- message.format(
- first_name=first_word(name),
- course=context['course'],
- )
- )
-
- try:
- sesv2_client.send_email(
- Content={
- 'Raw': {
- 'Data': emailmsg.as_bytes(),
- },
- }
- )
- dynamodb_persistence_layer.put_item(
- item={
- 'id': event['id'],
- 'sk': f'{event_name}#EXECUTED',
- 'created_at': now_,
- }
- )
- logger.info('Email sent')
- except Exception as exc:
- logger.exception(exc)
-
- dynamodb_persistence_layer.put_item(
- item={
- 'id': event['id'],
- 'sk': f'{event_name}#FAILED',
- 'created_at': now_,
- }
- )
-
- return False
- else:
- return True
diff --git a/enrollments-events/app/events/emails/reminder_access_period_before_30_days.py b/enrollments-events/app/events/emails/reminder_access_period_before_30_days.py
index 121b30e..f6c80ef 100644
--- a/enrollments-events/app/events/emails/reminder_access_period_before_30_days.py
+++ b/enrollments-events/app/events/emails/reminder_access_period_before_30_days.py
@@ -1,22 +1,4 @@
-from aws_lambda_powertools import Logger
-from aws_lambda_powertools.utilities.data_classes import (
- EventBridgeEvent,
- event_source,
-)
-from aws_lambda_powertools.utilities.typing import LambdaContext
-from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
-
-from boto3clients import dynamodb_client, sesv2_client
-from config import (
- EMAIL_SENDER,
- ENROLLMENT_TABLE,
-)
-
-from .email_ import send_email
-
-logger = Logger(__name__)
-enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
-
+# 30 days before the course access period ends
SUBJECT = 'Seu acesso ao curso {course} termina em 30 dias'
MESSAGE = """
@@ -27,34 +9,3 @@ Conclua dentro desse prazo para garantir sua certificação.
👉 Acesse agora seu curso
"""
-
-
-@event_source(data_class=EventBridgeEvent)
-@logger.inject_lambda_context
-def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
- """30 days before the course access period ends."""
- old_image = event.detail['old_image']
-
- # Post-migration: Remove the following lines
- if 'email' not in old_image:
- # If email is missing, use enrollment email
- cur_image = enrollment_layer.get_item(KeyPair(old_image['id'], '0'))
- old_image['name'] = cur_image['user']['name']
- old_image['email'] = cur_image['user']['email']
- old_image['course'] = cur_image['course']['name']
-
- return send_email(
- to=(old_image['name'], old_image['email']),
- subject=SUBJECT,
- message=MESSAGE,
- context={
- 'course': old_image['course'],
- },
- sender=EMAIL_SENDER,
- sesv2_client=sesv2_client,
- event={
- 'id': old_image['id'],
- 'sk': 'SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS',
- },
- dynamodb_persistence_layer=enrollment_layer,
- )
diff --git a/enrollments-events/app/events/emails/reminder_cert_expiration_before_30_days.py b/enrollments-events/app/events/emails/reminder_cert_expiration_before_30_days.py
index 57cfd58..e8f026e 100644
--- a/enrollments-events/app/events/emails/reminder_cert_expiration_before_30_days.py
+++ b/enrollments-events/app/events/emails/reminder_cert_expiration_before_30_days.py
@@ -1,22 +1,7 @@
-from aws_lambda_powertools import Logger
-from aws_lambda_powertools.utilities.data_classes import (
- EventBridgeEvent,
- event_source,
-)
-from aws_lambda_powertools.utilities.typing import LambdaContext
-from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
-
-from boto3clients import dynamodb_client, sesv2_client
-from config import (
- EMAIL_SENDER,
- ENROLLMENT_TABLE,
-)
-
-from .email_ import send_email
-
-logger = Logger(__name__)
-enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
-
+"""
+If a certificate exists, remind the user 30 days before
+the certificate expires
+"""
SUBJECT = 'Seu certificado {course} vai expirar em breve'
MESSAGE = """
@@ -28,35 +13,3 @@ antes da expiração.
👉 Acesse o curso e renove sua certificação
"""
-
-
-@event_source(data_class=EventBridgeEvent)
-@logger.inject_lambda_context
-def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
- """If a certificate exists, remind the user 30 days before
- the certificate expires."""
- old_image = event.detail['old_image']
-
- # Post-migration: Remove the following lines
- if 'email' not in old_image:
- # If email is missing, use enrollment email
- cur_image = enrollment_layer.get_item(KeyPair(old_image['id'], '0'))
- old_image['name'] = cur_image['user']['name']
- old_image['email'] = cur_image['user']['email']
- old_image['course'] = cur_image['course']['name']
-
- return send_email(
- to=(old_image['name'], old_image['email']),
- subject=SUBJECT,
- message=MESSAGE,
- context={
- 'course': old_image['course'],
- },
- sender=EMAIL_SENDER,
- sesv2_client=sesv2_client,
- event={
- 'id': old_image['id'],
- 'sk': 'SCHEDULE#REMINDER_CERT_EXPIRATION_BEFORE_30_DAYS',
- },
- dynamodb_persistence_layer=enrollment_layer,
- )
diff --git a/enrollments-events/app/events/emails/reminder_no_access_after_3_days.py b/enrollments-events/app/events/emails/reminder_no_access_after_3_days.py
index e708269..c0f0b51 100644
--- a/enrollments-events/app/events/emails/reminder_no_access_after_3_days.py
+++ b/enrollments-events/app/events/emails/reminder_no_access_after_3_days.py
@@ -1,22 +1,7 @@
-from aws_lambda_powertools import Logger
-from aws_lambda_powertools.utilities.data_classes import (
- EventBridgeEvent,
- event_source,
-)
-from aws_lambda_powertools.utilities.typing import LambdaContext
-from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
-
-from boto3clients import dynamodb_client, sesv2_client
-from config import (
- EMAIL_SENDER,
- ENROLLMENT_TABLE,
-)
-
-from .email_ import send_email
-
-logger = Logger(__name__)
-enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
-
+"""
+If the user does not access the course within 3 days
+after enrollment creation
+"""
SUBJECT = 'Seu curso {course} está esperando por você na EDUSEG®'
MESSAGE = """
@@ -28,35 +13,3 @@ ao máximo seu curso!
👉 Acesse seu curso agora
"""
-
-
-@event_source(data_class=EventBridgeEvent)
-@logger.inject_lambda_context
-def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
- """If the user does not access the course within 3 days after
- enrollment creation."""
- old_image = event.detail['old_image']
-
- # Post-migration: Remove the following lines
- if 'email' not in old_image:
- # If email is missing, use enrollment email
- cur_image = enrollment_layer.get_item(KeyPair(old_image['id'], '0'))
- old_image['name'] = cur_image['user']['name']
- old_image['email'] = cur_image['user']['email']
- old_image['course'] = cur_image['course']['name']
-
- return send_email(
- to=(old_image['name'], old_image['email']),
- subject=SUBJECT,
- message=MESSAGE,
- context={
- 'course': old_image['course'],
- },
- sender=EMAIL_SENDER,
- sesv2_client=sesv2_client,
- event={
- 'id': old_image['id'],
- 'sk': 'SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS',
- },
- dynamodb_persistence_layer=enrollment_layer,
- )
diff --git a/enrollments-events/app/events/emails/reminder_no_activity_after_7_days.py b/enrollments-events/app/events/emails/reminder_no_activity_after_7_days.py
index f240333..e27f430 100644
--- a/enrollments-events/app/events/emails/reminder_no_activity_after_7_days.py
+++ b/enrollments-events/app/events/emails/reminder_no_activity_after_7_days.py
@@ -1,22 +1,4 @@
-from aws_lambda_powertools import Logger
-from aws_lambda_powertools.utilities.data_classes import (
- EventBridgeEvent,
- event_source,
-)
-from aws_lambda_powertools.utilities.typing import LambdaContext
-from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
-
-from boto3clients import dynamodb_client, sesv2_client
-from config import (
- EMAIL_SENDER,
- ENROLLMENT_TABLE,
-)
-
-from .email_ import send_email
-
-logger = Logger(__name__)
-enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
-
+# 7 days after the user's last activity in the course
SUBJECT = 'Seu curso {course} está parado há 7 dias...'
MESSAGE = """
@@ -27,34 +9,3 @@ Não deixe seu período de acesso expirar! Retome seu aprendizado agora mesmo.👉 Clique aqui para acessar seu curso
"""
-
-
-@event_source(data_class=EventBridgeEvent)
-@logger.inject_lambda_context
-def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
- """7 days after the user's last activity in the course."""
- old_image = event.detail['old_image']
-
- # Post-migration: Remove the following lines
- if 'email' not in old_image:
- # If email is missing, use enrollment email
- cur_image = enrollment_layer.get_item(KeyPair(old_image['id'], '0'))
- old_image['name'] = cur_image['user']['name']
- old_image['email'] = cur_image['user']['email']
- old_image['course'] = cur_image['course']['name']
-
- return send_email(
- to=(old_image['name'], old_image['email']),
- subject=SUBJECT,
- message=MESSAGE,
- context={
- 'course': old_image['course'],
- },
- sender=EMAIL_SENDER,
- sesv2_client=sesv2_client,
- event={
- 'id': old_image['id'],
- 'sk': 'SCHEDULE#REMINDER_NO_ACTIVITY_AFTER_7_DAYS',
- },
- dynamodb_persistence_layer=enrollment_layer,
- )
diff --git a/enrollments-events/app/events/send_reminder_emails.py b/enrollments-events/app/events/send_reminder_emails.py
new file mode 100644
index 0000000..0a2b127
--- /dev/null
+++ b/enrollments-events/app/events/send_reminder_emails.py
@@ -0,0 +1,49 @@
+from aws_lambda_powertools import Logger
+from aws_lambda_powertools.utilities.data_classes import (
+ EventBridgeEvent,
+ event_source,
+)
+from aws_lambda_powertools.utilities.typing import LambdaContext
+from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
+
+from boto3clients import dynamodb_client, sesv2_client
+from config import (
+ EMAIL_SENDER,
+ ENROLLMENT_TABLE,
+)
+
+from .emails import TEMPLATES, send_email
+
+logger = Logger(__name__)
+dyn = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
+
+
+@event_source(data_class=EventBridgeEvent)
+@logger.inject_lambda_context
+def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
+ old_image = event.detail['old_image']
+ event_name = old_image['sk'].removeprefix('SCHEDULE#').lower()
+ template = TEMPLATES[event_name]
+
+ # If email is missing, use enrollment email
+ if 'email' not in old_image:
+ r = dyn.get_item(KeyPair(pk=old_image['id'], sk='0'))
+ old_image['name'] = r['user']['name']
+ old_image['email'] = r['user']['email']
+ old_image['course'] = r['course']['name']
+
+ return send_email(
+ to=(old_image['name'], old_image['email']),
+ subject=template['subject'],
+ message=template['message'],
+ context={
+ 'course': old_image['course'],
+ },
+ sender=EMAIL_SENDER,
+ sesv2_client=sesv2_client,
+ event={
+ 'id': old_image['id'],
+ 'sk': old_image['sk'],
+ },
+ dynamodb_persistence_layer=dyn,
+ )
diff --git a/enrollments-events/app/events/stopgap/patch_course_metadata.py b/enrollments-events/app/events/stopgap/patch_course_metadata.py
index 64f55cd..21939d2 100644
--- a/enrollments-events/app/events/stopgap/patch_course_metadata.py
+++ b/enrollments-events/app/events/stopgap/patch_course_metadata.py
@@ -9,15 +9,12 @@ from aws_lambda_powertools.utilities.data_classes import (
)
from aws_lambda_powertools.utilities.typing import LambdaContext
from layercake.dateutils import now
-from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
-from sqlite_utils import Database
+from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey
from boto3clients import dynamodb_client
from config import (
COURSE_TABLE,
ENROLLMENT_TABLE,
- SQLITE_DATABASE,
- SQLITE_TABLE,
)
sqlite3.register_converter('json', json.loads)
@@ -72,23 +69,24 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
class CourseNotFoundError(Exception):
def __init__(self, *args):
- super().__init__('Course not found in SQLite')
+ super().__init__('Course not found')
-def _get_course(course_id: str) -> dict:
- course = course_layer.get_item(KeyPair(pk=course_id, sk='0'))
+def _get_course(id: str) -> dict:
+ course = course_layer.get_item(KeyPair(pk=id, sk='0'))
if course:
return course
- with sqlite3.connect(
- database=SQLITE_DATABASE, detect_types=sqlite3.PARSE_DECLTYPES
- ) as conn:
- db = Database(conn)
- rows = db[SQLITE_TABLE].rows_where(
- "json->>'$.metadata__betaeducacao_id' = ?", [course_id]
- )
-
- for row in rows:
- return row['json']
-
- raise CourseNotFoundError
+ course_id = course_layer.collection.get_item(
+ KeyPair(
+ pk='MIGRATION',
+ sk=SortKey(
+ f'COURSE#{id}',
+ path_spec='course_id',
+ ),
+ ),
+ exc_cls=CourseNotFoundError,
+ )
+ return course_layer.collection.get_item(
+ KeyPair(pk=course_id, sk='0'),
+ )
diff --git a/enrollments-events/template.yaml b/enrollments-events/template.yaml
index 805f4a6..268e576 100644
--- a/enrollments-events/template.yaml
+++ b/enrollments-events/template.yaml
@@ -219,11 +219,10 @@ Resources:
- !Sub arn:aws:ses:${AWS::Region}:${AWS::AccountId}:identity/eduseg.com.br
- !Sub arn:aws:ses:${AWS::Region}:${AWS::AccountId}:configuration-set/tracking
- # If the user does not access the course within 3 days after enrollment creation
- EventReminderNoAccessAfter3DaysFunction:
+ EventSendReminderEmailsFunction:
Type: AWS::Serverless::Function
Properties:
- Handler: events.emails.reminder_no_access_after_3_days.lambda_handler
+ Handler: events.emails.send_reminder_emails.lambda_handler
LoggingConfig:
LogGroup: !Ref EventLog
Policies:
@@ -239,17 +238,19 @@ Resources:
detail-type: [EXPIRE]
detail:
keys:
- sk: [SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS]
+ sk:
+ - SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS
+ - SCHEDULE#REMINDER_NO_ACTIVITY_AFTER_7_DAYS
+ - SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS
+ - SCHEDULE#REMINDER_CERT_EXPIRATION_BEFORE_30_DAYS
- # 7 days after the user's last activity in the course
- EventReminderNoActivityAfter7DaysFunction:
+ EventScheduleRemindersFunction:
Type: AWS::Serverless::Function
Properties:
- Handler: events.emails.reminder_no_activity_after_7_days.lambda_handler
+ Handler: events.schedule_reminders.lambda_handler
LoggingConfig:
LogGroup: !Ref EventLog
Policies:
- - !Ref SesPolicy
- DynamoDBCrudPolicy:
TableName: !Ref EnrollmentTable
Events:
@@ -258,54 +259,11 @@ Resources:
Properties:
Pattern:
resources: [!Ref EnrollmentTable]
- detail-type: [EXPIRE]
+ detail-type: [INSERT]
detail:
- keys:
- sk: [SCHEDULE#REMINDER_NO_ACTIVITY_AFTER_7_DAYS]
-
- # 30 days before the course access period ends.
- EventReminderAccessPeriodBefore30DaysFunction:
- Type: AWS::Serverless::Function
- Properties:
- Handler: events.emails.reminder_access_period_before_30_days.lambda_handler
- LoggingConfig:
- LogGroup: !Ref EventLog
- Policies:
- - !Ref SesPolicy
- - DynamoDBCrudPolicy:
- TableName: !Ref EnrollmentTable
- Events:
- DynamoDBEvent:
- Type: EventBridgeRule
- Properties:
- Pattern:
- resources: [!Ref EnrollmentTable]
- detail-type: [EXPIRE]
- detail:
- keys:
- sk: [SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS]
-
- # If a certificate exists, remind the user 30 days before the certificate expires
- EventReminderCertExpirationBefore30DaysFunction:
- Type: AWS::Serverless::Function
- Properties:
- Handler: events.emails.reminder_cert_expiration_before_30_days.lambda_handler
- LoggingConfig:
- LogGroup: !Ref EventLog
- Policies:
- - !Ref SesPolicy
- - DynamoDBCrudPolicy:
- TableName: !Ref EnrollmentTable
- Events:
- DynamoDBEvent:
- Type: EventBridgeRule
- Properties:
- Pattern:
- resources: [!Ref EnrollmentTable]
- detail-type: [EXPIRE]
- detail:
- keys:
- sk: [SCHEDULE#REMINDER_CERT_EXPIRATION_BEFORE_30_DAYS]
+ new_image:
+ sk: ["0"]
+ status: [PENDING]
EventSetAccessExpiredFunction:
Type: AWS::Serverless::Function
@@ -347,27 +305,6 @@ Resources:
keys:
sk: [SCHEDULE#SET_CERT_EXPIRED, SCHEDULE#SET_AS_ARCHIVED]
- EventScheduleRemindersFunction:
- Type: AWS::Serverless::Function
- Properties:
- Handler: events.schedule_reminders.lambda_handler
- LoggingConfig:
- LogGroup: !Ref EventLog
- Policies:
- - DynamoDBCrudPolicy:
- TableName: !Ref EnrollmentTable
- Events:
- DynamoDBEvent:
- Type: EventBridgeRule
- Properties:
- Pattern:
- resources: [!Ref EnrollmentTable]
- detail-type: [INSERT]
- detail:
- new_image:
- sk: ["0"]
- status: [PENDING]
-
EventIssueCertFunction:
Type: AWS::Serverless::Function
Properties:
diff --git a/enrollments-events/tests/events/emails/test_reminder_access_period_before_30_days.py b/enrollments-events/tests/events/emails/test_reminder_access_period_before_30_days.py
index bbd1d51..91f652b 100644
--- a/enrollments-events/tests/events/emails/test_reminder_access_period_before_30_days.py
+++ b/enrollments-events/tests/events/emails/test_reminder_access_period_before_30_days.py
@@ -1,4 +1,4 @@
-import app.events.emails.reminder_access_period_before_30_days as app
+import app.events.send_reminder_emails as app
from aws_lambda_powertools.utilities.typing import LambdaContext
diff --git a/enrollments-events/tests/events/emails/test_reminder_cert_expiration_before_30_days.py b/enrollments-events/tests/events/emails/test_reminder_cert_expiration_before_30_days.py
index 2a6fecf..8fb6595 100644
--- a/enrollments-events/tests/events/emails/test_reminder_cert_expiration_before_30_days.py
+++ b/enrollments-events/tests/events/emails/test_reminder_cert_expiration_before_30_days.py
@@ -1,4 +1,4 @@
-import app.events.emails.reminder_cert_expiration_before_30_days as app
+import app.events.send_reminder_emails as app
from aws_lambda_powertools.utilities.typing import LambdaContext
diff --git a/enrollments-events/tests/events/emails/test_reminder_no_access_after_3_days.py b/enrollments-events/tests/events/emails/test_reminder_no_access_after_3_days.py
index fb25c7d..1ed3faf 100644
--- a/enrollments-events/tests/events/emails/test_reminder_no_access_after_3_days.py
+++ b/enrollments-events/tests/events/emails/test_reminder_no_access_after_3_days.py
@@ -1,4 +1,4 @@
-import app.events.emails.reminder_no_access_after_3_days as app
+import app.events.send_reminder_emails as app
from aws_lambda_powertools.utilities.typing import LambdaContext
diff --git a/enrollments-events/tests/events/emails/test_reminder_no_activity_after_7_days.py b/enrollments-events/tests/events/emails/test_reminder_no_activity_after_7_days.py
index 5635f5e..2d70025 100644
--- a/enrollments-events/tests/events/emails/test_reminder_no_activity_after_7_days.py
+++ b/enrollments-events/tests/events/emails/test_reminder_no_activity_after_7_days.py
@@ -1,4 +1,4 @@
-import app.events.emails.reminder_no_activity_after_7_days as app
+import app.events.send_reminder_emails as app
from aws_lambda_powertools.utilities.typing import LambdaContext
diff --git a/enrollments-events/tests/seeds.jsonl b/enrollments-events/tests/seeds.jsonl
index a9548c3..bc29400 100644
--- a/enrollments-events/tests/seeds.jsonl
+++ b/enrollments-events/tests/seeds.jsonl
@@ -16,6 +16,9 @@
{"id": "6a403773-aeac-4e6a-ac39-dc958e4be52a", "sk": "0", "access_period": "360", "cert": {"exp_interval": 360}, "created_at": "2025-07-14T15:09:18.559528-03:00", "metadata__konviva_class_id": "281", "name": "Reciclagem em NR-11 - Operador de Empilhadeira", "tenant_id": "*"}
{"id": "e1c44881-2fe3-484e-ada2-12b6bf5b9398", "sk": "0", "name": "NR-35 Segurança nos Trabalhos em Altura (Teórico)", "updated_at": "2025-08-22T00:00:24.431267-03:00", "access_period": 360, "created_at": "2024-12-30T00:11:33.088916-03:00", "metadata__konviva_class_id": 1, "tenant_id": "*", "cert": {"exp_interval": 700}, "metadata__unit_price": 119}
+// Course migration
+{"id": "MIGRATION", "sk": "COURSE#57", "course_id": "e1c44881-2fe3-484e-ada2-12b6bf5b9398"}
+
// User data
{"id": "5OxmMjL-ujoR5IMGegQz", "sk": "konviva", "konvivaId": 26943}
@@ -34,4 +37,5 @@
{"id": "294e9864-8284-4287-b153-927b15d90900", "sk": "METADATA#COURSE", "access_period": 360, "created_at": "2025-09-09T09:11:29.292760-03:00", "cert": {"exp_interval": 360}}
{"id": "294e9864-8284-4287-b153-927b15d90900", "sk": "konviva", "class_id": 34, "user_id": 26943, "created_at": "2025-09-09T09:11:29.315247-03:00", "enrollment_id": 244488}
{"id": "294e9864-8284-4287-b153-927b15d90900", "sk": "METADATA#DEDUPLICATION_WINDOW", "offset_days": 90, "created_at": "2025-09-11T09:00:45.923035-03:00"}
-{"id": "294e9864-8284-4287-b153-927b15d90900", "sk": "tenant", "org_id": "123", "name": "EDUSEG", "create_date": "2025-09-12T17:11:00.556907-03:00"}
\ No newline at end of file
+{"id": "294e9864-8284-4287-b153-927b15d90900", "sk": "tenant", "org_id": "123", "name": "EDUSEG", "create_date": "2025-09-12T17:11:00.556907-03:00"}
+