add the ability to provide a custom sort order; use backend for find

This commit is contained in:
Damien Elmes 2020-03-21 07:54:43 +10:00
parent 1f8a1126a4
commit 5debd3e0f8
9 changed files with 78 additions and 57 deletions

View file

@ -24,8 +24,8 @@ message BackendInput {
TemplateRequirementsIn template_requirements = 16; TemplateRequirementsIn template_requirements = 16;
SchedTimingTodayIn sched_timing_today = 17; SchedTimingTodayIn sched_timing_today = 17;
Empty deck_tree = 18; Empty deck_tree = 18;
FindCardsIn find_cards = 19; SearchCardsIn search_cards = 19;
BrowserRowsIn browser_rows = 20; // BrowserRowsIn browser_rows = 20;
RenderCardIn render_card = 21; RenderCardIn render_card = 21;
int64 local_minutes_west = 22; int64 local_minutes_west = 22;
string strip_av_tags = 23; string strip_av_tags = 23;
@ -43,7 +43,6 @@ message BackendInput {
Empty restore_trash = 35; Empty restore_trash = 35;
OpenCollectionIn open_collection = 36; OpenCollectionIn open_collection = 36;
Empty close_collection = 37; Empty close_collection = 37;
SearchCardsIn search_cards = 38;
} }
} }
@ -63,8 +62,8 @@ message BackendOutput {
// fallible commands // fallible commands
TemplateRequirementsOut template_requirements = 16; TemplateRequirementsOut template_requirements = 16;
DeckTreeOut deck_tree = 18; DeckTreeOut deck_tree = 18;
FindCardsOut find_cards = 19; SearchCardsOut search_cards = 19;
BrowserRowsOut browser_rows = 20; // BrowserRowsOut browser_rows = 20;
RenderCardOut render_card = 21; RenderCardOut render_card = 21;
string add_media_file = 26; string add_media_file = 26;
Empty sync_media = 27; Empty sync_media = 27;
@ -74,7 +73,6 @@ message BackendOutput {
Empty restore_trash = 35; Empty restore_trash = 35;
Empty open_collection = 36; Empty open_collection = 36;
Empty close_collection = 37; Empty close_collection = 37;
SearchCardsOut search_cards = 38;
BackendError error = 2047; BackendError error = 2047;
} }
@ -191,23 +189,6 @@ message DeckTreeNode {
bool collapsed = 7; bool collapsed = 7;
} }
message FindCardsIn {
string search = 1;
}
message FindCardsOut {
repeated int64 card_ids = 1;
}
message BrowserRowsIn {
repeated int64 card_ids = 1;
}
message BrowserRowsOut {
// just sort fields for proof of concept
repeated string sort_fields = 1;
}
message RenderCardIn { message RenderCardIn {
string question_template = 1; string question_template = 1;
string answer_template = 2; string answer_template = 2;
@ -337,9 +318,18 @@ message OpenCollectionIn {
message SearchCardsIn { message SearchCardsIn {
string search = 1; string search = 1;
SortOrder order = 2;
} }
message SearchCardsOut { message SearchCardsOut {
repeated int64 card_ids = 1; repeated int64 card_ids = 1;
} }
message SortOrder {
oneof value {
Empty from_config = 1;
Empty none = 2;
string custom = 3;
}
}

View file

@ -15,7 +15,7 @@ import time
import traceback import traceback
import unicodedata import unicodedata
import weakref import weakref
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union
import anki.find import anki.find
import anki.latex # sets up hook import anki.latex # sets up hook
@ -616,8 +616,8 @@ where c.nid = n.id and c.id in %s group by nid"""
# Finding cards # Finding cards
########################################################################## ##########################################################################
def findCards(self, query: str, order: Union[bool, str] = False) -> Any: def findCards(self, query: str, order: Union[bool, str] = False) -> Sequence[int]:
return anki.find.Finder(self).findCards(query, order) return self.backend.search_cards(query, order)
def findNotes(self, query: str) -> Any: def findNotes(self, query: str) -> Any:
return anki.find.Finder(self).findNotes(query) return anki.find.Finder(self).findNotes(query)

View file

@ -48,19 +48,19 @@ class PythonBackend:
native = self.col.sched.deckDueTree() native = self.col.sched.deckDueTree()
return native_deck_tree_to_proto(native) return native_deck_tree_to_proto(native)
def find_cards(self, input: pb.FindCardsIn) -> pb.FindCardsOut: # def find_cards(self, input: pb.FindCardsIn) -> pb.FindCardsOut:
cids = self.col.findCards(input.search) # cids = self.col.findCards(input.search)
return pb.FindCardsOut(card_ids=cids) # return pb.FindCardsOut(card_ids=cids)
#
def browser_rows(self, input: pb.BrowserRowsIn) -> pb.BrowserRowsOut: # def browser_rows(self, input: pb.BrowserRowsIn) -> pb.BrowserRowsOut:
sort_fields = [] # sort_fields = []
for cid in input.card_ids: # for cid in input.card_ids:
sort_fields.append( # sort_fields.append(
self.col.db.scalar( # self.col.db.scalar(
"select sfld from notes n,cards c where n.id=c.nid and c.id=?", cid # "select sfld from notes n,cards c where n.id=c.nid and c.id=?", cid
) # )
) # )
return pb.BrowserRowsOut(sort_fields=sort_fields) # return pb.BrowserRowsOut(sort_fields=sort_fields)
def native_deck_tree_to_proto(native): def native_deck_tree_to_proto(native):

View file

@ -423,9 +423,15 @@ class RustBackend:
def _db_command(self, input: Dict[str, Any]) -> Any: def _db_command(self, input: Dict[str, Any]) -> Any:
return orjson.loads(self._backend.db_command(orjson.dumps(input))) return orjson.loads(self._backend.db_command(orjson.dumps(input)))
def search_cards(self, search: str) -> Sequence[int]: def search_cards(self, search: str, order: Union[bool, str]) -> Sequence[int]:
if isinstance(order, str):
mode = pb.SortOrder(custom=order)
elif not order:
mode = pb.SortOrder(none=pb.Empty())
else:
mode = pb.SortOrder(from_config=pb.Empty())
return self._run_command( return self._run_command(
pb.BackendInput(search_cards=pb.SearchCardsIn(search=search)) pb.BackendInput(search_cards=pb.SearchCardsIn(search=search, order=mode))
).search_cards.card_ids ).search_cards.card_ids

View file

@ -8,7 +8,7 @@ import random
import time import time
from heapq import * from heapq import *
from operator import itemgetter from operator import itemgetter
from typing import Any, Dict, List, Optional, Tuple, Union from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
import anki import anki
from anki import hooks from anki import hooks
@ -707,7 +707,7 @@ did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""",
# Dynamic deck handling # Dynamic deck handling
########################################################################## ##########################################################################
def rebuildDyn(self, did: Optional[int] = None) -> Optional[List[int]]: # type: ignore[override] def rebuildDyn(self, did: Optional[int] = None) -> Optional[Sequence[int]]: # type: ignore[override]
"Rebuild a dynamic deck." "Rebuild a dynamic deck."
did = did or self.col.decks.selected() did = did or self.col.decks.selected()
deck = self.col.decks.get(did) deck = self.col.decks.get(did)
@ -721,7 +721,7 @@ did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""",
self.col.decks.select(did) self.col.decks.select(did)
return ids return ids
def _fillDyn(self, deck: Dict[str, Any]) -> List[int]: # type: ignore[override] def _fillDyn(self, deck: Dict[str, Any]) -> Sequence[int]: # type: ignore[override]
search, limit, order = deck["terms"][0] search, limit, order = deck["terms"][0]
orderlimit = self._dynOrder(order, limit) orderlimit = self._dynOrder(order, limit)
if search.strip(): if search.strip():
@ -751,7 +751,7 @@ due = odue, odue = 0, odid = 0, usn = ? where %s"""
self.col.usn(), self.col.usn(),
) )
def _moveToDyn(self, did: int, ids: List[int]) -> None: # type: ignore[override] def _moveToDyn(self, did: int, ids: Sequence[int]) -> None: # type: ignore[override]
deck = self.col.decks.get(did) deck = self.col.decks.get(did)
data = [] data = []
t = intTime() t = intTime()

View file

@ -11,7 +11,7 @@ from heapq import *
from operator import itemgetter from operator import itemgetter
# from anki.collection import _Collection # from anki.collection import _Collection
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Tuple, Union
import anki # pylint: disable=unused-import import anki # pylint: disable=unused-import
from anki import hooks from anki import hooks
@ -1215,7 +1215,7 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe
t = "c.due, c.ord" t = "c.due, c.ord"
return t + " limit %d" % l return t + " limit %d" % l
def _moveToDyn(self, did: int, ids: List[int], start: int = -100000) -> None: def _moveToDyn(self, did: int, ids: Sequence[int], start: int = -100000) -> None:
deck = self.col.decks.get(did) deck = self.col.decks.get(did)
data = [] data = []
u = self.col.usn() u = self.col.usn()

View file

@ -14,7 +14,7 @@ use crate::media::sync::MediaSyncProgress;
use crate::media::MediaManager; use crate::media::MediaManager;
use crate::sched::cutoff::{local_minutes_west_for_stamp, sched_timing_today_v2_new}; use crate::sched::cutoff::{local_minutes_west_for_stamp, sched_timing_today_v2_new};
use crate::sched::timespan::{answer_button_time, learning_congrats, studied_today, time_span}; use crate::sched::timespan::{answer_button_time, learning_congrats, studied_today, time_span};
use crate::search::search_cards; use crate::search::{search_cards, SortMode};
use crate::template::{ use crate::template::{
render_card, without_legacy_template_directives, FieldMap, FieldRequirements, ParsedTemplate, render_card, without_legacy_template_directives, FieldMap, FieldRequirements, ParsedTemplate,
RenderedNode, RenderedNode,
@ -200,8 +200,6 @@ impl Backend {
OValue::SchedTimingToday(self.sched_timing_today(input)) OValue::SchedTimingToday(self.sched_timing_today(input))
} }
Value::DeckTree(_) => todo!(), Value::DeckTree(_) => todo!(),
Value::FindCards(_) => todo!(),
Value::BrowserRows(_) => todo!(),
Value::RenderCard(input) => OValue::RenderCard(self.render_template(input)?), Value::RenderCard(input) => OValue::RenderCard(self.render_template(input)?),
Value::LocalMinutesWest(stamp) => { Value::LocalMinutesWest(stamp) => {
OValue::LocalMinutesWest(local_minutes_west_for_stamp(stamp)) OValue::LocalMinutesWest(local_minutes_west_for_stamp(stamp))
@ -583,7 +581,18 @@ impl Backend {
fn search_cards(&self, input: pb::SearchCardsIn) -> Result<pb::SearchCardsOut> { fn search_cards(&self, input: pb::SearchCardsIn) -> Result<pb::SearchCardsOut> {
self.with_col(|col| { self.with_col(|col| {
col.with_ctx(|ctx| { col.with_ctx(|ctx| {
let cids = search_cards(ctx, &input.search)?; let order = if let Some(order) = input.order {
use pb::sort_order::Value as V;
match order.value {
Some(V::None(_)) => SortMode::NoOrder,
Some(V::Custom(s)) => SortMode::Custom(s),
Some(V::FromConfig(_)) => SortMode::FromConfig,
None => SortMode::FromConfig,
}
} else {
SortMode::FromConfig
};
let cids = search_cards(ctx, &input.search, order)?;
Ok(pb::SearchCardsOut { card_ids: cids }) Ok(pb::SearchCardsOut { card_ids: cids })
}) })
}) })

View file

@ -10,21 +10,38 @@ use crate::search::parser::parse;
use crate::types::ObjID; use crate::types::ObjID;
use rusqlite::params; use rusqlite::params;
pub(crate) enum SortMode {
NoOrder,
FromConfig,
Custom(String),
}
pub(crate) fn search_cards<'a, 'b>( pub(crate) fn search_cards<'a, 'b>(
req: &'a mut RequestContext<'b>, req: &'a mut RequestContext<'b>,
search: &'a str, search: &'a str,
order: SortMode,
) -> Result<Vec<ObjID>> { ) -> Result<Vec<ObjID>> {
let top_node = Node::Group(parse(search)?); let top_node = Node::Group(parse(search)?);
let (sql, args) = node_to_sql(req, &top_node)?; let (sql, args) = node_to_sql(req, &top_node)?;
let conf = req.storage.all_config()?;
prepare_sort(req, &conf.browser_sort_kind)?;
let mut sql = format!( let mut sql = format!(
"select c.id from cards c, notes n where c.nid=n.id and {}", "select c.id from cards c, notes n where c.nid=n.id and {}",
sql sql
); );
match order {
SortMode::NoOrder => (),
SortMode::FromConfig => {
let conf = req.storage.all_config()?;
prepare_sort(req, &conf.browser_sort_kind)?;
sql.push_str(" order by ");
write_order(&mut sql, &conf.browser_sort_kind, conf.browser_sort_reverse)?; write_order(&mut sql, &conf.browser_sort_kind, conf.browser_sort_reverse)?;
}
SortMode::Custom(order_clause) => {
sql.push_str(" order by ");
sql.push_str(&order_clause);
}
}
let mut stmt = req.storage.db.prepare(&sql)?; let mut stmt = req.storage.db.prepare(&sql)?;
let ids: Vec<i64> = stmt let ids: Vec<i64> = stmt
@ -59,7 +76,6 @@ fn write_order(sql: &mut String, kind: &SortKind, reverse: bool) -> Result<()> {
if order.is_empty() { if order.is_empty() {
return Ok(()); return Ok(());
} }
sql.push_str(" order by ");
sql.push_str(order); sql.push_str(order);
if reverse { if reverse {
sql.push_str(" desc"); sql.push_str(" desc");

View file

@ -2,4 +2,4 @@ mod cards;
mod parser; mod parser;
mod sqlwriter; mod sqlwriter;
pub(crate) use cards::search_cards; pub(crate) use cards::{search_cards, SortMode};