// src/economic/storage.rs use super::types::*; use super::helpers::*; use tokio::fs; use chrono::{NaiveDate, Datelike}; use std::collections::HashMap; pub async fn scan_existing_chunks() -> anyhow::Result> { let dir = std::path::Path::new("economic_events"); let mut chunks = Vec::new(); if dir.exists() { let mut entries = fs::read_dir(dir).await?; while let Some(entry) = entries.next_entry().await? { let path = entry.path(); if path.extension().map(|e| e == "json").unwrap_or(false) { if let Some(name) = path.file_name().and_then(|n| n.to_str()) { if name.starts_with("chunk_") { if let Some(content) = fs::read_to_string(&path).await.ok() { if let Ok(events) = serde_json::from_str::>(&content) { let start = name[6..16].to_string(); let end = name[17..27].to_string(); chunks.push(ChunkInfo { start_date: start, end_date: end, path, event_count: events.len() }); } } } } } } } chunks.sort_by_key(|c| c.start_date.clone()); Ok(chunks) } pub async fn load_existing_events(chunks: &[ChunkInfo]) -> anyhow::Result> { let mut map = HashMap::new(); for chunk in chunks { let content = fs::read_to_string(&chunk.path).await?; let events: Vec = serde_json::from_str(&content)?; for e in events { map.insert(event_key(&e), e); } } Ok(map) } pub async fn save_optimized_chunks(events: HashMap) -> anyhow::Result<()> { let dir = std::path::Path::new("economic_events"); fs::create_dir_all(dir).await?; // Delete all old chunk files to prevent duplicates and overlaps println!("Removing old chunks..."); let mut entries = fs::read_dir(dir).await?; while let Some(entry) = entries.next_entry().await? { let path = entry.path(); if let Some(name) = path.file_name().and_then(|n| n.to_str()) { if name.starts_with("chunk_") && path.extension().map(|e| e == "json").unwrap_or(false) { fs::remove_file(&path).await?; } } } let mut sorted: Vec<_> = events.into_values().collect(); sorted.sort_by_key(|e| e.date.clone()); let mut chunk: Vec = Vec::new(); const MAX_EVENTS_PER_CHUNK: usize = ( 30000 / 2 ) / 11; // (30000 - 2) / 11 = 2727 for e in sorted { if !chunk.is_empty() && chunk.len() >= MAX_EVENTS_PER_CHUNK { save_chunk(&chunk, dir).await?; chunk.clear(); } chunk.push(e); } if !chunk.is_empty() { save_chunk(&chunk, dir).await?; } Ok(()) } async fn save_chunk(events: &[EconomicEvent], dir: &std::path::Path) -> anyhow::Result<()> { let start = events.iter().map(|e| &e.date).min().unwrap().clone(); let end = events.iter().map(|e| &e.date).max().unwrap().clone(); let path = dir.join(format!("chunk_{}_{}.json", start, end)); fs::write(&path, serde_json::to_string_pretty(events)?).await?; Ok(()) } pub async fn save_changes(changes: &[EventChange]) -> anyhow::Result<()> { if changes.is_empty() { return Ok(()); } let dir = std::path::Path::new("economic_event_changes"); fs::create_dir_all(dir).await?; let mut by_month: HashMap> = HashMap::new(); for c in changes { if let Ok(d) = NaiveDate::parse_from_str(&c.date, "%Y-%m-%d") { let key = format!("{:02}_{}", d.month(), d.year()); by_month.entry(key).or_default().push(c.clone()); } } for (month, list) in by_month { let path = dir.join(format!("event_changes_{}.json", month)); let mut all = if path.exists() { let s = fs::read_to_string(&path).await?; serde_json::from_str(&s).unwrap_or_default() } else { vec![] }; all.extend(list); fs::write(&path, serde_json::to_string_pretty(&all)?).await?; } Ok(()) }