mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
update_card()
This commit is contained in:
parent
1acfe7d3cc
commit
47504245dc
6 changed files with 115 additions and 30 deletions
|
@ -48,6 +48,7 @@ message BackendInput {
|
||||||
OpenCollectionIn open_collection = 36;
|
OpenCollectionIn open_collection = 36;
|
||||||
Empty close_collection = 37;
|
Empty close_collection = 37;
|
||||||
int64 get_card = 38;
|
int64 get_card = 38;
|
||||||
|
Card update_card = 39;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@ message BackendOutput {
|
||||||
Empty open_collection = 36;
|
Empty open_collection = 36;
|
||||||
Empty close_collection = 37;
|
Empty close_collection = 37;
|
||||||
GetCardOut get_card = 38;
|
GetCardOut get_card = 38;
|
||||||
|
Empty update_card = 39;
|
||||||
|
|
||||||
BackendError error = 2047;
|
BackendError error = 2047;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ from anki import hooks
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.models import NoteType, Template
|
from anki.models import NoteType, Template
|
||||||
from anki.notes import Note
|
from anki.notes import Note
|
||||||
|
from anki.rsbackend import BackendCard
|
||||||
from anki.sound import AVTag
|
from anki.sound import AVTag
|
||||||
from anki.utils import intTime, joinFields, timestampID
|
from anki.utils import intTime, joinFields, timestampID
|
||||||
|
|
||||||
|
@ -33,9 +34,7 @@ class Card:
|
||||||
lastIvl: int
|
lastIvl: int
|
||||||
ord: int
|
ord: int
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, col: anki.storage._Collection, id: Optional[int] = None) -> None:
|
||||||
self, col: anki.storage._Collection, id: Optional[int] = None
|
|
||||||
) -> None:
|
|
||||||
self.col = col.weakref()
|
self.col = col.weakref()
|
||||||
self.timerStarted = None
|
self.timerStarted = None
|
||||||
self._render_output: Optional[anki.template.TemplateRenderOutput] = None
|
self._render_output: Optional[anki.template.TemplateRenderOutput] = None
|
||||||
|
@ -98,30 +97,27 @@ class Card:
|
||||||
|
|
||||||
def flush(self) -> None:
|
def flush(self) -> None:
|
||||||
self._preFlush()
|
self._preFlush()
|
||||||
self.col.db.execute(
|
card = BackendCard(
|
||||||
"""
|
id=self.id,
|
||||||
insert or replace into cards values
|
nid=self.nid,
|
||||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
did=self.did,
|
||||||
self.id,
|
ord=self.ord,
|
||||||
self.nid,
|
mtime=self.mod,
|
||||||
self.did,
|
usn=self.usn,
|
||||||
self.ord,
|
ctype=self.type,
|
||||||
self.mod,
|
queue=self.queue,
|
||||||
self.usn,
|
due=self.due,
|
||||||
self.type,
|
ivl=self.ivl,
|
||||||
self.queue,
|
factor=self.factor,
|
||||||
self.due,
|
reps=self.reps,
|
||||||
self.ivl,
|
lapses=self.lapses,
|
||||||
self.factor,
|
left=self.left,
|
||||||
self.reps,
|
odue=self.odue,
|
||||||
self.lapses,
|
odid=self.odid,
|
||||||
self.left,
|
flags=self.flags,
|
||||||
self.odue,
|
data=self.data,
|
||||||
self.odid,
|
|
||||||
self.flags,
|
|
||||||
self.data,
|
|
||||||
)
|
)
|
||||||
self.col.log(self)
|
self.col.backend.update_card(card)
|
||||||
|
|
||||||
def question(self, reload: bool = False, browser: bool = False) -> str:
|
def question(self, reload: bool = False, browser: bool = False) -> str:
|
||||||
return self.css() + self.render_output(reload, browser).question_text
|
return self.css() + self.render_output(reload, browser).question_text
|
||||||
|
|
|
@ -36,6 +36,7 @@ assert ankirspy.buildhash() == anki.buildinfo.buildhash
|
||||||
|
|
||||||
SchedTimingToday = pb.SchedTimingTodayOut
|
SchedTimingToday = pb.SchedTimingTodayOut
|
||||||
BuiltinSortKind = pb.BuiltinSortKind
|
BuiltinSortKind = pb.BuiltinSortKind
|
||||||
|
BackendCard = pb.Card
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import orjson
|
import orjson
|
||||||
|
@ -481,9 +482,11 @@ class RustBackend:
|
||||||
).search_notes.note_ids
|
).search_notes.note_ids
|
||||||
|
|
||||||
def get_card(self, cid: int) -> Optional[pb.Card]:
|
def get_card(self, cid: int) -> Optional[pb.Card]:
|
||||||
return self._run_command(
|
return self._run_command(pb.BackendInput(get_card=cid)).get_card.card
|
||||||
pb.BackendInput(get_card=cid)
|
|
||||||
).get_card.card
|
def update_card(self, card: BackendCard) -> None:
|
||||||
|
self._run_command(pb.BackendInput(update_card=card))
|
||||||
|
|
||||||
|
|
||||||
def translate_string_in(
|
def translate_string_in(
|
||||||
key: TR, **kwargs: Union[str, int, float]
|
key: TR, **kwargs: Union[str, int, float]
|
||||||
|
|
|
@ -5,8 +5,10 @@ use crate::backend::dbproxy::db_command_bytes;
|
||||||
use crate::backend_proto::backend_input::Value;
|
use crate::backend_proto::backend_input::Value;
|
||||||
use crate::backend_proto::{BuiltinSortKind, Empty, RenderedTemplateReplacement, SyncMediaIn};
|
use crate::backend_proto::{BuiltinSortKind, Empty, RenderedTemplateReplacement, SyncMediaIn};
|
||||||
use crate::card::{Card, CardID};
|
use crate::card::{Card, CardID};
|
||||||
|
use crate::card::{CardQueue, CardType};
|
||||||
use crate::collection::{open_collection, Collection};
|
use crate::collection::{open_collection, Collection};
|
||||||
use crate::config::SortKind;
|
use crate::config::SortKind;
|
||||||
|
use crate::decks::DeckID;
|
||||||
use crate::err::{AnkiError, NetworkErrorKind, Result, SyncErrorKind};
|
use crate::err::{AnkiError, NetworkErrorKind, Result, SyncErrorKind};
|
||||||
use crate::i18n::{tr_args, FString, I18n};
|
use crate::i18n::{tr_args, FString, I18n};
|
||||||
use crate::latex::{extract_latex, extract_latex_expanding_clozes, ExtractedLatex};
|
use crate::latex::{extract_latex, extract_latex_expanding_clozes, ExtractedLatex};
|
||||||
|
@ -14,6 +16,7 @@ use crate::log::{default_logger, Logger};
|
||||||
use crate::media::check::MediaChecker;
|
use crate::media::check::MediaChecker;
|
||||||
use crate::media::sync::MediaSyncProgress;
|
use crate::media::sync::MediaSyncProgress;
|
||||||
use crate::media::MediaManager;
|
use crate::media::MediaManager;
|
||||||
|
use crate::notes::NoteID;
|
||||||
use crate::sched::cutoff::{local_minutes_west_for_stamp, sched_timing_today};
|
use crate::sched::cutoff::{local_minutes_west_for_stamp, sched_timing_today};
|
||||||
use crate::sched::timespan::{answer_button_time, learning_congrats, studied_today, time_span};
|
use crate::sched::timespan::{answer_button_time, learning_congrats, studied_today, time_span};
|
||||||
use crate::search::{search_cards, search_notes, SortMode};
|
use crate::search::{search_cards, search_notes, SortMode};
|
||||||
|
@ -22,10 +25,13 @@ use crate::template::{
|
||||||
RenderedNode,
|
RenderedNode,
|
||||||
};
|
};
|
||||||
use crate::text::{extract_av_tags, strip_av_tags, AVTag};
|
use crate::text::{extract_av_tags, strip_av_tags, AVTag};
|
||||||
|
use crate::timestamp::TimestampSecs;
|
||||||
|
use crate::types::Usn;
|
||||||
use crate::{backend_proto as pb, log};
|
use crate::{backend_proto as pb, log};
|
||||||
use fluent::FluentValue;
|
use fluent::FluentValue;
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
@ -250,6 +256,10 @@ impl Backend {
|
||||||
Value::SearchCards(input) => OValue::SearchCards(self.search_cards(input)?),
|
Value::SearchCards(input) => OValue::SearchCards(self.search_cards(input)?),
|
||||||
Value::SearchNotes(input) => OValue::SearchNotes(self.search_notes(input)?),
|
Value::SearchNotes(input) => OValue::SearchNotes(self.search_notes(input)?),
|
||||||
Value::GetCard(cid) => OValue::GetCard(self.get_card(cid)?),
|
Value::GetCard(cid) => OValue::GetCard(self.get_card(cid)?),
|
||||||
|
Value::UpdateCard(card) => {
|
||||||
|
self.update_card(card)?;
|
||||||
|
OValue::UpdateCard(pb::Empty {})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -625,6 +635,11 @@ impl Backend {
|
||||||
card: card.map(card_to_pb),
|
card: card.map(card_to_pb),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_card(&self, pbcard: pb::Card) -> Result<()> {
|
||||||
|
let card = pbcard_to_native(pbcard);
|
||||||
|
self.with_col(|col| col.with_ctx(|ctx| ctx.storage.update_card(&card)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_arg_to_fluent_val(arg: &pb::TranslateArgValue) -> FluentValue {
|
fn translate_arg_to_fluent_val(arg: &pb::TranslateArgValue) -> FluentValue {
|
||||||
|
@ -740,3 +755,26 @@ fn card_to_pb(c: Card) -> pb::Card {
|
||||||
data: c.data,
|
data: c.data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pbcard_to_native(c: pb::Card) -> Card {
|
||||||
|
Card {
|
||||||
|
id: CardID(c.id),
|
||||||
|
nid: NoteID(c.nid),
|
||||||
|
did: DeckID(c.did),
|
||||||
|
ord: c.ord as u16,
|
||||||
|
mtime: TimestampSecs(c.mtime),
|
||||||
|
usn: Usn(c.usn),
|
||||||
|
ctype: CardType::try_from(c.ctype as u8).unwrap_or(CardType::New),
|
||||||
|
queue: CardQueue::try_from(c.queue as i8).unwrap_or(CardQueue::New),
|
||||||
|
due: c.due,
|
||||||
|
ivl: c.ivl,
|
||||||
|
factor: c.factor as u16,
|
||||||
|
reps: c.reps,
|
||||||
|
lapses: c.lapses,
|
||||||
|
left: c.left,
|
||||||
|
odue: c.odue,
|
||||||
|
odid: DeckID(c.odid),
|
||||||
|
flags: c.flags,
|
||||||
|
data: c.data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use crate::cached_sql;
|
use crate::cached_sql;
|
||||||
use crate::card::{Card, CardID, CardQueue, CardType};
|
use crate::card::{Card, CardID, CardQueue, CardType};
|
||||||
use crate::err::Result;
|
use crate::err::{AnkiError, Result};
|
||||||
use rusqlite::params;
|
use rusqlite::params;
|
||||||
use rusqlite::{
|
use rusqlite::{
|
||||||
types::{FromSql, FromSqlError, ValueRef},
|
types::{FromSql, FromSqlError, ValueRef},
|
||||||
|
@ -69,4 +69,48 @@ flags, data from cards where id=?"
|
||||||
.optional()
|
.optional()
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn update_card(&mut self, card: &Card) -> Result<()> {
|
||||||
|
if card.id.0 == 0 {
|
||||||
|
return Err(AnkiError::invalid_input("card id not set"));
|
||||||
|
}
|
||||||
|
self.flush_card(card)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_card(&mut self, card: &Card) -> Result<()> {
|
||||||
|
let stmt = cached_sql!(
|
||||||
|
self.update_card_stmt,
|
||||||
|
self.db,
|
||||||
|
"
|
||||||
|
insert or replace into cards
|
||||||
|
(id, nid, did, ord, mod, usn, type, queue, due, ivl, factor,
|
||||||
|
reps, lapses, left, odue, odid, flags, data)
|
||||||
|
values
|
||||||
|
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
stmt.execute(params![
|
||||||
|
card.id,
|
||||||
|
card.nid,
|
||||||
|
card.did,
|
||||||
|
card.ord,
|
||||||
|
card.mtime,
|
||||||
|
card.usn,
|
||||||
|
card.ctype as u8,
|
||||||
|
card.queue as i8,
|
||||||
|
card.due,
|
||||||
|
card.ivl,
|
||||||
|
card.factor,
|
||||||
|
card.reps,
|
||||||
|
card.lapses,
|
||||||
|
card.left,
|
||||||
|
card.odue,
|
||||||
|
card.odid,
|
||||||
|
card.flags,
|
||||||
|
card.data,
|
||||||
|
])?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,6 +224,7 @@ pub(crate) struct StorageContext<'a> {
|
||||||
|
|
||||||
// cards
|
// cards
|
||||||
pub(super) get_card_stmt: Option<rusqlite::CachedStatement<'a>>,
|
pub(super) get_card_stmt: Option<rusqlite::CachedStatement<'a>>,
|
||||||
|
pub(super) update_card_stmt: Option<rusqlite::CachedStatement<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StorageContext<'_> {
|
impl StorageContext<'_> {
|
||||||
|
@ -234,6 +235,7 @@ impl StorageContext<'_> {
|
||||||
usn: None,
|
usn: None,
|
||||||
timing_today: None,
|
timing_today: None,
|
||||||
get_card_stmt: None,
|
get_card_stmt: None,
|
||||||
|
update_card_stmt: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue