diff --git a/proto/backend.proto b/proto/backend.proto index 638d9bc57..1cb0c1a43 100644 --- a/proto/backend.proto +++ b/proto/backend.proto @@ -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; + } +} diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 471f2c5a7..fcc99b7c3 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -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) diff --git a/pylib/anki/pybackend.py b/pylib/anki/pybackend.py index 5e27e7447..008ecc9ef 100644 --- a/pylib/anki/pybackend.py +++ b/pylib/anki/pybackend.py @@ -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): diff --git a/pylib/anki/rsbackend.py b/pylib/anki/rsbackend.py index eb3f69d63..56507a79c 100644 --- a/pylib/anki/rsbackend.py +++ b/pylib/anki/rsbackend.py @@ -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 diff --git a/pylib/anki/sched.py b/pylib/anki/sched.py index af426d744..0f696934b 100644 --- a/pylib/anki/sched.py +++ b/pylib/anki/sched.py @@ -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() diff --git a/pylib/anki/schedv2.py b/pylib/anki/schedv2.py index c6ecc6efc..ad72381aa 100644 --- a/pylib/anki/schedv2.py +++ b/pylib/anki/schedv2.py @@ -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() diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index 8640be245..06f0c30ac 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -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 { 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 }) }) }) diff --git a/rslib/src/search/cards.rs b/rslib/src/search/cards.rs index c20a8f93e..fd1953131 100644 --- a/rslib/src/search/cards.rs +++ b/rslib/src/search/cards.rs @@ -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> { 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 = 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"); diff --git a/rslib/src/search/mod.rs b/rslib/src/search/mod.rs index 417241051..2313c1d39 100644 --- a/rslib/src/search/mod.rs +++ b/rslib/src/search/mod.rs @@ -2,4 +2,4 @@ mod cards; mod parser; mod sqlwriter; -pub(crate) use cards::search_cards; +pub(crate) use cards::{search_cards, SortMode};