trial
Trial PDF Linking
Resumen
Se extendieron los endpoints POST /activities/trial y PATCH /activities/trial para aceptar un campo opcional reports con un array de UUIDs de archivos. Esto permite vincular PDFs a un ensayo en la misma request de creación o actualización.
Cambios realizados
Schema (features/trials/schema/trial_schema.py)
TrialPostSchema: se agregóreports = fields.List(fields.UUID(), load_default=None, validate=validate.Length(max=50))TrialPatchSchema: idem, másclass Meta: unknown = EXCLUDEpara ignorar campos extra del frontend (ej:isRowDisabled)
API (features/trials/api/trial.py)
- POST: extrae
reportsdel body, si viene no-null llama arepo.bulk_link_reports(trial_uuid, reports, trial_name) - PATCH: extrae
reportsdel body, si viene no-null busca el nombre del trial y llama arepo.replace_reports(trial_uuid, reports, trial_name) - Se eliminó el import de
TrialReportRepo— todo opera dentro deTrialRepo(misma transacción)
Service (features/trials/service/trial_service.py)
Se agregaron dos métodos a TrialRepo:
bulk_link_reports(trial_uuid, file_uuids, trial_name)
- De-duplica UUIDs
- Inserta en
trial_reportscon unWHERE NOT EXISTSpara no crear duplicados contra links activos - Genera el nombre como
{trial_name}_{1},{trial_name}_{2}, etc. - Una sola query al DB
replace_reports(trial_uuid, file_uuids, trial_name)
- Hace soft-delete de todos los reports activos del trial (
SET deleted_at = NOW()) - Si hay UUIDs nuevos, llama a
bulk_link_reports - Dos queries al DB (mínimo necesario)
Service (features/trials/service/trial_report_service.py)
- Se eliminaron
bulk_link_reports,replace_reportsy_validate_files_exist(duplicados, ahora viven enTrialRepo) - Se mantienen:
get_by_trial,create_report,_soft_delete_all,delete_report(usados por el endpoint standalonePOST/DELETE /activities/trial/reports) get_by_trialahora haceLEFT JOIN attachmentspara resolver el nombre real del archivo:COALESCE(a.filename, tr.name)
Contrato del frontend
POST /activities/trial
{
"name": "ensayo1",
"yeargroup": 2025,
"field_uuid": "8df6c91c-...",
"reports": ["010a1277-69e3-4d3f-91ca-eb0a5ff4356e", "otro-uuid"]
}
reportses opcional. Si no viene, el trial se crea sin reportes.- Máximo 50 UUIDs.
- El nombre de cada report se genera como
ensayo1_1,ensayo1_2, etc.
PATCH /activities/trial
{
"uuid": "30a112c9-...",
"reports": ["ff42c99d-...", "3489b087-..."]
}
reportses opcional. Si no viene, los reportes existentes no se tocan.- Si viene como array vacío
[], se eliminan (soft-delete) todos los reportes. - Si viene con UUIDs, se reemplazan todos los reportes anteriores por los nuevos.
- Campos desconocidos del frontend (ej:
isRowDisabled) se ignoran.
GET /activities/trial/reports?trial_uuid=...
Respuesta:
{
"reports": [
{
"report_uuid": "aaa-...",
"file_uuid": "010a1277-...",
"name": "archivo_original.pdf",
"created_at": "2025-07-13T17:48:38.384555"
}
]
}
- El
namese resuelve con prioridad: nombre real del archivo enattachments.filename, o fallback al nombre secuencial generado (ensayo1_1).
Notas
- Todo opera dentro de la misma transacción de
TrialRepo. Si falla la vinculación, se hace rollback del trial completo. - No se valida existencia de archivos contra la tabla
attachments(los ensayos se crean offline). - La de-duplicación se resuelve en la misma query SQL (
WHERE NOT EXISTS), sin queries extra.