Muyue f113ad6721 Initial commit - Activity Tracker MVP
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>
2025-10-16 09:05:39 +02:00

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");
}
}