use crate::db::breaks::{Break, Breaks};
use crate::db::pauses::Pauses;
use crate::db::workdays::Workday;
use crate::libs::config::{Config, ProductivityConfig};
use crate::libs::pause::Pause;
use anyhow::Result;
use chrono::Duration;
pub struct Productivity {
pub workday: Workday,
pub breaks: Vec<Break>,
pub short_pauses: Vec<Pause>,
pub long_pauses: Vec<Pause>,
pub config: ProductivityConfig,
}
impl Productivity {
pub fn new(workday: &Workday) -> Result<Self> {
let config = Config::read()?;
let monitor_config = config.monitor.unwrap_or_default();
let productivity_config = config.productivity.unwrap_or_default();
Ok(Self {
workday: workday.clone(),
breaks: Breaks::new()?.get_daily_breaks(workday.date)?,
short_pauses: Pauses::new()?
.set_max_duration(monitor_config.min_pause_duration)
.get_workday_pauses(workday)?,
long_pauses: Pauses::new()?
.set_min_duration(monitor_config.min_pause_duration)
.get_workday_pauses(workday)?,
config: productivity_config,
})
}
pub fn with_test_data(workday: &Workday, breaks: Vec<Break>, short_pauses: Vec<Pause>, long_pauses: Vec<Pause>) -> Self {
Self {
workday: workday.clone(),
breaks,
short_pauses,
long_pauses,
config: ProductivityConfig::default(),
}
}
pub fn calculate_needed_break_duration(&self, target_productivity: Option<f64>) -> u64 {
let end_time = self.workday.end.unwrap_or_else(|| chrono::Local::now().naive_local());
let gross_duration = end_time - self.workday.start;
let target_productivity = target_productivity.unwrap_or(self.config.min_productivity_threshold);
let pause_duration: Duration = self.long_pauses.iter().filter_map(|p| p.duration).sum();
let existing_break_duration: Duration = self.breaks.iter().map(|b| b.duration).sum();
let net_work_time = gross_duration - pause_duration - existing_break_duration;
if target_productivity <= 0.0 || target_productivity > 100.0 {
return 0; }
if net_work_time.num_seconds() <= 0 {
return 0; }
let required_available_time_seconds = (net_work_time.num_seconds() as f64 * 100.0 / target_productivity) as i64;
let required_available_time = Duration::seconds(required_available_time_seconds);
let total_needed_break_duration = gross_duration - required_available_time;
let additional_break_duration = total_needed_break_duration - existing_break_duration;
if additional_break_duration.num_minutes() > 0 {
additional_break_duration.num_minutes() as u64
} else {
0
}
}
pub fn should_suggest_productivity_improvements(&self) -> bool {
let now = chrono::Local::now().naive_local();
let elapsed = now - self.workday.start;
let expected_duration = Duration::seconds((self.config.workday_hours * 3600.0) as i64);
let min_duration = Duration::seconds((expected_duration.num_seconds() as f64 * self.config.min_workday_fraction_before_suggest) as i64);
elapsed >= min_duration
}
pub fn check_productivity_recommendations(&self) -> Option<u64> {
if !self.should_suggest_productivity_improvements() {
return None; }
let current_productivity = self.calculate_productivity();
if current_productivity >= self.config.min_productivity_threshold {
return None; }
let needed_minutes = self.calculate_needed_break_duration(None);
if needed_minutes >= self.config.min_break_duration && needed_minutes <= self.config.max_break_duration {
Some(needed_minutes)
} else {
None
}
}
pub fn calculate_productivity(&self) -> f64 {
let end_time = self.workday.end.unwrap_or_else(|| chrono::Local::now().naive_local());
let gross_duration = end_time - self.workday.start;
let break_duration: Duration = self.breaks.iter().map(|b| b.duration).sum();
let long_pause_duration: Duration = self.long_pauses.iter().filter_map(|p| p.duration).sum();
let mut short_pause_duration: Duration = self.short_pauses.iter().filter_map(|p| p.duration).sum();
short_pause_duration = if short_pause_duration <= break_duration {
Duration::zero()
} else {
short_pause_duration - break_duration
};
let work_time = gross_duration - break_duration - long_pause_duration;
let net_work_time = work_time - short_pause_duration;
if work_time.num_seconds() > 0 {
let productivity = (net_work_time.num_seconds() as f64 / work_time.num_seconds() as f64) * 100.0;
productivity.max(0.0).min(100.0) } else {
0.0
}
}
}