Implémentation complète du MVP (Minimum Viable Product) : ✅ Module de capture : - Screenshots avec compression WebP (qualité 80%) - Métadonnées des fenêtres actives - Détection d'inactivité (pause après 10min) ✅ Module de stockage : - Base SQLite avec schéma optimisé - Chiffrement AES-256-GCM des données sensibles - Dérivation de clé PBKDF2-HMAC-SHA512 (100k itérations) - Nettoyage automatique après 30 jours ✅ Module d'analyse IA : - Classification heuristique en 5 catégories - Extraction d'entités (projet, outil, langage) - Patterns optimisés pour Development, Meeting, Research, Design ✅ Module de rapport : - Génération de rapports JSON - Timeline d'activités avec statistiques - Export chiffré des données ✅ CLI complète : - activity-tracker start : capture en arrière-plan - activity-tracker report : génération de rapport - activity-tracker stats : statistiques de stockage - activity-tracker cleanup : nettoyage des données - activity-tracker export : export complet 📚 Documentation : - README complet avec exemples d'utilisation - Configuration via settings.toml - Tests unitaires pour chaque module 🔒 Sécurité : - Chiffrement end-to-end des screenshots - Pas de stockage du mot de passe - Protection RGPD avec consentement explicite Conformité avec le design-journal.md pour le MVP. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
98 lines
2.4 KiB
Rust
98 lines
2.4 KiB
Rust
/// Timeline visualization data structure
|
|
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Timeline of activities
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Timeline {
|
|
pub entries: Vec<TimelineEntry>,
|
|
pub start: DateTime<Utc>,
|
|
pub end: DateTime<Utc>,
|
|
}
|
|
|
|
impl Timeline {
|
|
pub fn new(start: DateTime<Utc>, end: DateTime<Utc>) -> Self {
|
|
Self {
|
|
entries: Vec::new(),
|
|
start,
|
|
end,
|
|
}
|
|
}
|
|
|
|
pub fn add_entry(&mut self, entry: TimelineEntry) {
|
|
self.entries.push(entry);
|
|
}
|
|
|
|
pub fn sort_by_time(&mut self) {
|
|
self.entries.sort_by_key(|e| e.timestamp);
|
|
}
|
|
|
|
pub fn duration_seconds(&self) -> i64 {
|
|
(self.end - self.start).num_seconds()
|
|
}
|
|
}
|
|
|
|
/// Single timeline entry
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TimelineEntry {
|
|
pub timestamp: DateTime<Utc>,
|
|
pub category: String,
|
|
pub activity: String,
|
|
pub duration_seconds: i64,
|
|
pub color: String, // For visualization
|
|
}
|
|
|
|
impl TimelineEntry {
|
|
pub fn new(
|
|
timestamp: DateTime<Utc>,
|
|
category: String,
|
|
activity: String,
|
|
duration_seconds: i64,
|
|
) -> Self {
|
|
let color = Self::category_color(&category);
|
|
Self {
|
|
timestamp,
|
|
category,
|
|
activity,
|
|
duration_seconds,
|
|
color,
|
|
}
|
|
}
|
|
|
|
fn category_color(category: &str) -> String {
|
|
match category {
|
|
"Development" => "#4CAF50".to_string(), // Green
|
|
"Meeting" => "#2196F3".to_string(), // Blue
|
|
"Research" => "#FF9800".to_string(), // Orange
|
|
"Design" => "#9C27B0".to_string(), // Purple
|
|
_ => "#9E9E9E".to_string(), // Gray
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_timeline_creation() {
|
|
let start = Utc::now();
|
|
let end = start + chrono::Duration::hours(8);
|
|
let mut timeline = Timeline::new(start, end);
|
|
|
|
assert_eq!(timeline.entries.len(), 0);
|
|
assert_eq!(timeline.duration_seconds(), 8 * 3600);
|
|
|
|
let entry = TimelineEntry::new(
|
|
start,
|
|
"Development".to_string(),
|
|
"Coding".to_string(),
|
|
3600,
|
|
);
|
|
timeline.add_entry(entry);
|
|
|
|
assert_eq!(timeline.entries.len(), 1);
|
|
assert_eq!(timeline.entries[0].color, "#4CAF50");
|
|
}
|
|
}
|