mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
add the ability to provide a custom sort order; use backend for find
This commit is contained in:
parent
1f8a1126a4
commit
5debd3e0f8
9 changed files with 78 additions and 57 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 })
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
||||
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");
|
||||
|
|
|
@ -2,4 +2,4 @@ mod cards;
|
|||
mod parser;
|
||||
mod sqlwriter;
|
||||
|
||||
pub(crate) use cards::search_cards;
|
||||
pub(crate) use cards::{search_cards, SortMode};
|
||||
|
|
Loading…
Reference in a new issue