2025-03-06 04:22:01 +08:00
|
|
|
use anyhow::{anyhow, Result};
|
2025-03-12 05:55:30 +08:00
|
|
|
use database::types::VersionHistory;
|
|
|
|
use diesel::{ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper};
|
2025-03-06 04:22:01 +08:00
|
|
|
use diesel_async::RunQueryDsl;
|
2025-03-12 05:55:30 +08:00
|
|
|
use serde_json::Value;
|
2025-03-06 04:22:01 +08:00
|
|
|
use serde_yaml;
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
use crate::metrics::types::{
|
|
|
|
BusterMetric, ColumnMetaData, ColumnType, DataMetadata, Dataset, MinMaxValue, SimpleType,
|
|
|
|
};
|
|
|
|
use database::enums::Verification;
|
|
|
|
use database::pool::get_pg_pool;
|
2025-03-12 05:55:30 +08:00
|
|
|
use database::schema::{datasets, metric_files};
|
2025-03-12 11:25:27 +08:00
|
|
|
use database::types::MetricYml;
|
2025-03-06 04:22:01 +08:00
|
|
|
|
2025-03-12 06:06:07 +08:00
|
|
|
use super::Version;
|
|
|
|
|
2025-03-06 04:22:01 +08:00
|
|
|
#[derive(Queryable, Selectable)]
|
|
|
|
#[diesel(table_name = metric_files)]
|
|
|
|
struct QueryableMetricFile {
|
|
|
|
id: Uuid,
|
|
|
|
name: String,
|
|
|
|
file_name: String,
|
2025-03-12 11:25:27 +08:00
|
|
|
content: MetricYml,
|
2025-03-06 04:22:01 +08:00
|
|
|
verification: Verification,
|
|
|
|
evaluation_obj: Option<Value>,
|
|
|
|
evaluation_summary: Option<String>,
|
|
|
|
evaluation_score: Option<f64>,
|
|
|
|
created_by: Uuid,
|
|
|
|
created_at: chrono::DateTime<chrono::Utc>,
|
|
|
|
updated_at: chrono::DateTime<chrono::Utc>,
|
2025-03-12 05:55:30 +08:00
|
|
|
version_history: VersionHistory,
|
2025-03-06 04:22:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Queryable)]
|
|
|
|
struct DatasetInfo {
|
|
|
|
id: Uuid,
|
|
|
|
name: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Queryable)]
|
|
|
|
#[diesel(table_name = users)]
|
|
|
|
struct UserInfo {
|
|
|
|
#[diesel(sql_type = diesel::sql_types::Nullable<diesel::sql_types::Text>)]
|
|
|
|
name: Option<String>,
|
|
|
|
#[diesel(sql_type = diesel::sql_types::Nullable<diesel::sql_types::Text>)]
|
|
|
|
avatar_url: Option<String>,
|
|
|
|
}
|
|
|
|
|
2025-03-20 00:49:56 +08:00
|
|
|
/// Handler to retrieve a metric by ID with optional version number
|
|
|
|
///
|
|
|
|
/// If version_number is provided, returns that specific version of the metric.
|
|
|
|
/// If version_number is None, returns the latest version of the metric.
|
|
|
|
pub async fn get_metric_handler(metric_id: &Uuid, user_id: &Uuid, version_number: Option<i32>) -> Result<BusterMetric> {
|
2025-03-06 04:22:01 +08:00
|
|
|
let mut conn = match get_pg_pool().get().await {
|
|
|
|
Ok(conn) => conn,
|
|
|
|
Err(e) => return Err(anyhow!("Failed to get database connection: {}", e)),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Query the metric file
|
|
|
|
let metric_file = metric_files::table
|
|
|
|
.filter(metric_files::id.eq(metric_id))
|
|
|
|
.filter(metric_files::deleted_at.is_null())
|
|
|
|
.select((
|
|
|
|
metric_files::id,
|
|
|
|
metric_files::name,
|
|
|
|
metric_files::file_name,
|
|
|
|
metric_files::content,
|
|
|
|
metric_files::verification,
|
|
|
|
metric_files::evaluation_obj,
|
|
|
|
metric_files::evaluation_summary,
|
|
|
|
metric_files::evaluation_score,
|
|
|
|
metric_files::created_by,
|
|
|
|
metric_files::created_at,
|
|
|
|
metric_files::updated_at,
|
2025-03-12 05:55:30 +08:00
|
|
|
metric_files::version_history,
|
2025-03-06 04:22:01 +08:00
|
|
|
))
|
|
|
|
.first::<QueryableMetricFile>(&mut conn)
|
|
|
|
.await
|
|
|
|
.map_err(|e| match e {
|
|
|
|
diesel::result::Error::NotFound => anyhow!("Metric file not found or unauthorized"),
|
|
|
|
_ => anyhow!("Database error: {}", e),
|
|
|
|
})?;
|
|
|
|
|
|
|
|
// Map evaluation score to High/Moderate/Low
|
|
|
|
let evaluation_score = metric_file.evaluation_score.map(|score| {
|
|
|
|
if score >= 0.8 {
|
|
|
|
"High".to_string()
|
|
|
|
} else if score >= 0.5 {
|
|
|
|
"Moderate".to_string()
|
|
|
|
} else {
|
|
|
|
"Low".to_string()
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2025-03-20 00:49:56 +08:00
|
|
|
// Determine which version to use based on version_number parameter
|
|
|
|
let (metric_content, version_num) = if let Some(version) = version_number {
|
|
|
|
// Get the specific version if it exists
|
|
|
|
if let Some(v) = metric_file.version_history.get_version(version) {
|
|
|
|
match &v.content {
|
|
|
|
database::types::VersionContent::MetricYml(content) => (content.clone(), v.version_number),
|
|
|
|
_ => return Err(anyhow!("Invalid version content type"))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err(anyhow!("Version {} not found", version));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Get the latest version
|
|
|
|
if let Some(v) = metric_file.version_history.get_latest_version() {
|
|
|
|
match &v.content {
|
|
|
|
database::types::VersionContent::MetricYml(content) => (content.clone(), v.version_number),
|
|
|
|
_ => return Err(anyhow!("Invalid version content type"))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Fall back to current content if no version history
|
|
|
|
(metric_file.content.clone(), 1)
|
|
|
|
}
|
|
|
|
};
|
2025-03-12 11:25:27 +08:00
|
|
|
|
2025-03-06 04:22:01 +08:00
|
|
|
// Convert content to pretty YAML
|
2025-03-20 00:49:56 +08:00
|
|
|
let file = match serde_yaml::to_string(&metric_content) {
|
2025-03-06 04:22:01 +08:00
|
|
|
Ok(yaml) => yaml,
|
|
|
|
Err(e) => return Err(anyhow!("Failed to convert content to YAML: {}", e)),
|
|
|
|
};
|
|
|
|
|
2025-03-20 00:49:56 +08:00
|
|
|
// Parse data metadata from the selected version's MetricYml
|
|
|
|
let data_metadata = metric_content.data_metadata.map(|metadata| {
|
2025-03-06 04:22:01 +08:00
|
|
|
DataMetadata {
|
|
|
|
column_count: metadata.len() as i32,
|
|
|
|
column_metadata: metadata
|
|
|
|
.iter()
|
|
|
|
.map(|col| ColumnMetaData {
|
|
|
|
name: col.name.clone(),
|
|
|
|
min_value: MinMaxValue::Number(0.0), // Default value
|
|
|
|
max_value: MinMaxValue::Number(0.0), // Default value
|
|
|
|
unique_values: 0, // Default value
|
|
|
|
simple_type: match col.data_type.as_str() {
|
|
|
|
"string" => SimpleType::Text,
|
|
|
|
"number" => SimpleType::Number,
|
|
|
|
"boolean" => SimpleType::Boolean,
|
|
|
|
"date" => SimpleType::Date,
|
|
|
|
_ => SimpleType::Text,
|
|
|
|
},
|
|
|
|
column_type: match col.data_type.as_str() {
|
|
|
|
"string" => ColumnType::Text,
|
|
|
|
"number" => ColumnType::Number,
|
|
|
|
"boolean" => ColumnType::Boolean,
|
|
|
|
"date" => ColumnType::Date,
|
|
|
|
_ => ColumnType::Text,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
row_count: 1, // Default value since it's not in the MetricYml structure
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Get dataset information for all dataset IDs
|
|
|
|
let mut datasets = Vec::new();
|
2025-03-20 00:49:56 +08:00
|
|
|
for dataset_id in &metric_content.dataset_ids {
|
2025-03-06 04:22:01 +08:00
|
|
|
if let Ok(dataset_info) = datasets::table
|
|
|
|
.filter(datasets::id.eq(dataset_id))
|
|
|
|
.filter(datasets::deleted_at.is_null())
|
|
|
|
.select((datasets::id, datasets::name))
|
|
|
|
.first::<DatasetInfo>(&mut conn)
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
datasets.push(Dataset {
|
|
|
|
id: dataset_info.id.to_string(),
|
|
|
|
name: dataset_info.name,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get user information
|
2025-03-10 23:41:32 +08:00
|
|
|
// let user_info = users::table
|
|
|
|
// .filter(users::id.eq(metric_file.created_by))
|
|
|
|
// .select((users::name, users::avatar_url))
|
|
|
|
// .first::<UserInfo>(&mut conn)
|
|
|
|
// .await
|
|
|
|
// .map_err(|e| anyhow!("Failed to get user information: {}", e))?;
|
2025-03-06 04:22:01 +08:00
|
|
|
|
2025-03-12 06:06:07 +08:00
|
|
|
let versions = metric_file
|
|
|
|
.version_history
|
|
|
|
.0
|
|
|
|
.values()
|
|
|
|
.map(|v| Version {
|
|
|
|
version_number: v.version_number,
|
|
|
|
updated_at: v.updated_at,
|
|
|
|
})
|
|
|
|
.collect();
|
2025-03-19 13:17:28 +08:00
|
|
|
|
2025-03-06 04:22:01 +08:00
|
|
|
// Construct BusterMetric
|
|
|
|
Ok(BusterMetric {
|
2025-03-12 09:30:46 +08:00
|
|
|
id: metric_file.id,
|
2025-03-06 04:22:01 +08:00
|
|
|
metric_type: "metric".to_string(),
|
2025-03-08 04:33:02 +08:00
|
|
|
title: metric_file.name,
|
2025-03-20 00:49:56 +08:00
|
|
|
version_number: version_num,
|
|
|
|
description: metric_content.description,
|
2025-03-06 04:22:01 +08:00
|
|
|
file_name: metric_file.file_name,
|
2025-03-20 00:49:56 +08:00
|
|
|
time_frame: metric_content.time_frame,
|
2025-03-06 04:22:01 +08:00
|
|
|
datasets,
|
|
|
|
data_source_id: "".to_string(), // This would need to be fetched from another source
|
|
|
|
error: None,
|
2025-03-20 00:49:56 +08:00
|
|
|
chart_config: Some(metric_content.chart_config),
|
2025-03-06 04:22:01 +08:00
|
|
|
data_metadata,
|
|
|
|
status: metric_file.verification,
|
|
|
|
evaluation_score,
|
|
|
|
evaluation_summary: metric_file.evaluation_summary.unwrap_or_default(),
|
|
|
|
file,
|
2025-03-12 09:30:46 +08:00
|
|
|
created_at: metric_file.created_at,
|
|
|
|
updated_at: metric_file.updated_at,
|
|
|
|
sent_by_id: metric_file.created_by,
|
2025-03-10 23:41:32 +08:00
|
|
|
sent_by_name: "".to_string(),
|
|
|
|
sent_by_avatar_url: None,
|
2025-03-06 04:22:01 +08:00
|
|
|
code: None,
|
|
|
|
dashboards: vec![], // TODO: Get associated dashboards
|
|
|
|
collections: vec![], // TODO: Get associated collections
|
2025-03-12 06:06:07 +08:00
|
|
|
versions,
|
2025-03-06 04:22:01 +08:00
|
|
|
})
|
|
|
|
}
|