From e46294a02215f3a825eaac983cea958cc82422c7 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Sat, 4 Apr 2026 22:30:13 +0200 Subject: [PATCH] fix: more on progress in tasks --- crates/bds-core/src/engine/search.rs | 32 +++++++++++--- .../src/engine/validate_translations.rs | 42 +++++++++++++++---- crates/bds-ui/src/app.rs | 29 +++++++++---- 3 files changed, 81 insertions(+), 22 deletions(-) diff --git a/crates/bds-core/src/engine/search.rs b/crates/bds-core/src/engine/search.rs index 34a1293..0043e36 100644 --- a/crates/bds-core/src/engine/search.rs +++ b/crates/bds-core/src/engine/search.rs @@ -12,11 +12,24 @@ pub struct ReindexReport { pub media_indexed: usize, } +/// Per-item progress callback: (current_item, total_items, item_description). +pub type ItemProgressFn = Box; + /// Drop and rebuild the entire FTS index for all posts and media in a project. pub fn reindex_all( conn: &Connection, project_id: &str, main_language: &str, +) -> EngineResult { + reindex_all_with_progress(conn, project_id, main_language, None) +} + +/// Like `reindex_all` but with optional per-item progress. +pub fn reindex_all_with_progress( + conn: &Connection, + project_id: &str, + main_language: &str, + on_item: Option, ) -> EngineResult { // Wipe existing FTS content conn.execute("DELETE FROM posts_fts", [])?; @@ -25,8 +38,16 @@ pub fn reindex_all( // Reindex all posts let posts = post_q::list_posts_by_project(conn, project_id)?; + // Reindex all media + let media_items = media_q::list_media_by_project(conn, project_id)?; + + let total = posts.len() + media_items.len(); + let mut posts_indexed = 0; - for post in &posts { + for (i, post) in posts.iter().enumerate() { + if let Some(ref cb) = on_item { + cb(i + 1, total, &post.title); + } let translations = post_translation::list_post_translations_by_post(conn, &post.id)?; let trans_pairs: Vec<(String, String)> = translations @@ -58,11 +79,12 @@ pub fn reindex_all( posts_indexed += 1; } - // Reindex all media - let media_items = media_q::list_media_by_project(conn, project_id)?; - + let offset = posts.len(); let mut media_indexed = 0; - for m in &media_items { + for (i, m) in media_items.iter().enumerate() { + if let Some(ref cb) = on_item { + cb(offset + i + 1, total, &m.original_name); + } let translations = media_translation::list_media_translations_by_media(conn, &m.id)?; let trans_pairs: Vec<(String, String)> = translations diff --git a/crates/bds-core/src/engine/validate_translations.rs b/crates/bds-core/src/engine/validate_translations.rs index f32d4f0..5a7e78e 100644 --- a/crates/bds-core/src/engine/validate_translations.rs +++ b/crates/bds-core/src/engine/validate_translations.rs @@ -50,11 +50,24 @@ pub struct TranslationValidationReport { pub checked_fs_files: usize, } +/// Per-item progress callback: (current_item, total_items, item_description). +pub type ItemProgressFn = Box; + /// Validate all translations in a project against consistency rules. pub fn validate_translations( conn: &Connection, data_dir: &Path, project_id: &str, +) -> EngineResult { + validate_translations_with_progress(conn, data_dir, project_id, None) +} + +/// Like `validate_translations` but with optional per-item progress. +pub fn validate_translations_with_progress( + conn: &Connection, + data_dir: &Path, + project_id: &str, + on_item: Option, ) -> EngineResult { let posts = post_q::list_posts_by_project(conn, project_id)?; @@ -64,8 +77,12 @@ pub fn validate_translations( // Phase 1: database validation let mut db_issues = Vec::new(); let mut checked_db_rows = 0usize; + let post_count = posts.len(); - for post in &posts { + for (i, post) in posts.iter().enumerate() { + if let Some(ref cb) = on_item { + cb(i + 1, post_count, &post.title); + } let translations = post_translation::list_post_translations_by_post(conn, &post.id)?; for t in &translations { @@ -113,19 +130,26 @@ pub fn validate_translations( let posts_dir = data_dir.join("posts"); if posts_dir.exists() { - for entry in walkdir::WalkDir::new(&posts_dir) + // Collect translation files first so we know the total + let translation_files: Vec<_> = walkdir::WalkDir::new(&posts_dir) .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.file_type().is_file()) - { - let path = entry.path(); - let Some(ext) = path.extension() else { continue }; - if ext != "md" { continue; } + .filter(|e| e.path().extension().is_some_and(|ext| ext == "md")) + .filter(|e| { + let stem = e.path().file_stem().and_then(|s| s.to_str()).unwrap_or(""); + is_translation_stem(stem) + }) + .collect(); - // Check if this is a translation file (has language code before .md) + let fs_total = translation_files.len(); + + for (i, entry) in translation_files.iter().enumerate() { + let path = entry.path(); let stem = path.file_stem().and_then(|s| s.to_str()).unwrap_or(""); - if !is_translation_stem(stem) { - continue; + + if let Some(ref cb) = on_item { + cb(i + 1, fs_total, stem); } checked_fs_files += 1; diff --git a/crates/bds-ui/src/app.rs b/crates/bds-ui/src/app.rs index 3c9d58e..06824f6 100644 --- a/crates/bds-ui/src/app.rs +++ b/crates/bds-ui/src/app.rs @@ -498,9 +498,15 @@ impl BdsApp { .ok() .and_then(|m| m.main_language) .unwrap_or_else(|| "en".to_string()); - tm.report_progress(tid, Some(0.10), Some("Reindexing...".into())); - let report = engine::search::reindex_all(db.conn(), &project_id, &main_lang) - .map_err(|e| e.to_string())?; + let tm2 = Arc::clone(&tm); + let on_item: engine::search::ItemProgressFn = Box::new(move |current, total, name| { + let pct = if total > 0 { current as f32 / total as f32 } else { 1.0 }; + let msg = format!("Indexing: {current}/{total} \u{2014} {name}"); + tm2.report_progress(tid, Some(pct), Some(msg)); + }); + let report = engine::search::reindex_all_with_progress( + db.conn(), &project_id, &main_lang, Some(on_item), + ).map_err(|e| e.to_string())?; Ok(format!("posts={}, media={}", report.posts_indexed, report.media_indexed)) }, ) @@ -510,9 +516,10 @@ impl BdsApp { "engine.calendarStarted", |db_path, project_id, data_dir, tm, tid| { let db = Database::open(&db_path).map_err(|e| e.to_string())?; - tm.report_progress(tid, Some(0.10), Some("Generating calendar JSON...".into())); + tm.report_progress(tid, Some(0.20), Some("Loading posts...".into())); engine::calendar::regenerate_calendar(db.conn(), &data_dir, &project_id) .map_err(|e| e.to_string())?; + tm.report_progress(tid, Some(0.90), Some("Writing calendar JSON...".into())); Ok("done".to_string()) }, ) @@ -523,9 +530,14 @@ impl BdsApp { "engine.validateTranslationsStarted", |db_path, project_id, data_dir, tm, tid| { let db = Database::open(&db_path).map_err(|e| e.to_string())?; - tm.report_progress(tid, Some(0.10), Some("Checking translations...".into())); - let report = engine::validate_translations::validate_translations( - db.conn(), &data_dir, &project_id, + let tm2 = Arc::clone(&tm); + let on_item: engine::validate_translations::ItemProgressFn = Box::new(move |current, total, name| { + let pct = if total > 0 { current as f32 / total as f32 } else { 1.0 }; + let msg = format!("Checking: {current}/{total} \u{2014} {name}"); + tm2.report_progress(tid, Some(pct), Some(msg)); + }); + let report = engine::validate_translations::validate_translations_with_progress( + db.conn(), &data_dir, &project_id, Some(on_item), ).map_err(|e| e.to_string())?; Ok(format!("db_issues={}, fs_issues={}", report.db_issues.len(), report.fs_issues.len())) }, @@ -536,9 +548,10 @@ impl BdsApp { "engine.generateSiteStarted", |db_path, project_id, data_dir, tm, tid| { let db = Database::open(&db_path).map_err(|e| e.to_string())?; - tm.report_progress(tid, Some(0.10), Some("Generating calendar...".into())); + tm.report_progress(tid, Some(0.20), Some("Generating calendar...".into())); engine::calendar::regenerate_calendar(db.conn(), &data_dir, &project_id) .map_err(|e| e.to_string())?; + tm.report_progress(tid, Some(0.90), Some("Calendar written".into())); Ok("done".to_string()) }, )