fix billing

This commit is contained in:
2025-08-27 22:17:33 -03:00
parent b3a4b48fe5
commit 83c97f409b
12 changed files with 129 additions and 45 deletions

View File

@@ -30,9 +30,9 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
item={ item={
'id': enrollment_id, 'id': enrollment_id,
'sk': 'SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS', 'sk': 'SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS',
'name': user.name, 'name': user['name'],
'email': user.email, 'email': user['email'],
'course': course.name, 'course': course['name'],
'created_at': now_, 'created_at': now_,
'ttl': ttl(days=3, start_dt=now_), 'ttl': ttl(days=3, start_dt=now_),
}, },
@@ -56,9 +56,9 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
item={ item={
'id': enrollment_id, 'id': enrollment_id,
'sk': 'SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS', 'sk': 'SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS',
'name': user.name, 'name': user['name'],
'email': user.email, 'email': user['email'],
'course': course.name, 'course': course['name'],
'created_at': now_, 'created_at': now_,
'ttl': ttl(start_dt=now_, days=course.access_period - 30), 'ttl': ttl(start_dt=now_, days=course.access_period - 30),
}, },

View File

@@ -39,7 +39,7 @@ def get_enrollments():
tenant: Tenant = router.context['tenant'] tenant: Tenant = router.context['tenant']
event = router.current_event event = router.current_event
query = parse.unquote(event.get_query_string_value('q', '')) query = parse.unquote(event.get_query_string_value('q', ''))
sort = event.get_query_string_value('sort', 'create_date:desc') sort = event.get_query_string_value('sort', 'created_at:desc')
page = int(event.get_query_string_value('page', '1')) page = int(event.get_query_string_value('page', '1'))
hits_per_page = int(event.get_query_string_value('hitsPerPage', '25')) hits_per_page = int(event.get_query_string_value('hitsPerPage', '25'))
filter_ = meili.parse(event.get_query_string_value('filter', '')) filter_ = meili.parse(event.get_query_string_value('filter', ''))
@@ -70,15 +70,14 @@ def get_enrollment(id: str):
return enrollment_layer.collection.get_items( return enrollment_layer.collection.get_items(
TransactKey(id) TransactKey(id)
+ SortKey('0') + SortKey('0')
+ SortKey('started_date') + SortKey('STARTED', rename_key='started_at', path_spec='started_at')
+ SortKey('finished_date') + SortKey('COMPLETED', rename_key='completed_at', path_spec='completed_at')
+ SortKey('failed_date') + SortKey('FAILED', rename_key='failed_at', path_spec='failed_at')
+ SortKey('canceled_date') + SortKey('CANCELED', rename_key='canceled')
+ SortKey('archived_date') + SortKey('ARCHIVED', rename_key='archived_at', path_spec='archived_at')
+ SortKey('cancel_policy') + SortKey('CANCEL_POLICY', rename_key='cancel_policy')
+ SortKey('LOCK', rename_key='lock')
+ SortKey('parent_vacancy', path_spec='vacancy') + SortKey('parent_vacancy', path_spec='vacancy')
+ SortKey('lock')
+ SortKey('author') + SortKey('author')
+ SortKey('tenant') + SortKey('tenant')
+ SortKey('cert')
) )

View File

@@ -41,7 +41,7 @@ def cancel(id: str, payload: Cancel):
set_status_as_canceled( set_status_as_canceled(
id, id,
lock_hash=payload.lock_hash, lock_hash=payload.lock_hash,
author={ created_by={
'id': user.id, 'id': user.id,
'name': user.name, 'name': user.name,
}, },

View File

@@ -17,7 +17,10 @@ def lookup(username: str):
r = meili_client.index(USER_TABLE).search(f'"{username}"') r = meili_client.index(USER_TABLE).search(f'"{username}"')
if user := glom(r, 'hits.0', default=None): if user := glom(r, 'hits.0', default=None):
return pick(('id', 'name', 'email', 'cognito__sub'), user) cognito_sub = user.get('cognito:sub')
return {'cognito__sub': cognito_sub} | pick(
('id', 'name', 'email', 'cognito__sub'), user
)
return JSONResponse( return JSONResponse(
body={'message': 'User not found.'}, body={'message': 'User not found.'},

View File

@@ -211,7 +211,7 @@ def set_status_as_canceled(
id: str, id: str,
*, *,
lock_hash: str | None = None, lock_hash: str | None = None,
author: Author, created_by: Author,
course: Course | None = None, course: Course | None = None,
vacancy_key: KeyPair | None = None, vacancy_key: KeyPair | None = None,
persistence_layer: DynamoDBPersistenceLayer, persistence_layer: DynamoDBPersistenceLayer,
@@ -235,25 +235,30 @@ def set_status_as_canceled(
transact.put( transact.put(
item={ item={
'id': id, 'id': id,
'sk': 'canceled', 'sk': 'CANCELED',
'author': author, 'canceled_by': created_by,
'canceled_at': now_, 'canceled_at': now_,
}, },
) )
transact.delete( transact.delete(
key=KeyPair(id, 'cancel_policy'), key=KeyPair(id, 'CANCEL_POLICY'),
cond_expr='attribute_exists(sk)', cond_expr='attribute_exists(sk)',
) )
# Remove schedules lifecycle events, referencies and locks # Remove schedules lifecycle events, referencies and locks
transact.delete(key=KeyPair(id, LifecycleEvents.ARCHIVE_IT)) transact.delete(key=KeyPair(id, 'SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS'))
transact.delete(key=KeyPair(id, LifecycleEvents.NO_ACTIVITY)) transact.delete(key=KeyPair(id, 'SCHEDULE#REMINDER_NO_ACTIVITY_AFTER_7_DAYS'))
transact.delete(key=KeyPair(id, LifecycleEvents.ACCESS_PERIOD_ENDS)) transact.delete(
transact.delete(key=KeyPair(id, LifecycleEvents.DOES_NOT_ACCESS)) key=KeyPair(id, 'SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS')
)
transact.delete(
key=KeyPair(id, 'SCHEDULE#REMINDER_CERT_EXPIRATION_BEFORE_30_DAYS')
)
transact.delete(key=KeyPair(id, 'SCHEDULE#SET_AS_EXPIRED'))
transact.delete(key=KeyPair(id, 'parent_vacancy')) transact.delete(key=KeyPair(id, 'parent_vacancy'))
if lock_hash: if lock_hash:
transact.delete(key=KeyPair(id, 'lock')) transact.delete(key=KeyPair(id, 'LOCK'))
transact.delete(key=KeyPair('lock', lock_hash)) transact.delete(key=KeyPair('LOCK', lock_hash))
if vacancy_key and course: if vacancy_key and course:
vacancy_pk, vacancy_sk = vacancy_key.values() vacancy_pk, vacancy_sk = vacancy_key.values()

View File

@@ -42,7 +42,7 @@ if __name__ == '__main__':
with sqlite3.connect('mydatabase.db') as conn: with sqlite3.connect('mydatabase.db') as conn:
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute( cursor.execute(
'CREATE TABLE IF NOT EXISTS %s (id TEXT, sk TEXT, json JSON)' 'CREATE TABLE IF NOT EXISTS %s (pk TEXT, sk TEXT, json JSON)'
% table_name % table_name
) )
@@ -51,10 +51,10 @@ if __name__ == '__main__':
desc=f'⏳ Inserting into table {table_name}', desc=f'⏳ Inserting into table {table_name}',
): ):
cursor.execute( cursor.execute(
'INSERT INTO %s (id, sk, json) VALUES (:id, :sk, :json)' 'INSERT INTO %s (pk, sk, json) VALUES (:pk, :sk, :json)'
% table_name, % table_name,
{ {
'id': record['id'], 'pk': record['id'],
'sk': record['sk'], 'sk': record['sk'],
'json': record, 'json': record,
}, },

View File

@@ -26,7 +26,7 @@ Globals:
Architectures: Architectures:
- x86_64 - x86_64
Layers: Layers:
- !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:86 - !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:96
Environment: Environment:
Variables: Variables:
TZ: America/Sao_Paulo TZ: America/Sao_Paulo

48
http-api/uv.lock generated
View File

@@ -506,7 +506,7 @@ wheels = [
[[package]] [[package]]
name = "layercake" name = "layercake"
version = "0.9.12" version = "0.9.14"
source = { directory = "../layercake" } source = { directory = "../layercake" }
dependencies = [ dependencies = [
{ name = "arnparse" }, { name = "arnparse" },
@@ -518,6 +518,7 @@ dependencies = [
{ name = "meilisearch" }, { name = "meilisearch" },
{ name = "orjson" }, { name = "orjson" },
{ name = "passlib" }, { name = "passlib" },
{ name = "psycopg", extra = ["binary"] },
{ name = "pycpfcnpj" }, { name = "pycpfcnpj" },
{ name = "pydantic", extra = ["email"] }, { name = "pydantic", extra = ["email"] },
{ name = "pydantic-extra-types" }, { name = "pydantic-extra-types" },
@@ -540,6 +541,7 @@ requires-dist = [
{ name = "meilisearch", specifier = ">=0.34.0" }, { name = "meilisearch", specifier = ">=0.34.0" },
{ name = "orjson", specifier = ">=3.10.15" }, { name = "orjson", specifier = ">=3.10.15" },
{ name = "passlib", specifier = ">=1.7.4" }, { name = "passlib", specifier = ">=1.7.4" },
{ name = "psycopg", extras = ["binary"], specifier = ">=3.2.9" },
{ 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" },
@@ -696,6 +698,41 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce", size = 49567, upload-time = "2018-02-15T19:01:27.172Z" }, { url = "https://files.pythonhosted.org/packages/a3/58/35da89ee790598a0700ea49b2a66594140f44dec458c07e8e3d4979137fc/ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce", size = 49567, upload-time = "2018-02-15T19:01:27.172Z" },
] ]
[[package]]
name = "psycopg"
version = "3.2.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/27/4a/93a6ab570a8d1a4ad171a1f4256e205ce48d828781312c0bbaff36380ecb/psycopg-3.2.9.tar.gz", hash = "sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700", size = 158122, upload-time = "2025-05-13T16:11:15.533Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/44/b0/a73c195a56eb6b92e937a5ca58521a5c3346fb233345adc80fd3e2f542e2/psycopg-3.2.9-py3-none-any.whl", hash = "sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6", size = 202705, upload-time = "2025-05-13T16:06:26.584Z" },
]
[package.optional-dependencies]
binary = [
{ name = "psycopg-binary", marker = "implementation_name != 'pypy'" },
]
[[package]]
name = "psycopg-binary"
version = "3.2.9"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/28/0b/f61ff4e9f23396aca674ed4d5c9a5b7323738021d5d72d36d8b865b3deaf/psycopg_binary-3.2.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:98bbe35b5ad24a782c7bf267596638d78aa0e87abc7837bdac5b2a2ab954179e", size = 4017127, upload-time = "2025-05-13T16:08:21.391Z" },
{ url = "https://files.pythonhosted.org/packages/bc/00/7e181fb1179fbfc24493738b61efd0453d4b70a0c4b12728e2b82db355fd/psycopg_binary-3.2.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:72691a1615ebb42da8b636c5ca9f2b71f266be9e172f66209a361c175b7842c5", size = 4080322, upload-time = "2025-05-13T16:08:24.049Z" },
{ url = "https://files.pythonhosted.org/packages/58/fd/94fc267c1d1392c4211e54ccb943be96ea4032e761573cf1047951887494/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ab464bfba8c401f5536d5aa95f0ca1dd8257b5202eede04019b4415f491351", size = 4655097, upload-time = "2025-05-13T16:08:27.376Z" },
{ url = "https://files.pythonhosted.org/packages/41/17/31b3acf43de0b2ba83eac5878ff0dea5a608ca2a5c5dd48067999503a9de/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8aeefebe752f46e3c4b769e53f1d4ad71208fe1150975ef7662c22cca80fab", size = 4482114, upload-time = "2025-05-13T16:08:30.781Z" },
{ url = "https://files.pythonhosted.org/packages/85/78/b4d75e5fd5a85e17f2beb977abbba3389d11a4536b116205846b0e1cf744/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7e4e4dd177a8665c9ce86bc9caae2ab3aa9360b7ce7ec01827ea1baea9ff748", size = 4737693, upload-time = "2025-05-13T16:08:34.625Z" },
{ url = "https://files.pythonhosted.org/packages/3b/95/7325a8550e3388b00b5e54f4ced5e7346b531eb4573bf054c3dbbfdc14fe/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fc2915949e5c1ea27a851f7a472a7da7d0a40d679f0a31e42f1022f3c562e87", size = 4437423, upload-time = "2025-05-13T16:08:37.444Z" },
{ url = "https://files.pythonhosted.org/packages/1a/db/cef77d08e59910d483df4ee6da8af51c03bb597f500f1fe818f0f3b925d3/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a1fa38a4687b14f517f049477178093c39c2a10fdcced21116f47c017516498f", size = 3758667, upload-time = "2025-05-13T16:08:40.116Z" },
{ url = "https://files.pythonhosted.org/packages/95/3e/252fcbffb47189aa84d723b54682e1bb6d05c8875fa50ce1ada914ae6e28/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5be8292d07a3ab828dc95b5ee6b69ca0a5b2e579a577b39671f4f5b47116dfd2", size = 3320576, upload-time = "2025-05-13T16:08:43.243Z" },
{ url = "https://files.pythonhosted.org/packages/1c/cd/9b5583936515d085a1bec32b45289ceb53b80d9ce1cea0fef4c782dc41a7/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:778588ca9897b6c6bab39b0d3034efff4c5438f5e3bd52fda3914175498202f9", size = 3411439, upload-time = "2025-05-13T16:08:47.321Z" },
{ url = "https://files.pythonhosted.org/packages/45/6b/6f1164ea1634c87956cdb6db759e0b8c5827f989ee3cdff0f5c70e8331f2/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f0d5b3af045a187aedbd7ed5fc513bd933a97aaff78e61c3745b330792c4345b", size = 3477477, upload-time = "2025-05-13T16:08:51.166Z" },
{ url = "https://files.pythonhosted.org/packages/7b/1d/bf54cfec79377929da600c16114f0da77a5f1670f45e0c3af9fcd36879bc/psycopg_binary-3.2.9-cp313-cp313-win_amd64.whl", hash = "sha256:2290bc146a1b6a9730350f695e8b670e1d1feb8446597bed0bbe7c3c30e0abcb", size = 2928009, upload-time = "2025-05-13T16:08:53.67Z" },
]
[[package]] [[package]]
name = "psycopg2-binary" name = "psycopg2-binary"
version = "2.9.10" version = "2.9.10"
@@ -1035,6 +1072,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5", size = 45683, upload-time = "2025-03-26T03:49:40.35Z" }, { url = "https://files.pythonhosted.org/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5", size = 45683, upload-time = "2025-03-26T03:49:40.35Z" },
] ]
[[package]]
name = "tzdata"
version = "2025.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" },
]
[[package]] [[package]]
name = "unidecode" name = "unidecode"
version = "1.4.0" version = "1.4.0"

View File

@@ -31,6 +31,8 @@ def postback():
progress = round(Decimal(json_body['ANDAMENTO']), 2) progress = round(Decimal(json_body['ANDAMENTO']), 2)
enrollment_id = dyn.collection.get_item( enrollment_id = dyn.collection.get_item(
KeyPair( KeyPair(
# Post-migration: uncomment the following line
# pk='KONVIVA',
pk='konviva', pk='konviva',
sk=SortKey(json_body['ID_MATRICULA'], path_spec='enrollment_id'), sk=SortKey(json_body['ID_MATRICULA'], path_spec='enrollment_id'),
), ),
@@ -41,7 +43,7 @@ def postback():
update_progress(enrollment_id, progress, dynamodb_persistence_layer=dyn) update_progress(enrollment_id, progress, dynamodb_persistence_layer=dyn)
if status == 'COMPLETED': if status == 'COMPLETED':
set_score(enrollment_id, score, dynamodb_persistence_layer=dyn) set_score(enrollment_id, score, progress, dynamodb_persistence_layer=dyn)
return Response(status_code=HTTPStatus.NO_CONTENT) return Response(status_code=HTTPStatus.NO_CONTENT)

View File

@@ -86,21 +86,27 @@ def update_progress(
} }
) )
# Remove reminders and policies that no longer apply # Remove reminders and policies that no longer apply
transact.delete(
key=KeyPair(
pk=id,
sk='CANCEL_POLICY',
),
)
transact.delete( transact.delete(
key=KeyPair( key=KeyPair(
pk=id, pk=id,
sk='SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS', sk='SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS',
) )
) )
transact.delete(
key=KeyPair(pk=id, sk='CANCEL_POLICY'),
)
return True return True
def set_score( def set_score(
id: str, id: str,
/,
score: Decimal, score: Decimal,
progress: Decimal,
*, *,
dynamodb_persistence_layer: DynamoDBPersistenceLayer, dynamodb_persistence_layer: DynamoDBPersistenceLayer,
): ):
@@ -138,6 +144,7 @@ def set_score(
return _set_status_as_completed( return _set_status_as_completed(
id, id,
score, score,
progress=progress,
user_id=user_id, user_id=user_id,
course_id=course_id, course_id=course_id,
cert_exp_interval=int( cert_exp_interval=int(
@@ -161,6 +168,7 @@ def set_score(
return _set_status_as_failed( return _set_status_as_failed(
id, id,
score, score,
progress=progress,
user_id=user_id, user_id=user_id,
course_id=course_id, course_id=course_id,
dynamodb_persistence_layer=dynamodb_persistence_layer, dynamodb_persistence_layer=dynamodb_persistence_layer,
@@ -174,6 +182,7 @@ def _set_status_as_completed(
id: str, id: str,
/, /,
score: Decimal, score: Decimal,
progress: Decimal,
*, *,
user_id: str, user_id: str,
course_id: str, course_id: str,
@@ -199,8 +208,8 @@ def _set_status_as_completed(
with dynamodb_persistence_layer.transact_writer() as transact: with dynamodb_persistence_layer.transact_writer() as transact:
transact.update( transact.update(
key=KeyPair(pk=id, sk='0'), key=KeyPair(pk=id, sk='0'),
update_expr='SET #status = :completed, score = :score, \ update_expr='SET #status = :completed, progress = :progress, \
updated_at = :updated_at', score = :score, updated_at = :updated_at',
cond_expr='#status = :in_progress', cond_expr='#status = :in_progress',
expr_attr_names={'#status': 'status'}, expr_attr_names={'#status': 'status'},
expr_attr_values={ expr_attr_values={
@@ -208,6 +217,7 @@ def _set_status_as_completed(
':in_progress': 'IN_PROGRESS', ':in_progress': 'IN_PROGRESS',
':score': score, ':score': score,
':updated_at': now_, ':updated_at': now_,
':progress': progress,
}, },
exc_cls=EnrollmentConflictError, exc_cls=EnrollmentConflictError,
) )
@@ -242,6 +252,7 @@ def _set_status_as_completed(
'id': id, 'id': id,
'sk': 'LOCK', 'sk': 'LOCK',
'ttl': deduplication_lock_ttl, 'ttl': deduplication_lock_ttl,
'hash': lock_hash,
'created_at': now_, 'created_at': now_,
} }
) )
@@ -256,6 +267,12 @@ def _set_status_as_completed(
) )
# Remove reminders and policies that no longer apply # Remove reminders and policies that no longer apply
transact.delete(
key=KeyPair(
pk=id,
sk='CANCEL_POLICY',
)
)
transact.delete( transact.delete(
key=KeyPair( key=KeyPair(
pk=id, pk=id,
@@ -282,6 +299,7 @@ def _set_status_as_failed(
id: str, id: str,
/, /,
score: Decimal, score: Decimal,
progress: Decimal,
user_id: str, user_id: str,
course_id: str, course_id: str,
*, *,
@@ -293,14 +311,15 @@ def _set_status_as_failed(
with dynamodb_persistence_layer.transact_writer() as transact: with dynamodb_persistence_layer.transact_writer() as transact:
transact.update( transact.update(
key=KeyPair(pk=id, sk='0'), key=KeyPair(pk=id, sk='0'),
update_expr='SET #status = :failed, score = :score, \ update_expr='SET #status = :failed, progress = :progress, \
updated_at = :updated_at', score = :score, updated_at = :updated_at',
cond_expr='#status = :in_progress', cond_expr='#status = :in_progress',
expr_attr_names={'#status': 'status'}, expr_attr_names={'#status': 'status'},
expr_attr_values={ expr_attr_values={
':failed': 'FAILED', ':failed': 'FAILED',
':in_progress': 'IN_PROGRESS', ':in_progress': 'IN_PROGRESS',
':score': score, ':score': score,
':progress': progress,
':updated_at': now_, ':updated_at': now_,
}, },
exc_cls=EnrollmentConflictError, exc_cls=EnrollmentConflictError,
@@ -314,6 +333,12 @@ def _set_status_as_failed(
cond_expr='attribute_not_exists(sk)', cond_expr='attribute_not_exists(sk)',
) )
# Remove reminders and events that no longer apply # Remove reminders and events that no longer apply
transact.delete(
key=KeyPair(
pk=id,
sk='CANCEL_POLICY',
)
)
transact.delete( transact.delete(
key=KeyPair( key=KeyPair(
pk=id, pk=id,
@@ -413,6 +438,12 @@ def _set_status_as_expired(
cond_expr='attribute_not_exists(sk)', cond_expr='attribute_not_exists(sk)',
) )
# Remove events and policies that no longer apply # Remove events and policies that no longer apply
transact.delete(
key=KeyPair(
pk=id,
sk='CANCEL_POLICY',
),
)
transact.delete( transact.delete(
key=KeyPair( key=KeyPair(
pk=id, pk=id,

View File

@@ -59,7 +59,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
if old_course: if old_course:
enrollment['course'] = old_course enrollment['course'] = old_course
created_at: datetime = fromisoformat(enrollment['create_date']) # type: ignore created_at: datetime = fromisoformat(enrollment['created_at']) # type: ignore
start_date, end_date = get_billing_period( start_date, end_date = get_billing_period(
billing_day=new_image['billing_day'], billing_day=new_image['billing_day'],
date_=created_at, date_=created_at,
@@ -122,9 +122,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
'user': pick(('id', 'name'), enrollment['user']), 'user': pick(('id', 'name'), enrollment['user']),
'course': pick(('id', 'name'), enrollment['course']), 'course': pick(('id', 'name'), enrollment['course']),
'unit_price': course['unit_price'], 'unit_price': course['unit_price'],
# Post-migration: uncomment the following line 'enrolled_at': enrollment['created_at'],
# 'enrolled_at': enrollment['created_at'],
'enrolled_at': enrollment['create_date'],
'created_at': now_, 'created_at': now_,
} }
# Add author if present # Add author if present

View File

@@ -33,7 +33,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
+ SortKey('canceled', path_spec='author', rename_key='canceled_by') + SortKey('canceled', path_spec='author', rename_key='canceled_by')
) )
created_at: datetime = fromisoformat(new_image['create_date']) # type: ignore created_at: datetime = fromisoformat(new_image['created_at']) # type: ignore
start_date, end_date = get_billing_period( start_date, end_date = get_billing_period(
billing_day=int(subscription['billing_day']), billing_day=int(subscription['billing_day']),
date_=created_at, date_=created_at,