From 5c8e3df612147c604f2779cab1b79e9a6552f76d Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 14 Feb 2020 16:15:18 +1000 Subject: [PATCH] include report in MediaCheckOutput --- proto/backend.proto | 4 +- qt/aqt/mediacheck.py | 59 +---------------- rslib/src/backend.rs | 8 +-- rslib/src/i18n/media-check.ftl | 19 ++++++ rslib/src/i18n/mod.rs | 19 +++++- rslib/src/media/check.rs | 114 +++++++++++++++++++++++++++++++-- 6 files changed, 152 insertions(+), 71 deletions(-) create mode 100644 rslib/src/i18n/media-check.ftl diff --git a/proto/backend.proto b/proto/backend.proto index 2405a5aa5..5e6e151c8 100644 --- a/proto/backend.proto +++ b/proto/backend.proto @@ -271,9 +271,7 @@ message SyncMediaIn { message MediaCheckOut { repeated string unused = 1; repeated string missing = 2; - repeated string dirs = 3; - repeated string oversize = 4; - map renamed = 5; + string report = 3; } message TrashMediaFilesIn { diff --git a/qt/aqt/mediacheck.py b/qt/aqt/mediacheck.py index 0d78a03c6..d963f0845 100644 --- a/qt/aqt/mediacheck.py +++ b/qt/aqt/mediacheck.py @@ -60,7 +60,7 @@ class MediaChecker: "Run the check on a background thread." return self.mw.col.media.check() - def _on_finished(self, future: Future): + def _on_finished(self, future: Future) -> None: hooks.bg_thread_progress_callback.remove(self._on_progress) self.mw.progress.finish() self.progress_dialog = None @@ -70,8 +70,8 @@ class MediaChecker: if isinstance(exc, Interrupted): return - output = future.result() - report = describe_output(output) + output: MediaCheckOutput = future.result() + report = output.report # show report and offer to delete diag = QDialog(self.mw) @@ -156,56 +156,3 @@ class MediaChecker: self.progress_dialog = None tooltip(_("Files moved to trash.")) - - -def describe_output(output: MediaCheckOutput) -> str: - buf = [] - - buf.append(_("Missing files: {}").format(len(output.missing))) - buf.append(_("Unused files: {}").format(len(output.unused))) - if output.renamed: - buf.append(_("Renamed files: {}").format(len(output.renamed))) - if output.oversize: - buf.append(_("Over 100MB: {}".format(output.oversize))) - if output.dirs: - buf.append(_("Subfolders: {}".format(output.dirs))) - - buf.append("") - - if output.renamed: - buf.append(_("Some files have been renamed for compatibility:")) - buf.extend( - _("Renamed: %(old)s -> %(new)s") % dict(old=k, new=v) - for (k, v) in sorted(output.renamed.items()) - ) - buf.append("") - - if output.oversize: - buf.append(_("Files over 100MB can not be synced with AnkiWeb.")) - buf.extend(_("Over 100MB: {}").format(f) for f in sorted(output.oversize)) - buf.append("") - - if output.dirs: - buf.append(_("Folders inside the media folder are not supported.")) - buf.extend(_("Folder: {}").format(f) for f in sorted(output.dirs)) - buf.append("") - - if output.missing: - buf.append( - _( - "The following files are referenced by cards, but were not found in the media folder:" - ) - ) - buf.extend(_("Missing: {}").format(f) for f in sorted(output.missing)) - buf.append("") - - if output.unused: - buf.append( - _( - "The following files were found in the media folder, but do not appear to be used on any cards:" - ) - ) - buf.extend(_("Unused: {}").format(f) for f in sorted(output.unused)) - buf.append("") - - return "\n".join(buf) diff --git a/rslib/src/backend.rs b/rslib/src/backend.rs index 7df2cd6d7..4e1c66717 100644 --- a/rslib/src/backend.rs +++ b/rslib/src/backend.rs @@ -361,14 +361,14 @@ impl Backend { let mgr = MediaManager::new(&self.media_folder, &self.media_db)?; let mut checker = MediaChecker::new(&mgr, &self.col_path, callback, &self.i18n); - let output = checker.check()?; + let mut output = checker.check()?; + + let report = checker.summarize_output(&mut output); Ok(pb::MediaCheckOut { unused: output.unused, missing: output.missing, - renamed: output.renamed, - dirs: output.dirs, - oversize: output.oversize, + report, }) } diff --git a/rslib/src/i18n/media-check.ftl b/rslib/src/i18n/media-check.ftl new file mode 100644 index 000000000..43ef0d4c1 --- /dev/null +++ b/rslib/src/i18n/media-check.ftl @@ -0,0 +1,19 @@ +missing-count = Missing files: {$count} +unused-count = Unused files: {$count} +renamed-count = Renamed files: {$count} +oversize-count = Over 100MB: {$count} +subfolder-count = Subfolders: {$count} + +renamed-header = Some files have been renamed for compatibility: +oversize-header = Files over 100MB can not be synced with AnkiWeb. +subfolder-header = Folders inside the media folder are not supported. +missing-header = + The following files are referenced by cards, but were not found in the media folder: +unused-header = + The following files were found in the media folder, but do not appear to be used on any cards: + +renamed-file = Renamed: {$old} -> {$new} +oversize-file = Over 100MB: {$filename} +subfolder-file = Folder: {$filename} +missing-file = Missing: {$filename} +unused-file = Unused: {$filename} diff --git a/rslib/src/i18n/mod.rs b/rslib/src/i18n/mod.rs index 10e639bba..37bbb9fb1 100644 --- a/rslib/src/i18n/mod.rs +++ b/rslib/src/i18n/mod.rs @@ -10,6 +10,21 @@ use unic_langid::LanguageIdentifier; pub use fluent::fluent_args as tr_args; +/// Helper for creating args with &strs +#[macro_export] +macro_rules! tr_strs { + ( $($key:expr => $value:expr),* ) => { + { + let mut args: fluent::FluentArgs = fluent::FluentArgs::new(); + $( + args.insert($key, $value.to_string().into()); + )* + args + } + }; +} +pub use tr_strs; + /// All languages we (currently) support, excluding the fallback /// English. #[derive(Debug, PartialEq, Clone, Copy)] @@ -169,8 +184,8 @@ impl I18nCategory { } /// Get translation with one or more arguments. - pub fn trn(&self, key: &str, args: FluentArgs) -> Cow { - self.tr_(key, Some(args)) + pub fn trn(&self, key: &str, args: FluentArgs) -> String { + self.tr_(key, Some(args)).into() } fn tr_<'a>(&'a self, key: &str, args: Option) -> Cow<'a, str> { diff --git a/rslib/src/media/check.rs b/rslib/src/media/check.rs index eeb651de6..3c5f479a2 100644 --- a/rslib/src/media/check.rs +++ b/rslib/src/media/check.rs @@ -3,7 +3,7 @@ use crate::cloze::expand_clozes_to_reveal_latex; use crate::err::{AnkiError, Result}; -use crate::i18n::I18n; +use crate::i18n::{tr_args, tr_strs, I18n, TranslationFile}; use crate::latex::extract_latex; use crate::media::col::{ for_every_note, get_note_types, mark_collection_modified, open_or_create_collection_db, @@ -78,9 +78,6 @@ where let folder_check = self.check_media_folder(&mut ctx)?; let referenced_files = self.check_media_references(&folder_check.renamed)?; let (unused, missing) = find_unused_and_missing(folder_check.files, referenced_files); - - let _ = self.i18n; - Ok(MediaCheckOutput { unused, missing, @@ -90,6 +87,88 @@ where }) } + pub fn summarize_output(&self, output: &mut MediaCheckOutput) -> String { + let mut buf = String::new(); + let cat = self.i18n.get(TranslationFile::MediaCheck); + + // top summary area + buf += &cat.trn("missing-count", tr_args!["count"=>output.missing.len()]); + buf.push('\n'); + + buf += &cat.trn("unused-count", tr_args!["count"=>output.unused.len()]); + buf.push('\n'); + + if !output.renamed.is_empty() { + buf += &cat.trn("renamed-count", tr_args!["count"=>output.renamed.len()]); + buf.push('\n'); + } + if !output.oversize.is_empty() { + buf += &cat.trn("oversize-count", tr_args!["count"=>output.oversize.len()]); + buf.push('\n'); + } + if !output.dirs.is_empty() { + buf += &cat.trn("subfolder-count", tr_args!["count"=>output.dirs.len()]); + buf.push('\n'); + } + + buf.push('\n'); + + if !output.renamed.is_empty() { + buf += &cat.tr("renamed-header"); + buf.push('\n'); + for (old, new) in &output.renamed { + buf += &cat.trn("renamed-file", tr_strs!["old"=>old,"new"=>new]); + buf.push('\n'); + } + buf.push('\n') + } + + if !output.oversize.is_empty() { + output.oversize.sort(); + buf += &cat.tr("oversize-header"); + buf.push('\n'); + for fname in &output.oversize { + buf += &cat.trn("oversize-file", tr_strs!["filename"=>fname]); + buf.push('\n'); + } + buf.push('\n') + } + + if !output.dirs.is_empty() { + output.dirs.sort(); + buf += &cat.tr("subfolder-header"); + buf.push('\n'); + for fname in &output.dirs { + buf += &cat.trn("subfolder-file", tr_strs!["filename"=>fname]); + buf.push('\n'); + } + buf.push('\n') + } + + if !output.missing.is_empty() { + output.missing.sort(); + buf += &cat.tr("missing-header"); + buf.push('\n'); + for fname in &output.missing { + buf += &cat.trn("missing-file", tr_strs!["filename"=>fname]); + buf.push('\n'); + } + buf.push('\n') + } + + if !output.unused.is_empty() { + output.unused.sort(); + buf += &cat.tr("unused-header"); + buf.push('\n'); + for fname in &output.unused { + buf += &cat.trn("unused-file", tr_strs!["filename"=>fname]); + buf.push('\n'); + } + } + + buf + } + /// Check all the files in the media folder. /// /// - Renames files with invalid names @@ -377,17 +456,18 @@ mod test { fs::write(&mgr.media_folder.join("normal.jpg"), "normal")?; fs::write(&mgr.media_folder.join("foo[.jpg"), "foo")?; fs::write(&mgr.media_folder.join("_under.jpg"), "foo")?; + fs::write(&mgr.media_folder.join("unused.jpg"), "foo")?; let i18n = I18n::new(&["zz"], "dummy"); let progress = |_n| true; let mut checker = MediaChecker::new(&mgr, &col_path, progress, &i18n); - let output = checker.check()?; + let mut output = checker.check()?; assert_eq!( output, MediaCheckOutput { - unused: vec![], + unused: vec!["unused.jpg".into()], missing: vec!["ぱぱ.jpg".into()], renamed: vec![("foo[.jpg".into(), "foo.jpg".into())] .into_iter() @@ -400,6 +480,28 @@ mod test { assert!(fs::metadata(&mgr.media_folder.join("foo[.jpg")).is_err()); assert!(fs::metadata(&mgr.media_folder.join("foo.jpg")).is_ok()); + let report = checker.summarize_output(&mut output); + assert_eq!( + report, + "Missing files: 1 +Unused files: 1 +Renamed files: 1 +Subfolders: 1 + +Some files have been renamed for compatibility: +Renamed: foo[.jpg -> foo.jpg + +Folders inside the media folder are not supported. +Folder: folder + +The following files are referenced by cards, but were not found in the media folder: +Missing: ぱぱ.jpg + +The following files were found in the media folder, but do not appear to be used on any cards: +Unused: unused.jpg +" + ); + Ok(()) }