mirror of
https://github.com/ankitects/anki.git
synced 2025-09-23 16:26:40 -04:00
Merge pull request #918 from hgiesel/ratedflags
Introduce `resched:n` instead of `rated:n:0`
This commit is contained in:
commit
7d81d39be7
5 changed files with 57 additions and 24 deletions
|
@ -36,7 +36,7 @@ use crate::{
|
||||||
sched::timespan::{answer_button_time, time_span},
|
sched::timespan::{answer_button_time, time_span},
|
||||||
search::{
|
search::{
|
||||||
concatenate_searches, negate_search, normalize_search, replace_search_term, write_nodes,
|
concatenate_searches, negate_search, normalize_search, replace_search_term, write_nodes,
|
||||||
BoolSeparator, Node, SearchNode, SortMode, StateKind, TemplateKind,
|
BoolSeparator, EaseKind, Node, SearchNode, SortMode, StateKind, TemplateKind,
|
||||||
},
|
},
|
||||||
stats::studied_today,
|
stats::studied_today,
|
||||||
sync::{
|
sync::{
|
||||||
|
@ -295,11 +295,11 @@ impl From<pb::FilterToSearchIn> for Node<'_> {
|
||||||
NamedFilter::AddedToday => Node::Search(SearchNode::AddedInDays(1)),
|
NamedFilter::AddedToday => Node::Search(SearchNode::AddedInDays(1)),
|
||||||
NamedFilter::StudiedToday => Node::Search(SearchNode::Rated {
|
NamedFilter::StudiedToday => Node::Search(SearchNode::Rated {
|
||||||
days: 1,
|
days: 1,
|
||||||
ease: None,
|
ease: EaseKind::AnyAnswerButton,
|
||||||
}),
|
}),
|
||||||
NamedFilter::AgainToday => Node::Search(SearchNode::Rated {
|
NamedFilter::AgainToday => Node::Search(SearchNode::Rated {
|
||||||
days: 1,
|
days: 1,
|
||||||
ease: Some(1),
|
ease: EaseKind::AnswerButton(1),
|
||||||
}),
|
}),
|
||||||
NamedFilter::New => Node::Search(SearchNode::State(StateKind::New)),
|
NamedFilter::New => Node::Search(SearchNode::State(StateKind::New)),
|
||||||
NamedFilter::Learn => Node::Search(SearchNode::State(StateKind::Learning)),
|
NamedFilter::Learn => Node::Search(SearchNode::State(StateKind::Learning)),
|
||||||
|
|
|
@ -5,7 +5,7 @@ mod sqlwriter;
|
||||||
mod writer;
|
mod writer;
|
||||||
|
|
||||||
pub use cards::SortMode;
|
pub use cards::SortMode;
|
||||||
pub use parser::{Node, PropertyKind, SearchNode, StateKind, TemplateKind};
|
pub use parser::{EaseKind, Node, PropertyKind, SearchNode, StateKind, TemplateKind};
|
||||||
pub use writer::{
|
pub use writer::{
|
||||||
concatenate_searches, negate_search, normalize_search, replace_search_term, write_nodes,
|
concatenate_searches, negate_search, normalize_search, replace_search_term, write_nodes,
|
||||||
BoolSeparator,
|
BoolSeparator,
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub enum SearchNode<'a> {
|
||||||
NoteType(Cow<'a, str>),
|
NoteType(Cow<'a, str>),
|
||||||
Rated {
|
Rated {
|
||||||
days: u32,
|
days: u32,
|
||||||
ease: Option<u8>,
|
ease: EaseKind,
|
||||||
},
|
},
|
||||||
Tag(Cow<'a, str>),
|
Tag(Cow<'a, str>),
|
||||||
Duplicates {
|
Duplicates {
|
||||||
|
@ -118,6 +118,13 @@ pub enum TemplateKind<'a> {
|
||||||
Name(Cow<'a, str>),
|
Name(Cow<'a, str>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum EaseKind {
|
||||||
|
AnswerButton(u8),
|
||||||
|
AnyAnswerButton,
|
||||||
|
ManualReschedule,
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse the input string into a list of nodes.
|
/// Parse the input string into a list of nodes.
|
||||||
pub(super) fn parse(input: &str) -> Result<Vec<Node>> {
|
pub(super) fn parse(input: &str) -> Result<Vec<Node>> {
|
||||||
let input = input.trim();
|
let input = input.trim();
|
||||||
|
@ -286,6 +293,7 @@ fn search_node_for_text_with_argument<'a>(
|
||||||
"is" => parse_state(val)?,
|
"is" => parse_state(val)?,
|
||||||
"flag" => parse_flag(val)?,
|
"flag" => parse_flag(val)?,
|
||||||
"rated" => parse_rated(val)?,
|
"rated" => parse_rated(val)?,
|
||||||
|
"resched" => parse_resched(val)?,
|
||||||
"dupe" => parse_dupes(val)?,
|
"dupe" => parse_dupes(val)?,
|
||||||
"prop" => parse_prop(val)?,
|
"prop" => parse_prop(val)?,
|
||||||
"re" => SearchNode::Regex(unescape_quotes(val)),
|
"re" => SearchNode::Regex(unescape_quotes(val)),
|
||||||
|
@ -350,7 +358,7 @@ fn parse_flag(s: &str) -> ParseResult<SearchNode<'static>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// eg rated:3 or rated:10:2
|
/// eg rated:3 or rated:10:2
|
||||||
/// second arg must be between 0-4
|
/// second arg must be between 1-4
|
||||||
fn parse_rated(val: &str) -> ParseResult<SearchNode<'static>> {
|
fn parse_rated(val: &str) -> ParseResult<SearchNode<'static>> {
|
||||||
let mut it = val.splitn(2, ':');
|
let mut it = val.splitn(2, ':');
|
||||||
|
|
||||||
|
@ -359,19 +367,31 @@ fn parse_rated(val: &str) -> ParseResult<SearchNode<'static>> {
|
||||||
|
|
||||||
let ease = match it.next() {
|
let ease = match it.next() {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
let n: u8 = v.parse()?;
|
let u: u8 = v.parse()?;
|
||||||
if n < 5 {
|
if (1..5).contains(&u) {
|
||||||
Some(n)
|
EaseKind::AnswerButton(u)
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError {});
|
return Err(ParseError {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => None,
|
None => EaseKind::AnyAnswerButton,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(SearchNode::Rated { days, ease })
|
Ok(SearchNode::Rated { days, ease })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// eg resched:3
|
||||||
|
fn parse_resched(val: &str) -> ParseResult<SearchNode<'static>> {
|
||||||
|
let mut it = val.splitn(1, ':');
|
||||||
|
|
||||||
|
let n: u32 = it.next().unwrap().parse()?;
|
||||||
|
let days = n.max(1).min(365);
|
||||||
|
|
||||||
|
let ease = EaseKind::ManualReschedule;
|
||||||
|
|
||||||
|
Ok(SearchNode::Rated { days, ease })
|
||||||
|
}
|
||||||
|
|
||||||
/// eg dupes:1231,hello
|
/// eg dupes:1231,hello
|
||||||
fn parse_dupes(val: &str) -> ParseResult<SearchNode> {
|
fn parse_dupes(val: &str) -> ParseResult<SearchNode> {
|
||||||
let mut it = val.splitn(2, ',');
|
let mut it = val.splitn(2, ',');
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
use super::parser::{Node, PropertyKind, SearchNode, StateKind, TemplateKind};
|
use super::parser::{EaseKind, Node, PropertyKind, SearchNode, StateKind, TemplateKind};
|
||||||
use crate::{
|
use crate::{
|
||||||
card::{CardQueue, CardType},
|
card::{CardQueue, CardType},
|
||||||
collection::Collection,
|
collection::Collection,
|
||||||
|
@ -144,7 +144,7 @@ impl SqlWriter<'_> {
|
||||||
write!(self.sql, "c.did = {}", did).unwrap();
|
write!(self.sql, "c.did = {}", did).unwrap();
|
||||||
}
|
}
|
||||||
SearchNode::NoteType(notetype) => self.write_note_type(&norm(notetype))?,
|
SearchNode::NoteType(notetype) => self.write_note_type(&norm(notetype))?,
|
||||||
SearchNode::Rated { days, ease } => self.write_rated(*days, *ease)?,
|
SearchNode::Rated { days, ease } => self.write_rated(*days, ease)?,
|
||||||
|
|
||||||
SearchNode::Tag(tag) => self.write_tag(&norm(tag))?,
|
SearchNode::Tag(tag) => self.write_tag(&norm(tag))?,
|
||||||
SearchNode::State(state) => self.write_state(state)?,
|
SearchNode::State(state) => self.write_state(state)?,
|
||||||
|
@ -214,20 +214,22 @@ impl SqlWriter<'_> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_rated(&mut self, days: u32, ease: Option<u8>) -> Result<()> {
|
fn write_rated(&mut self, days: u32, ease: &EaseKind) -> Result<()> {
|
||||||
let today_cutoff = self.col.timing_today()?.next_day_at;
|
let today_cutoff = self.col.timing_today()?.next_day_at;
|
||||||
let target_cutoff_ms = (today_cutoff - 86_400 * i64::from(days)) * 1_000;
|
let target_cutoff_ms = (today_cutoff - 86_400 * i64::from(days)) * 1_000;
|
||||||
write!(
|
write!(
|
||||||
self.sql,
|
self.sql,
|
||||||
"c.id in (select cid from revlog where id>{}",
|
"c.id in (select cid from revlog where id>{}",
|
||||||
target_cutoff_ms
|
target_cutoff_ms,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if let Some(ease) = ease {
|
|
||||||
write!(self.sql, " and ease={})", ease).unwrap();
|
match ease {
|
||||||
} else {
|
EaseKind::AnswerButton(u) => write!(self.sql, " and ease = {})", u),
|
||||||
write!(self.sql, ")").unwrap();
|
EaseKind::AnyAnswerButton => write!(self.sql, " and ease > 0)"),
|
||||||
|
EaseKind::ManualReschedule => write!(self.sql, " and ease = 0)"),
|
||||||
}
|
}
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -717,7 +719,7 @@ mod test {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
s(ctx, "rated:2").0,
|
s(ctx, "rated:2").0,
|
||||||
format!(
|
format!(
|
||||||
"(c.id in (select cid from revlog where id>{}))",
|
"(c.id in (select cid from revlog where id>{} and ease > 0))",
|
||||||
(timing.next_day_at - (86_400 * 2)) * 1_000
|
(timing.next_day_at - (86_400 * 2)) * 1_000
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -730,6 +732,15 @@ mod test {
|
||||||
);
|
);
|
||||||
assert_eq!(s(ctx, "rated:0").0, s(ctx, "rated:1").0);
|
assert_eq!(s(ctx, "rated:0").0, s(ctx, "rated:1").0);
|
||||||
|
|
||||||
|
// resched
|
||||||
|
assert_eq!(
|
||||||
|
s(ctx, "resched:400").0,
|
||||||
|
format!(
|
||||||
|
"(c.id in (select cid from revlog where id>{} and ease = 0))",
|
||||||
|
(timing.next_day_at - (86_400 * 365)) * 1_000
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// props
|
// props
|
||||||
assert_eq!(s(ctx, "prop:lapses=3").0, "(lapses = 3)".to_string());
|
assert_eq!(s(ctx, "prop:lapses=3").0, "(lapses = 3)".to_string());
|
||||||
assert_eq!(s(ctx, "prop:ease>=2.5").0, "(factor >= 2500)".to_string());
|
assert_eq!(s(ctx, "prop:ease>=2.5").0, "(factor >= 2500)".to_string());
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
decks::DeckID as DeckIDType,
|
decks::DeckID as DeckIDType,
|
||||||
err::Result,
|
err::Result,
|
||||||
notetype::NoteTypeID as NoteTypeIDType,
|
notetype::NoteTypeID as NoteTypeIDType,
|
||||||
search::parser::{parse, Node, PropertyKind, SearchNode, StateKind, TemplateKind},
|
search::parser::{parse, EaseKind, Node, PropertyKind, SearchNode, StateKind, TemplateKind},
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -154,10 +154,12 @@ fn write_template(template: &TemplateKind) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_rated(days: &u32, ease: &Option<u8>) -> String {
|
fn write_rated(days: &u32, ease: &EaseKind) -> String {
|
||||||
|
use EaseKind::*;
|
||||||
match ease {
|
match ease {
|
||||||
Some(u) => format!("\"rated:{}:{}\"", days, u),
|
AnswerButton(n) => format!("\"rated:{}:{}\"", days, n),
|
||||||
None => format!("\"rated:{}\"", days),
|
AnyAnswerButton => format!("\"rated:{}\"", days),
|
||||||
|
ManualReschedule => format!("\"resched:{}\"", days),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue