pub mod semantic;
pub mod statistical;
pub mod structural;
pub mod templates;
use ndarray::{Array2, ArrayView2};
use rand::Rng;
use crate::config::CaptionKey;
use crate::error::Result;
use semantic::{ActivityEvent, MoodEvent, SleepEvent};
#[derive(Debug, Default)]
pub struct CaptionContext {
pub activities: Vec<ActivityEvent>,
pub sleep: Vec<SleepEvent>,
pub moods: Vec<MoodEvent>,
pub top_k_activity: usize,
pub top_k_sleep: usize,
pub min_activity_duration: usize,
pub max_structural_per_category: usize,
}
impl CaptionContext {
pub fn new() -> Self {
Self {
top_k_activity: 8,
top_k_sleep: 2,
min_activity_duration: 20,
max_structural_per_category: 7,
..Default::default()
}
}
}
pub fn generate_caption<R: Rng>(
x_norm: &ArrayView2<f64>,
mask: Option<&Array2<u8>>,
ctx: &CaptionContext,
key: CaptionKey,
rng: &mut R,
) -> Result<String> {
let need_low = matches!(key, CaptionKey::LowLevel | CaptionKey::MiddleLow
| CaptionKey::HighLow | CaptionKey::HighMiddleLow);
let need_mid = matches!(key, CaptionKey::MiddleLevel | CaptionKey::MiddleLow
| CaptionKey::HighMiddle | CaptionKey::HighMiddleLow);
let need_high = matches!(key, CaptionKey::HighLevelSummary | CaptionKey::HighLevelAll
| CaptionKey::HighLow | CaptionKey::HighMiddle
| CaptionKey::HighMiddleLow);
let low = if need_low {
statistical::generate_statistical_caption(x_norm, mask, rng)?
} else {
String::new()
};
let mid = if need_mid {
structural::generate_structural_caption(
x_norm,
ctx.max_structural_per_category,
rng,
)?
} else {
String::new()
};
let high = if need_high {
semantic::generate_semantic_caption(
&ctx.activities,
&ctx.sleep,
&ctx.moods,
ctx.top_k_activity,
ctx.top_k_sleep,
ctx.min_activity_duration,
rng,
)
} else {
String::new()
};
let parts: Vec<&str> = [high.as_str(), mid.as_str(), low.as_str()]
.into_iter()
.filter(|s| !s.is_empty())
.collect();
Ok(parts.join("\n"))
}