feat: Phase 1 handover - schema migration, correction mechanism, API fixes
Schema changes: dev.chunks->dev.chunk, remove old_chunk_id/chunk_index Correction: asr-1.json format, generate/apply scripts API: 37/37 endpoints fixed and tested Docs: HANDOVER_V2.0.md for M4
This commit is contained in:
@@ -25,6 +25,8 @@ pub fn trace_agent_routes() -> Router<crate::api::server::AppState> {
|
||||
struct TracesRequest {
|
||||
min_faces: Option<i64>,
|
||||
sort_by: Option<String>,
|
||||
page: Option<i64>,
|
||||
page_size: Option<i64>,
|
||||
limit: Option<i64>,
|
||||
min_confidence: Option<f64>,
|
||||
max_confidence: Option<f64>,
|
||||
@@ -49,6 +51,8 @@ struct TracesResponse {
|
||||
file_uuid: String,
|
||||
total_traces: i64,
|
||||
total_faces: i64,
|
||||
page: i64,
|
||||
page_size: i64,
|
||||
traces: Vec<TraceInfo>,
|
||||
}
|
||||
|
||||
@@ -59,7 +63,11 @@ async fn list_traces_sorted(
|
||||
) -> Result<Json<TracesResponse>, (StatusCode, String)> {
|
||||
let min_faces = req.min_faces.unwrap_or(1);
|
||||
let sort = req.sort_by.as_deref().unwrap_or("first_appearance");
|
||||
let limit = req.limit.unwrap_or(500);
|
||||
let page = req.page.unwrap_or(1).max(1);
|
||||
let page_size = req.page_size.unwrap_or(50).max(1).min(500);
|
||||
let hard_limit = req.limit.unwrap_or(500);
|
||||
let effective_limit = hard_limit.min(page_size);
|
||||
let db_offset = (page - 1) * page_size;
|
||||
let min_confidence = req.min_confidence.unwrap_or(0.0);
|
||||
let max_confidence = req.max_confidence.unwrap_or(1.0);
|
||||
|
||||
@@ -92,11 +100,11 @@ async fn list_traces_sorted(
|
||||
AVG(confidence) AS avg_confidence
|
||||
FROM dev.face_detections
|
||||
WHERE file_uuid = $1 AND trace_id IS NOT NULL
|
||||
AND confidence >= $4 AND confidence <= $5
|
||||
AND confidence >= $5 AND confidence <= $6
|
||||
GROUP BY trace_id
|
||||
HAVING COUNT(*) >= $2
|
||||
ORDER BY {}
|
||||
LIMIT $3
|
||||
LIMIT $3 OFFSET $4
|
||||
) tt
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT id FROM dev.face_detections
|
||||
@@ -111,7 +119,8 @@ async fn list_traces_sorted(
|
||||
sqlx::query_as(&query)
|
||||
.bind(&file_uuid)
|
||||
.bind(min_faces)
|
||||
.bind(limit)
|
||||
.bind(effective_limit)
|
||||
.bind(db_offset)
|
||||
.bind(min_confidence)
|
||||
.bind(max_confidence)
|
||||
.fetch_all(state.db.pool())
|
||||
@@ -146,6 +155,8 @@ async fn list_traces_sorted(
|
||||
file_uuid,
|
||||
total_traces,
|
||||
total_faces,
|
||||
page,
|
||||
page_size,
|
||||
traces,
|
||||
}))
|
||||
}
|
||||
@@ -154,6 +165,8 @@ async fn list_traces_sorted(
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct TraceFacesQuery {
|
||||
page: Option<i64>,
|
||||
page_size: Option<i64>,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
interpolate: Option<bool>,
|
||||
@@ -194,7 +207,14 @@ async fn list_trace_faces(
|
||||
Query(q): Query<TraceFacesQuery>,
|
||||
) -> Result<Json<TraceFacesResponse>, (StatusCode, String)> {
|
||||
let limit = q.limit.unwrap_or(200).min(1000);
|
||||
let offset = q.offset.unwrap_or(0);
|
||||
// Support both page/page_size and offset; page/page_size takes precedence
|
||||
let offset = if q.page.is_some() || q.page_size.is_some() {
|
||||
let p = q.page.unwrap_or(1).max(1);
|
||||
let ps = q.page_size.unwrap_or(200).max(1).min(1000);
|
||||
(p - 1) * ps
|
||||
} else {
|
||||
q.offset.unwrap_or(0)
|
||||
};
|
||||
let interpolate = q.interpolate.unwrap_or(false);
|
||||
|
||||
let fps: f64 =
|
||||
@@ -206,7 +226,7 @@ async fn list_trace_faces(
|
||||
.unwrap_or(24.0);
|
||||
|
||||
let total_detected: i64 = sqlx::query_scalar(
|
||||
"SELECT COUNT(*) FROM dev.face_detections WHERE file_uuid = $1 AND trace_id = $2"
|
||||
"SELECT COUNT(*) FROM dev.face_detections WHERE file_uuid = $1 AND trace_id = $2",
|
||||
)
|
||||
.bind(&file_uuid)
|
||||
.bind(trace_id)
|
||||
@@ -214,21 +234,28 @@ async fn list_trace_faces(
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
|
||||
let rows: Vec<(i32, i32, Option<i32>, Option<i32>, Option<i32>, Option<i32>, f32)> =
|
||||
sqlx::query_as(
|
||||
"SELECT id, frame_number, x, y, width, height, confidence
|
||||
let rows: Vec<(
|
||||
i32,
|
||||
i32,
|
||||
Option<i32>,
|
||||
Option<i32>,
|
||||
Option<i32>,
|
||||
Option<i32>,
|
||||
f32,
|
||||
)> = sqlx::query_as(
|
||||
"SELECT id, frame_number, x, y, width, height, confidence
|
||||
FROM dev.face_detections
|
||||
WHERE file_uuid = $1 AND trace_id = $2
|
||||
ORDER BY frame_number ASC
|
||||
LIMIT $3 OFFSET $4"
|
||||
)
|
||||
.bind(&file_uuid)
|
||||
.bind(trace_id)
|
||||
.bind(limit)
|
||||
.bind(offset)
|
||||
.fetch_all(state.db.pool())
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
LIMIT $3 OFFSET $4",
|
||||
)
|
||||
.bind(&file_uuid)
|
||||
.bind(trace_id)
|
||||
.bind(limit)
|
||||
.bind(offset)
|
||||
.fetch_all(state.db.pool())
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
|
||||
let mut faces: Vec<TraceFaceItem> = Vec::new();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user