use chrono::{DateTime, Utc};
use serde::Serialize;
use utoipa::ToSchema;
use uuid::Uuid;
use ceres_core::{DatabaseStats, HarvestJob, SearchResult, SyncStats};
#[derive(Debug, Serialize, ToSchema)]
pub struct HealthResponse {
pub status: String,
pub version: String,
pub database: ServiceStatus,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct ServiceStatus {
pub healthy: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct StatsResponse {
pub total_datasets: i64,
pub datasets_with_embeddings: i64,
pub total_portals: i64,
pub last_update: Option<DateTime<Utc>>,
pub stale_datasets: i64,
}
impl From<DatabaseStats> for StatsResponse {
fn from(s: DatabaseStats) -> Self {
Self {
total_datasets: s.total_datasets,
datasets_with_embeddings: s.datasets_with_embeddings,
total_portals: s.total_portals,
last_update: s.last_update,
stale_datasets: s.stale_datasets,
}
}
}
#[derive(Debug, Serialize, ToSchema)]
pub struct SearchResponse {
pub query: String,
pub count: usize,
pub results: Vec<SearchResultDto>,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct SearchResultDto {
pub id: Uuid,
pub title: String,
pub description: Option<String>,
pub url: String,
pub source_portal: String,
pub similarity_score: f32,
}
impl From<SearchResult> for SearchResultDto {
fn from(r: SearchResult) -> Self {
Self {
id: r.dataset.id,
title: r.dataset.title,
description: r.dataset.description,
url: r.dataset.url,
source_portal: r.dataset.source_portal,
similarity_score: r.similarity_score,
}
}
}
#[derive(Debug, Serialize, ToSchema)]
pub struct PortalInfoResponse {
pub name: String,
pub url: String,
pub portal_type: String,
pub profile: Option<String>,
pub sparql_endpoint: Option<String>,
pub enabled: bool,
pub description: Option<String>,
pub last_sync: Option<DateTime<Utc>>,
pub dataset_count: Option<i64>,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct PortalStatsResponse {
pub name: String,
pub url: String,
pub dataset_count: i64,
pub last_sync: Option<DateTime<Utc>>,
pub last_sync_mode: Option<String>,
pub last_sync_status: Option<String>,
pub last_sync_datasets: Option<i32>,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct HarvestJobResponse {
pub job_id: Uuid,
pub status: String,
pub portal_url: String,
pub portal_name: Option<String>,
pub created_at: DateTime<Utc>,
pub started_at: Option<DateTime<Utc>>,
pub completed_at: Option<DateTime<Utc>>,
pub sync_stats: Option<SyncStatsDto>,
pub error_message: Option<String>,
}
impl From<HarvestJob> for HarvestJobResponse {
fn from(job: HarvestJob) -> Self {
Self {
job_id: job.id,
status: job.status.as_str().to_string(),
portal_url: job.portal_url,
portal_name: job.portal_name,
created_at: job.created_at,
started_at: job.started_at,
completed_at: job.completed_at,
sync_stats: job.sync_stats.map(SyncStatsDto::from),
error_message: job.error_message,
}
}
}
#[derive(Debug, Serialize, ToSchema)]
pub struct SyncStatsDto {
pub unchanged: usize,
pub updated: usize,
pub created: usize,
pub failed: usize,
pub skipped: usize,
pub total: usize,
}
impl From<SyncStats> for SyncStatsDto {
fn from(s: SyncStats) -> Self {
Self {
unchanged: s.unchanged,
updated: s.updated,
created: s.created,
failed: s.failed,
skipped: s.skipped,
total: s.total(),
}
}
}
#[derive(Debug, Serialize, ToSchema)]
pub struct HarvestStatusResponse {
pub pending_jobs: i64,
pub running_jobs: i64,
pub recent_jobs: Vec<HarvestJobResponse>,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct DatasetResponse {
pub id: Uuid,
pub original_id: String,
pub source_portal: String,
pub url: String,
pub title: String,
pub description: Option<String>,
pub metadata: serde_json::Value,
pub first_seen_at: DateTime<Utc>,
pub last_updated_at: DateTime<Utc>,
}
impl From<ceres_core::Dataset> for DatasetResponse {
fn from(d: ceres_core::Dataset) -> Self {
Self {
id: d.id,
original_id: d.original_id,
source_portal: d.source_portal,
url: d.url,
title: d.title,
description: d.description,
metadata: d.metadata,
first_seen_at: d.first_seen_at,
last_updated_at: d.last_updated_at,
}
}
}