use chrono::{Local, NaiveDateTime};
use rand::Rng;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct TimeEntry {
pub id: i64,
pub description: String,
pub start_time: NaiveDateTime,
pub end_time: Option<NaiveDateTime>,
pub display_order: i64,
pub color: u8, pub issue_key: String,
pub logged: bool,
pub off_work: bool,
pub worklog_id: String,
}
impl TimeEntry {
pub fn random_color() -> u8 {
rand::thread_rng().gen_range(0..6)
}
pub fn pick_best_color(color_usage: &[(u8, i64)]) -> u8 {
const NUM_COLORS: u8 = 6;
let mut scores = vec![0i64; NUM_COLORS as usize];
for (idx, (color, count)) in color_usage.iter().enumerate() {
let color_idx = *color as usize;
if color_idx < NUM_COLORS as usize {
scores[color_idx] += count;
if idx == 0 {
scores[color_idx] += 1;
}
}
}
let min_score = scores.iter().min().copied().unwrap_or(0);
let best_colors: Vec<u8> = scores
.iter()
.enumerate()
.filter(|(_, &score)| score == min_score)
.map(|(idx, _)| idx as u8)
.collect();
if best_colors.is_empty() {
Self::random_color()
} else {
let idx = rand::thread_rng().gen_range(0..best_colors.len());
best_colors[idx]
}
}
}
#[derive(Debug, Clone)]
pub struct Task {
pub id: i64,
pub issue_key: String,
pub name: String,
pub project: String,
}
impl TimeEntry {
pub fn duration_minutes(&self) -> Option<i64> {
self.end_time.map(|end| {
let duration = end.signed_duration_since(self.start_time);
duration.num_minutes()
})
}
pub fn duration_formatted(&self) -> String {
let end = self.end_time.unwrap_or_else(|| Local::now().naive_local());
let duration = end.signed_duration_since(self.start_time);
let total_seconds = duration.num_seconds();
let hours = total_seconds / 3600;
let minutes = (total_seconds % 3600) / 60;
let seconds = total_seconds % 60;
if hours > 0 {
format!("{}h {:02}m {:02}s", hours, minutes, seconds)
} else if minutes > 0 {
format!("{}m {:02}s", minutes, seconds)
} else {
format!("{}s", seconds)
}
}
pub fn is_running(&self) -> bool {
self.end_time.is_none()
}
}