76 lines
3.7 KiB
Rust
76 lines
3.7 KiB
Rust
use crate::db::models::{DashboardSummary, QueueStatusCount, ReviewQueueSummary};
|
|
use crate::db::{Db, db_error};
|
|
use crate::error::AppResult;
|
|
|
|
pub async fn summary(db: &Db) -> AppResult<DashboardSummary> {
|
|
let client = db.client().await?;
|
|
let row = client
|
|
.query_one(
|
|
"SELECT
|
|
(SELECT count(*)::bigint FROM podcast_channels WHERE deleted_at IS NULL AND status IN ('selected', 'active')) AS active_channels,
|
|
(SELECT count(*)::bigint FROM videos WHERE deleted_at IS NULL) AS videos,
|
|
(SELECT count(*)::bigint FROM interviewees WHERE deleted_at IS NULL AND status = 'active') AS active_interviewees,
|
|
(SELECT count(*)::bigint FROM contacts WHERE deleted_at IS NULL AND status = 'active') AS active_contacts,
|
|
(SELECT count(*)::bigint FROM pipeline_runs WHERE status IN ('running', 'cancelling')) AS running_runs,
|
|
(SELECT count(*)::bigint FROM jobs WHERE status IN ('queued', 'retry_scheduled')) AS queued_jobs,
|
|
(SELECT count(*)::bigint FROM jobs WHERE status = 'failed') AS failed_jobs,
|
|
(SELECT count(*)::bigint FROM interviewee_candidates WHERE status = 'pending') AS pending_interviewee_reviews,
|
|
(SELECT count(*)::bigint FROM contact_candidates WHERE status IN ('pending', 'needs_review')) AS pending_contact_reviews,
|
|
(SELECT COALESCE(sum(cost_micros), 0)::bigint FROM ai_calls) AS ai_cost_micros",
|
|
&[],
|
|
)
|
|
.await
|
|
.map_err(db_error)?;
|
|
Ok(DashboardSummary {
|
|
active_channels: row.get("active_channels"),
|
|
videos: row.get("videos"),
|
|
active_interviewees: row.get("active_interviewees"),
|
|
active_contacts: row.get("active_contacts"),
|
|
running_runs: row.get("running_runs"),
|
|
queued_jobs: row.get("queued_jobs"),
|
|
failed_jobs: row.get("failed_jobs"),
|
|
pending_interviewee_reviews: row.get("pending_interviewee_reviews"),
|
|
pending_contact_reviews: row.get("pending_contact_reviews"),
|
|
ai_cost_micros: row.get("ai_cost_micros"),
|
|
})
|
|
}
|
|
|
|
pub async fn review_queue(db: &Db) -> AppResult<ReviewQueueSummary> {
|
|
let client = db.client().await?;
|
|
let row = client
|
|
.query_one(
|
|
"SELECT
|
|
(SELECT count(*)::bigint FROM interviewee_candidates WHERE status = 'pending') AS interviewee_candidates_pending,
|
|
(SELECT count(*)::bigint FROM interviewees WHERE deleted_at IS NULL AND status = 'active' AND dedup_review_status = 'needs_review') AS interviewees_needing_dedup_review,
|
|
(SELECT count(*)::bigint FROM contact_candidates WHERE status = 'pending') AS contacts_pending,
|
|
(SELECT count(*)::bigint FROM contact_candidates WHERE status = 'needs_review') AS contacts_needing_review",
|
|
&[],
|
|
)
|
|
.await
|
|
.map_err(db_error)?;
|
|
Ok(ReviewQueueSummary {
|
|
interviewee_candidates_pending: row.get("interviewee_candidates_pending"),
|
|
interviewees_needing_dedup_review: row.get("interviewees_needing_dedup_review"),
|
|
contacts_pending: row.get("contacts_pending"),
|
|
contacts_needing_review: row.get("contacts_needing_review"),
|
|
})
|
|
}
|
|
|
|
pub async fn job_status_counts(db: &Db) -> AppResult<Vec<QueueStatusCount>> {
|
|
let client = db.client().await?;
|
|
let rows = client
|
|
.query(
|
|
"SELECT status, count(*)::bigint AS count FROM jobs GROUP BY status ORDER BY status",
|
|
&[],
|
|
)
|
|
.await
|
|
.map_err(db_error)?;
|
|
Ok(rows
|
|
.into_iter()
|
|
.map(|row| QueueStatusCount {
|
|
status: row.get("status"),
|
|
count: row.get("count"),
|
|
})
|
|
.collect())
|
|
}
|