diff --git a/ftl/core/browsing.ftl b/ftl/core/browsing.ftl index e07435e9c..24091e4f8 100644 --- a/ftl/core/browsing.ftl +++ b/ftl/core/browsing.ftl @@ -8,6 +8,7 @@ browsing-answer = Answer browsing-any-cards-mapped-to-nothing-will = Any cards mapped to nothing will be deleted. If a note has no remaining cards, it will be lost. Are you sure you want to continue? browsing-any-flag = Any Flag browsing-average-ease = Average Ease +browsing-average-interval = Average Interval browsing-browser-appearance = Browser Appearance browsing-browser-options = Browser Options browsing-buried = Buried diff --git a/qt/aqt/table.py b/qt/aqt/table.py index 665a13d41..2e41d37e9 100644 --- a/qt/aqt/table.py +++ b/qt/aqt/table.py @@ -720,6 +720,7 @@ class NoteState(ItemState): ("noteDue", tr.statistics_due_date()), ("noteEase", tr.browsing_average_ease()), ("noteFld", tr.browsing_sort_field()), + ("noteIvl", tr.browsing_average_interval()), ("noteLapses", tr.scheduling_lapses()), ("noteMod", tr.search_note_modified()), ("noteReps", tr.scheduling_reviews()), diff --git a/rslib/backend.proto b/rslib/backend.proto index dbc10b2b1..2ca96464b 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -811,22 +811,23 @@ message SortOrder { enum Kind { NOTE_CARDS = 0; NOTE_CREATION = 1; - NOTE_DUE = 17; - NOTE_EASE = 2; - NOTE_FIELD = 3; - NOTE_LAPSES = 4; - NOTE_MOD = 5; - NOTE_REPS = 6; - NOTE_TAGS = 7; - NOTETYPE = 8; - CARD_MOD = 9; - CARD_REPS = 10; - CARD_DUE = 11; - CARD_EASE = 12; - CARD_LAPSES = 13; - CARD_INTERVAL = 14; - CARD_DECK = 15; - CARD_TEMPLATE = 16; + NOTE_DUE = 2; + NOTE_EASE = 3; + NOTE_FIELD = 4; + NOTE_INTERVAL = 5; + NOTE_LAPSES = 6; + NOTE_MOD = 7; + NOTE_REPS = 8; + NOTE_TAGS = 9; + NOTETYPE = 10; + CARD_MOD = 11; + CARD_REPS = 12; + CARD_DUE = 13; + CARD_EASE = 14; + CARD_LAPSES = 15; + CARD_INTERVAL = 16; + CARD_DECK = 17; + CARD_TEMPLATE = 18; } Kind kind = 1; bool reverse = 2; diff --git a/rslib/src/backend/search/browser_table.rs b/rslib/src/backend/search/browser_table.rs index 4538a208e..cb405769a 100644 --- a/rslib/src/backend/search/browser_table.rs +++ b/rslib/src/backend/search/browser_table.rs @@ -27,6 +27,7 @@ impl From for browser_table::Column { "noteDue" => browser_table::Column::NoteDue, "noteEase" => browser_table::Column::NoteEase, "noteFld" => browser_table::Column::NoteField, + "noteIvl" => browser_table::Column::NoteInterval, "noteLapses" => browser_table::Column::NoteLapses, "noteMod" => browser_table::Column::NoteMod, "noteReps" => browser_table::Column::NoteReps, diff --git a/rslib/src/backend/search/mod.rs b/rslib/src/backend/search/mod.rs index ea3aeead7..5aaf25780 100644 --- a/rslib/src/backend/search/mod.rs +++ b/rslib/src/backend/search/mod.rs @@ -111,6 +111,7 @@ impl From for SortKind { SortKindProto::NoteCreation => SortKind::NoteCreation, SortKindProto::NoteDue => SortKind::NoteDue, SortKindProto::NoteEase => SortKind::NoteEase, + SortKindProto::NoteInterval => SortKind::NoteInterval, SortKindProto::NoteLapses => SortKind::NoteLapses, SortKindProto::NoteMod => SortKind::NoteMod, SortKindProto::NoteField => SortKind::NoteField, diff --git a/rslib/src/browser_table.rs b/rslib/src/browser_table.rs index a8d1bf5c5..ba0150772 100644 --- a/rslib/src/browser_table.rs +++ b/rslib/src/browser_table.rs @@ -40,6 +40,7 @@ pub enum Column { NoteDue, NoteEase, NoteField, + NoteInterval, NoteLapses, NoteMod, NoteReps, @@ -492,6 +493,26 @@ impl<'a> NoteRowContext<'a> { .map(|time| time.date_string()) .unwrap_or_else(|| "".into()) } + + /// Returns the average interval of the review and relearn cards or the empty string if there + /// aren't any. + fn note_interval_str(&self) -> String { + let intervals: Vec = self + .cards + .iter() + .filter(|c| matches!(c.ctype, CardType::Review | CardType::Relearn)) + .map(|c| c.interval) + .collect(); + if intervals.is_empty() { + "".into() + } else { + time_span( + (intervals.iter().sum::() * 86400 / (intervals.len() as u32)) as f32, + self.tr, + false, + ) + } + } } impl RowContext for NoteRowContext<'_> { @@ -502,6 +523,7 @@ impl RowContext for NoteRowContext<'_> { Column::NoteDue => self.note_due_str(), Column::NoteEase => self.note_ease_str(), Column::NoteField => self.note_field_str(), + Column::NoteInterval => self.note_interval_str(), Column::NoteLapses => self.cards.iter().map(|c| c.lapses).sum::().to_string(), Column::NoteMod => self.note.mtime.date_string(), Column::NoteReps => self.cards.iter().map(|c| c.reps).sum::().to_string(), diff --git a/rslib/src/config/mod.rs b/rslib/src/config/mod.rs index 4598ee184..a076934f3 100644 --- a/rslib/src/config/mod.rs +++ b/rslib/src/config/mod.rs @@ -273,6 +273,8 @@ pub enum SortKind { NoteCreation, NoteDue, NoteEase, + #[serde(rename = "noteIvl")] + NoteInterval, NoteLapses, NoteMod, #[serde(rename = "noteFld")] diff --git a/rslib/src/search/mod.rs b/rslib/src/search/mod.rs index 7c1300232..d3fa0a6ec 100644 --- a/rslib/src/search/mod.rs +++ b/rslib/src/search/mod.rs @@ -93,6 +93,7 @@ impl SortKind { | SortKind::NoteDue | SortKind::NoteEase | SortKind::NoteField + | SortKind::NoteInterval | SortKind::NoteLapses | SortKind::NoteMod | SortKind::NoteReps @@ -256,6 +257,7 @@ fn note_order_from_sortkind(kind: SortKind) -> Cow<'static, str> { SortKind::NoteCards | SortKind::NoteDue | SortKind::NoteEase + | SortKind::NoteInterval | SortKind::NoteLapses | SortKind::NoteReps => "(select pos from sort_order where nid = n.id) asc".into(), SortKind::NoteCreation => "n.id asc".into(), @@ -275,6 +277,7 @@ fn prepare_sort(col: &mut Collection, kind: SortKind) -> Result<()> { NoteCards => include_str!("note_cards_order.sql"), NoteDue => include_str!("note_due_order.sql"), NoteEase => include_str!("note_ease_order.sql"), + NoteInterval => include_str!("note_interval_order.sql"), NoteLapses => include_str!("note_lapses_order.sql"), NoteReps => include_str!("note_reps_order.sql"), Notetype => include_str!("notetype_order.sql"), diff --git a/rslib/src/search/note_interval_order.sql b/rslib/src/search/note_interval_order.sql new file mode 100644 index 000000000..375bf10bf --- /dev/null +++ b/rslib/src/search/note_interval_order.sql @@ -0,0 +1,11 @@ +DROP TABLE IF EXISTS sort_order; +CREATE TEMPORARY TABLE sort_order ( + pos integer PRIMARY KEY, + nid integer NOT NULL UNIQUE +); +INSERT INTO sort_order (nid) +SELECT nid +FROM cards +WHERE type IN (2, 3) +GROUP BY nid +ORDER BY AVG(ivl); \ No newline at end of file