Merge pull request #1138 from RumovZ/introduced

Add search keyword for "first review in x days"
This commit is contained in:
Damien Elmes 2021-04-19 18:22:15 +10:00 committed by GitHub
commit 2cc23ce2bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 53 additions and 4 deletions

View file

@ -146,6 +146,7 @@ browsing-studied-today = Studied
browsing-added-today = Added browsing-added-today = Added
browsing-again-today = Again browsing-again-today = Again
browsing-edited-today = Edited browsing-edited-today = Edited
browsing-sidebar-first-review = First Review
browsing-sidebar-due-today = Due browsing-sidebar-due-today = Due
browsing-sidebar-untagged = Untagged browsing-sidebar-untagged = Untagged
browsing-sidebar-overdue = Overdue browsing-sidebar-overdue = Overdue

View file

@ -513,6 +513,12 @@ class SidebarTreeView(QTreeView):
type=type, type=type,
search_node=SearchNode(rated=SearchNode.Rated(days=1)), 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( root.add_simple(
name=tr.browsing_again_today(), name=tr.browsing_again_today(),
icon=icon, icon=icon,

View file

@ -872,6 +872,7 @@ message SearchNode {
int32 due_on_day = 16; int32 due_on_day = 16;
string tag = 17; string tag = 17;
string note = 18; string note = 18;
uint32 introduced_in_days = 19;
} }
} }

View file

@ -48,6 +48,7 @@ impl TryFrom<pb::SearchNode> for Node {
ease: rated.rating().into(), ease: rated.rating().into(),
}), }),
Filter::AddedInDays(u) => Node::Search(SearchNode::AddedInDays(u)), Filter::AddedInDays(u) => Node::Search(SearchNode::AddedInDays(u)),
Filter::IntroducedInDays(u) => Node::Search(SearchNode::IntroducedInDays(u)),
Filter::DueInDays(i) => Node::Search(SearchNode::Property { Filter::DueInDays(i) => Node::Search(SearchNode::Property {
operator: "<=".to_string(), operator: "<=".to_string(),
kind: PropertyKind::Due(i), kind: PropertyKind::Due(i),

View file

@ -74,6 +74,7 @@ pub enum SearchNode {
CardTemplate(TemplateKind), CardTemplate(TemplateKind),
Deck(String), Deck(String),
DeckId(DeckId), DeckId(DeckId),
IntroducedInDays(u32),
NotetypeId(NotetypeId), NotetypeId(NotetypeId),
Notetype(String), Notetype(String),
Rated { Rated {
@ -334,6 +335,7 @@ fn search_node_for_text_with_argument<'a>(
"prop" => parse_prop(val)?, "prop" => parse_prop(val)?,
"added" => parse_added(val)?, "added" => parse_added(val)?,
"edited" => parse_edited(val)?, "edited" => parse_edited(val)?,
"introduced" => parse_introduced(val)?,
"rated" => parse_rated(val)?, "rated" => parse_rated(val)?,
"is" => parse_state(val)?, "is" => parse_state(val)?,
"did" => parse_did(val)?, "did" => parse_did(val)?,
@ -528,6 +530,11 @@ fn parse_edited(s: &str) -> ParseResult<SearchNode> {
parse_u32(s, "edited:").map(|n| SearchNode::EditedInDays(n.max(1))) parse_u32(s, "edited:").map(|n| SearchNode::EditedInDays(n.max(1)))
} }
/// eg introduced:1
fn parse_introduced(s: &str) -> ParseResult<SearchNode> {
parse_u32(s, "introduced:").map(|n| SearchNode::IntroducedInDays(n.max(1)))
}
/// eg rated:3 or rated:10:2 /// eg rated:3 or rated:10:2
/// second arg must be between 1-4 /// second arg must be between 1-4
fn parse_rated(s: &str) -> ParseResult<SearchNode> { fn parse_rated(s: &str) -> ParseResult<SearchNode> {

View file

@ -129,6 +129,7 @@ impl SqlWriter<'_> {
// other // other
SearchNode::AddedInDays(days) => self.write_added(*days)?, SearchNode::AddedInDays(days) => self.write_added(*days)?,
SearchNode::EditedInDays(days) => self.write_edited(*days)?, SearchNode::EditedInDays(days) => self.write_edited(*days)?,
SearchNode::IntroducedInDays(days) => self.write_introduced(*days)?,
SearchNode::CardTemplate(template) => match template { SearchNode::CardTemplate(template) => match template {
TemplateKind::Ordinal(_) => self.write_template(template), TemplateKind::Ordinal(_) => self.write_template(template),
TemplateKind::Name(name) => { TemplateKind::Name(name) => {
@ -479,20 +480,37 @@ impl SqlWriter<'_> {
Ok(()) Ok(())
} }
fn write_added(&mut self, days: u32) -> Result<()> { fn previous_day_cutoff(&mut self, days_back: u32) -> Result<TimestampSecs> {
let timing = self.col.timing_today()?; 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.adding_secs(-86_400 * days_back as i64))
}
fn write_added(&mut self, days: u32) -> Result<()> {
let cutoff = self.previous_day_cutoff(days)?.as_millis();
write!(self.sql, "c.id > {}", cutoff).unwrap(); write!(self.sql, "c.id > {}", cutoff).unwrap();
Ok(()) Ok(())
} }
fn write_edited(&mut self, days: u32) -> Result<()> { fn write_edited(&mut self, days: u32) -> Result<()> {
let timing = self.col.timing_today()?; let cutoff = self.previous_day_cutoff(days)?;
let cutoff = timing.next_day_at.0 - (86_400 * (days as i64));
write!(self.sql, "n.mod > {}", cutoff).unwrap(); write!(self.sql, "n.mod > {}", cutoff).unwrap();
Ok(()) Ok(())
} }
fn write_introduced(&mut self, days: u32) -> Result<()> {
let cutoff = self.previous_day_cutoff(days)?.as_millis();
write!(
self.sql,
concat!(
"(select min(id) > {cutoff} from revlog where cid = c.id)",
"and c.id in (select cid from revlog where id > {cutoff})"
),
cutoff = cutoff,
)
.unwrap();
Ok(())
}
fn write_regex(&mut self, word: &str) { fn write_regex(&mut self, word: &str) {
self.sql.push_str("n.flds regexp ?"); self.sql.push_str("n.flds regexp ?");
self.args.push(format!(r"(?i){}", word)); self.args.push(format!(r"(?i){}", word));
@ -547,6 +565,7 @@ impl SearchNode {
fn required_table(&self) -> RequiredTable { fn required_table(&self) -> RequiredTable {
match self { match self {
SearchNode::AddedInDays(_) => RequiredTable::Cards, SearchNode::AddedInDays(_) => RequiredTable::Cards,
SearchNode::IntroducedInDays(_) => RequiredTable::Cards,
SearchNode::Deck(_) => RequiredTable::Cards, SearchNode::Deck(_) => RequiredTable::Cards,
SearchNode::DeckId(_) => RequiredTable::Cards, SearchNode::DeckId(_) => RequiredTable::Cards,
SearchNode::Rated { .. } => RequiredTable::Cards, SearchNode::Rated { .. } => RequiredTable::Cards,
@ -652,6 +671,19 @@ mod test {
); );
assert_eq!(s(ctx, "added:0").0, s(ctx, "added:1").0,); assert_eq!(s(ctx, "added:0").0, s(ctx, "added:1").0,);
// introduced
assert_eq!(
s(ctx, "introduced:3").0,
format!(
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,);
// deck // deck
assert_eq!( assert_eq!(
s(ctx, "deck:default"), s(ctx, "deck:default"),

View file

@ -84,6 +84,7 @@ fn write_search_node(node: &SearchNode) -> String {
SingleField { field, text, is_re } => write_single_field(field, text, *is_re), SingleField { field, text, is_re } => write_single_field(field, text, *is_re),
AddedInDays(u) => format!("added:{}", u), AddedInDays(u) => format!("added:{}", u),
EditedInDays(u) => format!("edited:{}", u), EditedInDays(u) => format!("edited:{}", u),
IntroducedInDays(u) => format!("introduced:{}", u),
CardTemplate(t) => write_template(t), CardTemplate(t) => write_template(t),
Deck(s) => maybe_quote(&format!("deck:{}", s)), Deck(s) => maybe_quote(&format!("deck:{}", s)),
DeckId(DeckIdType(i)) => format!("did:{}", i), DeckId(DeckIdType(i)) => format!("did:{}", i),