diff --git a/README.md b/README.md
index bcbc3f6..65b776c 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ O gestor responsável pela ação também é relacionado à compra, com base no
```json
{"id": "101", "sk": "0", "name": "EDUSEG", "cnpj": "15608435000190", "org_id": "100"}
-{"id": "101", "sk": "AUTHOR", "name": "Sérgio", "email": "sergio@somosbeta.com.br", "user_id": "123"}
+{"id": "101", "sk": "CREATED_BY", "name": "Sérgio", "email": "sergio@somosbeta.com.br", "user_id": "123"}
{"id": "101", "sk": "SLOT", "status": "PENDING", "mode": "BATCH"}
{"id": "101", "sk": "SLOT#ENROLLMENT#9omWNKymwU5U4aeun6mWzZ", "status": "SUCCESS"}
{"id": "101", "sk": "SLOT#ENROLLMENT#12", "status": "ROLLBACK"}
@@ -37,8 +37,9 @@ Quando o responsável é uma pessoa física (CPF).
```json
{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "0", "course": {"id": "10", "name": "pytest"}, "org_id": "100"}
{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "METADATA#COURSE", "access_period": 360, "cert": {"exp_interval": 365}}
-{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "OWNER_ORG", "org_id": "100", "name": "EDUSEG"}
-{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "AUTHOR", "user_id": "202", "name": "Tiago Maciel"}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "METADATA#DEDUPLICATION_WINDOW", "offset_days": 90}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "ORG", "org_id": "100", "name": "EDUSEG"}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "CREATED_BY", "user_id": "202", "name": "Tiago Maciel"}
{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "KONVIVA", "user_id": 122, "class_id": 123, "enrollment_id": 1239}
{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "STARTED", "started_at": "2025-04-06T11:07:32.762178-03:00"}
{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "COMPLETED", "completed_at": "2025-04-06T11:07:32.762178-03:00"}
@@ -47,6 +48,11 @@ Quando o responsável é uma pessoa física (CPF).
{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "ARCHIVED", "archived_at": "2025-04-06T11:07:32.762178-03:00"}
{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "EXPIRED", "expired_at": "2025-04-06T11:07:32.762178-03:00"}
{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "LINKED_ENTITIES#ORDER", "order_id": "101"}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "LOCK", "hash": "1e67e29464877783e49e07fb7d9dd372", "ttl": 1874507093}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "SCHEDUE#REMINDER_NO_ACCESS_AFTER_3_DAYS", "ttl": 1874507093}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "SCHEDUE#REMINDER_NO_ACTIVITY_AFTER_7_DAYS", "ttl": 1874507093}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "SCHEDUE#REMINDER_ACCESS_PERIOD_BEFORE_15_DAYS", "ttl": 1874507093}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "SCHEDUE#REMINDER_CERT_EXPIRATION_BEFORE_30_DAYS", "ttl": 1874507093}
```
### Vagas disponíveis
@@ -62,21 +68,21 @@ Quando uma matrícula é criada, também é agendados emails/eventos.
- `REMINDER_NO_ACCESS_AFTER_3_DAYS` se o usuário não acessar o curso 3 dias após a criação.
- `REMINDER_NO_ACTIVITY_AFTER_7_DAYS` 7 dias após a última atividade do usuário no curso.
-- `REMINDER_ACCESS_PERIOD_BEFORE_15_DAYS` 30 dias antes do período de acesso ao curso terminar.
+- `REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS` 30 dias antes do período de acesso ao curso terminar.
- `REMINDER_CERT_EXPIRATION_BEFORE_30_DAYS` se houver certificado, avisa 30 dias antes do certificado expirar.
- `SET_AS_ARCHIVED` após o certificado expirar, a matrícula será marcada como **arquivada (ARCHIVED)**.
- `SET_AS_EXPIRED` se não houver certificado e o período de acesso for atingido, a matrícula será marcada com **expirada (EXPIRED)**.
```json
-{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "SCHEDULES#REMINDER_NO_ACCESS_3_DAYS", "name": "Sérgio R Siqueira", "email": "osergiosiqueira@gmail.com", "ttl": 1874507093}
-{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "SCHEDULES#COURSE_EXPIRED", "name": "Sérgio R Siqueira", "email": "osergiosiqueira@gmail.com", "ttl": 1874507093}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "SCHEDULE#REMINDER_NO_ACCESS_3_DAYS", "name": "Sérgio R Siqueira", "email": "osergiosiqueira@gmail.com", "ttl": 1874507093}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "SCHEDULE#SET_AS_EXPIRED", "name": "Sérgio R Siqueira", "email": "osergiosiqueira@gmail.com", "ttl": 1874507093}
```
-Quando o status da matrícula for alterado para `COMPLETED`, os eventos `COURSE_EXPIRED` e `ACCESS_PERIOD_REMINDER_30_DAYS` serão removidos a adicionado o evento `COURSE_ARCHIVED`.
+Quando o status da matrícula for alterado para `COMPLETED`, os eventos `SET_AS_EXPIRED` e `REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS` serão _removidos_ a adicionado o evento `SET_AS_ARCHIVED`.
```json
-{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "SCHEDULES#REMINDER_NO_ACCESS_3_DAYS", "name": "Sérgio R Siqueira", "email": "osergiosiqueira@gmail.com", "ttl": 1874507093}
-{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "SCHEDULES#COURSE_EXPIRED", "name": "Sérgio R Siqueira", "email": "osergiosiqueira@gmail.com", "ttl": 1874507093}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "0", ...}
+{"id": "9omWNKymwU5U4aeun6mWzZ", "sk": "SCHEDULE#SET_AS_ARCHIVED", "ttl": 1874507093}
```
### Proteção contra duplicação
diff --git a/enrollments-events/app/enrollment.py b/enrollments-events/app/enrollment.py
index 513b07f..f4d4017 100644
--- a/enrollments-events/app/enrollment.py
+++ b/enrollments-events/app/enrollment.py
@@ -1,6 +1,4 @@
from dataclasses import asdict, dataclass
-from datetime import timedelta
-from enum import Enum
from typing import Self, TypedDict
from layercake.dateutils import now, ttl
@@ -36,35 +34,6 @@ class Slot:
return LinkedEntity(idx, 'ORDER')
-class LifecycleEvents(str, Enum):
- """Lifecycle events related to scheduling actions."""
-
- # Reminder if the user does not access within 3 days
- # REMINDER_NO_ACCESS_AFTER_3_DAYS = 'SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS'
- DOES_NOT_ACCESS = 'schedules#does_not_access'
-
- # When there is no activity 7 days after the first access
- # REMINDER_NO_ACTIVITY_AFTER_7_DAYS = 'SCHEDULE#REMINDER_NO_ACTIVITY_AFTER_7_DAYS'
- NO_ACTIVITY = 'schedules#no_activity'
-
- # Reminder 30 days before the access period expires
- # REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS = 'SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS'
- ACCESS_PERIOD_ENDS = 'schedules#access_period_ends'
-
- # Reminder for certificate expiration set to 30 days from now
- REMINDER_CERT_EXPIRATION_BEFORE_30_DAYS = (
- 'SCHEDULE#REMINDER_CERT_EXPIRATION_BEFORE_30_DAYS'
- )
-
- # Archive the course after the certificate expires
- # SET_AS_ARCHIVE = 'SCHEDULE#SET_AS_ARCHIVE'
- ARCHIVE_IT = 'schedules#archive_it'
-
- # When the access period ends for a course without a certificate
- # SET_AS_EXPIRE = 'SCHEDULE#SET_AS_EXPIRE'
- EXPIRATION = 'schedules#expiration'
-
-
class DeduplicationConflictError(Exception):
def __init__(self, *args):
super().__init__('Enrollment already exists')
@@ -105,53 +74,11 @@ def enroll(
transact.put(
item={
'id': enrollment.id,
- 'sk': 'metadata#course',
+ 'sk': 'METADATA#COURSE',
'created_at': now_,
**course.model_dump(include={'cert', 'access_period'}),
}
)
- transact.put(
- item={
- 'id': enrollment.id,
- # Post-migration: uncomment the following line
- # 'sk': LifecycleEvents.REMINDER_NO_ACCESS_3_DAYS,
- 'sk': LifecycleEvents.DOES_NOT_ACCESS,
- 'name': user.name,
- 'email': user.email,
- 'course': course.name,
- 'created_at': now_,
- 'ttl': ttl(days=3, start_dt=now_),
- },
- )
- # Enrollment expires by default when the access period ends.
- # When the course is completed, it is automatically removed,
- # and the `SCHEDULE#SET_AS_ARCHIVE` event is created.
- transact.put(
- item={
- 'id': enrollment.id,
- 'sk': LifecycleEvents.EXPIRATION,
- # Post-migration: uncomment the following line
- # 'sk': LifecycleEvents.COURSE_EXPIRED,
- 'name': user.name,
- 'email': user.email,
- 'course': course.name,
- 'created_at': now_,
- 'ttl': ttl(start_dt=now_ + timedelta(days=course.access_period)),
- },
- )
- transact.put(
- item={
- 'id': enrollment.id,
- # Post-migration: uncomment the following line
- # 'sk': LifecycleEvents.ACCESS_PERIOD_REMINDER_30_DAYS,
- 'sk': LifecycleEvents.ACCESS_PERIOD_ENDS,
- 'name': user.name,
- 'email': user.email,
- 'course': course.name,
- 'created_at': now_,
- 'ttl': ttl(start_dt=now_ + timedelta(days=course.access_period - 30)),
- },
- )
for entity in linked_entities:
keyprefix = entity.type.lower()
@@ -184,7 +111,7 @@ def enroll(
transact.put(
item={
'id': enrollment.id,
- 'sk': 'cancel_policy',
+ 'sk': 'CANCEL_POLICY',
'created_at': now_,
}
)
@@ -204,16 +131,17 @@ def enroll(
# the deduplication window expires or is removed.
if deduplication_window:
offset_days = deduplication_window['offset_days']
- ttl_expiration = ttl(
- start_dt=now_ + timedelta(days=course.access_period - offset_days)
+ ttl_ = ttl(
+ start_dt=now_,
+ days=course.access_period - offset_days,
)
transact.put(
item={
- 'id': 'lock',
+ 'id': 'LOCK',
'sk': lock_hash,
'enrollment_id': enrollment.id,
'created_at': now_,
- 'ttl': ttl_expiration,
+ 'ttl': ttl_,
},
cond_expr='attribute_not_exists(sk)',
exc_cls=DeduplicationConflictError,
@@ -221,24 +149,24 @@ def enroll(
transact.put(
item={
'id': enrollment.id,
- 'sk': 'lock',
+ 'sk': 'LOCK',
'hash': lock_hash,
'created_at': now_,
- 'ttl': ttl_expiration,
+ 'ttl': ttl_,
},
)
# Deduplication window can be recalculated if needed
transact.put(
item={
'id': enrollment.id,
- 'sk': 'metadata#deduplication_window',
+ 'sk': 'METADATA#DEDUPLICATION_WINDOW',
'offset_days': offset_days,
'created_at': now_,
},
)
else:
transact.condition(
- key=KeyPair('lock', lock_hash),
+ key=KeyPair('LOCK', lock_hash),
cond_expr='attribute_not_exists(sk)',
exc_cls=DeduplicationConflictError,
)
diff --git a/enrollments-events/app/events/emails/email_.py b/enrollments-events/app/events/emails/email_.py
index e5f2562..5838793 100644
--- a/enrollments-events/app/events/emails/email_.py
+++ b/enrollments-events/app/events/emails/email_.py
@@ -32,10 +32,15 @@ def send_email(
emailmsg = Message(
from_=sender,
to=to,
- subject=subject.format(course=truncate_str(context['course'])),
+ subject=subject.format(
+ course=truncate_str(context['course']),
+ ),
)
emailmsg.add_alternative(
- message.format(first_name=first_word(name), course=context['course'])
+ message.format(
+ first_name=first_word(name),
+ course=context['course'],
+ )
)
try:
@@ -46,6 +51,13 @@ def send_email(
},
}
)
+ dynamodb_persistence_layer.put_item(
+ item={
+ 'id': event['id'],
+ 'sk': f'{event["sk"]}#EXECUTED',
+ 'created_at': now_,
+ }
+ )
logger.info('Email sent')
except Exception as exc:
logger.exception(exc)
@@ -59,13 +71,5 @@ def send_email(
)
return False
- else:
- dynamodb_persistence_layer.put_item(
- item={
- 'id': event['id'],
- 'sk': f'{event["sk"]}#EXECUTED',
- 'created_at': now_,
- }
- )
- return True
+ 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 b43d828..121b30e 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
@@ -18,7 +18,7 @@ logger = Logger(__name__)
enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
-SUBJECT = 'Seu acesso ao curso de {course} termina em 30 dias'
+SUBJECT = 'Seu acesso ao curso {course} termina em 30 dias'
MESSAGE = """
Oi {first_name}, tudo bem?
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 63acca9..1fd2b16 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
@@ -18,7 +18,7 @@ logger = Logger(__name__)
enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
-SUBJECT = 'Seu certificado de {course} está prestes a expirar'
+SUBJECT = 'Seu certificado {course} vai expirar em breve'
MESSAGE = """
Oi {first_name}, tudo bem?
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 d96dfdd..093f058 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
@@ -18,7 +18,7 @@ logger = Logger(__name__)
enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
-SUBJECT = 'Seu curso de {course} está esperando por você na EDUSEG®'
+SUBJECT = 'Seu curso {course} está esperando por você na EDUSEG®'
MESSAGE = """
Oi {first_name}, tudo bem?
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 a8c81b1..f240333 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
@@ -18,7 +18,7 @@ logger = Logger(__name__)
enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
-SUBJECT = 'Seu curso de {course} está parado há 7 dias...'
+SUBJECT = 'Seu curso {course} está parado há 7 dias...'
MESSAGE = """
Oi {first_name}, tudo bem?
diff --git a/enrollments-events/app/events/issue_cert.py b/enrollments-events/app/events/issue_cert.py
index 817507d..7290e06 100644
--- a/enrollments-events/app/events/issue_cert.py
+++ b/enrollments-events/app/events/issue_cert.py
@@ -4,7 +4,7 @@ from aws_lambda_powertools.utilities.data_classes import (
event_source,
)
from aws_lambda_powertools.utilities.typing import LambdaContext
-from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, SortKey, TransactKey
+from layercake.dynamodb import DynamoDBPersistenceLayer, SortKey, TransactKey
from boto3clients import dynamodb_client
from config import (
@@ -21,8 +21,8 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
new_image = event.detail['new_image']
enrollment = enrollment_layer.collection.get_items(
TransactKey(pk=new_image['id'])
- + SortKey('METADATA#DEDUPLICATION_WINDOW', rename_key='DEDUPLICATION_WINDOW')
- + SortKey('METADATA#COURSE', rename_key='COURSE'),
+ + SortKey('METADATA#DEDUPLICATION_WINDOW', rename_key='deduplication_window')
+ + SortKey('METADATA#COURSE', rename_key='course'),
)
return True
diff --git a/enrollments-events/app/events/reenroll_if_failed.py b/enrollments-events/app/events/reenroll_if_failed.py
index 4fdb40a..ef12a9d 100644
--- a/enrollments-events/app/events/reenroll_if_failed.py
+++ b/enrollments-events/app/events/reenroll_if_failed.py
@@ -19,16 +19,11 @@ enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
@logger.inject_lambda_context
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
new_image = event.detail['new_image']
- subscription = enrollment_layer.collection.get_items(
+ data = enrollment_layer.collection.get_items(
TransactKey(new_image['id'])
- + SortKey('METADATA#SUBSCRIPTION_COVERED')
- + SortKey('author', rename_key='CREATED_BY')
- + SortKey('tenant', rename_key='ORG')
- + SortKey('CREATED_BY')
- + SortKey('ORG')
+ + SortKey('METADATA#SUBSCRIPTION_COVERED', rename_key='subscription')
+ + SortKey('author', rename_key='created_by')
+ + SortKey('tenant', rename_key='org')
)
- with enrollment_layer.transact_writer() as transact_writer:
- ...
-
return True
diff --git a/enrollments-events/app/events/schedule_reminders.py b/enrollments-events/app/events/schedule_reminders.py
index 0f61a44..b80c2b0 100644
--- a/enrollments-events/app/events/schedule_reminders.py
+++ b/enrollments-events/app/events/schedule_reminders.py
@@ -37,8 +37,10 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
'ttl': ttl(days=3, start_dt=now_),
},
)
+
# By default, the enrollment will expire when the access period ends
# (scheduled below).
+
# If the enrollment is completed earlier (e.g., certificate issued),
# the expiration schedule is canceled and an archive schedule
# (`SCHEDULE#SET_AS_ARCHIVED`) is created instead.
@@ -53,7 +55,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
transact.put(
item={
'id': enrollment_id,
- 'sk': 'SCHEDULE#REMINDER_CERT_EXPIRATION_BEFORE_30_DAYS',
+ 'sk': 'SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS',
'name': user.name,
'email': user.email,
'course': course.name,
diff --git a/enrollments-events/app/events/stopgap/patch_course_metadata.py b/enrollments-events/app/events/stopgap/patch_course_metadata.py
index 78c6a22..855b759 100644
--- a/enrollments-events/app/events/stopgap/patch_course_metadata.py
+++ b/enrollments-events/app/events/stopgap/patch_course_metadata.py
@@ -8,11 +8,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
+from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
from sqlite_utils import Database
from boto3clients import dynamodb_client
from config import (
+ COURSE_TABLE,
ENROLLMENT_TABLE,
SQLITE_DATABASE,
SQLITE_TABLE,
@@ -22,6 +23,7 @@ sqlite3.register_converter('json', json.loads)
logger = Logger(__name__)
enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
+course_layer = DynamoDBPersistenceLayer(COURSE_TABLE, dynamodb_client)
@event_source(data_class=EventBridgeEvent)
@@ -37,7 +39,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
transact.put(
item={
'id': new_image['id'],
- 'sk': 'metadata#deduplication_window',
+ 'sk': 'METADATA#DEDUPLICATION_WINDOW',
'offset_days': 90,
'created_at': now_,
}
@@ -45,7 +47,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
transact.put(
item={
'id': new_image['id'],
- 'sk': 'metadata#course',
+ 'sk': 'METADATA#COURSE',
'created_at': now_,
'access_period': int(course['access_period']),
'cert': {
@@ -66,6 +68,10 @@ class CourseNotFoundError(Exception):
def _get_course(course_id: str) -> dict:
+ course = course_layer.get_item(KeyPair(pk=course_id, sk='0'))
+ if course:
+ return course
+
with sqlite3.connect(
database=SQLITE_DATABASE, detect_types=sqlite3.PARSE_DECLTYPES
) as conn:
diff --git a/enrollments-events/template.yaml b/enrollments-events/template.yaml
index 5a0123e..f73b929 100644
--- a/enrollments-events/template.yaml
+++ b/enrollments-events/template.yaml
@@ -76,6 +76,8 @@ Resources:
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref EnrollmentTable
+ - DynamoDBCrudPolicy:
+ TableName: !Ref CourseTable
Events:
DynamoDBEvent:
Type: EventBridgeRule
@@ -281,7 +283,7 @@ Resources:
detail:
keys:
sk:
- - SCHEDULE#REMINDER_CERT_EXPIRATION_BEFORE_30_DAYS
+ - SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS
# Post-migration: remove the following line
- schedules#access_period_ends
@@ -310,6 +312,8 @@ Resources:
# Post-migration: remove the following line
- schedules#expiration
+ # If there is no certificate and the access period has ended,
+ # the enrollment will be marked as expired
EventSetAsExpiredFunction:
Type: AWS::Serverless::Function
Properties:
@@ -328,28 +332,55 @@ Resources:
detail-type: [EXPIRE]
detail:
keys:
- sk: [SCHEDULE#SET_AS_EXPIRED]
+ sk:
+ - SCHEDULE#SET_AS_EXPIRED
+ # Post-migration: remove the following line
+ - schedules#access_period_ends
- # 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]
+ # After the certificate expires, the enrollment will be marked as archived
+ EventSetAsArchivedFunction:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: events.set_as_archived.lambda_handler
+ LoggingConfig:
+ LogGroup: !Ref EventLog
+ Policies:
+ - DynamoDBCrudPolicy:
+ TableName: !Ref EnrollmentTable
+ Events:
+ DynamoDBEvent:
+ Type: EventBridgeRule
+ Properties:
+ Pattern:
+ resources: [!Ref EnrollmentTable]
+ detail-type: [EXPIRE]
+ detail:
+ keys:
+ sk:
+ - SCHEDULE#SET_AS_ARCHIVED
+ # Post-migration: remove the following line
+ - schedules#archive_it
+
+ 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
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 58b30a1..343ec54 100644
--- a/enrollments-events/tests/events/stopgap/test_patch_course_metadata.py
+++ b/enrollments-events/tests/events/stopgap/test_patch_course_metadata.py
@@ -30,9 +30,9 @@ def test_enroll(
result = dynamodb_persistence_layer.collection.get_items(
TransactKey('47ZxxcVBjvhDS5TE98tpfQ')
+ SortKey('0')
- + SortKey('metadata#deduplication_window')
- + SortKey('metadata#course')
+ + SortKey('METADATA#DEDUPLICATION_WINDOW')
+ + SortKey('METADATA#COURSE')
)
- assert 'metadata#course' in result
- assert 'metadata#deduplication_window' in result
+ assert 'METADATA#COURSE' in result
+ assert 'METADATA#DEDUPLICATION_WINDOW' in result
diff --git a/enrollments-events/tests/seeds.jsonl b/enrollments-events/tests/seeds.jsonl
index f257de0..b7fcac6 100644
--- a/enrollments-events/tests/seeds.jsonl
+++ b/enrollments-events/tests/seeds.jsonl
@@ -14,6 +14,7 @@
{"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": "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}
// User data
{"id": "5OxmMjL-ujoR5IMGegQz", "sk": "konviva", "konvivaId": 26943}
diff --git a/http-api/app/routes/enrollments/enroll.py b/http-api/app/routes/enrollments/enroll.py
index bdf48ec..0af9609 100644
--- a/http-api/app/routes/enrollments/enroll.py
+++ b/http-api/app/routes/enrollments/enroll.py
@@ -5,7 +5,6 @@ from http import HTTPStatus
from aws_lambda_powertools.event_handler.api_gateway import Router
from layercake.batch import BatchProcessor
from layercake.dynamodb import (
- DynamoDBCollection,
DynamoDBPersistenceLayer,
)
from pydantic import BaseModel
diff --git a/http-api/app/rules/enrollment.py b/http-api/app/rules/enrollment.py
index 5b56eb0..ad17998 100644
--- a/http-api/app/rules/enrollment.py
+++ b/http-api/app/rules/enrollment.py
@@ -113,48 +113,6 @@ def enroll(
'created_at': now_,
},
)
- transact.put(
- item={
- 'id': enrollment.id,
- # Post-migration: uncomment the following line
- # 'sk': LifecycleEvents.REMINDER_NO_ACCESS_3_DAYS,
- 'sk': LifecycleEvents.DOES_NOT_ACCESS,
- 'name': user.name,
- 'email': user.email,
- 'course': course.name,
- 'created_at': now_,
- 'ttl': ttl(days=3, start_dt=now_),
- },
- )
- # Enrollment expires by default when the access period ends.
- # When the course is finished, it is automatically removed,
- # and the `schedules#course_archived` event is created.
- transact.put(
- item={
- 'id': enrollment.id,
- 'sk': LifecycleEvents.EXPIRATION,
- # Post-migration: uncomment the following line
- # 'sk': LifecycleEvents.COURSE_EXPIRED,
- 'name': user.name,
- 'email': user.email,
- 'course': course.name,
- 'created_at': now_,
- 'ttl': ttl(start_dt=now_ + timedelta(days=course.access_period)),
- },
- )
- transact.put(
- item={
- 'id': enrollment.id,
- # Post-migration: uncomment the following line
- # 'sk': LifecycleEvents.ACCESS_PERIOD_REMINDER_30_DAYS,
- 'sk': LifecycleEvents.ACCESS_PERIOD_ENDS,
- 'name': user.name,
- 'email': user.email,
- 'course': course.name,
- 'created_at': now_,
- 'ttl': ttl(start_dt=now_ + timedelta(days=course.access_period - 30)),
- },
- )
for entity in linked_entities:
type = entity.type.lower()
diff --git a/http-api/compose.yaml b/http-api/compose.yaml
index a3823c1..b93ce7d 100644
--- a/http-api/compose.yaml
+++ b/http-api/compose.yaml
@@ -19,3 +19,17 @@ services:
restart: unless-stopped
ports:
- 7700:7700
+
+ postgres:
+ image: postgres
+ container_name: postgres
+ restart: always
+ environment:
+ - POSTGRES_USER=postgres
+ - POSTGRES_PASSWORD=pgsql@pwd
+ - PGID=1000
+ - PUID=1000
+ ports:
+ - 5432:5432
+ volumes:
+ - ./.postgres_data:/var/lib/postgresql/data
diff --git a/http-api/pyproject.toml b/http-api/pyproject.toml
index 80e68d2..214d7df 100644
--- a/http-api/pyproject.toml
+++ b/http-api/pyproject.toml
@@ -10,6 +10,8 @@ dependencies = ["layercake"]
dev = [
"boto3-stubs[essential]>=1.38.26",
"jsonlines>=4.0.0",
+ "psycopg2-binary>=2.9.10",
+ "pycouchdb>=1.16.0",
"pytest>=8.3.4",
"pytest-cov>=6.0.0",
"ruff>=0.9.1",
diff --git a/http-api/template.yaml b/http-api/template.yaml
index 5f5f479..1e4811a 100644
--- a/http-api/template.yaml
+++ b/http-api/template.yaml
@@ -40,7 +40,7 @@ Globals:
COURSE_TABLE: !Ref CourseTable
KONVIVA_API_URL: https://lms.saladeaula.digital
KONVIVA_SECRET_KEY: "{{resolve:ssm:/betaeducacao/konviva/secret_key/str}}"
- MEILISEARCH_HOST: https://meili.eduseg.com.br
+ MEILISEARCH_HOST: https://meili.saladeaula.digital
MEILISEARCH_API_KEY: "{{resolve:ssm:/saladeaula/meili_api_key}}"
Resources:
diff --git a/http-api/uv.lock b/http-api/uv.lock
index bccd5fa..36fa30d 100644
--- a/http-api/uv.lock
+++ b/http-api/uv.lock
@@ -210,6 +210,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" },
]
+[[package]]
+name = "chardet"
+version = "5.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618, upload-time = "2023-08-01T19:23:02.662Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385, upload-time = "2023-08-01T19:23:00.661Z" },
+]
+
[[package]]
name = "charset-normalizer"
version = "3.4.1"
@@ -347,18 +356,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" },
]
-[[package]]
-name = "ecdsa"
-version = "0.19.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "six" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c0/1f/924e3caae75f471eae4b26bd13b698f6af2c44279f67af317439c2f4c46a/ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61", size = 201793, upload-time = "2025-03-13T11:52:43.25Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/cb/a3/460c57f094a4a165c84a1341c373b0a4f5ec6ac244b998d5021aade89b77/ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3", size = 150607, upload-time = "2025-03-13T11:52:41.757Z" },
-]
-
[[package]]
name = "email-validator"
version = "2.2.0"
@@ -431,6 +428,8 @@ dependencies = [
dev = [
{ name = "boto3-stubs", extra = ["essential"] },
{ name = "jsonlines" },
+ { name = "psycopg2-binary" },
+ { name = "pycouchdb" },
{ name = "pytest" },
{ name = "pytest-cov" },
{ name = "ruff" },
@@ -445,6 +444,8 @@ requires-dist = [{ name = "layercake", directory = "../layercake" }]
dev = [
{ name = "boto3-stubs", extras = ["essential"], specifier = ">=1.38.26" },
{ name = "jsonlines", specifier = ">=4.0.0" },
+ { name = "psycopg2-binary", specifier = ">=2.9.10" },
+ { name = "pycouchdb", specifier = ">=1.16.0" },
{ name = "pytest", specifier = ">=8.3.4" },
{ name = "pytest-cov", specifier = ">=6.0.0" },
{ name = "ruff", specifier = ">=0.9.1" },
@@ -470,18 +471,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
]
-[[package]]
-name = "jinja2"
-version = "3.1.6"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markupsafe" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
-]
-
[[package]]
name = "jmespath"
version = "1.0.1"
@@ -517,7 +506,7 @@ wheels = [
[[package]]
name = "layercake"
-version = "0.9.9"
+version = "0.9.12"
source = { directory = "../layercake" }
dependencies = [
{ name = "arnparse" },
@@ -526,7 +515,6 @@ dependencies = [
{ name = "dictdiffer" },
{ name = "ftfy" },
{ name = "glom" },
- { name = "jinja2" },
{ name = "meilisearch" },
{ name = "orjson" },
{ name = "passlib" },
@@ -534,7 +522,6 @@ dependencies = [
{ name = "pydantic", extra = ["email"] },
{ name = "pydantic-extra-types" },
{ name = "pyjwt" },
- { name = "python-jose", extra = ["cryptography"] },
{ name = "pytz" },
{ name = "requests" },
{ name = "smart-open", extra = ["s3"] },
@@ -550,7 +537,6 @@ requires-dist = [
{ name = "dictdiffer", specifier = ">=0.9.0" },
{ name = "ftfy", specifier = ">=6.3.1" },
{ name = "glom", specifier = ">=24.11.0" },
- { name = "jinja2", specifier = ">=3.1.6" },
{ name = "meilisearch", specifier = ">=0.34.0" },
{ name = "orjson", specifier = ">=3.10.15" },
{ name = "passlib", specifier = ">=1.7.4" },
@@ -558,7 +544,6 @@ requires-dist = [
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
{ name = "pyjwt", specifier = ">=2.10.1" },
- { name = "python-jose", extras = ["cryptography"], specifier = ">=3.5.0" },
{ name = "pytz", specifier = ">=2025.1" },
{ name = "requests", specifier = ">=2.32.3" },
{ name = "smart-open", extras = ["s3"], specifier = ">=7.1.0" },
@@ -577,34 +562,6 @@ dev = [
{ name = "ruff", specifier = ">=0.11.1" },
]
-[[package]]
-name = "markupsafe"
-version = "3.0.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" },
- { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" },
- { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" },
- { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" },
- { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" },
- { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" },
- { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" },
- { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" },
- { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" },
- { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" },
- { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" },
- { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" },
- { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" },
- { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" },
- { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" },
- { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" },
- { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" },
- { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" },
- { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" },
- { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
-]
-
[[package]]
name = "meilisearch"
version = "0.34.0"
@@ -740,12 +697,35 @@ wheels = [
]
[[package]]
-name = "pyasn1"
-version = "0.6.1"
+name = "psycopg2-binary"
+version = "2.9.10"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764, upload-time = "2024-10-16T11:24:58.126Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699, upload-time = "2024-10-16T11:21:42.841Z" },
+ { url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245, upload-time = "2024-10-16T11:21:51.989Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631, upload-time = "2024-10-16T11:21:57.584Z" },
+ { url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140, upload-time = "2024-10-16T11:22:02.005Z" },
+ { url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762, upload-time = "2024-10-16T11:22:06.412Z" },
+ { url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967, upload-time = "2024-10-16T11:22:11.583Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326, upload-time = "2024-10-16T11:22:16.406Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712, upload-time = "2024-10-16T11:22:21.366Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155, upload-time = "2024-10-16T11:22:25.684Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356, upload-time = "2024-10-16T11:22:30.562Z" },
+ { url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224, upload-time = "2025-01-04T20:09:19.234Z" },
+]
+
+[[package]]
+name = "pycouchdb"
+version = "1.16.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "chardet" },
+ { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c1/b4/4f699a686a2ce14ab31cb17902693f2cf201ba51c3a6fb7aba210725c154/pycouchdb-1.16.0.tar.gz", hash = "sha256:309d71c3ce3f98bbee5731db00f514753438b81e6e7adefbb8c134312200a4f9", size = 11351, upload-time = "2024-05-29T10:00:11.726Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4c/63/b4397a7215c089c7951afb258069cc58a06788224f1bb6a0d4f976f2d476/pycouchdb-1.16.0-py3-none-any.whl", hash = "sha256:e26ce58f626fcabbe2f5b15b3ad2b89cdd3f6d666da673632037476d1191ab67", size = 12560, upload-time = "2024-05-29T10:00:09.31Z" },
]
[[package]]
@@ -894,25 +874,6 @@ 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-jose"
-version = "3.5.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "ecdsa" },
- { name = "pyasn1" },
- { name = "rsa" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c6/77/3a1c9039db7124eb039772b935f2244fbb73fc8ee65b9acf2375da1c07bf/python_jose-3.5.0.tar.gz", hash = "sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b", size = 92726, upload-time = "2025-05-28T17:31:54.288Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d9/c3/0bd11992072e6a1c513b16500a5d07f91a24017c5909b02c72c62d7ad024/python_jose-3.5.0-py2.py3-none-any.whl", hash = "sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771", size = 34624, upload-time = "2025-05-28T17:31:52.802Z" },
-]
-
-[package.optional-dependencies]
-cryptography = [
- { name = "cryptography" },
-]
-
[[package]]
name = "pytz"
version = "2025.2"
@@ -937,18 +898,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" },
]
-[[package]]
-name = "rsa"
-version = "4.9.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pyasn1" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" },
-]
-
[[package]]
name = "ruff"
version = "0.11.2"
diff --git a/konviva-events/app/boto3clients.py b/konviva-events/app/boto3clients.py
index 05de43d..7d2a8c1 100644
--- a/konviva-events/app/boto3clients.py
+++ b/konviva-events/app/boto3clients.py
@@ -1,13 +1,19 @@
import os
+from typing import TYPE_CHECKING
import boto3
+if TYPE_CHECKING:
+ from mypy_boto3_dynamodb.client import DynamoDBClient
+else:
+ DynamoDBClient = object
-def get_dynamodb_client():
+
+def get_dynamodb_client() -> DynamoDBClient:
if os.getenv('AWS_LAMBDA_FUNCTION_NAME'):
return boto3.client('dynamodb')
return boto3.client('dynamodb', endpoint_url='http://127.0.0.1:8000')
-dynamodb_client = get_dynamodb_client()
+dynamodb_client: DynamoDBClient = get_dynamodb_client()
diff --git a/konviva-events/app/events/create_user.py b/konviva-events/app/events/create_user.py
index 2847aa5..432e93a 100644
--- a/konviva-events/app/events/create_user.py
+++ b/konviva-events/app/events/create_user.py
@@ -39,7 +39,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
user_id = glom(result, '0.IDUsuario')
if not result:
- raise UserNotFoundError()
+ raise UserNotFoundError('User not found')
except Exception:
raise
@@ -47,7 +47,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
transact.update(
key=KeyPair(new_image['id'], '0'),
update_expr='SET metadata__konviva_user_id = :user_id, \
- updated_at = :updated_at',
+ updated_at = :updated_at',
expr_attr_values={
':user_id': user_id,
':updated_at': now_,
diff --git a/konviva-events/app/events/enroll.py b/konviva-events/app/events/enroll.py
index 07670e3..c875c24 100644
--- a/konviva-events/app/events/enroll.py
+++ b/konviva-events/app/events/enroll.py
@@ -41,6 +41,7 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
':enrollment_id': enrollment_id,
},
)
+ # To relate with the enrollment
transact.put(
item={
'id': 'konviva',
diff --git a/konviva-events/app/konviva.py b/konviva-events/app/konviva.py
index 00ae75d..4c6045e 100644
--- a/konviva-events/app/konviva.py
+++ b/konviva-events/app/konviva.py
@@ -26,7 +26,7 @@ class EmailAlreadyExistsError(KonvivaError):
pass
-def _raise_konviva_errors_if_any(data: dict = {}) -> None:
+def _raise_errors_if_any(data: dict = {}) -> None:
"""Raises specific exceptions if Konviva API returned errors in the response body"""
errors = glom(data, 'errors', default=None)
if not errors:
@@ -72,7 +72,7 @@ def create_user(
)
r.raise_for_status()
data = r.json()
- _raise_konviva_errors_if_any(data)
+ _raise_errors_if_any(data)
return int(data.get('IDUsuario'))
@@ -93,7 +93,7 @@ def update_user(id: str, **kwargs) -> dict:
)
r.raise_for_status()
data = r.json()
- _raise_konviva_errors_if_any(data)
+ _raise_errors_if_any(data)
return data
@@ -126,7 +126,7 @@ def _post_enrollment(json: dict) -> dict:
)
r.raise_for_status()
data = r.json()
- _raise_konviva_errors_if_any(data)
+ _raise_errors_if_any(data)
return data
diff --git a/konviva-events/pyproject.toml b/konviva-events/pyproject.toml
index 7386c57..9a8f08d 100644
--- a/konviva-events/pyproject.toml
+++ b/konviva-events/pyproject.toml
@@ -8,6 +8,7 @@ dependencies = ["layercake"]
[dependency-groups]
dev = [
+ "boto3-stubs[dynamodb]>=1.40.15",
"jsonlines>=4.0.0",
"pytest>=8.3.4",
"pytest-cov>=6.0.0",
diff --git a/konviva-events/template.yaml b/konviva-events/template.yaml
index 451b028..f7f9a2d 100644
--- a/konviva-events/template.yaml
+++ b/konviva-events/template.yaml
@@ -17,7 +17,7 @@ Globals:
Architectures:
- x86_64
Layers:
- - !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:86
+ - !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:94
Environment:
Variables:
TZ: America/Sao_Paulo
@@ -36,6 +36,33 @@ Resources:
Properties:
RetentionInDays: 90
+ HttpLog:
+ Type: AWS::Logs::LogGroup
+ Properties:
+ RetentionInDays: 90
+
+ HttpApi:
+ Type: AWS::Serverless::HttpApi
+ Properties:
+ CorsConfiguration:
+ AllowOrigins: ["*"]
+ AllowMethods: [POST, OPTIONS]
+ AllowHeaders: [Content-Type, X-Requested-With]
+
+ HttpApiFunction:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: app.lambda_handler
+ LoggingConfig:
+ LogGroup: !Ref HttpLog
+ Events:
+ Session:
+ Type: HttpApi
+ Properties:
+ Path: /
+ Method: POST
+ ApiId: !Ref HttpApi
+
EventCreateUserFunction:
Type: AWS::Serverless::Function
Properties:
@@ -60,6 +87,29 @@ Resources:
metadata__konviva_user_id:
- exists: false
+ EventUpdateUserFunction:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: events.update_user.lambda_handler
+ LoggingConfig:
+ LogGroup: !Ref EventLog
+ Policies:
+ - DynamoDBReadPolicy:
+ TableName: !Ref UserTable
+ Events:
+ DynamoDBEvent:
+ Type: EventBridgeRule
+ Properties:
+ Pattern:
+ resources: [!Ref UserTable]
+ detail-type: [MODIFY]
+ detail:
+ new_image:
+ sk: ["0"]
+ metadata__konviva_user_id:
+ - exists: true
+ changes: [name, email, cpf]
+
EventEnrollFunction:
Type: AWS::Serverless::Function
Properties:
@@ -78,7 +128,7 @@ Resources:
detail-type: [INSERT]
detail:
new_image:
- sk: ["konviva"]
+ sk: [konviva, KONVIVA]
enrollment_id:
- exists: false
@@ -104,26 +154,3 @@ Resources:
status: [CANCELED]
old_image:
status: [PENDING]
-
- EventUpdateUserFunction:
- Type: AWS::Serverless::Function
- Properties:
- Handler: events.update_user.lambda_handler
- LoggingConfig:
- LogGroup: !Ref EventLog
- Policies:
- - DynamoDBReadPolicy:
- TableName: !Ref UserTable
- Events:
- DynamoDBEvent:
- Type: EventBridgeRule
- Properties:
- Pattern:
- resources: [!Ref UserTable]
- detail-type: [MODIFY]
- detail:
- new_image:
- sk: ["0"]
- metadata__konviva_user_id:
- - exists: true
- changes: [name, email, cpf]
diff --git a/konviva-events/tests/conftest.py b/konviva-events/tests/conftest.py
index 340de12..1d1ded1 100644
--- a/konviva-events/tests/conftest.py
+++ b/konviva-events/tests/conftest.py
@@ -1,5 +1,8 @@
+import base64
+import json
import os
from dataclasses import dataclass
+from http import HTTPMethod
import jsonlines
import pytest
@@ -27,11 +30,83 @@ class LambdaContext:
aws_request_id: str = '52fdfc07-2182-154f-163f-5f0f9a621d72'
+class HttpApiProxy:
+ def __call__(
+ self,
+ raw_path: str,
+ method: str = HTTPMethod.GET,
+ body: dict = {},
+ *,
+ headers: dict = {},
+ auth_flow_type: str = 'USER_AUTH',
+ queryStringParameters: dict = {},
+ **kwargs,
+ ) -> dict:
+ return {
+ 'version': '2.0',
+ 'routeKey': '$default',
+ 'rawPath': raw_path,
+ 'rawQueryString': 'parameter1=value1¶meter1=value2¶meter2=value',
+ 'cookies': ['cookie1', 'cookie2'],
+ 'headers': headers,
+ 'queryStringParameters': queryStringParameters,
+ 'requestContext': {
+ 'accountId': '123456789012',
+ 'apiId': 'api-id',
+ 'authorizer': {
+ 'lambda': {
+ 'user': {
+ 'name': 'Sérgio R Siqueira',
+ 'email': 'sergio@somosbeta.com.br',
+ 'email_verified': 'true',
+ 'custom:user_id': '5OxmMjL-ujoR5IMGegQz',
+ 'sub': 'c4f30dbd-083e-4b84-aa50-c31afe9b9c01',
+ },
+ 'auth_flow_type': auth_flow_type,
+ },
+ 'jwt': {
+ 'claims': {'claim1': 'value1', 'claim2': 'value2'},
+ 'scopes': ['scope1', 'scope2'],
+ },
+ },
+ 'domainName': 'id.execute-api.us-east-1.amazonaws.com',
+ 'domainPrefix': 'id',
+ 'http': {
+ 'method': str(method),
+ 'path': raw_path,
+ 'protocol': 'HTTP/1.1',
+ 'sourceIp': '192.168.0.1/32',
+ 'userAgent': 'agent',
+ },
+ 'requestId': 'id',
+ 'routeKey': '$default',
+ 'stage': '$default',
+ 'time': '12/Mar/2020:19:03:58 +0000',
+ 'timeEpoch': 1583348638390,
+ },
+ 'body': _base64_dict(body),
+ 'pathParameters': {'parameter1': 'value1'},
+ 'isBase64Encoded': True,
+ 'stageVariables': {'stageVariable1': 'value1', 'stageVariable2': 'value2'},
+ }
+
+
+def _base64_dict(obj: dict = {}) -> str | None:
+ if not obj:
+ return None
+ return base64.b64encode(json.dumps(obj).encode()).decode()
+
+
@pytest.fixture
def lambda_context() -> LambdaContext:
return LambdaContext()
+@pytest.fixture
+def http_api_proxy():
+ return HttpApiProxy()
+
+
@pytest.fixture
def dynamodb_client():
from boto3clients import dynamodb_client as client
@@ -57,8 +132,29 @@ def dynamodb_client():
client.delete_table(TableName=PYTEST_TABLE_NAME)
+@pytest.fixture
+def dynamodb_persistence_layer(dynamodb_client):
+ from layercake.dynamodb import DynamoDBPersistenceLayer
+
+ return DynamoDBPersistenceLayer(PYTEST_TABLE_NAME, dynamodb_client)
+
+
@pytest.fixture()
-def dynamodb_seeds(dynamodb_client):
- with jsonlines.open('tests/seeds.jsonl') as lines:
- for line in lines:
- dynamodb_client.put_item(TableName=PYTEST_TABLE_NAME, Item=line)
+def seeds(dynamodb_client):
+ from layercake.dynamodb import serialize
+
+ with open('tests/seeds.jsonl', 'rb') as fp:
+ reader = jsonlines.Reader(fp)
+
+ for line in reader.iter(type=dict, skip_invalid=True):
+ dynamodb_client.put_item(
+ TableName=PYTEST_TABLE_NAME,
+ Item=serialize(line),
+ )
+
+
+@pytest.fixture
+def app():
+ import app
+
+ return app
diff --git a/konviva-events/tests/events/test_create_user.py b/konviva-events/tests/events/test_create_user.py
index f396f47..7e1373f 100644
--- a/konviva-events/tests/events/test_create_user.py
+++ b/konviva-events/tests/events/test_create_user.py
@@ -3,7 +3,10 @@ from aws_lambda_powertools.utilities.typing import LambdaContext
import events.create_user as app
-def test_create_user(dynamodb_client, dynamodb_seeds, lambda_context: LambdaContext):
+def test_create_user(
+ seeds,
+ lambda_context: LambdaContext,
+):
event = {
'detail': {
'new_image': {
diff --git a/konviva-events/tests/events/test_enroll.py b/konviva-events/tests/events/test_enroll.py
index 3ed2b24..e73dccb 100644
--- a/konviva-events/tests/events/test_enroll.py
+++ b/konviva-events/tests/events/test_enroll.py
@@ -3,7 +3,10 @@ from aws_lambda_powertools.utilities.typing import LambdaContext
import events.enroll as app
-def test_enroll(dynamodb_client, dynamodb_seeds, lambda_context: LambdaContext):
+def test_enroll(
+ seeds,
+ lambda_context: LambdaContext,
+):
event = {
'detail': {
'new_image': {
diff --git a/konviva-events/tests/events/test_update_user.py b/konviva-events/tests/events/test_update_user.py
index 80bfb04..50b1c05 100644
--- a/konviva-events/tests/events/test_update_user.py
+++ b/konviva-events/tests/events/test_update_user.py
@@ -14,4 +14,4 @@ def test_update_user(lambda_context: LambdaContext):
},
}
- assert app.lambda_handler(event, lambda_context)
+ assert app.lambda_handler(event, lambda_context) # type: ignore
diff --git a/konviva-events/tests/seeds.jsonl b/konviva-events/tests/seeds.jsonl
index 6f43cf0..e1b68b9 100644
--- a/konviva-events/tests/seeds.jsonl
+++ b/konviva-events/tests/seeds.jsonl
@@ -1,2 +1,32 @@
-{"id": {"S": "123"}, "sk": {"S": "0"}}
-{"id": {"S": "03ad39c6-055e-48d8-acc8-e11385ed8904"}, "sk": {"S": "konviva"}, "user_id": {"N": "275"}, "class_id": {"N": "26943"}}
\ No newline at end of file
+// Users
+{"id": "123", "sk": "0"}
+
+// Enrollments
+{"id": "197991aa-52e2-4e0c-b2a4-a7c53bcfee02", "sk": "0", "progress": 10, "status": "IN_PROGRESS"}
+
+{"id": "03ad39c6-055e-48d8-acc8-e11385ed8904", "sk": "0"}
+{"id": "03ad39c6-055e-48d8-acc8-e11385ed8904", "sk": "konviva", "user_id": 275, "class_id": 26943}
+
+{"id": "d9da85f2-e09f-472d-9515-3d91d70f1e8a", "sk": "0", "progress": 0, "status": "PENDING"}
+{"id": "d9da85f2-e09f-472d-9515-3d91d70f1e8a", "sk": "SCHEDULE#REMINDER_NO_ACCESS_AFTER_3_DAYS"}
+
+{"id": "6c7e3d9b-f5d1-4da4-9e55-0825bb6ff2b8", "sk": "0", "progress": 80, "status": "IN_PROGRESS", "user": {"id": "123", "name": "Myles Kennedy"}, "course": {"id": "432", "name": "pytest"}, "created_at": "2022-04-06T11:07:32.762178-03:00"}
+{"id": "6c7e3d9b-f5d1-4da4-9e55-0825bb6ff2b8", "sk": "METADATA#COURSE", "access_period": 360, "cert": {"exp_interval": 365}, "created_at": "2025-04-06T11:07:32.762178-03:00"}
+{"id": "6c7e3d9b-f5d1-4da4-9e55-0825bb6ff2b8", "sk": "METADATA#DEDUPLICATION_WINDOW", "offset_days": 90, "created_at": "2025-04-06T11:07:32.762178-03:00"}
+{"id": "6c7e3d9b-f5d1-4da4-9e55-0825bb6ff2b8", "sk": "SCHEDULE#REMINDER_ACCESS_PERIOD_BEFORE_30_DAYS"}
+{"id": "6c7e3d9b-f5d1-4da4-9e55-0825bb6ff2b8", "sk": "SCHEDULE#SET_AS_EXPIRED"}
+
+{"id": "cc2c3bce-c34a-4e82-aa6c-1a19e70ec5ae", "sk": "0", "progress": 109, "status": "COMPLETED", "user": {"id": "321", "name": "Chester Bennington"}, "course": {"id": "432", "name": "pytest"}, "created_at": "2022-04-06T11:07:32.762178-03:00"}
+{"id": "cc2c3bce-c34a-4e82-aa6c-1a19e70ec5ae", "sk": "METADATA#COURSE", "access_period": 360, "cert": {"exp_interval": 365}, "created_at": "2025-04-06T11:07:32.762178-03:00"}
+{"id": "cc2c3bce-c34a-4e82-aa6c-1a19e70ec5ae", "sk": "METADATA#DEDUPLICATION_WINDOW", "offset_days": 90, "created_at": "2025-04-06T11:07:32.762178-03:00"}
+{"id": "cc2c3bce-c34a-4e82-aa6c-1a19e70ec5ae", "sk": "SCHEDULE#SET_AS_ARCHIVED"}
+
+{"id": "5db53b35-0bae-4907-afda-a213cb5bf651", "sk": "0", "progress": 0, "status": "PENDING", "user": {"id": "1234", "name": "Michael Jackinson"}, "course": {"id": "432", "name": "pytest"}, "created_at": "2022-04-06T11:07:32.762178-03:00"}
+{"id": "5db53b35-0bae-4907-afda-a213cb5bf651", "sk": "METADATA#COURSE", "access_period": 360, "cert": {"exp_interval": 365}, "created_at": "2025-04-06T11:07:32.762178-03:00"}
+
+// To relate with the enrollment
+{"id": "konviva", "sk": "123", "enrollment_id": "d9da85f2-e09f-472d-9515-3d91d70f1e8a"}
+{"id": "konviva", "sk": "456", "enrollment_id": "197991aa-52e2-4e0c-b2a4-a7c53bcfee02"}
+{"id": "konviva", "sk": "567", "enrollment_id": "6c7e3d9b-f5d1-4da4-9e55-0825bb6ff2b8"}
+{"id": "konviva", "sk": "899", "enrollment_id": "cc2c3bce-c34a-4e82-aa6c-1a19e70ec5ae"}
+{"id": "konviva", "sk": "221", "enrollment_id": "5db53b35-0bae-4907-afda-a213cb5bf651"}
\ No newline at end of file
diff --git a/konviva-events/uv.lock b/konviva-events/uv.lock
index eddbf0f..04ae4ab 100644
--- a/konviva-events/uv.lock
+++ b/konviva-events/uv.lock
@@ -29,6 +29,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" },
]
+[[package]]
+name = "authlib"
+version = "1.6.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cryptography" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8e/a1/d8d1c6f8bc922c0b87ae0d933a8ed57be1bef6970894ed79c2852a153cd3/authlib-1.6.1.tar.gz", hash = "sha256:4dffdbb1460ba6ec8c17981a4c67af7d8af131231b5a36a88a1e8c80c111cdfd", size = 159988, upload-time = "2025-07-20T07:38:42.834Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f9/58/cc6a08053f822f98f334d38a27687b69c6655fb05cd74a7a5e70a2aeed95/authlib-1.6.1-py2.py3-none-any.whl", hash = "sha256:e9d2031c34c6309373ab845afc24168fe9e93dc52d252631f52642f21f5ed06e", size = 239299, upload-time = "2025-07-20T07:38:39.259Z" },
+]
+
[[package]]
name = "aws-encryption-sdk"
version = "4.0.2"
@@ -46,15 +58,15 @@ wheels = [
[[package]]
name = "aws-lambda-powertools"
-version = "3.16.0"
+version = "3.19.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jmespath" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/3b/b9/7e69ec5b47f4c899f0f96f4d1efaa5511ebe753c1cf3d8142ada2ce47381/aws_lambda_powertools-3.16.0.tar.gz", hash = "sha256:df0e5b6e575bf8fd30101d3c39358deb3ccbed195756343e44af34d05345f128", size = 683586, upload-time = "2025-07-02T10:39:41.513Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/db/eb2708f7c27ab02b8d85936ce9308538e1e22c8c8224be5f00da3e6f44f7/aws_lambda_powertools-3.19.0.tar.gz", hash = "sha256:8897ba4be0b3a51f2b8f68946d650f3ef574fa2c40395544de03bd0c61979999", size = 689768, upload-time = "2025-08-12T08:45:46.887Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/8d/6f/6ffa68d026d26e4be251bff053993e73780e4a2629e755fcbb5432cc77e9/aws_lambda_powertools-3.16.0-py3-none-any.whl", hash = "sha256:b37843d4873b88fedd8eed507fdbf3eb7193e48a6b662b1e7b41e741cdcc765b", size = 824530, upload-time = "2025-07-02T10:39:39.416Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/52/5a73194286af329309263e9c4e2a57b8feac63bb6027be8d2d6222cd4da7/aws_lambda_powertools-3.19.0-py3-none-any.whl", hash = "sha256:98f18d35f843cd46b80ccadcf39eefc0c489325bea116383bd93048a5241d9fc", size = 832645, upload-time = "2025-08-12T08:45:44.982Z" },
]
[package.optional-dependencies]
@@ -103,6 +115,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/12/5c/93292e4d8c809950c13950b3168e0eabdac828629c21047959251ad3f28c/boto3-1.39.4-py3-none-any.whl", hash = "sha256:f8e9534b429121aa5c5b7c685c6a94dd33edf14f87926e9a182d5b50220ba284", size = 139908, upload-time = "2025-07-09T19:22:59.808Z" },
]
+[[package]]
+name = "boto3-stubs"
+version = "1.40.15"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "botocore-stubs" },
+ { name = "types-s3transfer" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b1/54/c8a0d43c5d17e20433b23ee78cc8348b0cba5a5255d6c2f66aafa86c64ad/boto3_stubs-1.40.15.tar.gz", hash = "sha256:47370ffdfd9f1899900bba554f4ae1846423c459beaccf11e2eae46896af5119", size = 101393, upload-time = "2025-08-21T19:48:27.26Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/40/fe3cb27e3eee35815902a37d26b7c0af308fe2b08cd0671fb0a8475dfa12/boto3_stubs-1.40.15-py3-none-any.whl", hash = "sha256:95b6a828b758ed56d90ea2530a6794506ca403cfbef3bd2584a2e7c43e3f6607", size = 70011, upload-time = "2025-08-21T19:48:20.458Z" },
+]
+
+[package.optional-dependencies]
+dynamodb = [
+ { name = "mypy-boto3-dynamodb" },
+]
+
[[package]]
name = "botocore"
version = "1.39.4"
@@ -118,40 +148,15 @@ wheels = [
]
[[package]]
-name = "brotli"
-version = "1.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/2f/c2/f9e977608bdf958650638c3f1e28f85a1b075f075ebbe77db8555463787b/Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", size = 7372270, upload-time = "2023-09-07T14:05:41.643Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0a/9f/fb37bb8ffc52a8da37b1c03c459a8cd55df7a57bdccd8831d500e994a0ca/Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5", size = 815681, upload-time = "2024-10-18T12:32:34.942Z" },
- { url = "https://files.pythonhosted.org/packages/06/b3/dbd332a988586fefb0aa49c779f59f47cae76855c2d00f450364bb574cac/Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8", size = 422475, upload-time = "2024-10-18T12:32:36.485Z" },
- { url = "https://files.pythonhosted.org/packages/bb/80/6aaddc2f63dbcf2d93c2d204e49c11a9ec93a8c7c63261e2b4bd35198283/Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f", size = 2906173, upload-time = "2024-10-18T12:32:37.978Z" },
- { url = "https://files.pythonhosted.org/packages/ea/1d/e6ca79c96ff5b641df6097d299347507d39a9604bde8915e76bf026d6c77/Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648", size = 2943803, upload-time = "2024-10-18T12:32:39.606Z" },
- { url = "https://files.pythonhosted.org/packages/ac/a3/d98d2472e0130b7dd3acdbb7f390d478123dbf62b7d32bda5c830a96116d/Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0", size = 2918946, upload-time = "2024-10-18T12:32:41.679Z" },
- { url = "https://files.pythonhosted.org/packages/c4/a5/c69e6d272aee3e1423ed005d8915a7eaa0384c7de503da987f2d224d0721/Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089", size = 2845707, upload-time = "2024-10-18T12:32:43.478Z" },
- { url = "https://files.pythonhosted.org/packages/58/9f/4149d38b52725afa39067350696c09526de0125ebfbaab5acc5af28b42ea/Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368", size = 2936231, upload-time = "2024-10-18T12:32:45.224Z" },
- { url = "https://files.pythonhosted.org/packages/5a/5a/145de884285611838a16bebfdb060c231c52b8f84dfbe52b852a15780386/Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c", size = 2848157, upload-time = "2024-10-18T12:32:46.894Z" },
- { url = "https://files.pythonhosted.org/packages/50/ae/408b6bfb8525dadebd3b3dd5b19d631da4f7d46420321db44cd99dcf2f2c/Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284", size = 3035122, upload-time = "2024-10-18T12:32:48.844Z" },
- { url = "https://files.pythonhosted.org/packages/af/85/a94e5cfaa0ca449d8f91c3d6f78313ebf919a0dbd55a100c711c6e9655bc/Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7", size = 2930206, upload-time = "2024-10-18T12:32:51.198Z" },
- { url = "https://files.pythonhosted.org/packages/c2/f0/a61d9262cd01351df22e57ad7c34f66794709acab13f34be2675f45bf89d/Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0", size = 333804, upload-time = "2024-10-18T12:32:52.661Z" },
- { url = "https://files.pythonhosted.org/packages/7e/c1/ec214e9c94000d1c1974ec67ced1c970c148aa6b8d8373066123fc3dbf06/Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b", size = 358517, upload-time = "2024-10-18T12:32:54.066Z" },
-]
-
-[[package]]
-name = "brotlicffi"
-version = "1.1.0.0"
+name = "botocore-stubs"
+version = "1.38.46"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "cffi" },
+ { name = "types-awscrt" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/95/9d/70caa61192f570fcf0352766331b735afa931b4c6bc9a348a0925cc13288/brotlicffi-1.1.0.0.tar.gz", hash = "sha256:b77827a689905143f87915310b93b273ab17888fd43ef350d4832c4a71083c13", size = 465192, upload-time = "2023-09-14T14:22:40.707Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/05/45/27cabc7c3022dcb12de5098cc646b374065f5e72fae13600ff1756f365ee/botocore_stubs-1.38.46.tar.gz", hash = "sha256:a04e69766ab8bae338911c1897492f88d05cd489cd75f06e6eb4f135f9da8c7b", size = 42299, upload-time = "2025-06-29T22:58:24.765Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a2/11/7b96009d3dcc2c931e828ce1e157f03824a69fb728d06bfd7b2fc6f93718/brotlicffi-1.1.0.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9b7ae6bd1a3f0df532b6d67ff674099a96d22bc0948955cb338488c31bfb8851", size = 453786, upload-time = "2023-09-14T14:21:57.72Z" },
- { url = "https://files.pythonhosted.org/packages/d6/e6/a8f46f4a4ee7856fbd6ac0c6fb0dc65ed181ba46cd77875b8d9bbe494d9e/brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19ffc919fa4fc6ace69286e0a23b3789b4219058313cf9b45625016bf7ff996b", size = 2911165, upload-time = "2023-09-14T14:21:59.613Z" },
- { url = "https://files.pythonhosted.org/packages/be/20/201559dff14e83ba345a5ec03335607e47467b6633c210607e693aefac40/brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9feb210d932ffe7798ee62e6145d3a757eb6233aa9a4e7db78dd3690d7755814", size = 2927895, upload-time = "2023-09-14T14:22:01.22Z" },
- { url = "https://files.pythonhosted.org/packages/cd/15/695b1409264143be3c933f708a3f81d53c4a1e1ebbc06f46331decbf6563/brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84763dbdef5dd5c24b75597a77e1b30c66604725707565188ba54bab4f114820", size = 2851834, upload-time = "2023-09-14T14:22:03.571Z" },
- { url = "https://files.pythonhosted.org/packages/b4/40/b961a702463b6005baf952794c2e9e0099bde657d0d7e007f923883b907f/brotlicffi-1.1.0.0-cp37-abi3-win32.whl", hash = "sha256:1b12b50e07c3911e1efa3a8971543e7648100713d4e0971b13631cce22c587eb", size = 341731, upload-time = "2023-09-14T14:22:05.74Z" },
- { url = "https://files.pythonhosted.org/packages/1c/fa/5408a03c041114ceab628ce21766a4ea882aa6f6f0a800e04ee3a30ec6b9/brotlicffi-1.1.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:994a4f0681bb6c6c3b0925530a1926b7a189d878e6e5e38fae8efa47c5d9c613", size = 366783, upload-time = "2023-09-14T14:22:07.096Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/84/06490071e26bab22ac79a684e98445df118adcf80c58c33ba5af184030f2/botocore_stubs-1.38.46-py3-none-any.whl", hash = "sha256:cc21d9a7dd994bdd90872db4664d817c4719b51cda8004fd507a4bf65b085a75", size = 66083, upload-time = "2025-06-29T22:58:22.234Z" },
]
[[package]]
@@ -320,19 +325,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/79/b3/28ac139109d9005ad3f6b6f8976ffede6706a6478e21c889ce36c840918e/cryptography-45.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:90cb0a7bb35959f37e23303b7eed0a32280510030daba3f7fdfbb65defde6a97", size = 3390016, upload-time = "2025-07-02T13:05:50.811Z" },
]
-[[package]]
-name = "cssselect2"
-version = "0.8.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "tinycss2" },
- { name = "webencodings" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/9f/86/fd7f58fc498b3166f3a7e8e0cddb6e620fe1da35b02248b1bd59e95dbaaa/cssselect2-0.8.0.tar.gz", hash = "sha256:7674ffb954a3b46162392aee2a3a0aedb2e14ecf99fcc28644900f4e6e3e9d3a", size = 35716, upload-time = "2025-03-05T14:46:07.988Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0f/e7/aa315e6a749d9b96c2504a1ba0ba031ba2d0517e972ce22682e3fccecb09/cssselect2-0.8.0-py3-none-any.whl", hash = "sha256:46fc70ebc41ced7a32cd42d58b1884d72ade23d21e5a4eaaf022401c13f0e76e", size = 15454, upload-time = "2025-03-05T14:46:06.463Z" },
-]
-
[[package]]
name = "dictdiffer"
version = "0.9.0"
@@ -385,30 +377,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667", size = 23924, upload-time = "2024-12-02T10:55:07.599Z" },
]
-[[package]]
-name = "fonttools"
-version = "4.58.5"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/52/97/5735503e58d3816b0989955ef9b2df07e4c99b246469bd8b3823a14095da/fonttools-4.58.5.tar.gz", hash = "sha256:b2a35b0a19f1837284b3a23dd64fd7761b8911d50911ecd2bdbaf5b2d1b5df9c", size = 3526243, upload-time = "2025-07-03T14:04:47.736Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3a/ee/764dd8b99891f815241f449345863cfed9e546923d9cef463f37fd1d7168/fonttools-4.58.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f4b6f1360da13cecc88c0d60716145b31e1015fbe6a59e32f73a4404e2ea92cf", size = 2745867, upload-time = "2025-07-03T14:04:10.586Z" },
- { url = "https://files.pythonhosted.org/packages/e2/23/8fef484c02fef55e226dfeac4339a015c5480b6a496064058491759ac71e/fonttools-4.58.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a036822e915692aa2c03e2decc60f49a8190f8111b639c947a4f4e5774d0d7a", size = 2317933, upload-time = "2025-07-03T14:04:12.335Z" },
- { url = "https://files.pythonhosted.org/packages/ab/47/f92b135864fa777e11ad68420bf89446c91a572fe2782745586f8e6aac0c/fonttools-4.58.5-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6d7709fcf4577b0f294ee6327088884ca95046e1eccde87c53bbba4d5008541", size = 4877844, upload-time = "2025-07-03T14:04:14.58Z" },
- { url = "https://files.pythonhosted.org/packages/3e/65/6c1a83511d8ac32411930495645edb3f8dfabebcb78f08cf6009ba2585ec/fonttools-4.58.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9b5099ca99b79d6d67162778b1b1616fc0e1de02c1a178248a0da8d78a33852", size = 4940106, upload-time = "2025-07-03T14:04:16.563Z" },
- { url = "https://files.pythonhosted.org/packages/fa/90/df8eb77d6cf266cbbba01866a1349a3e9121e0a63002cf8d6754e994f755/fonttools-4.58.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3f2c05a8d82a4d15aebfdb3506e90793aea16e0302cec385134dd960647a36c0", size = 4879458, upload-time = "2025-07-03T14:04:19.584Z" },
- { url = "https://files.pythonhosted.org/packages/26/b1/e32f8de51b7afcfea6ad62780da2fa73212c43a32cd8cafcc852189d7949/fonttools-4.58.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79f0c4b1cc63839b61deeac646d8dba46f8ed40332c2ac1b9997281462c2e4ba", size = 5021917, upload-time = "2025-07-03T14:04:21.736Z" },
- { url = "https://files.pythonhosted.org/packages/89/72/578aa7fe32918dd763c62f447aaed672d665ee10e3eeb1725f4d6493fe96/fonttools-4.58.5-cp313-cp313-win32.whl", hash = "sha256:a1a9a2c462760976882131cbab7d63407813413a2d32cd699e86a1ff22bf7aa5", size = 2186827, upload-time = "2025-07-03T14:04:24.237Z" },
- { url = "https://files.pythonhosted.org/packages/71/a3/21e921b16cb9c029d3308e0cb79c9a937e9ff1fc1ee28c2419f0957b9e7c/fonttools-4.58.5-cp313-cp313-win_amd64.whl", hash = "sha256:bca61b14031a4b7dc87e14bf6ca34c275f8e4b9f7a37bc2fe746b532a924cf30", size = 2235706, upload-time = "2025-07-03T14:04:26.082Z" },
- { url = "https://files.pythonhosted.org/packages/d7/d4/1d85a1996b6188cd2713230e002d79a6f3a289bb17cef600cba385848b72/fonttools-4.58.5-py3-none-any.whl", hash = "sha256:e48a487ed24d9b611c5c4b25db1e50e69e9854ca2670e39a3486ffcd98863ec4", size = 1115318, upload-time = "2025-07-03T14:04:45.378Z" },
-]
-
-[package.optional-dependencies]
-woff = [
- { name = "brotli", marker = "platform_python_implementation == 'CPython'" },
- { name = "brotlicffi", marker = "platform_python_implementation != 'CPython'" },
- { name = "zopfli" },
-]
-
[[package]]
name = "ftfy"
version = "6.3.1"
@@ -453,18 +421,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
]
-[[package]]
-name = "jinja2"
-version = "3.1.6"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markupsafe" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
-]
-
[[package]]
name = "jmespath"
version = "1.0.1"
@@ -508,6 +464,7 @@ dependencies = [
[package.dev-dependencies]
dev = [
+ { name = "boto3-stubs", extra = ["dynamodb"] },
{ name = "jsonlines" },
{ name = "pytest" },
{ name = "pytest-cov" },
@@ -519,6 +476,7 @@ requires-dist = [{ name = "layercake", directory = "../layercake" }]
[package.metadata.requires-dev]
dev = [
+ { name = "boto3-stubs", extras = ["dynamodb"], specifier = ">=1.40.15" },
{ name = "jsonlines", specifier = ">=4.0.0" },
{ name = "pytest", specifier = ">=8.3.4" },
{ name = "pytest-cov", specifier = ">=6.0.0" },
@@ -527,49 +485,49 @@ dev = [
[[package]]
name = "layercake"
-version = "0.7.2"
+version = "0.9.12"
source = { directory = "../layercake" }
dependencies = [
{ name = "arnparse" },
+ { name = "authlib" },
{ name = "aws-lambda-powertools", extra = ["all"] },
{ name = "dictdiffer" },
{ name = "ftfy" },
{ name = "glom" },
- { name = "jinja2" },
{ name = "meilisearch" },
{ name = "orjson" },
+ { name = "passlib" },
{ name = "pycpfcnpj" },
{ name = "pydantic", extra = ["email"] },
{ name = "pydantic-extra-types" },
+ { name = "pyjwt" },
{ name = "pytz" },
- { name = "qrcode" },
{ name = "requests" },
{ name = "smart-open", extra = ["s3"] },
{ name = "sqlite-utils" },
{ name = "unidecode" },
- { name = "weasyprint" },
]
[package.metadata]
requires-dist = [
{ name = "arnparse", specifier = ">=0.0.2" },
- { name = "aws-lambda-powertools", extras = ["all"], specifier = ">=3.8.0" },
+ { name = "authlib", specifier = ">=1.6.1" },
+ { name = "aws-lambda-powertools", extras = ["all"], specifier = ">=3.18.0" },
{ name = "dictdiffer", specifier = ">=0.9.0" },
{ name = "ftfy", specifier = ">=6.3.1" },
{ name = "glom", specifier = ">=24.11.0" },
- { name = "jinja2", specifier = ">=3.1.6" },
{ name = "meilisearch", specifier = ">=0.34.0" },
{ name = "orjson", specifier = ">=3.10.15" },
+ { name = "passlib", specifier = ">=1.7.4" },
{ name = "pycpfcnpj", specifier = ">=1.8" },
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
+ { name = "pyjwt", specifier = ">=2.10.1" },
{ name = "pytz", specifier = ">=2025.1" },
- { name = "qrcode", specifier = ">=8.2" },
{ name = "requests", specifier = ">=2.32.3" },
{ name = "smart-open", extras = ["s3"], specifier = ">=7.1.0" },
{ name = "sqlite-utils", specifier = ">=3.38" },
{ name = "unidecode", specifier = ">=1.4.0" },
- { name = "weasyprint", specifier = ">=65.0" },
]
[package.metadata.requires-dev]
@@ -577,39 +535,12 @@ dev = [
{ name = "boto3", specifier = ">=1.37.16" },
{ name = "boto3-stubs", extras = ["essential"], specifier = ">=1.37.33" },
{ name = "jsonlines", specifier = ">=4.0.0" },
+ { name = "moto", extras = ["all"], specifier = ">=5.1.9" },
{ name = "pytest", specifier = ">=8.3.5" },
{ name = "pytest-cov", specifier = ">=6.0.0" },
{ name = "ruff", specifier = ">=0.11.1" },
]
-[[package]]
-name = "markupsafe"
-version = "3.0.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" },
- { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" },
- { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" },
- { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" },
- { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" },
- { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" },
- { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" },
- { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" },
- { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" },
- { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" },
- { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" },
- { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" },
- { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" },
- { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" },
- { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" },
- { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" },
- { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" },
- { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" },
- { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" },
- { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
-]
-
[[package]]
name = "meilisearch"
version = "0.36.0"
@@ -623,6 +554,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/76/fb/cbc1247429269460e5eb762a798beb702953673d88ce87a26a21263a44d8/meilisearch-0.36.0-py3-none-any.whl", hash = "sha256:f3f0882da7531c038fc6698f18fa492c7d46f8e238600a5af9eb627c7ff21d9e", size = 27666, upload-time = "2025-06-20T02:14:09.268Z" },
]
+[[package]]
+name = "mypy-boto3-dynamodb"
+version = "1.40.14"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/de/85/f5d4261c084cac14e4f19bb074f9292f68e18493174289fb21e07339f25c/mypy_boto3_dynamodb-1.40.14.tar.gz", hash = "sha256:7ec8eb714ac080e7d5572ec8c556953930aba5d2fbcc058aa3cbb87ccce4ac79", size = 47978, upload-time = "2025-08-20T19:27:30.166Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d9/b3/6f2e15a44e66a8cc98fd1032f3aba770f946ba361782a0a979a115fdf6e2/mypy_boto3_dynamodb-1.40.14-py3-none-any.whl", hash = "sha256:302cc169dde3b87a41924855dcfbae173247e18833dee80919f7cc690189f376", size = 57017, upload-time = "2025-08-20T19:27:20.701Z" },
+]
+
[[package]]
name = "orjson"
version = "3.10.18"
@@ -656,58 +596,12 @@ wheels = [
]
[[package]]
-name = "pillow"
-version = "11.3.0"
+name = "passlib"
+version = "1.7.4"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/b6/06/9da9ee59a67fae7761aab3ccc84fa4f3f33f125b370f1ccdb915bf967c11/passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04", size = 689844, upload-time = "2020-10-08T19:00:52.121Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" },
- { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" },
- { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" },
- { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" },
- { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" },
- { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" },
- { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" },
- { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" },
- { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" },
- { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" },
- { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" },
- { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" },
- { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" },
- { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" },
- { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" },
- { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" },
- { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" },
- { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" },
- { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" },
- { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" },
- { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" },
- { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" },
- { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" },
- { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" },
- { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" },
- { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" },
- { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" },
- { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" },
- { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" },
- { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" },
- { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" },
- { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" },
- { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" },
- { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" },
- { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" },
- { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" },
- { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" },
- { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" },
- { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" },
- { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" },
- { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" },
- { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" },
- { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" },
- { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" },
- { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" },
- { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" },
- { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/a4/ab6b7589382ca3df236e03faa71deac88cae040af60c071a78d254a62172/passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1", size = 525554, upload-time = "2020-10-08T19:00:49.856Z" },
]
[[package]]
@@ -821,15 +715,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" },
]
-[[package]]
-name = "pydyf"
-version = "0.11.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/2e/c2/97fc6ce4ce0045080dc99446def812081b57750ed8aa67bfdfafa4561fe5/pydyf-0.11.0.tar.gz", hash = "sha256:394dddf619cca9d0c55715e3c55ea121a9bf9cbc780cdc1201a2427917b86b64", size = 17769, upload-time = "2024-07-12T12:26:51.95Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c9/ac/d5db977deaf28c6ecbc61bbca269eb3e8f0b3a1f55c8549e5333e606e005/pydyf-0.11.0-py3-none-any.whl", hash = "sha256:0aaf9e2ebbe786ec7a78ec3fbffa4cdcecde53fd6f563221d53c6bc1328848a3", size = 8104, upload-time = "2024-07-12T12:26:49.896Z" },
-]
-
[[package]]
name = "pygments"
version = "2.19.2"
@@ -840,12 +725,12 @@ wheels = [
]
[[package]]
-name = "pyphen"
-version = "0.17.2"
+name = "pyjwt"
+version = "2.10.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/69/56/e4d7e1bd70d997713649c5ce530b2d15a5fc2245a74ca820fc2d51d89d4d/pyphen-0.17.2.tar.gz", hash = "sha256:f60647a9c9b30ec6c59910097af82bc5dd2d36576b918e44148d8b07ef3b4aa3", size = 2079470, upload-time = "2025-01-20T13:18:36.296Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/7b/1f/c2142d2edf833a90728e5cdeb10bdbdc094dde8dbac078cee0cf33f5e11b/pyphen-0.17.2-py3-none-any.whl", hash = "sha256:3a07fb017cb2341e1d9ff31b8634efb1ae4dc4b130468c7c39dd3d32e7c3affd", size = 2079358, upload-time = "2025-01-20T13:18:29.629Z" },
+ { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" },
]
[[package]]
@@ -908,18 +793,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
]
-[[package]]
-name = "qrcode"
-version = "8.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/8f/b2/7fc2931bfae0af02d5f53b174e9cf701adbb35f39d69c2af63d4a39f81a9/qrcode-8.2.tar.gz", hash = "sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c", size = 43317, upload-time = "2025-05-01T15:44:24.726Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/dd/b8/d2d6d731733f51684bbf76bf34dab3b70a9148e8f2cef2bb544fccec681a/qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f", size = 45986, upload-time = "2025-05-01T15:44:22.781Z" },
-]
-
[[package]]
name = "requests"
version = "2.32.4"
@@ -1034,27 +907,21 @@ wheels = [
]
[[package]]
-name = "tinycss2"
-version = "1.4.0"
+name = "types-awscrt"
+version = "0.27.6"
source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "webencodings" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085, upload-time = "2024-10-24T14:58:29.895Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/56/ce/5d84526a39f44c420ce61b16654193f8437d74b54f21597ea2ac65d89954/types_awscrt-0.27.6.tar.gz", hash = "sha256:9d3f1865a93b8b2c32f137514ac88cb048b5bc438739945ba19d972698995bfb", size = 16937, upload-time = "2025-08-13T01:54:54.659Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/af/e3d20e3e81d235b3964846adf46a334645a8a9b25a0d3d472743eb079552/types_awscrt-0.27.6-py3-none-any.whl", hash = "sha256:18aced46da00a57f02eb97637a32e5894dc5aa3dc6a905ba3e5ed85b9f3c526b", size = 39626, upload-time = "2025-08-13T01:54:53.454Z" },
]
[[package]]
-name = "tinyhtml5"
-version = "2.0.0"
+name = "types-s3transfer"
+version = "0.13.0"
source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "webencodings" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/fd/03/6111ed99e9bf7dfa1c30baeef0e0fb7e0bd387bd07f8e5b270776fe1de3f/tinyhtml5-2.0.0.tar.gz", hash = "sha256:086f998833da24c300c414d9fe81d9b368fd04cb9d2596a008421cbc705fcfcc", size = 179507, upload-time = "2024-10-29T15:37:14.078Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/42/c1/45038f259d6741c252801044e184fec4dbaeff939a58f6160d7c32bf4975/types_s3transfer-0.13.0.tar.gz", hash = "sha256:203dadcb9865c2f68fb44bc0440e1dc05b79197ba4a641c0976c26c9af75ef52", size = 14175, upload-time = "2025-05-28T02:16:07.614Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/5c/de/27c57899297163a4a84104d5cec0af3b1ac5faf62f44667e506373c6b8ce/tinyhtml5-2.0.0-py3-none-any.whl", hash = "sha256:13683277c5b176d070f82d099d977194b7a1e26815b016114f581a74bbfbf47e", size = 39793, upload-time = "2024-10-29T15:37:11.743Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/5d/6bbe4bf6a79fb727945291aef88b5ecbdba857a603f1bbcf1a6be0d3f442/types_s3transfer-0.13.0-py3-none-any.whl", hash = "sha256:79c8375cbf48a64bff7654c02df1ec4b20d74f8c5672fc13e382f593ca5565b3", size = 19588, upload-time = "2025-05-28T02:16:06.709Z" },
]
[[package]]
@@ -1105,34 +972,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" },
]
-[[package]]
-name = "weasyprint"
-version = "65.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "cffi" },
- { name = "cssselect2" },
- { name = "fonttools", extra = ["woff"] },
- { name = "pillow" },
- { name = "pydyf" },
- { name = "pyphen" },
- { name = "tinycss2" },
- { name = "tinyhtml5" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/38/76/7f865f0019120be20276813097b5729b8487b93dd4aff339aa77ed8c7ad2/weasyprint-65.1.tar.gz", hash = "sha256:120281bdbd42ffaa7d7e5cedbe3182a2cef36ea5ad97fe9f357e43be6a1e58ea", size = 499028, upload-time = "2025-04-14T12:15:02.654Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fe/9a/14f4e5fd4bba988d3684602b72f04c0b299c0f368d26c11a79ceab97aa68/weasyprint-65.1-py3-none-any.whl", hash = "sha256:9baa54282dc86929f6b877034d06b0416e2a7cacb1af3f73d80960592fd0af89", size = 298040, upload-time = "2025-04-14T12:15:00.695Z" },
-]
-
-[[package]]
-name = "webencodings"
-version = "0.5.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" },
-]
-
[[package]]
name = "wrapt"
version = "1.17.2"
@@ -1163,21 +1002,3 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750, upload-time = "2025-01-14T10:35:03.378Z" },
{ url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594, upload-time = "2025-01-14T10:35:44.018Z" },
]
-
-[[package]]
-name = "zopfli"
-version = "0.2.3.post1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/5e/7c/a8f6696e694709e2abcbccd27d05ef761e9b6efae217e11d977471555b62/zopfli-0.2.3.post1.tar.gz", hash = "sha256:96484dc0f48be1c5d7ae9f38ed1ce41e3675fd506b27c11a6607f14b49101e99", size = 175629, upload-time = "2024-10-18T15:42:05.946Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2b/24/0e552e2efce9a20625b56e9609d1e33c2966be33fc008681121ec267daec/zopfli-0.2.3.post1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecb7572df5372abce8073df078207d9d1749f20b8b136089916a4a0868d56051", size = 295485, upload-time = "2024-10-18T15:41:12.57Z" },
- { url = "https://files.pythonhosted.org/packages/08/83/b2564369fb98797a617fe2796097b1d719a4937234375757ad2a3febc04b/zopfli-0.2.3.post1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1cf720896d2ce998bc8e051d4b4ce0d8bec007aab6243102e8e1d22a0b2fb3f", size = 163000, upload-time = "2024-10-18T15:41:13.743Z" },
- { url = "https://files.pythonhosted.org/packages/3c/55/81d419739c2aab35e19b58bce5498dcb58e6446e5eb69f2d3c748b1c9151/zopfli-0.2.3.post1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aad740b4d4fcbaaae4887823925166ffd062db3b248b3f432198fc287381d1a", size = 823699, upload-time = "2024-10-18T15:41:14.874Z" },
- { url = "https://files.pythonhosted.org/packages/9e/91/89f07c8ea3c9bc64099b3461627b07a8384302235ee0f357eaa86f98f509/zopfli-0.2.3.post1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6617fb10f9e4393b331941861d73afb119cd847e88e4974bdbe8068ceef3f73f", size = 826612, upload-time = "2024-10-18T15:41:16.069Z" },
- { url = "https://files.pythonhosted.org/packages/41/31/46670fc0c7805d42bc89702440fa9b73491d68abbc39e28d687180755178/zopfli-0.2.3.post1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a53b18797cdef27e019db595d66c4b077325afe2fd62145953275f53d84ce40c", size = 851148, upload-time = "2024-10-18T15:41:17.403Z" },
- { url = "https://files.pythonhosted.org/packages/22/00/71ad39277bbb88f9fd20fb786bd3ff2ea4025c53b31652a0da796fb546cd/zopfli-0.2.3.post1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b78008a69300d929ca2efeffec951b64a312e9a811e265ea4a907ab546d79fa6", size = 1754215, upload-time = "2024-10-18T15:41:18.661Z" },
- { url = "https://files.pythonhosted.org/packages/d0/4e/e542c508d20c3dfbef1b90fcf726f824f505e725747f777b0b7b7d1deb95/zopfli-0.2.3.post1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa5f90d6298bda02a95bc8dc8c3c19004d5a4e44bda00b67ca7431d857b4b54", size = 1905988, upload-time = "2024-10-18T15:41:19.933Z" },
- { url = "https://files.pythonhosted.org/packages/ba/a5/817ac1ecc888723e91dc172e8c6eeab9f48a1e52285803b965084e11bbd5/zopfli-0.2.3.post1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2768c877f76c8a0e7519b1c86c93757f3c01492ddde55751e9988afb7eff64e1", size = 1835907, upload-time = "2024-10-18T15:41:21.582Z" },
- { url = "https://files.pythonhosted.org/packages/cd/35/2525f90c972d8aafc39784a8c00244eeee8e8221b26cbc576748ee9dc1cd/zopfli-0.2.3.post1-cp313-cp313-win32.whl", hash = "sha256:71390dbd3fbf6ebea9a5d85ffed8c26ee1453ee09248e9b88486e30e0397b775", size = 82742, upload-time = "2024-10-18T15:41:23.362Z" },
- { url = "https://files.pythonhosted.org/packages/2f/c6/49b27570923956d52d37363e8f5df3a31a61bd7719bb8718527a9df3ae5f/zopfli-0.2.3.post1-cp313-cp313-win_amd64.whl", hash = "sha256:a86eb88e06bd87e1fff31dac878965c26b0c26db59ddcf78bb0379a954b120de", size = 99408, upload-time = "2024-10-18T15:41:24.377Z" },
-]
diff --git a/layercake/pyproject.toml b/layercake/pyproject.toml
index 804bff6..52b30be 100644
--- a/layercake/pyproject.toml
+++ b/layercake/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "layercake"
-version = "0.9.12"
+version = "0.9.14"
description = "Packages shared dependencies to optimize deployment and ensure consistency across functions."
readme = "README.md"
authors = [
@@ -26,6 +26,7 @@ dependencies = [
"authlib>=1.6.1",
"passlib>=1.7.4",
"pyjwt>=2.10.1",
+ "psycopg[binary]>=3.2.9",
]
[dependency-groups]
diff --git a/layercake/uv.lock b/layercake/uv.lock
index eb87007..c471302 100644
--- a/layercake/uv.lock
+++ b/layercake/uv.lock
@@ -675,7 +675,7 @@ wheels = [
[[package]]
name = "layercake"
-version = "0.9.11"
+version = "0.9.13"
source = { editable = "." }
dependencies = [
{ name = "arnparse" },
@@ -687,6 +687,7 @@ dependencies = [
{ name = "meilisearch" },
{ name = "orjson" },
{ name = "passlib" },
+ { name = "psycopg", extra = ["binary"] },
{ name = "pycpfcnpj" },
{ name = "pydantic", extra = ["email"] },
{ name = "pydantic-extra-types" },
@@ -720,6 +721,7 @@ requires-dist = [
{ name = "meilisearch", specifier = ">=0.34.0" },
{ name = "orjson", specifier = ">=3.10.15" },
{ name = "passlib", specifier = ">=1.7.4" },
+ { name = "psycopg", extras = ["binary"], specifier = ">=3.2.9" },
{ name = "pycpfcnpj", specifier = ">=1.8" },
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
@@ -1059,6 +1061,53 @@ 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" },
]
+[[package]]
+name = "psycopg"
+version = "3.2.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { 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/29/6f/ec9957e37a606cd7564412e03f41f1b3c3637a5be018d0849914cb06e674/psycopg_binary-3.2.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be7d650a434921a6b1ebe3fff324dbc2364393eb29d7672e638ce3e21076974e", size = 4022205, upload-time = "2025-05-13T16:07:48.195Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/ba/497b8bea72b20a862ac95a94386967b745a472d9ddc88bc3f32d5d5f0d43/psycopg_binary-3.2.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a76b4722a529390683c0304501f238b365a46b1e5fb6b7249dbc0ad6fea51a0", size = 4083795, upload-time = "2025-05-13T16:07:50.917Z" },
+ { url = "https://files.pythonhosted.org/packages/42/07/af9503e8e8bdad3911fd88e10e6a29240f9feaa99f57d6fac4a18b16f5a0/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96a551e4683f1c307cfc3d9a05fec62c00a7264f320c9962a67a543e3ce0d8ff", size = 4655043, upload-time = "2025-05-13T16:07:54.857Z" },
+ { url = "https://files.pythonhosted.org/packages/28/ed/aff8c9850df1648cc6a5cc7a381f11ee78d98a6b807edd4a5ae276ad60ad/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61d0a6ceed8f08c75a395bc28cb648a81cf8dee75ba4650093ad1a24a51c8724", size = 4477972, upload-time = "2025-05-13T16:07:57.925Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/bd/8e9d1b77ec1a632818fe2f457c3a65af83c68710c4c162d6866947d08cc5/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad280bbd409bf598683dda82232f5215cfc5f2b1bf0854e409b4d0c44a113b1d", size = 4737516, upload-time = "2025-05-13T16:08:01.616Z" },
+ { url = "https://files.pythonhosted.org/packages/46/ec/222238f774cd5a0881f3f3b18fb86daceae89cc410f91ef6a9fb4556f236/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76eddaf7fef1d0994e3d536ad48aa75034663d3a07f6f7e3e601105ae73aeff6", size = 4436160, upload-time = "2025-05-13T16:08:04.278Z" },
+ { url = "https://files.pythonhosted.org/packages/37/78/af5af2a1b296eeca54ea7592cd19284739a844974c9747e516707e7b3b39/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:52e239cd66c4158e412318fbe028cd94b0ef21b0707f56dcb4bdc250ee58fd40", size = 3753518, upload-time = "2025-05-13T16:08:07.567Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/ac/8a3ed39ea069402e9e6e6a2f79d81a71879708b31cc3454283314994b1ae/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:08bf9d5eabba160dd4f6ad247cf12f229cc19d2458511cab2eb9647f42fa6795", size = 3313598, upload-time = "2025-05-13T16:08:09.999Z" },
+ { url = "https://files.pythonhosted.org/packages/da/43/26549af068347c808fbfe5f07d2fa8cef747cfff7c695136172991d2378b/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1b2cf018168cad87580e67bdde38ff5e51511112f1ce6ce9a8336871f465c19a", size = 3407289, upload-time = "2025-05-13T16:08:12.66Z" },
+ { url = "https://files.pythonhosted.org/packages/67/55/ea8d227c77df8e8aec880ded398316735add8fda5eb4ff5cc96fac11e964/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:14f64d1ac6942ff089fc7e926440f7a5ced062e2ed0949d7d2d680dc5c00e2d4", size = 3472493, upload-time = "2025-05-13T16:08:15.672Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/02/6ff2a5bc53c3cd653d281666728e29121149179c73fddefb1e437024c192/psycopg_binary-3.2.9-cp312-cp312-win_amd64.whl", hash = "sha256:7a838852e5afb6b4126f93eb409516a8c02a49b788f4df8b6469a40c2157fa21", size = 2927400, upload-time = "2025-05-13T16:08:18.652Z" },
+ { 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]]
name = "py-partiql-parser"
version = "0.6.1"
@@ -1643,6 +1692,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" },
]
+[[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]]
name = "unidecode"
version = "1.4.0"
diff --git a/streams-events/app/config.py b/streams-events/app/config.py
index 199a75a..240c5a2 100644
--- a/streams-events/app/config.py
+++ b/streams-events/app/config.py
@@ -2,3 +2,9 @@ import os
MEILISEARCH_HOST: str = os.getenv('MEILISEARCH_HOST') # type: ignore
MEILISEARCH_API_KEY: str = os.getenv('MEILISEARCH_API_KEY') # type: ignore
+
+POSTGRES_DB: str = os.getenv('POSTGRES_DB') # type: ignore
+POSTGRES_HOST: str = os.getenv('POSTGRES_HOST') # type: ignore
+POSTGRES_PORT: str = os.getenv('POSTGRES_PORT') # type: ignore
+POSTGRES_USER: str = os.getenv('POSTGRES_USER') # type: ignore
+POSTGRES_PASSWORD: str = os.getenv('POSTGRES_PASSWORD') # type: ignore
diff --git a/streams-events/app/events/docs_into_eventbus.py b/streams-events/app/events/docs_into_eventbus.py
index c176fb1..c3e77fc 100644
--- a/streams-events/app/events/docs_into_eventbus.py
+++ b/streams-events/app/events/docs_into_eventbus.py
@@ -16,6 +16,7 @@ from aws_lambda_powertools.utilities.data_classes.dynamo_db_stream_event import
)
from aws_lambda_powertools.utilities.typing import LambdaContext
from layercake.dateutils import now, ttl
+
from utils import diff, table_from_arn
if TYPE_CHECKING:
diff --git a/streams-events/app/events/index_docs_into_meili.py b/streams-events/app/events/index_docs_into_meili.py
index c136bbf..3d72f45 100644
--- a/streams-events/app/events/index_docs_into_meili.py
+++ b/streams-events/app/events/index_docs_into_meili.py
@@ -4,9 +4,10 @@ from aws_lambda_powertools.utilities.data_classes import (
event_source,
)
from aws_lambda_powertools.utilities.typing import LambdaContext
+from meilisearch import Client as Meilisearch
+
from config import MEILISEARCH_API_KEY, MEILISEARCH_HOST
from meili import Op
-from meilisearch import Client as Meilisearch
from utils import table_from_arn
logger = Logger(__name__)
diff --git a/streams-events/app/events/replicate_into_meili.py b/streams-events/app/events/replicate_into_meili.py
deleted file mode 100644
index 239d888..0000000
--- a/streams-events/app/events/replicate_into_meili.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from aws_lambda_powertools import Logger, Tracer
-from aws_lambda_powertools.utilities.data_classes import (
- DynamoDBStreamEvent,
- event_source,
-)
-from aws_lambda_powertools.utilities.typing import LambdaContext
-from config import MEILISEARCH_API_KEY, MEILISEARCH_HOST
-from layercake.strutils import md5_hash
-from meili import Op
-from meilisearch import Client as Meilisearch
-from utils import table_from_arn
-
-logger = Logger(__name__)
-tracer = Tracer()
-meili_client = Meilisearch(MEILISEARCH_HOST, MEILISEARCH_API_KEY)
-
-
-@event_source(data_class=DynamoDBStreamEvent)
-@logger.inject_lambda_context
-@tracer.capture_lambda_handler
-def lambda_handler(event: DynamoDBStreamEvent, context: LambdaContext):
- with Op(meili_client) as op:
- for record in event.records:
- new_image = record.dynamodb.new_image # type: ignore
- index = table_from_arn(record.event_source_arn) # type: ignore
- keys = record.dynamodb.keys # type: ignore
- _id = md5_hash(str(keys))
-
- op.append(
- f'_{index}_replication',
- op=record.event_name, # type: ignore
- data=({'_id': _id} | new_image) or _id,
- )
diff --git a/streams-events/pyproject.toml b/streams-events/pyproject.toml
index a3458df..e9affc7 100644
--- a/streams-events/pyproject.toml
+++ b/streams-events/pyproject.toml
@@ -21,8 +21,15 @@ dev = [
pythonpath = ["app/"]
addopts = "--cov --cov-report html -v"
+[tool.ruff]
+target-version = "py311"
+src = ["app"]
+
[tool.ruff.format]
quote-style = "single"
+[tool.ruff.lint]
+select = ["E", "F", "I"]
+
[tool.uv.sources]
layercake = { path = "../layercake" }
diff --git a/streams-events/template.yaml b/streams-events/template.yaml
index d14e769..cb42ad1 100644
--- a/streams-events/template.yaml
+++ b/streams-events/template.yaml
@@ -8,15 +8,20 @@ Globals:
Architectures:
- x86_64
Layers:
- - !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:94
+ - !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:96
Environment:
Variables:
- LOG_LEVEL: INFO
+ LOG_LEVEL: DEBUG
TZ: America/Sao_Paulo
POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1
POWERTOOLS_LOGGER_LOG_EVENT: true
- MEILISEARCH_HOST: https://meili.eduseg.com.br
+ MEILISEARCH_HOST: https://meili.saladeaula.digital
MEILISEARCH_API_KEY: "{{resolve:ssm:/saladeaula/meili_api_key}}"
+ POSTGRES_DB: saladeaula.digital
+ POSTGRES_HOST: sp-node01.saladeaula.digital
+ POSTGRES_PORT: 5432
+ POSTGRES_USER: "{{resolve:ssm:/saladeaula/postgres_user}}"
+ POSTGRES_PASSWORD: "{{resolve:ssm:/saladeaula/postgres_password}}"
Resources:
MeilisearchLog:
@@ -29,6 +34,11 @@ Resources:
Properties:
RetentionInDays: 90
+ EventPostgresLog:
+ Type: AWS::Logs::LogGroup
+ Properties:
+ RetentionInDays: 90
+
EventIndexDocsIntoMeiliFunction:
Type: AWS::Serverless::Function
Properties:
@@ -46,7 +56,6 @@ Resources:
FilterCriteria:
Filters:
- Pattern: '{ "dynamodb" : { "Keys" : { "sk" : { "S" : [ "0" ] } } } }'
-
Users:
Type: DynamoDB
Properties:
@@ -57,7 +66,6 @@ Resources:
FilterCriteria:
Filters:
- Pattern: '{ "dynamodb" : { "Keys" : { "sk" : { "S" : [ "0" ] } } } }'
-
Orders:
Type: DynamoDB
Properties:
@@ -68,7 +76,6 @@ Resources:
FilterCriteria:
Filters:
- Pattern: '{ "dynamodb" : { "Keys" : { "sk" : { "S" : [ "0" ] } } } }'
-
Courses:
Type: DynamoDB
Properties:
@@ -97,7 +104,7 @@ Resources:
StartingPosition: LATEST
MaximumRetryAttempts: 5
BatchSize: 25
- Enrolllments:
+ Enrollments:
Type: DynamoDB
Properties:
Stream: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/betaeducacao-prod-enrollments/stream/2023-08-22T22:56:55.612
@@ -119,15 +126,12 @@ Resources:
MaximumRetryAttempts: 5
BatchSize: 25
- EventReplicateIntoMeiliFunction:
+ EventReplicateIntoPostgresFunction:
Type: AWS::Serverless::Function
Properties:
- Handler: events.replicate_into_meili.lambda_handler
+ Handler: events.replicate_into_postgres.lambda_handler
LoggingConfig:
- LogGroup: !Ref EventBusLog
- Policies:
- - EventBridgePutEventsPolicy:
- EventBusName: default
+ LogGroup: !Ref EventPostgresLog
Events:
Users:
Type: DynamoDB
@@ -136,7 +140,7 @@ Resources:
StartingPosition: LATEST
MaximumRetryAttempts: 5
BatchSize: 25
- Enrolllments:
+ Enrollments:
Type: DynamoDB
Properties:
Stream: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/betaeducacao-prod-enrollments/stream/2023-08-22T22:56:55.612
diff --git a/streams-events/tests/conftest.py b/streams-events/tests/conftest.py
index 3b06bae..b7b40b2 100644
--- a/streams-events/tests/conftest.py
+++ b/streams-events/tests/conftest.py
@@ -9,6 +9,11 @@ import pytest
def pytest_configure():
os.environ['TZ'] = 'America/Sao_Paulo'
os.environ['MEILISEARCH_HOST'] = 'http://127.0.0.1:7700'
+ os.environ['POSTGRES_DB'] = 'pytest'
+ os.environ['POSTGRES_HOST'] = 'localhost'
+ os.environ['POSTGRES_PORT'] = '5432'
+ os.environ['POSTGRES_USER'] = 'postgres'
+ os.environ['POSTGRES_PASSWORD'] = 'pgsql@pwd'
def load_jsonfile(path: str) -> dict:
diff --git a/streams-events/tests/test_replicate_into_meili.py b/streams-events/tests/test_replicate_into_meili.py
deleted file mode 100644
index 9aede9f..0000000
--- a/streams-events/tests/test_replicate_into_meili.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import app.events.replicate_into_meili as app
-
-
-def test_record_handler(dynamodb_stream_event, lambda_context):
- app.lambda_handler(dynamodb_stream_event, lambda_context)
diff --git a/streams-events/uv.lock b/streams-events/uv.lock
index 2d1278c..76b1b72 100644
--- a/streams-events/uv.lock
+++ b/streams-events/uv.lock
@@ -484,7 +484,7 @@ wheels = [
[[package]]
name = "layercake"
-version = "0.9.12"
+version = "0.9.14"
source = { directory = "../layercake" }
dependencies = [
{ name = "arnparse" },
@@ -496,6 +496,7 @@ dependencies = [
{ name = "meilisearch" },
{ name = "orjson" },
{ name = "passlib" },
+ { name = "psycopg", extra = ["binary"] },
{ name = "pycpfcnpj" },
{ name = "pydantic", extra = ["email"] },
{ name = "pydantic-extra-types" },
@@ -518,6 +519,7 @@ requires-dist = [
{ name = "meilisearch", specifier = ">=0.34.0" },
{ name = "orjson", specifier = ">=3.10.15" },
{ name = "passlib", specifier = ">=1.7.4" },
+ { name = "psycopg", extras = ["binary"], specifier = ">=3.2.9" },
{ name = "pycpfcnpj", specifier = ">=1.8" },
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
@@ -634,6 +636,53 @@ 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" },
]
+[[package]]
+name = "psycopg"
+version = "3.2.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+ { 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/29/6f/ec9957e37a606cd7564412e03f41f1b3c3637a5be018d0849914cb06e674/psycopg_binary-3.2.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be7d650a434921a6b1ebe3fff324dbc2364393eb29d7672e638ce3e21076974e", size = 4022205, upload-time = "2025-05-13T16:07:48.195Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/ba/497b8bea72b20a862ac95a94386967b745a472d9ddc88bc3f32d5d5f0d43/psycopg_binary-3.2.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a76b4722a529390683c0304501f238b365a46b1e5fb6b7249dbc0ad6fea51a0", size = 4083795, upload-time = "2025-05-13T16:07:50.917Z" },
+ { url = "https://files.pythonhosted.org/packages/42/07/af9503e8e8bdad3911fd88e10e6a29240f9feaa99f57d6fac4a18b16f5a0/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96a551e4683f1c307cfc3d9a05fec62c00a7264f320c9962a67a543e3ce0d8ff", size = 4655043, upload-time = "2025-05-13T16:07:54.857Z" },
+ { url = "https://files.pythonhosted.org/packages/28/ed/aff8c9850df1648cc6a5cc7a381f11ee78d98a6b807edd4a5ae276ad60ad/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61d0a6ceed8f08c75a395bc28cb648a81cf8dee75ba4650093ad1a24a51c8724", size = 4477972, upload-time = "2025-05-13T16:07:57.925Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/bd/8e9d1b77ec1a632818fe2f457c3a65af83c68710c4c162d6866947d08cc5/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad280bbd409bf598683dda82232f5215cfc5f2b1bf0854e409b4d0c44a113b1d", size = 4737516, upload-time = "2025-05-13T16:08:01.616Z" },
+ { url = "https://files.pythonhosted.org/packages/46/ec/222238f774cd5a0881f3f3b18fb86daceae89cc410f91ef6a9fb4556f236/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76eddaf7fef1d0994e3d536ad48aa75034663d3a07f6f7e3e601105ae73aeff6", size = 4436160, upload-time = "2025-05-13T16:08:04.278Z" },
+ { url = "https://files.pythonhosted.org/packages/37/78/af5af2a1b296eeca54ea7592cd19284739a844974c9747e516707e7b3b39/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:52e239cd66c4158e412318fbe028cd94b0ef21b0707f56dcb4bdc250ee58fd40", size = 3753518, upload-time = "2025-05-13T16:08:07.567Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/ac/8a3ed39ea069402e9e6e6a2f79d81a71879708b31cc3454283314994b1ae/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:08bf9d5eabba160dd4f6ad247cf12f229cc19d2458511cab2eb9647f42fa6795", size = 3313598, upload-time = "2025-05-13T16:08:09.999Z" },
+ { url = "https://files.pythonhosted.org/packages/da/43/26549af068347c808fbfe5f07d2fa8cef747cfff7c695136172991d2378b/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1b2cf018168cad87580e67bdde38ff5e51511112f1ce6ce9a8336871f465c19a", size = 3407289, upload-time = "2025-05-13T16:08:12.66Z" },
+ { url = "https://files.pythonhosted.org/packages/67/55/ea8d227c77df8e8aec880ded398316735add8fda5eb4ff5cc96fac11e964/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:14f64d1ac6942ff089fc7e926440f7a5ced062e2ed0949d7d2d680dc5c00e2d4", size = 3472493, upload-time = "2025-05-13T16:08:15.672Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/02/6ff2a5bc53c3cd653d281666728e29121149179c73fddefb1e437024c192/psycopg_binary-3.2.9-cp312-cp312-win_amd64.whl", hash = "sha256:7a838852e5afb6b4126f93eb409516a8c02a49b788f4df8b6469a40c2157fa21", size = 2927400, upload-time = "2025-05-13T16:08:18.652Z" },
+ { 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]]
name = "pycouchdb"
version = "1.16.0"
@@ -1003,6 +1052,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125, upload-time = "2025-02-25T17:27:57.754Z" },
]
+[[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]]
name = "unidecode"
version = "1.4.0"