feat(analysis): admin coverage + per-page inspection API
Read-only admin endpoints (admin-gated, not analysis-enabled-gated) for the dashboard's coverage overview and page-detail view: - GET /v1/admin/analysis/mangas?search= — paginated per-manga coverage (analyzed/total pages; only mangas with pages). - GET /v1/admin/analysis/mangas/:id/chapters — per-chapter coverage. - GET /v1/admin/analysis/chapters/:id/pages — per-page status grid (done | failed | queued | none). - GET /v1/admin/analysis/pages/:id — full result (status, is_nsfw, scene, model, analyzed_at, error, OCR lines with kind, tags, content warnings); "none" for an existing-but-unanalyzed page, 404 for a missing page. repo::page_analysis gains manga_coverage / chapter_coverage / chapter_page_status / page_detail; domain adds the matching row types. Tests: coverage counts (manga + chapter), per-page status, full detail + unanalyzed "none" + 404, non-admin 403. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -128,6 +128,61 @@ pub struct PageSearchItem {
|
||||
pub rank: f32,
|
||||
}
|
||||
|
||||
/// Analysis coverage for one manga (admin overview): how many of its pages
|
||||
/// have a completed analysis vs. how many exist.
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
pub struct MangaCoverage {
|
||||
pub manga_id: Uuid,
|
||||
pub title: String,
|
||||
pub total_pages: i64,
|
||||
pub analyzed_pages: i64,
|
||||
}
|
||||
|
||||
/// Analysis coverage for one chapter.
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
pub struct ChapterCoverage {
|
||||
pub chapter_id: Uuid,
|
||||
pub number: i32,
|
||||
pub title: Option<String>,
|
||||
pub total_pages: i64,
|
||||
pub analyzed_pages: i64,
|
||||
}
|
||||
|
||||
/// Per-page analysis status for a chapter's page grid. `status` is one of
|
||||
/// `done` | `failed` | `queued` (a pending/running job) | `none`.
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
pub struct PageStatusItem {
|
||||
pub page_id: Uuid,
|
||||
pub page_number: i32,
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
/// One OCR line in the page-detail view.
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
pub struct OcrLine {
|
||||
pub kind: OcrKind,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
/// Full analysis result for one page, for the admin detail modal. `status`
|
||||
/// is `done` | `failed` | `none` (no analysis row yet).
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct PageAnalysisDetail {
|
||||
pub page_id: Uuid,
|
||||
pub page_number: i32,
|
||||
pub chapter_id: Uuid,
|
||||
pub manga_id: Uuid,
|
||||
pub status: String,
|
||||
pub is_nsfw: bool,
|
||||
pub scene_description: Option<String>,
|
||||
pub model: Option<String>,
|
||||
pub error: Option<String>,
|
||||
pub analyzed_at: Option<DateTime<Utc>>,
|
||||
pub ocr: Vec<OcrLine>,
|
||||
pub tags: Vec<String>,
|
||||
pub content_warnings: Vec<ContentWarning>,
|
||||
}
|
||||
|
||||
/// One persisted `page_analysis` row.
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
pub struct PageAnalysis {
|
||||
|
||||
Reference in New Issue
Block a user