Refactor code structure for improved readability and maintainability

This commit is contained in:
2026-04-24 13:05:28 -04:00
parent 466f74b717
commit 9c2edad614
5 changed files with 264 additions and 44 deletions
+186 -1
View File
@@ -1,4 +1,5 @@
use crate::handlers::run_transcription_task;
use crate::handlers_assets::{ingest_asset_for_rag_core, set_zip_rag_task_status};
use axum::{
Json,
extract::{Path, State},
@@ -6,6 +7,7 @@ use axum::{
};
use serde::Serialize;
use sqlx::{FromRow, PgPool};
use tokio::time::Duration;
use uuid::Uuid;
#[derive(Debug, Serialize, FromRow)]
@@ -74,11 +76,24 @@ struct LessonStatusRow {
transcription_status: Option<String>,
}
#[derive(sqlx::FromRow)]
struct ZipRetryTaskRow {
id: Uuid,
organization_id: Uuid,
created_by: Uuid,
metadata: serde_json::Value,
created_at: chrono::DateTime<chrono::Utc>,
}
#[derive(sqlx::FromRow)]
struct AssetIdRow {
id: Uuid,
}
pub async fn retry_task(
State(pool): State<PgPool>,
Path(id): Path<Uuid>,
) -> Result<StatusCode, (StatusCode, String)> {
// Check lessons for transcription failures
let lesson = sqlx::query_as::<_, LessonStatusRow>("SELECT transcription_status FROM lessons WHERE id = $1")
.bind(id)
@@ -97,6 +112,176 @@ pub async fn retry_task(
}
}
let zip_task = sqlx::query_as::<_, ZipRetryTaskRow>(
r#"
SELECT id, organization_id, created_by, metadata, created_at
FROM background_tasks
WHERE id = $1
AND task_type = 'zip_rag_import'
AND status = 'failed'
"#,
)
.bind(id)
.fetch_optional(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
if let Some(task) = zip_task {
let zip_batch_id_from_metadata = task
.metadata
.get("zip_batch_id")
.and_then(|v| v.as_str())
.map(|raw| {
Uuid::parse_str(raw).map_err(|_| {
(
StatusCode::BAD_REQUEST,
"zip_batch_id inválido en metadata de la tarea".to_string(),
)
})
})
.transpose()?;
let zip_batch_id = if let Some(id) = zip_batch_id_from_metadata {
id
} else {
#[derive(sqlx::FromRow)]
struct ZipBatchIdRow {
zip_batch_id: Uuid,
}
let candidates = sqlx::query_as::<_, ZipBatchIdRow>(
r#"
SELECT zip_batch_id
FROM assets
WHERE organization_id = $1
AND uploaded_by = $2
AND zip_batch_id IS NOT NULL
AND created_at BETWEEN ($3 - INTERVAL '30 minutes') AND ($3 + INTERVAL '30 minutes')
GROUP BY zip_batch_id
ORDER BY COUNT(*) DESC, MAX(created_at) DESC
"#,
)
.bind(task.organization_id)
.bind(task.created_by)
.bind(task.created_at)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
if candidates.len() == 1 {
candidates[0].zip_batch_id
} else if candidates.is_empty() {
return Err((
StatusCode::BAD_REQUEST,
"La tarea no tiene zip_batch_id y no se pudo inferir un lote asociado. Vuelve a cargar el ZIP para reingestar.".to_string(),
));
} else {
return Err((
StatusCode::BAD_REQUEST,
"La tarea no tiene zip_batch_id y hay múltiples lotes candidatos; vuelve a cargar el ZIP para evitar ambigüedad.".to_string(),
));
}
};
let assets = sqlx::query_as::<_, AssetIdRow>(
"SELECT id FROM assets WHERE organization_id = $1 AND zip_batch_id = $2 ORDER BY created_at ASC",
)
.bind(task.organization_id)
.bind(zip_batch_id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
if assets.is_empty() {
return Err((
StatusCode::BAD_REQUEST,
"No se encontraron assets para ese zip_batch_id".to_string(),
));
}
let pool_clone = pool.clone();
tokio::spawn(async move {
let total = assets.len();
let mut processed = 0usize;
let mut failed = 0usize;
let _ = set_zip_rag_task_status(&pool_clone, task.id, "processing", 0, 0, 0, None).await;
for row in assets {
let ingest_result = tokio::time::timeout(
Duration::from_secs(120),
ingest_asset_for_rag_core(&pool_clone, task.organization_id, task.created_by, row.id),
)
.await;
match ingest_result {
Ok(Ok(_)) => {
processed += 1;
}
Ok(Err(msg)) => {
failed += 1;
tracing::warn!(
"Retry ZIP RAG: fallo ingesta asset {} en task {}: {}",
row.id,
task.id,
msg
);
}
Err(_elapsed) => {
failed += 1;
tracing::warn!(
"Retry ZIP RAG: timeout (120s) en asset {} en task {}",
row.id,
task.id
);
}
}
let progress = (((processed + failed) as f32 / total as f32) * 100.0)
.round()
.clamp(0.0, 100.0) as i32;
let _ = set_zip_rag_task_status(
&pool_clone,
task.id,
"processing",
progress,
processed,
failed,
None,
)
.await;
}
let final_status = if failed > 0 { "failed" } else { "completed" };
let final_msg = if failed > 0 {
Some("Reintento RAG completado con errores parciales")
} else {
None
};
if let Err(e) = set_zip_rag_task_status(
&pool_clone,
task.id,
final_status,
100,
processed,
failed,
final_msg,
)
.await
{
tracing::error!(
"Retry ZIP RAG: no se pudo actualizar estado final de task {} a '{}': {:?}",
task.id,
final_status,
e
);
}
});
return Ok(StatusCode::ACCEPTED);
}
Ok(StatusCode::NOT_FOUND)
}