Files
saladeaula.digital/api.saladeaula.digital/app/routes/courses/__init__.py
2025-10-30 02:02:23 -03:00

172 lines
4.7 KiB
Python

import json
from datetime import datetime
from http import HTTPStatus
from io import BytesIO
from typing import Annotated, Any
from urllib.parse import urlparse
import requests
from aws_lambda_powertools.event_handler.api_gateway import Response, Router
from aws_lambda_powertools.event_handler.exceptions import (
BadRequestError,
NotFoundError,
)
from aws_lambda_powertools.event_handler.openapi.params import Body
from layercake.dateutils import now
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
from pydantic import UUID4, BaseModel
from api_gateway import JSONResponse
from boto3clients import dynamodb_client, s3_client
from config import BUCKET_NAME, COURSE_TABLE, PAPERFORGE_API
from form_data import parse
router = Router()
dyn = DynamoDBPersistenceLayer(COURSE_TABLE, dynamodb_client)
@router.get('/<course_id>')
def get_course(course_id: str):
return dyn.collection.get_item(
KeyPair(course_id, '0'),
exc_cls=NotFoundError,
)
class Cert(BaseModel):
exp_interval: int | None = None
s3_uri: str | None = None
def model_dump(self, **kwargs) -> dict[str, Any]:
return super().model_dump(exclude_none=True, **kwargs)
class Course(BaseModel):
id: UUID4
name: str
access_period: int
cert: Cert
draft: bool = False
rawfile: bytes | None = None
@router.put('/<course_id>')
def put_course(course_id: str):
event = router.current_event
if not event.decoded_body:
raise BadRequestError('Invalid request body')
now_ = now()
body = parse(
event.headers,
BytesIO(event.decoded_body.encode()),
)
course = Course.model_validate({'id': course_id, 'cert': {}} | body)
if course.rawfile:
object_key = f'certs/templates/{course_id}.html'
course.cert.s3_uri = f's3://{BUCKET_NAME}/{object_key}'
s3_client.put_object(
Bucket=BUCKET_NAME,
Key=object_key,
Body=course.rawfile,
ContentType='text/html',
)
with dyn.transact_writer() as transact:
transact.update(
key=KeyPair(str(course.id), '0'),
update_expr='SET #name = :name, \
access_period = :access_period, \
cert = :cert, \
draft = :draft, \
updated_at = :updated_at',
expr_attr_names={
'#name': 'name',
},
expr_attr_values={
':name': course.name,
':cert': course.cert.model_dump(),
':access_period': course.access_period,
':draft': course.draft,
':updated_at': now_,
},
cond_expr='attribute_exists(sk)',
exc_cls=BadRequestError,
)
return JSONResponse(HTTPStatus.NO_CONTENT)
@router.post('/<course_id>/sample')
def sample(course_id: str, s3_uri: Annotated[str, Body(embed=True)]):
now_ = now()
# Send template URI and data to Paperforge API to generate a PDF
r = requests.post(
PAPERFORGE_API,
data=json.dumps(
{
'template_uri': s3_uri,
'args': {
'name': 'Juscelino Kubitschek',
'cpf': '***.810.132-**',
'score': 100,
'started_at': now_.strftime('%d/%m/%Y'),
'completed_at': now_.strftime('%d/%m/%Y'),
'today': _datefmt(now_),
'year': now_.strftime('%Y'),
'expires_at': now_.strftime('%d/%m/%Y'),
},
},
),
)
r.raise_for_status()
return Response(
body=r.content,
content_type='application/pdf',
status_code=HTTPStatus.OK,
headers={
'Content-Disposition': f'attachment; filename="{course_id}.pdf"',
},
)
@router.post('/<course_id>/template')
def template(course_id: str, s3_uri: Annotated[str, Body(embed=True)]):
parsed = urlparse(s3_uri)
bucket = parsed.netloc
object_key = parsed.path.lstrip('/')
r = s3_client.get_object(Bucket=bucket, Key=object_key)
return Response(
body=r['Body'].read(),
content_type='text/html',
status_code=HTTPStatus.OK,
headers={
'Content-Disposition': f'attachment; filename="{course_id}.html"',
},
)
def _datefmt(dt: datetime) -> str:
months = [
'Janeiro',
'Fevereiro',
'Março',
'Abril',
'Maio',
'Junho',
'Julho',
'Agosto',
'Setembro',
'Outubro',
'Novembro',
'Dezembro',
]
return f'{dt.day:02d} de {months[dt.month - 1]} de {dt.year}'