add batch

This commit is contained in:
2025-05-21 15:48:59 -03:00
parent 7f4fec6e1e
commit 249116cc76
20 changed files with 786 additions and 627 deletions

View File

@@ -32,6 +32,8 @@ app.use(middlewares=[AuthenticationMiddleware()])
app.include_router(courses.router, prefix='/courses')
app.include_router(enrollments.router, prefix='/enrollments')
app.include_router(enrollments.vacancies, prefix='/enrollments')
app.include_router(enrollments.enroll, prefix='/enrollments')
app.include_router(enrollments.cancel, prefix='/enrollments')
app.include_router(orders.router, prefix='/orders')
app.include_router(users.router, prefix='/users')
app.include_router(users.logs, prefix='/users')

View File

@@ -1,4 +1,4 @@
from typing import Annotated
from typing import Annotated, Literal
from uuid import uuid4
from layercake.extra_types import CnpjStr, CpfStr, NameStr
@@ -37,3 +37,11 @@ class Course(BaseModel):
name: str
cert: Cert | None = None
access_period: int = 90 # 3 months
class Enrollment(BaseModel):
id: UUID4 = Field(default_factory=uuid4)
user: User
course: Course
progress: int = Field(default=0, ge=0, le=100)
status: Literal['PENDING'] = 'PENDING'

View File

@@ -5,22 +5,19 @@ from elasticsearch import Elasticsearch
from layercake.dynamodb import (
DynamoDBCollection,
DynamoDBPersistenceLayer,
KeyPair,
SortKey,
TransactKey,
)
from pydantic import UUID4, BaseModel
import elastic
from boto3clients import dynamodb_client
from conf import ELASTIC_CONN, ENROLLMENT_TABLE, USER_TABLE
import elastic
from middlewares.audit_log_middleware import AuditLogMiddleware
from middlewares.authentication_middleware import User
from rules.enrollment import set_status_as_canceled
from .cancel import router as cancel
from .enroll import router as enroll
from .vacancies import router as vacancies
__all__ = ['vacancies']
__all__ = ['vacancies', 'cancel', 'enroll']
router = Router()
@@ -62,38 +59,3 @@ def get_enrollment(id: str):
+ SortKey('tenant')
+ SortKey('cert')
)
class Cancel(BaseModel):
id: UUID4 | str
lock_hash: str
course: dict = {}
vacancy: dict = {}
@router.patch(
'/<id>/cancel',
compress=True,
tags=['Enrollment'],
middlewares=[
AuditLogMiddleware('ENROLLMENT_CANCEL', user_collect, ('id', 'course'))
],
)
def cancel(id: str, payload: Cancel):
user: User = router.context['user']
set_status_as_canceled(
id,
lock_hash=payload.lock_hash,
author=user.model_dump(), # type: ignore
course=payload.course, # type: ignore
vacancy_key=KeyPair.parse_obj(payload.vacancy),
persistence_layer=enrollment_layer,
)
return payload
@router.post('/', compress=True, tags=['Enrollment'])
def enroll():
return {}

View File

@@ -1,10 +1,19 @@
from typing import TypedDict
from datetime import timedelta
from enum import Enum
from typing import Literal, TypedDict
from uuid import uuid4
from layercake.dateutils import now
from layercake.dateutils import now, ttl
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, TransactItems
from layercake.strutils import md5_hash
from conf import ORDER_TABLE
from models import Course, Enrollment
class Tenant(TypedDict):
id: str
name: str
class Author(TypedDict):
@@ -12,10 +21,170 @@ class Author(TypedDict):
name: str
class Course(TypedDict):
class Vacancy(TypedDict): ...
class Rel(TypedDict):
id: str
name: str
time_in_days: int
scope: Literal['ORG', 'USER', 'ENROLLMENT']
class LifecycleEvents(str, Enum):
"""Schedules lifecycle events."""
# Reminder if the user does not access within 3 days
REMINDER_NO_ACCESS_3_DAYS = 'schedules#reminder_no_access_3_days'
# When there is no activity 7 days after the first access
NO_ACTIVITY_7_DAYS = 'schedules#no_activity_7_days'
# When the access period expires
ACCESS_PERIOD_EXPIRED = 'schedules#access_period_expired'
# When the course certificate expires
CERTIFICATE_EXPIRATION = 'schedules#certificate_expiration'
# Archive the course after the certificate expires
COURSE_ARCHIVED = 'schedules#course_archived'
def enroll(
enrollment: Enrollment,
*,
tenant: Tenant,
rel: tuple[Rel, ...] | Rel = (),
author: Author | None = None,
vacancy: Vacancy | None = None,
ensure_vacancy: bool = True,
persistence_layer: DynamoDBPersistenceLayer,
) -> bool:
"""Enrolls a user into a course and schedules lifecycle events."""
now_ = now()
user = enrollment.user
course = enrollment.course
exp_interval = course.exp_interval
lock_hash = md5_hash('%s%s' % (user.id, course.id))
ttl_date = now_ + timedelta(days=exp_interval - 30)
transact = TransactItems(persistence_layer.table_name)
transact.put(
item={
'sk': '0',
'create_date': now_,
'tenant__org_id': tenant['id'],
**enrollment.model_dump(),
},
)
transact.put(
item={
'id': enrollment.id,
'sk': 'metadata#tenant',
'org_id': tenant['id'],
'name': tenant['name'],
'create_date': now_,
},
)
transact.put(
item={
'id': enrollment.id,
'sk': LifecycleEvents.COURSE_ARCHIVED,
'create_date': now_,
'ttl': ttl(days=exp_interval, start_dt=now_),
},
)
transact.put(
item={
'id': enrollment.id,
'sk': LifecycleEvents.REMINDER_NO_ACCESS_3_DAYS,
'name': user.name,
'email': user.email,
'course': course.name,
'create_date': now_,
'ttl': ttl(days=3, start_dt=now_),
},
)
transact.put(
item={
'id': enrollment.id,
'sk': LifecycleEvents.ACCESS_PERIOD_EXPIRED,
'name': user.name,
'email': user.email,
'course': course.name,
'create_date': now_,
'ttl': ttl(start_dt=now_ + timedelta(days=course.access_period - 30)),
},
)
for r in rel:
print(r['id'])
transact.put(
item={
'id': enrollment.id,
# 'sk': 'rel#{}' % r['id'],
'create_date': now_,
},
)
if author:
transact.put(
item={
'id': enrollment.id,
'sk': 'metadata#author',
'user_id': author['id'],
'name': author['name'],
'create_date': now_,
},
)
if vacancy:
transact.put(
item={
'id': enrollment.id,
'sk': 'parent_vacancy',
# 'vacancy': vacancy.model_dump(),
}
)
if ensure_vacancy:
# Ensures that there's a vacancy
transact.delete(
key=vacancy.model_dump(),
cond_expr='attribute_exists(sk)',
)
# Add cancel policy if there is a vacancy
if vacancy:
transact.put(
item={
'id': enrollment.id,
'sk': 'metadata#cancel_policy',
'create_date': now_,
}
)
# To ensure that the user does not enroll in the same course again until
# the certificate expires.
transact.put(
item={
'id': 'metadata#lock',
'sk': lock_hash,
'enrollment_id': enrollment.id,
'create_date': vacancy,
'ttl': ttl(start_dt=ttl_date),
},
cond_expr='attribute_not_exists(sk)',
)
transact.put(
item={
'id': enrollment.id,
'sk': 'lock',
'hash': lock_hash,
'create_date': vacancy,
'ttl': ttl(start_dt=ttl_date),
},
)
return persistence_layer.transact_write_items(transact)
def set_status_as_canceled(