Files
leadcast/backend/src/db/querys/dashboard.rs
T
2026-07-26 21:17:57 -03:00

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())
}