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;
SchedTimingTodayIn sched_timing_today = 17;
Empty deck_tree = 18;
FindCardsIn find_cards = 19;
BrowserRowsIn browser_rows = 20;
SearchCardsIn search_cards = 19;
// BrowserRowsIn browser_rows = 20;
RenderCardIn render_card = 21;
int64 local_minutes_west = 22;
string strip_av_tags = 23;
@ -43,7 +43,6 @@ message BackendInput {
Empty restore_trash = 35;
OpenCollectionIn open_collection = 36;
Empty close_collection = 37;
SearchCardsIn search_cards = 38;
}
}
@ -63,8 +62,8 @@ message BackendOutput {
// fallible commands
TemplateRequirementsOut template_requirements = 16;
DeckTreeOut deck_tree = 18;
FindCardsOut find_cards = 19;
BrowserRowsOut browser_rows = 20;
SearchCardsOut search_cards = 19;
// BrowserRowsOut browser_rows = 20;
RenderCardOut render_card = 21;
string add_media_file = 26;
Empty sync_media = 27;
@ -74,7 +73,6 @@ message BackendOutput {
Empty restore_trash = 35;
Empty open_collection = 36;
Empty close_collection = 37;
SearchCardsOut search_cards = 38;
BackendError error = 2047;
}
@ -191,23 +189,6 @@ message DeckTreeNode {
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 {
string question_template = 1;
string answer_template = 2;
@ -337,9 +318,18 @@ message OpenCollectionIn {
message SearchCardsIn {
string search = 1;
SortOrder order = 2;
}
message SearchCardsOut {
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 unicodedata
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.latex # sets up hook
@ -616,8 +616,8 @@ where c.nid = n.id and c.id in %s group by nid"""
# Finding cards
##########################################################################
def findCards(self, query: str, order: Union[bool, str] = False) -> Any:
return anki.find.Finder(self).findCards(query, order)
def findCards(self, query: str, order: Union[bool, str] = False) -> Sequence[int]:
return self.backend.search_cards(query, order)
def findNotes(self, query: str) -> Any:
return anki.find.Finder(self).findNotes(query)

View file

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

View file

@ -423,9 +423,15 @@ class RustBackend:
def _db_command(self, input: Dict[str, Any]) -> Any:
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(
pb.BackendInput(search_cards=pb.SearchCardsIn(search=search))
pb.BackendInput(search_cards=pb.SearchCardsIn(search=search, order=mode))
).search_cards.card_ids

View file

@ -8,7 +8,7 @@ import random
import time
from heapq import *
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
from anki import hooks
@ -707,7 +707,7 @@ did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""",
# 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."
did = did or self.col.decks.selected()
deck = self.col.decks.get(did)
@ -721,7 +721,7 @@ did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""",
self.col.decks.select(did)
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]
orderlimit = self._dynOrder(order, limit)
if search.strip():
@ -751,7 +751,7 @@ due = odue, odue = 0, odid = 0, usn = ? where %s"""
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)
data = []
t = intTime()

View file

@ -11,7 +11,7 @@ from heapq import *
from operator import itemgetter
# 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
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"
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)
data = []
u = self.col.usn()

View file

@ -14,7 +14,7 @@ use crate::media::sync::MediaSyncProgress;
use crate::media::MediaManager;
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::search::search_cards;
use crate::search::{search_cards, SortMode};
use crate::template::{
render_card, without_legacy_template_directives, FieldMap, FieldRequirements, ParsedTemplate,
RenderedNode,
@ -200,8 +200,6 @@ impl Backend {
OValue::SchedTimingToday(self.sched_timing_today(input))
}
Value::DeckTree(_) => todo!(),
Value::FindCards(_) => todo!(),
Value::BrowserRows(_) => todo!(),
Value::RenderCard(input) => OValue::RenderCard(self.render_template(input)?),
Value::LocalMinutesWest(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> {
self.with_col(|col| {
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 })
})
})

View file

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

View file

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