195 lines
4.8 KiB
Rust
195 lines
4.8 KiB
Rust
//! Data quality monitoring with automated checks and remediation
|
|
|
|
use crate::{DataRecord, Result, monitoring::EtlMetrics, state::StateManager};
|
|
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
use tokio::sync::RwLock;
|
|
|
|
/// Quality monitoring configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct QualityConfig {
|
|
pub enable_profiling: bool,
|
|
pub enable_alerts: bool,
|
|
pub quality_threshold: f64,
|
|
}
|
|
|
|
impl Default for QualityConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
enable_profiling: true,
|
|
enable_alerts: true,
|
|
quality_threshold: 0.8,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Quality monitor
|
|
pub struct QualityMonitor {
|
|
config: QualityConfig,
|
|
state_manager: Arc<StateManager>,
|
|
metrics: Arc<EtlMetrics>,
|
|
rules: Arc<RwLock<Vec<QualityRule>>>,
|
|
}
|
|
|
|
/// Quality rule
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct QualityRule {
|
|
pub rule_id: String,
|
|
pub name: String,
|
|
pub description: Option<String>,
|
|
pub rule_type: QualityRuleType,
|
|
pub threshold: f64,
|
|
pub enabled: bool,
|
|
}
|
|
|
|
/// Types of quality rules
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum QualityRuleType {
|
|
Completeness,
|
|
Accuracy,
|
|
Consistency,
|
|
Uniqueness,
|
|
Timeliness,
|
|
Validity,
|
|
}
|
|
|
|
/// Quality metrics for a task
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct QualityMetrics {
|
|
pub task_id: String,
|
|
pub quality_score: f64,
|
|
pub timestamp: DateTime<Utc>,
|
|
}
|
|
|
|
/// Quality check result
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct QualityCheck {
|
|
pub check_id: String,
|
|
pub rule_id: String,
|
|
pub timestamp: DateTime<Utc>,
|
|
pub passed: bool,
|
|
pub score: f64,
|
|
pub details: HashMap<String, String>,
|
|
}
|
|
|
|
/// Quality report
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct QualityReport {
|
|
pub report_id: String,
|
|
pub dataset: String,
|
|
pub timestamp: DateTime<Utc>,
|
|
pub overall_score: f64,
|
|
pub checks: Vec<QualityCheck>,
|
|
pub recommendations: Vec<String>,
|
|
}
|
|
|
|
/// Quality alert
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct QualityAlert {
|
|
pub alert_id: String,
|
|
pub severity: AlertSeverity,
|
|
pub message: String,
|
|
pub timestamp: DateTime<Utc>,
|
|
pub dataset: String,
|
|
}
|
|
|
|
/// Alert severity levels
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum AlertSeverity {
|
|
Critical,
|
|
High,
|
|
Medium,
|
|
Low,
|
|
}
|
|
|
|
/// Data profiler
|
|
#[derive(Debug)]
|
|
pub struct DataProfiler {
|
|
config: HashMap<String, String>,
|
|
}
|
|
|
|
impl QualityMonitor {
|
|
pub async fn new() -> Result<Self> {
|
|
let config = QualityConfig::default();
|
|
let state_manager =
|
|
Arc::new(StateManager::new(crate::state::StateConfig::default()).await?);
|
|
let metrics = Arc::new(EtlMetrics::new());
|
|
|
|
Ok(Self {
|
|
config,
|
|
state_manager,
|
|
metrics,
|
|
rules: Arc::new(RwLock::new(Vec::new())),
|
|
})
|
|
}
|
|
|
|
pub async fn with_dependencies(
|
|
config: QualityConfig,
|
|
state_manager: Arc<StateManager>,
|
|
metrics: Arc<EtlMetrics>,
|
|
) -> Result<Self> {
|
|
Ok(Self {
|
|
config,
|
|
state_manager,
|
|
metrics,
|
|
rules: Arc::new(RwLock::new(Vec::new())),
|
|
})
|
|
}
|
|
|
|
pub async fn add_rule(&self, rule: QualityRule) {
|
|
let mut rules = self.rules.write().await;
|
|
rules.push(rule);
|
|
}
|
|
|
|
pub async fn check_quality(&self, _records: &[DataRecord]) -> Result<QualityReport> {
|
|
Ok(QualityReport {
|
|
report_id: uuid::Uuid::new_v4().to_string(),
|
|
dataset: "default".to_string(),
|
|
timestamp: Utc::now(),
|
|
overall_score: 0.9,
|
|
checks: Vec::new(),
|
|
recommendations: Vec::new(),
|
|
})
|
|
}
|
|
|
|
pub async fn record_quality_score(&self, task_id: &str, quality_score: f64) -> Result<()> {
|
|
let key = format!("quality_score_{task_id}");
|
|
let value = serde_json::json!({
|
|
"score": quality_score,
|
|
"timestamp": Utc::now(),
|
|
});
|
|
self.state_manager.set_state(&key, value).await
|
|
}
|
|
|
|
pub async fn get_task_quality_metrics(&self, task_id: &str) -> Result<Option<QualityMetrics>> {
|
|
let key = format!("quality_score_{task_id}");
|
|
if let Some(value) = self.state_manager.get_state(&key).await?
|
|
&& let Some(score) = value.get("score").and_then(serde_json::Value::as_f64)
|
|
{
|
|
return Ok(Some(QualityMetrics {
|
|
task_id: task_id.to_string(),
|
|
quality_score: score,
|
|
timestamp: Utc::now(),
|
|
}));
|
|
}
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
impl DataProfiler {
|
|
#[must_use]
|
|
pub fn new() -> Self {
|
|
Self {
|
|
config: HashMap::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for DataProfiler {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|