mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
use backend to get card
This commit is contained in:
parent
8abba00496
commit
7ddaf93f7a
9 changed files with 222 additions and 22 deletions
|
@ -47,6 +47,7 @@ message BackendInput {
|
|||
Empty restore_trash = 35;
|
||||
OpenCollectionIn open_collection = 36;
|
||||
Empty close_collection = 37;
|
||||
int64 get_card = 38;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,6 +78,7 @@ message BackendOutput {
|
|||
Empty restore_trash = 35;
|
||||
Empty open_collection = 36;
|
||||
Empty close_collection = 37;
|
||||
GetCardOut get_card = 38;
|
||||
|
||||
BackendError error = 2047;
|
||||
}
|
||||
|
@ -367,3 +369,28 @@ enum BuiltinSortKind {
|
|||
CARD_DECK = 11;
|
||||
CARD_TEMPLATE = 12;
|
||||
}
|
||||
|
||||
message GetCardOut {
|
||||
Card card = 1;
|
||||
}
|
||||
|
||||
message Card {
|
||||
int64 id = 1;
|
||||
int64 nid = 2;
|
||||
int64 did = 3;
|
||||
uint32 ord = 4;
|
||||
int64 mtime = 5;
|
||||
sint32 usn = 6;
|
||||
uint32 ctype = 7;
|
||||
sint32 queue = 8;
|
||||
int64 due = 9;
|
||||
int64 ivl = 10;
|
||||
uint32 factor = 11;
|
||||
int64 reps = 12;
|
||||
int64 lapses = 13;
|
||||
int64 left = 14;
|
||||
int64 odue = 15;
|
||||
int64 odid = 16;
|
||||
int64 flags = 17;
|
||||
string data = 18;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ class Card:
|
|||
ord: int
|
||||
|
||||
def __init__(
|
||||
self, col: anki.collection._Collection, id: Optional[int] = None
|
||||
self, col: anki.storage._Collection, id: Optional[int] = None
|
||||
) -> None:
|
||||
self.col = col.weakref()
|
||||
self.timerStarted = None
|
||||
|
@ -61,28 +61,27 @@ class Card:
|
|||
self.data = ""
|
||||
|
||||
def load(self) -> None:
|
||||
(
|
||||
self.id,
|
||||
self.nid,
|
||||
self.did,
|
||||
self.ord,
|
||||
self.mod,
|
||||
self.usn,
|
||||
self.type,
|
||||
self.queue,
|
||||
self.due,
|
||||
self.ivl,
|
||||
self.factor,
|
||||
self.reps,
|
||||
self.lapses,
|
||||
self.left,
|
||||
self.odue,
|
||||
self.odid,
|
||||
self.flags,
|
||||
self.data,
|
||||
) = self.col.db.first("select * from cards where id = ?", self.id)
|
||||
self._render_output = None
|
||||
self._note = None
|
||||
c = self.col.backend.get_card(self.id)
|
||||
assert c
|
||||
self.nid = c.nid
|
||||
self.did = c.did
|
||||
self.ord = c.ord
|
||||
self.mod = c.mtime
|
||||
self.usn = c.usn
|
||||
self.type = c.ctype
|
||||
self.queue = c.queue
|
||||
self.due = c.due
|
||||
self.ivl = c.ivl
|
||||
self.factor = c.factor
|
||||
self.reps = c.reps
|
||||
self.lapses = c.lapses
|
||||
self.left = c.left
|
||||
self.odue = c.odue
|
||||
self.odid = c.odid
|
||||
self.flags = c.flags
|
||||
self.data = c.data
|
||||
|
||||
def _preFlush(self) -> None:
|
||||
hooks.card_will_flush(self)
|
||||
|
|
|
@ -480,6 +480,10 @@ class RustBackend:
|
|||
pb.BackendInput(search_notes=pb.SearchNotesIn(search=search))
|
||||
).search_notes.note_ids
|
||||
|
||||
def get_card(self, cid: int) -> Optional[pb.Card]:
|
||||
return self._run_command(
|
||||
pb.BackendInput(get_card=cid)
|
||||
).get_card.card
|
||||
|
||||
def translate_string_in(
|
||||
key: TR, **kwargs: Union[str, int, float]
|
||||
|
|
|
@ -318,7 +318,7 @@ def test_modelChange():
|
|||
try:
|
||||
c1.load()
|
||||
assert 0
|
||||
except TypeError:
|
||||
except AssertionError:
|
||||
pass
|
||||
# but we have two cards, as a new one was generated
|
||||
assert len(f.cards()) == 2
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
use crate::backend::dbproxy::db_command_bytes;
|
||||
use crate::backend_proto::backend_input::Value;
|
||||
use crate::backend_proto::{BuiltinSortKind, Empty, RenderedTemplateReplacement, SyncMediaIn};
|
||||
use crate::card::{Card, CardID};
|
||||
use crate::collection::{open_collection, Collection};
|
||||
use crate::config::SortKind;
|
||||
use crate::err::{AnkiError, NetworkErrorKind, Result, SyncErrorKind};
|
||||
|
@ -248,6 +249,7 @@ impl Backend {
|
|||
}
|
||||
Value::SearchCards(input) => OValue::SearchCards(self.search_cards(input)?),
|
||||
Value::SearchNotes(input) => OValue::SearchNotes(self.search_notes(input)?),
|
||||
Value::GetCard(cid) => OValue::GetCard(self.get_card(cid)?),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -616,6 +618,13 @@ impl Backend {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn get_card(&self, cid: i64) -> Result<pb::GetCardOut> {
|
||||
let card = self.with_col(|col| col.with_ctx(|ctx| ctx.storage.get_card(CardID(cid))))?;
|
||||
Ok(pb::GetCardOut {
|
||||
card: card.map(card_to_pb),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_arg_to_fluent_val(arg: &pb::TranslateArgValue) -> FluentValue {
|
||||
|
@ -708,3 +717,26 @@ fn sort_kind_from_pb(kind: i32) -> SortKind {
|
|||
_ => SortKind::NoteCreation,
|
||||
}
|
||||
}
|
||||
|
||||
fn card_to_pb(c: Card) -> pb::Card {
|
||||
pb::Card {
|
||||
id: c.id.0,
|
||||
nid: c.nid.0,
|
||||
did: c.did.0,
|
||||
ord: c.ord as u32,
|
||||
mtime: c.mtime.0,
|
||||
usn: c.usn.0,
|
||||
ctype: c.ctype as u32,
|
||||
queue: c.queue as i32,
|
||||
due: c.due,
|
||||
ivl: c.ivl,
|
||||
factor: c.factor as u32,
|
||||
reps: c.reps,
|
||||
lapses: c.lapses,
|
||||
left: c.left,
|
||||
odue: c.odue,
|
||||
odid: c.odid.0,
|
||||
flags: c.flags,
|
||||
data: c.data,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
use crate::decks::DeckID;
|
||||
use crate::define_newtype;
|
||||
use crate::notes::NoteID;
|
||||
use crate::{timestamp::TimestampSecs, types::Usn};
|
||||
use num_enum::TryFromPrimitive;
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
define_newtype!(CardID, i64);
|
||||
|
||||
#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, TryFromPrimitive, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum CardType {
|
||||
|
@ -33,3 +37,50 @@ pub enum CardQueue {
|
|||
UserBuried = -2,
|
||||
SchedBuried = -3,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Card {
|
||||
pub(crate) id: CardID,
|
||||
pub(crate) nid: NoteID,
|
||||
pub(crate) did: DeckID,
|
||||
pub(crate) ord: u16,
|
||||
pub(crate) mtime: TimestampSecs,
|
||||
pub(crate) usn: Usn,
|
||||
pub(crate) ctype: CardType,
|
||||
pub(crate) queue: CardQueue,
|
||||
pub(crate) due: i64,
|
||||
pub(crate) ivl: i64,
|
||||
pub(crate) factor: u16,
|
||||
pub(crate) reps: i64,
|
||||
pub(crate) lapses: i64,
|
||||
pub(crate) left: i64,
|
||||
pub(crate) odue: i64,
|
||||
pub(crate) odid: DeckID,
|
||||
pub(crate) flags: i64,
|
||||
pub(crate) data: String,
|
||||
}
|
||||
|
||||
impl Default for Card {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: CardID(0),
|
||||
nid: NoteID(0),
|
||||
did: DeckID(0),
|
||||
ord: 0,
|
||||
mtime: TimestampSecs(0),
|
||||
usn: Usn(0),
|
||||
ctype: CardType::New,
|
||||
queue: CardQueue::New,
|
||||
due: 0,
|
||||
ivl: 0,
|
||||
factor: 0,
|
||||
reps: 0,
|
||||
lapses: 0,
|
||||
left: 0,
|
||||
odue: 0,
|
||||
odid: DeckID(0),
|
||||
flags: 0,
|
||||
data: "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
72
rslib/src/storage/card.rs
Normal file
72
rslib/src/storage/card.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
use crate::cached_sql;
|
||||
use crate::card::{Card, CardID, CardQueue, CardType};
|
||||
use crate::err::Result;
|
||||
use rusqlite::params;
|
||||
use rusqlite::{
|
||||
types::{FromSql, FromSqlError, ValueRef},
|
||||
OptionalExtension,
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl FromSql for CardType {
|
||||
fn column_result(value: ValueRef<'_>) -> std::result::Result<Self, FromSqlError> {
|
||||
if let ValueRef::Integer(i) = value {
|
||||
Ok(Self::try_from(i as u8).map_err(|_| FromSqlError::InvalidType)?)
|
||||
} else {
|
||||
Err(FromSqlError::InvalidType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for CardQueue {
|
||||
fn column_result(value: ValueRef<'_>) -> std::result::Result<Self, FromSqlError> {
|
||||
if let ValueRef::Integer(i) = value {
|
||||
Ok(Self::try_from(i as i8).map_err(|_| FromSqlError::InvalidType)?)
|
||||
} else {
|
||||
Err(FromSqlError::InvalidType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl super::StorageContext<'_> {
|
||||
pub fn get_card(&mut self, cid: CardID) -> Result<Option<Card>> {
|
||||
// the casts are required as Anki didn't prevent add-ons from
|
||||
// storing strings or floats in columns before
|
||||
let stmt = cached_sql!(
|
||||
self.get_card_stmt,
|
||||
self.db,
|
||||
"
|
||||
select nid, did, ord, cast(mod as integer), usn, type, queue, due,
|
||||
cast(ivl as integer), factor, reps, lapses, left, odue, odid,
|
||||
flags, data from cards where id=?"
|
||||
);
|
||||
|
||||
stmt.query_row(params![cid], |row| {
|
||||
Ok(Card {
|
||||
id: cid,
|
||||
nid: row.get(0)?,
|
||||
did: row.get(1)?,
|
||||
ord: row.get(2)?,
|
||||
mtime: row.get(3)?,
|
||||
usn: row.get(4)?,
|
||||
ctype: row.get(5)?,
|
||||
queue: row.get(6)?,
|
||||
due: row.get(7)?,
|
||||
ivl: row.get(8)?,
|
||||
factor: row.get(9)?,
|
||||
reps: row.get(10)?,
|
||||
lapses: row.get(11)?,
|
||||
left: row.get(12)?,
|
||||
odue: row.get(13)?,
|
||||
odid: row.get(14)?,
|
||||
flags: row.get(15)?,
|
||||
data: row.get(16)?,
|
||||
})
|
||||
})
|
||||
.optional()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
mod card;
|
||||
mod sqlite;
|
||||
|
||||
pub(crate) use sqlite::{SqliteStorage, StorageContext};
|
||||
|
|
|
@ -42,6 +42,16 @@ pub struct SqliteStorage {
|
|||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cached_sql {
|
||||
( $label:expr, $db:expr, $sql:expr ) => {{
|
||||
if $label.is_none() {
|
||||
$label = Some($db.prepare_cached($sql)?);
|
||||
}
|
||||
$label.as_mut().unwrap()
|
||||
}};
|
||||
}
|
||||
|
||||
fn open_or_create_collection_db(path: &Path) -> Result<Connection> {
|
||||
let mut db = Connection::open(path)?;
|
||||
|
||||
|
@ -211,6 +221,9 @@ pub(crate) struct StorageContext<'a> {
|
|||
usn: Option<Usn>,
|
||||
|
||||
timing_today: Option<SchedTimingToday>,
|
||||
|
||||
// cards
|
||||
pub(super) get_card_stmt: Option<rusqlite::CachedStatement<'a>>,
|
||||
}
|
||||
|
||||
impl StorageContext<'_> {
|
||||
|
@ -220,6 +233,7 @@ impl StorageContext<'_> {
|
|||
server,
|
||||
usn: None,
|
||||
timing_today: None,
|
||||
get_card_stmt: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue