Skip to content
Permalink
Browse files

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

  • Loading branch information
dae committed Mar 20, 2020
1 parent 1f8a112 commit 5debd3e0f836a350be6c59456c08966332eab1f9
@@ -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 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 @@ def updateFieldCache(self, nids: List[int]) -> None:
# 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 @@ def deck_tree(self, _input: pb.Empty) -> pb.DeckTreeOut:
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 @@ def db_rollback(self) -> None:
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 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 @@ def _adjRevIvl(self, card: Card, idealIvl: int) -> int:
# 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 @@ def rebuildDyn(self, did: Optional[int] = None) -> Optional[List[int]]: # type:
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 @@ def emptyDyn(self, did: Optional[int], lim: Optional[str] = None) -> None:
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 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 @@ def _dynOrder(self, o: int, l: int) -> str:
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
);
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");
@@ -2,4 +2,4 @@ mod cards;
mod parser;
mod sqlwriter;

pub(crate) use cards::search_cards;
pub(crate) use cards::{search_cards, SortMode};

0 comments on commit 5debd3e

Please sign in to comment.
You can’t perform that action at this time.