From 4fdf98a5b4f68e2c40b21df50c63c02525c9868f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Rafael=20Siqueira?= Date: Tue, 7 Oct 2025 16:56:00 -0300 Subject: [PATCH] remove METADATA#COURSE --- enrollments-events/app/events/issue_cert.py | 14 +++++++++++--- .../app/events/reenroll_if_failed.py | 3 +-- .../app/events/schedule_reminders.py | 10 ++++------ .../app/events/stopgap/patch_course_metadata.py | 9 --------- enrollments-events/template.yaml | 4 +++- .../events/stopgap/test_patch_course_metadata.py | 2 -- enrollments-events/tests/events/test_issue_cert.py | 4 ++++ .../tests/events/test_schedule_reminders.py | 2 +- enrollments-events/tests/seeds.jsonl | 10 ++++------ enrollments-events/uv.lock | 13 ++++++++++++- 10 files changed, 40 insertions(+), 31 deletions(-) diff --git a/enrollments-events/app/events/issue_cert.py b/enrollments-events/app/events/issue_cert.py index 0794a54..b026155 100644 --- a/enrollments-events/app/events/issue_cert.py +++ b/enrollments-events/app/events/issue_cert.py @@ -12,7 +12,13 @@ from layercake.dateutils import fromisoformat, now from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey from boto3clients import dynamodb_client, s3_client -from config import BUCKET_NAME, ENROLLMENT_TABLE, PAPERFORGE_API, SIGNATURE_URI +from config import ( + BUCKET_NAME, + COURSE_TABLE, + ENROLLMENT_TABLE, + PAPERFORGE_API, + SIGNATURE_URI, +) logger = Logger(__name__) dyn = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client) @@ -24,10 +30,12 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool: new_image = event.detail['new_image'] now_ = now() enrollment_id = new_image['id'] + course_id = new_image['course']['id'] cert = dyn.collection.get_item( KeyPair( - pk=new_image['id'], - sk=SortKey('METADATA#COURSE', path_spec='cert', rename_key='cert'), + pk=course_id, + sk=SortKey('0', path_spec='cert', rename_key='cert'), + table_name=COURSE_TABLE, ), raise_on_error=False, default=False, diff --git a/enrollments-events/app/events/reenroll_if_failed.py b/enrollments-events/app/events/reenroll_if_failed.py index 30e91f5..072dcb9 100644 --- a/enrollments-events/app/events/reenroll_if_failed.py +++ b/enrollments-events/app/events/reenroll_if_failed.py @@ -24,7 +24,6 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool: metadata = dyn.collection.get_items( TransactKey(new_image['id']) + SortKey('METADATA#SUBSCRIPTION_COVERED', rename_key='subscription') - + SortKey('METADATA#COURSE', rename_key='course') + SortKey( 'METADATA#DEDUPLICATION_WINDOW', path_spec='offset_days', @@ -38,7 +37,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool: flatten_top=False, ) user = User.model_validate(new_image['user']) - course = Course.model_validate(new_image['course'] | metadata['course']) + course = Course.model_validate(new_image['course']) subscription = metadata['subscription'] if 'subscription' in metadata else None enrollment = Enrollment( id=uuid4(), diff --git a/enrollments-events/app/events/schedule_reminders.py b/enrollments-events/app/events/schedule_reminders.py index c35d664..c17cd2c 100644 --- a/enrollments-events/app/events/schedule_reminders.py +++ b/enrollments-events/app/events/schedule_reminders.py @@ -1,12 +1,9 @@ -from os import access - 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 glom import glom from layercake.dateutils import now, ttl from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey @@ -26,14 +23,15 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool: now_ = now() enrollment_id = new_image['id'] user = new_image['user'] - course_name = glom(new_image, 'course.name') + course_name = new_image['course']['name'] + # Post-migration: after removing the stopgap file `patch_course_metadata.py`, # use `access_expires_at` from `new_image` access_period = int( dyn.collection.get_item( KeyPair( - pk=enrollment_id, - sk=SortKey('METADATA#COURSE', path_spec='access_period'), + pk=new_image['course']['id'], + sk=SortKey('0', path_spec='access_period'), ), ) ) diff --git a/enrollments-events/app/events/stopgap/patch_course_metadata.py b/enrollments-events/app/events/stopgap/patch_course_metadata.py index 21939d2..daee882 100644 --- a/enrollments-events/app/events/stopgap/patch_course_metadata.py +++ b/enrollments-events/app/events/stopgap/patch_course_metadata.py @@ -51,15 +51,6 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool: 'created_at': now_, } ) - transact.put( - item={ - 'id': new_image['id'], - 'sk': 'METADATA#COURSE', - 'created_at': now_, - 'access_period': access_period, - 'cert': course.get('cert', None), - } - ) except Exception as exc: logger.exception(exc) return False diff --git a/enrollments-events/template.yaml b/enrollments-events/template.yaml index e60fcce..02cfd03 100644 --- a/enrollments-events/template.yaml +++ b/enrollments-events/template.yaml @@ -26,7 +26,7 @@ Globals: Architectures: - x86_64 Layers: - - !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:96 + - !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:97 Environment: Variables: TZ: America/Sao_Paulo @@ -314,6 +314,8 @@ Resources: Policies: - DynamoDBCrudPolicy: TableName: !Ref EnrollmentTable + - DynamoDBReadPolicy: + TableName: !Ref CourseTable - S3WritePolicy: BucketName: !Ref BucketName Events: diff --git a/enrollments-events/tests/events/stopgap/test_patch_course_metadata.py b/enrollments-events/tests/events/stopgap/test_patch_course_metadata.py index 343ec54..7ee2421 100644 --- a/enrollments-events/tests/events/stopgap/test_patch_course_metadata.py +++ b/enrollments-events/tests/events/stopgap/test_patch_course_metadata.py @@ -31,8 +31,6 @@ def test_enroll( TransactKey('47ZxxcVBjvhDS5TE98tpfQ') + SortKey('0') + SortKey('METADATA#DEDUPLICATION_WINDOW') - + SortKey('METADATA#COURSE') ) - assert 'METADATA#COURSE' in result assert 'METADATA#DEDUPLICATION_WINDOW' in result diff --git a/enrollments-events/tests/events/test_issue_cert.py b/enrollments-events/tests/events/test_issue_cert.py index b0599b1..62711fe 100644 --- a/enrollments-events/tests/events/test_issue_cert.py +++ b/enrollments-events/tests/events/test_issue_cert.py @@ -20,6 +20,10 @@ def test_issue_cert( 'name': 'Jimi Hendrix', 'cpf': '74630003037', }, + 'course': { + 'id': '123', + 'name': 'pytest', + }, 'score': 79, 'status': 'COMPLETED', } diff --git a/enrollments-events/tests/events/test_schedule_reminders.py b/enrollments-events/tests/events/test_schedule_reminders.py index 29317c4..cafb1cb 100644 --- a/enrollments-events/tests/events/test_schedule_reminders.py +++ b/enrollments-events/tests/events/test_schedule_reminders.py @@ -33,4 +33,4 @@ def test_schedule_reminders( r = dynamodb_persistence_layer.collection.query( PartitionKey('14682b79-3df2-4351-9229-8b558af046a0') ) - assert len(r['items']) == 4 + assert len(r['items']) == 3 diff --git a/enrollments-events/tests/seeds.jsonl b/enrollments-events/tests/seeds.jsonl index bc29400..b7a1927 100644 --- a/enrollments-events/tests/seeds.jsonl +++ b/enrollments-events/tests/seeds.jsonl @@ -11,7 +11,8 @@ {"id": "cpYSbBcie2NDbZhDKCxCih", "sk": "generated_items"} // Course -{"id": "123", "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": "pytest", "tenant_id": "*"} +{"id": "123", "sk": "0", "access_period": 360, "cert": {"exp_interval": 700, "s3_uri": "s3://saladeaula.digital/certs/samples/cipa-grau-de-risco-1.html"}, "created_at": "2025-07-14T15:09:18.559528-03:00", "metadata__konviva_class_id": "281", "name": "pytest", "tenant_id": "*"} +{"id": "12334", "sk": "0", "access_period": 360} {"id": "a955518e-ebcb-4441-b914-ddc9ecef84f0", "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": "NR-11 Operador de Munck", "tenant_id": "*"} {"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} @@ -25,17 +26,14 @@ // Enrollment {"id": "6437a282-6fe8-4e4d-9eb0-da1007238007", "sk": "0", "status": "IN_PROGRESS", "progress": 10} {"id": "845fe390-e3c3-4514-97f8-c42de0566cf0", "sk": "0", "status": "COMPLETED", "progress": 100} -{"id": "14682b79-3df2-4351-9229-8b558af046a0", "sk": "METADATA#COURSE", "access_period": 360} -{"id": "1ee108ae-67d4-4545-bf6d-4e641cdaa4e0", "sk": "0", "status": "COMPLETED", "score": 100, "course": {"name": "CIPA Grau de Risco 1"}, "user": {"name": "Kurt Cobain"}, "issued_cert": {"s3_uri": "s3://saladeaula.digital/issuedcerts/1ee108ae-67d4-4545-bf6d-4e641cdaa4e0.pdf"}} -{"id": "1ee108ae-67d4-4545-bf6d-4e641cdaa4e0", "sk": "METADATA#COURSE", "cert": {"s3_uri": "s3://saladeaula.digital/certs/samples/cipa-grau-de-risco-1.html", "exp_interval": 700}} +{"id": "1ee108ae-67d4-4545-bf6d-4e641cdaa4e0", "sk": "0", "status": "COMPLETED", "score": 100, "course": {"id": "123", "name": "CIPA Grau de Risco 1"}, "user": {"name": "Kurt Cobain"}, "issued_cert": {"s3_uri": "s3://saladeaula.digital/issuedcerts/1ee108ae-67d4-4545-bf6d-4e641cdaa4e0.pdf"}} {"id": "1ee108ae-67d4-4545-bf6d-4e641cdaa4e0", "sk": "STARTED", "started_at": "2025-08-24T01:44:42.703012-03:06"} {"id": "1ee108ae-67d4-4545-bf6d-4e641cdaa4e0", "sk": "COMPLETED", "completed_at": "2025-08-31T21:59:10.842467-03:00"} {"id": "294e9864-8284-4287-b153-927b15d90900", "sk": "0"} {"id": "294e9864-8284-4287-b153-927b15d90900", "sk": "METADATA#SUBSCRIPTION_COVERED", "updated_at": "2025-09-09T15:20:17.187461-03:00", "billing_day": 1, "org_id": "123", "created_at": "2025-09-09T15:20:14.021500-03:00", "billing_period": "START#2025-09-01#END#2025-09-30"} -{"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": "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": "tenant", "org_id": "123", "name": "EDUSEG", "create_date": "2025-09-12T17:11:00.556907-03:00"} diff --git a/enrollments-events/uv.lock b/enrollments-events/uv.lock index 963300c..37f3a81 100644 --- a/enrollments-events/uv.lock +++ b/enrollments-events/uv.lock @@ -501,7 +501,7 @@ wheels = [ [[package]] name = "layercake" -version = "0.9.14" +version = "0.10.0" source = { directory = "../layercake" } dependencies = [ { name = "arnparse" }, @@ -518,6 +518,7 @@ dependencies = [ { name = "pycpfcnpj" }, { name = "pydantic", extra = ["email"] }, { name = "pydantic-extra-types" }, + { name = "python-multipart" }, { name = "pytz" }, { name = "requests" }, { name = "smart-open", extra = ["s3"] }, @@ -541,6 +542,7 @@ requires-dist = [ { name = "pycpfcnpj", specifier = ">=1.8" }, { name = "pydantic", extras = ["email"], specifier = ">=2.10.6" }, { name = "pydantic-extra-types", specifier = ">=2.10.3" }, + { name = "python-multipart", specifier = ">=0.0.20" }, { name = "pytz", specifier = ">=2025.1" }, { name = "requests", specifier = ">=2.32.3" }, { name = "smart-open", extras = ["s3"], specifier = ">=7.1.0" }, @@ -835,6 +837,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" }, ] +[[package]] +name = "python-multipart" +version = "0.0.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, +] + [[package]] name = "pytz" version = "2025.2"