From 5ec2e11de32da7cf7459cf7cbd4fadbe4b398fcd Mon Sep 17 00:00:00 2001 From: RumovZ Date: Sun, 18 Apr 2021 12:14:18 +0200 Subject: [PATCH 1/7] Add search keyword `introduced` Filters for cards that had their first review within the last x days. --- rslib/src/search/parser.rs | 7 +++++++ rslib/src/search/sqlwriter.rs | 14 ++++++++++++++ rslib/src/search/writer.rs | 1 + 3 files changed, 22 insertions(+) diff --git a/rslib/src/search/parser.rs b/rslib/src/search/parser.rs index 9317c450d..fb5d08619 100644 --- a/rslib/src/search/parser.rs +++ b/rslib/src/search/parser.rs @@ -73,6 +73,7 @@ pub enum SearchNode { CardTemplate(TemplateKind), Deck(String), DeckId(DeckId), + IntroducedInDays(u32), NotetypeId(NotetypeId), Notetype(String), Rated { @@ -333,6 +334,7 @@ fn search_node_for_text_with_argument<'a>( "prop" => parse_prop(val)?, "added" => parse_added(val)?, "edited" => parse_edited(val)?, + "introduced" => parse_introduced(val)?, "rated" => parse_rated(val)?, "is" => parse_state(val)?, "did" => parse_did(val)?, @@ -527,6 +529,11 @@ fn parse_edited(s: &str) -> ParseResult { parse_u32(s, "edited:").map(|n| SearchNode::EditedInDays(n.max(1))) } +/// eg introduced:1 +fn parse_introduced(s: &str) -> ParseResult { + parse_u32(s, "introduced:").map(|n| SearchNode::IntroducedInDays(n.max(1))) +} + /// eg rated:3 or rated:10:2 /// second arg must be between 1-4 fn parse_rated(s: &str) -> ParseResult { diff --git a/rslib/src/search/sqlwriter.rs b/rslib/src/search/sqlwriter.rs index 793b504aa..f3dee8377 100644 --- a/rslib/src/search/sqlwriter.rs +++ b/rslib/src/search/sqlwriter.rs @@ -128,6 +128,7 @@ impl SqlWriter<'_> { // other SearchNode::AddedInDays(days) => self.write_added(*days)?, SearchNode::EditedInDays(days) => self.write_edited(*days)?, + SearchNode::IntroducedInDays(days) => self.write_introduced(*days)?, SearchNode::CardTemplate(template) => match template { TemplateKind::Ordinal(_) => self.write_template(template), TemplateKind::Name(name) => { @@ -492,6 +493,18 @@ impl SqlWriter<'_> { Ok(()) } + fn write_introduced(&mut self, days: u32) -> Result<()> { + let timing = self.col.timing_today()?; + let cutoff = (timing.next_day_at.0 - (86_400 * (days as i64))) * 1_000; + write!( + self.sql, + "(select min(id) > {} from revlog where cid = c.id)", + cutoff + ) + .unwrap(); + Ok(()) + } + fn write_regex(&mut self, word: &str) { self.sql.push_str("n.flds regexp ?"); self.args.push(format!(r"(?i){}", word)); @@ -546,6 +559,7 @@ impl SearchNode { fn required_table(&self) -> RequiredTable { match self { SearchNode::AddedInDays(_) => RequiredTable::Cards, + SearchNode::IntroducedInDays(_) => RequiredTable::Cards, SearchNode::Deck(_) => RequiredTable::Cards, SearchNode::DeckId(_) => RequiredTable::Cards, SearchNode::Rated { .. } => RequiredTable::Cards, diff --git a/rslib/src/search/writer.rs b/rslib/src/search/writer.rs index f96823830..ffb4acaba 100644 --- a/rslib/src/search/writer.rs +++ b/rslib/src/search/writer.rs @@ -83,6 +83,7 @@ fn write_search_node(node: &SearchNode) -> String { SingleField { field, text, is_re } => write_single_field(field, text, *is_re), AddedInDays(u) => format!("added:{}", u), EditedInDays(u) => format!("edited:{}", u), + IntroducedInDays(u) => format!("introduced:{}", u), CardTemplate(t) => write_template(t), Deck(s) => maybe_quote(&format!("deck:{}", s)), DeckId(DeckIdType(i)) => format!("did:{}", i), From 699437df0f7b177ac7ce0e8562d558dee66cb7fb Mon Sep 17 00:00:00 2001 From: RumovZ Date: Sun, 18 Apr 2021 12:25:44 +0200 Subject: [PATCH 2/7] Add cutoff_in_secs_from_days() helper method --- rslib/src/search/sqlwriter.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/rslib/src/search/sqlwriter.rs b/rslib/src/search/sqlwriter.rs index f3dee8377..a561bd48c 100644 --- a/rslib/src/search/sqlwriter.rs +++ b/rslib/src/search/sqlwriter.rs @@ -479,23 +479,25 @@ impl SqlWriter<'_> { Ok(()) } - fn write_added(&mut self, days: u32) -> Result<()> { + fn cutoff_in_secs_from_days(&mut self, days: u32) -> Result { let timing = self.col.timing_today()?; - let cutoff = (timing.next_day_at.0 - (86_400 * (days as i64))) * 1_000; + Ok(timing.next_day_at.0 - (86_400 * (days as i64))) + } + + fn write_added(&mut self, days: u32) -> Result<()> { + let cutoff = self.cutoff_in_secs_from_days(days)? * 1_000; write!(self.sql, "c.id > {}", cutoff).unwrap(); Ok(()) } fn write_edited(&mut self, days: u32) -> Result<()> { - let timing = self.col.timing_today()?; - let cutoff = timing.next_day_at.0 - (86_400 * (days as i64)); + let cutoff = self.cutoff_in_secs_from_days(days)?; write!(self.sql, "n.mod > {}", cutoff).unwrap(); Ok(()) } fn write_introduced(&mut self, days: u32) -> Result<()> { - let timing = self.col.timing_today()?; - let cutoff = (timing.next_day_at.0 - (86_400 * (days as i64))) * 1_000; + let cutoff = self.cutoff_in_secs_from_days(days)? * 1_000; write!( self.sql, "(select min(id) > {} from revlog where cid = c.id)", From e274ff26ab653012dd2847e56d93775b57f6c19b Mon Sep 17 00:00:00 2001 From: RumovZ Date: Sun, 18 Apr 2021 12:32:02 +0200 Subject: [PATCH 3/7] Add sqlwriter test for introduced --- rslib/src/search/sqlwriter.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rslib/src/search/sqlwriter.rs b/rslib/src/search/sqlwriter.rs index a561bd48c..749a23595 100644 --- a/rslib/src/search/sqlwriter.rs +++ b/rslib/src/search/sqlwriter.rs @@ -667,6 +667,16 @@ mod test { ); assert_eq!(s(ctx, "added:0").0, s(ctx, "added:1").0,); + // introduced + assert_eq!( + s(ctx, "introduced:3").0, + format!( + "((select min(id) > {} from revlog where cid = c.id))", + (timing.next_day_at.0 - (86_400 * 3)) * 1_000 + ) + ); + assert_eq!(s(ctx, "introduced:0").0, s(ctx, "introduced:1").0,); + // deck assert_eq!( s(ctx, "deck:default"), From 3cecc7157bf6cce8c453d70bd2862ac0744b8a00 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Sun, 18 Apr 2021 13:27:10 +0200 Subject: [PATCH 4/7] Add sidebar filter "first review today" --- ftl/core/browsing.ftl | 1 + qt/aqt/browser/sidebar/tree.py | 6 ++++++ rslib/backend.proto | 1 + rslib/src/backend/search/search_node.rs | 1 + 4 files changed, 9 insertions(+) diff --git a/ftl/core/browsing.ftl b/ftl/core/browsing.ftl index b99e40952..ca5c72b60 100644 --- a/ftl/core/browsing.ftl +++ b/ftl/core/browsing.ftl @@ -146,6 +146,7 @@ browsing-studied-today = Studied browsing-added-today = Added browsing-again-today = Again browsing-edited-today = Edited +browsing-sidebar-first-review = First Review browsing-sidebar-due-today = Due browsing-sidebar-untagged = Untagged browsing-sidebar-overdue = Overdue diff --git a/qt/aqt/browser/sidebar/tree.py b/qt/aqt/browser/sidebar/tree.py index daba51389..a56588c2f 100644 --- a/qt/aqt/browser/sidebar/tree.py +++ b/qt/aqt/browser/sidebar/tree.py @@ -513,6 +513,12 @@ class SidebarTreeView(QTreeView): type=type, search_node=SearchNode(rated=SearchNode.Rated(days=1)), ) + root.add_simple( + name=tr.browsing_sidebar_first_review(), + icon=icon, + type=type, + search_node=SearchNode(introduced_in_days=1), + ) root.add_simple( name=tr.browsing_again_today(), icon=icon, diff --git a/rslib/backend.proto b/rslib/backend.proto index 85603fbe6..b757385d9 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -872,6 +872,7 @@ message SearchNode { int32 due_on_day = 16; string tag = 17; string note = 18; + uint32 introduced_in_days = 19; } } diff --git a/rslib/src/backend/search/search_node.rs b/rslib/src/backend/search/search_node.rs index 1a0897c80..668e10b0f 100644 --- a/rslib/src/backend/search/search_node.rs +++ b/rslib/src/backend/search/search_node.rs @@ -49,6 +49,7 @@ impl TryFrom for Node { ease: rated.rating().into(), }), Filter::AddedInDays(u) => Node::Search(SearchNode::AddedInDays(u)), + Filter::IntroducedInDays(u) => Node::Search(SearchNode::IntroducedInDays(u)), Filter::DueInDays(i) => Node::Search(SearchNode::Property { operator: "<=".to_string(), kind: PropertyKind::Due(i), From 11cd1d9a2636e6c457fd0629601826d7bf839aff Mon Sep 17 00:00:00 2001 From: RumovZ Date: Mon, 19 Apr 2021 08:19:19 +0200 Subject: [PATCH 5/7] Add sql condition for speedup in write_introduced --- rslib/src/search/sqlwriter.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/rslib/src/search/sqlwriter.rs b/rslib/src/search/sqlwriter.rs index 749a23595..4d86b1678 100644 --- a/rslib/src/search/sqlwriter.rs +++ b/rslib/src/search/sqlwriter.rs @@ -500,8 +500,11 @@ impl SqlWriter<'_> { let cutoff = self.cutoff_in_secs_from_days(days)? * 1_000; write!( self.sql, - "(select min(id) > {} from revlog where cid = c.id)", - cutoff + concat!( + "(select min(id) > {} from revlog where cid = c.id)", + "and c.id in (select cid from revlog where id > {})" + ), + cutoff, cutoff, ) .unwrap(); Ok(()) @@ -671,8 +674,11 @@ mod test { assert_eq!( s(ctx, "introduced:3").0, format!( - "((select min(id) > {} from revlog where cid = c.id))", - (timing.next_day_at.0 - (86_400 * 3)) * 1_000 + concat!( + "((select min(id) > {cutoff} from revlog where cid = c.id)", + "and c.id in (select cid from revlog where id > {cutoff}))" + ), + cutoff = (timing.next_day_at.0 - (86_400 * 3)) * 1_000, ) ); assert_eq!(s(ctx, "introduced:0").0, s(ctx, "introduced:1").0,); From 194d2510040fbb1272f61e288481580460d62428 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Mon, 19 Apr 2021 08:44:13 +0200 Subject: [PATCH 6/7] Use timestamp adding for writing cutoff --- rslib/src/search/sqlwriter.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rslib/src/search/sqlwriter.rs b/rslib/src/search/sqlwriter.rs index 4d86b1678..46fd9aace 100644 --- a/rslib/src/search/sqlwriter.rs +++ b/rslib/src/search/sqlwriter.rs @@ -479,25 +479,25 @@ impl SqlWriter<'_> { Ok(()) } - fn cutoff_in_secs_from_days(&mut self, days: u32) -> Result { + fn previous_day_cutoff(&mut self, days_back: u32) -> Result { let timing = self.col.timing_today()?; - Ok(timing.next_day_at.0 - (86_400 * (days as i64))) + Ok(timing.next_day_at.adding_secs(-86_400 * days_back as i64)) } fn write_added(&mut self, days: u32) -> Result<()> { - let cutoff = self.cutoff_in_secs_from_days(days)? * 1_000; + let cutoff = self.previous_day_cutoff(days)?.as_millis(); write!(self.sql, "c.id > {}", cutoff).unwrap(); Ok(()) } fn write_edited(&mut self, days: u32) -> Result<()> { - let cutoff = self.cutoff_in_secs_from_days(days)?; + let cutoff = self.previous_day_cutoff(days)?; write!(self.sql, "n.mod > {}", cutoff).unwrap(); Ok(()) } fn write_introduced(&mut self, days: u32) -> Result<()> { - let cutoff = self.cutoff_in_secs_from_days(days)? * 1_000; + let cutoff = self.previous_day_cutoff(days)?.as_millis(); write!( self.sql, concat!( From cac507b979829f0d94078057f8edd3a669ff0115 Mon Sep 17 00:00:00 2001 From: RumovZ Date: Mon, 19 Apr 2021 08:58:33 +0200 Subject: [PATCH 7/7] Use arg name instead of repeating it in format!() --- rslib/src/search/sqlwriter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rslib/src/search/sqlwriter.rs b/rslib/src/search/sqlwriter.rs index 46fd9aace..0d73b59bf 100644 --- a/rslib/src/search/sqlwriter.rs +++ b/rslib/src/search/sqlwriter.rs @@ -501,10 +501,10 @@ impl SqlWriter<'_> { write!( self.sql, concat!( - "(select min(id) > {} from revlog where cid = c.id)", - "and c.id in (select cid from revlog where id > {})" + "(select min(id) > {cutoff} from revlog where cid = c.id)", + "and c.id in (select cid from revlog where id > {cutoff})" ), - cutoff, cutoff, + cutoff = cutoff, ) .unwrap(); Ok(())