remove METADATA#COURSE
This commit is contained in:
@@ -12,7 +12,13 @@ from layercake.dateutils import fromisoformat, now
|
|||||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey
|
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey
|
||||||
|
|
||||||
from boto3clients import dynamodb_client, s3_client
|
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__)
|
logger = Logger(__name__)
|
||||||
dyn = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
|
dyn = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
|
||||||
@@ -24,10 +30,12 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
|||||||
new_image = event.detail['new_image']
|
new_image = event.detail['new_image']
|
||||||
now_ = now()
|
now_ = now()
|
||||||
enrollment_id = new_image['id']
|
enrollment_id = new_image['id']
|
||||||
|
course_id = new_image['course']['id']
|
||||||
cert = dyn.collection.get_item(
|
cert = dyn.collection.get_item(
|
||||||
KeyPair(
|
KeyPair(
|
||||||
pk=new_image['id'],
|
pk=course_id,
|
||||||
sk=SortKey('METADATA#COURSE', path_spec='cert', rename_key='cert'),
|
sk=SortKey('0', path_spec='cert', rename_key='cert'),
|
||||||
|
table_name=COURSE_TABLE,
|
||||||
),
|
),
|
||||||
raise_on_error=False,
|
raise_on_error=False,
|
||||||
default=False,
|
default=False,
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
|||||||
metadata = dyn.collection.get_items(
|
metadata = dyn.collection.get_items(
|
||||||
TransactKey(new_image['id'])
|
TransactKey(new_image['id'])
|
||||||
+ SortKey('METADATA#SUBSCRIPTION_COVERED', rename_key='subscription')
|
+ SortKey('METADATA#SUBSCRIPTION_COVERED', rename_key='subscription')
|
||||||
+ SortKey('METADATA#COURSE', rename_key='course')
|
|
||||||
+ SortKey(
|
+ SortKey(
|
||||||
'METADATA#DEDUPLICATION_WINDOW',
|
'METADATA#DEDUPLICATION_WINDOW',
|
||||||
path_spec='offset_days',
|
path_spec='offset_days',
|
||||||
@@ -38,7 +37,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
|||||||
flatten_top=False,
|
flatten_top=False,
|
||||||
)
|
)
|
||||||
user = User.model_validate(new_image['user'])
|
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
|
subscription = metadata['subscription'] if 'subscription' in metadata else None
|
||||||
enrollment = Enrollment(
|
enrollment = Enrollment(
|
||||||
id=uuid4(),
|
id=uuid4(),
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
from os import access
|
|
||||||
|
|
||||||
from aws_lambda_powertools import Logger
|
from aws_lambda_powertools import Logger
|
||||||
from aws_lambda_powertools.utilities.data_classes import (
|
from aws_lambda_powertools.utilities.data_classes import (
|
||||||
EventBridgeEvent,
|
EventBridgeEvent,
|
||||||
event_source,
|
event_source,
|
||||||
)
|
)
|
||||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||||
from glom import glom
|
|
||||||
from layercake.dateutils import now, ttl
|
from layercake.dateutils import now, ttl
|
||||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey
|
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey
|
||||||
|
|
||||||
@@ -26,14 +23,15 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
|||||||
now_ = now()
|
now_ = now()
|
||||||
enrollment_id = new_image['id']
|
enrollment_id = new_image['id']
|
||||||
user = new_image['user']
|
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`,
|
# Post-migration: after removing the stopgap file `patch_course_metadata.py`,
|
||||||
# use `access_expires_at` from `new_image`
|
# use `access_expires_at` from `new_image`
|
||||||
access_period = int(
|
access_period = int(
|
||||||
dyn.collection.get_item(
|
dyn.collection.get_item(
|
||||||
KeyPair(
|
KeyPair(
|
||||||
pk=enrollment_id,
|
pk=new_image['course']['id'],
|
||||||
sk=SortKey('METADATA#COURSE', path_spec='access_period'),
|
sk=SortKey('0', path_spec='access_period'),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -51,15 +51,6 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
|||||||
'created_at': now_,
|
'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:
|
except Exception as exc:
|
||||||
logger.exception(exc)
|
logger.exception(exc)
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ Globals:
|
|||||||
Architectures:
|
Architectures:
|
||||||
- x86_64
|
- x86_64
|
||||||
Layers:
|
Layers:
|
||||||
- !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:96
|
- !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:97
|
||||||
Environment:
|
Environment:
|
||||||
Variables:
|
Variables:
|
||||||
TZ: America/Sao_Paulo
|
TZ: America/Sao_Paulo
|
||||||
@@ -314,6 +314,8 @@ Resources:
|
|||||||
Policies:
|
Policies:
|
||||||
- DynamoDBCrudPolicy:
|
- DynamoDBCrudPolicy:
|
||||||
TableName: !Ref EnrollmentTable
|
TableName: !Ref EnrollmentTable
|
||||||
|
- DynamoDBReadPolicy:
|
||||||
|
TableName: !Ref CourseTable
|
||||||
- S3WritePolicy:
|
- S3WritePolicy:
|
||||||
BucketName: !Ref BucketName
|
BucketName: !Ref BucketName
|
||||||
Events:
|
Events:
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ def test_enroll(
|
|||||||
TransactKey('47ZxxcVBjvhDS5TE98tpfQ')
|
TransactKey('47ZxxcVBjvhDS5TE98tpfQ')
|
||||||
+ SortKey('0')
|
+ SortKey('0')
|
||||||
+ SortKey('METADATA#DEDUPLICATION_WINDOW')
|
+ SortKey('METADATA#DEDUPLICATION_WINDOW')
|
||||||
+ SortKey('METADATA#COURSE')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert 'METADATA#COURSE' in result
|
|
||||||
assert 'METADATA#DEDUPLICATION_WINDOW' in result
|
assert 'METADATA#DEDUPLICATION_WINDOW' in result
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ def test_issue_cert(
|
|||||||
'name': 'Jimi Hendrix',
|
'name': 'Jimi Hendrix',
|
||||||
'cpf': '74630003037',
|
'cpf': '74630003037',
|
||||||
},
|
},
|
||||||
|
'course': {
|
||||||
|
'id': '123',
|
||||||
|
'name': 'pytest',
|
||||||
|
},
|
||||||
'score': 79,
|
'score': 79,
|
||||||
'status': 'COMPLETED',
|
'status': 'COMPLETED',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,4 +33,4 @@ def test_schedule_reminders(
|
|||||||
r = dynamodb_persistence_layer.collection.query(
|
r = dynamodb_persistence_layer.collection.query(
|
||||||
PartitionKey('14682b79-3df2-4351-9229-8b558af046a0')
|
PartitionKey('14682b79-3df2-4351-9229-8b558af046a0')
|
||||||
)
|
)
|
||||||
assert len(r['items']) == 4
|
assert len(r['items']) == 3
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
{"id": "cpYSbBcie2NDbZhDKCxCih", "sk": "generated_items"}
|
{"id": "cpYSbBcie2NDbZhDKCxCih", "sk": "generated_items"}
|
||||||
|
|
||||||
// Course
|
// 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": "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": "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}
|
{"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
|
// Enrollment
|
||||||
{"id": "6437a282-6fe8-4e4d-9eb0-da1007238007", "sk": "0", "status": "IN_PROGRESS", "progress": 10}
|
{"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": "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": "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": "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": "STARTED", "started_at": "2025-08-24T01:44:42.703012-03:06"}
|
{"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": "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": "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#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": "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"}
|
{"id": "294e9864-8284-4287-b153-927b15d90900", "sk": "tenant", "org_id": "123", "name": "EDUSEG", "create_date": "2025-09-12T17:11:00.556907-03:00"}
|
||||||
|
|
||||||
|
|||||||
13
enrollments-events/uv.lock
generated
13
enrollments-events/uv.lock
generated
@@ -501,7 +501,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "layercake"
|
name = "layercake"
|
||||||
version = "0.9.14"
|
version = "0.10.0"
|
||||||
source = { directory = "../layercake" }
|
source = { directory = "../layercake" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "arnparse" },
|
{ name = "arnparse" },
|
||||||
@@ -518,6 +518,7 @@ dependencies = [
|
|||||||
{ name = "pycpfcnpj" },
|
{ name = "pycpfcnpj" },
|
||||||
{ name = "pydantic", extra = ["email"] },
|
{ name = "pydantic", extra = ["email"] },
|
||||||
{ name = "pydantic-extra-types" },
|
{ name = "pydantic-extra-types" },
|
||||||
|
{ name = "python-multipart" },
|
||||||
{ name = "pytz" },
|
{ name = "pytz" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
{ name = "smart-open", extra = ["s3"] },
|
{ name = "smart-open", extra = ["s3"] },
|
||||||
@@ -541,6 +542,7 @@ requires-dist = [
|
|||||||
{ name = "pycpfcnpj", specifier = ">=1.8" },
|
{ name = "pycpfcnpj", specifier = ">=1.8" },
|
||||||
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
|
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
|
||||||
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
|
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
|
||||||
|
{ name = "python-multipart", specifier = ">=0.0.20" },
|
||||||
{ name = "pytz", specifier = ">=2025.1" },
|
{ name = "pytz", specifier = ">=2025.1" },
|
||||||
{ name = "requests", specifier = ">=2.32.3" },
|
{ name = "requests", specifier = ">=2.32.3" },
|
||||||
{ name = "smart-open", extras = ["s3"], specifier = ">=7.1.0" },
|
{ 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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "pytz"
|
name = "pytz"
|
||||||
version = "2025.2"
|
version = "2025.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user