mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
migrate cards and notes
This commit is contained in:
parent
7550e6241c
commit
95735f106a
7 changed files with 234 additions and 194 deletions
|
@ -21,6 +21,10 @@ message Int32 {
|
||||||
sint32 val = 1;
|
sint32 val = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message UInt32 {
|
||||||
|
uint32 val = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message Int64 {
|
message Int64 {
|
||||||
int64 val = 1;
|
int64 val = 1;
|
||||||
}
|
}
|
||||||
|
@ -37,6 +41,19 @@ message Bool {
|
||||||
bool val = 1;
|
bool val = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message NoteTypeID {
|
||||||
|
int64 ntid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NoteID {
|
||||||
|
int64 nid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CardID {
|
||||||
|
int64 cid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// New style RPC definitions
|
// New style RPC definitions
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -86,6 +103,22 @@ service BackendService {
|
||||||
rpc NewDeckConfigLegacy (Empty) returns (Bytes);
|
rpc NewDeckConfigLegacy (Empty) returns (Bytes);
|
||||||
rpc RemoveDeckConfig (Int64) returns (Empty);
|
rpc RemoveDeckConfig (Int64) returns (Empty);
|
||||||
|
|
||||||
|
// cards
|
||||||
|
|
||||||
|
rpc GetCard (CardID) returns (Card);
|
||||||
|
rpc UpdateCard (Card) returns (Empty);
|
||||||
|
rpc AddCard (Card) returns (CardID);
|
||||||
|
|
||||||
|
// notes
|
||||||
|
|
||||||
|
rpc NewNote (NoteTypeID) returns (Note);
|
||||||
|
rpc AddNote (AddNoteIn) returns (NoteID);
|
||||||
|
rpc UpdateNote (Note) returns (Empty);
|
||||||
|
rpc GetNote (NoteID) returns (Note);
|
||||||
|
rpc AddNoteTags (AddNoteTagsIn) returns (UInt32);
|
||||||
|
rpc UpdateNoteTags (UpdateNoteTagsIn) returns (UInt32);
|
||||||
|
rpc ClozeNumbersInNote (Note) returns (ClozeNumbersInNoteOut);
|
||||||
|
|
||||||
// misc
|
// misc
|
||||||
|
|
||||||
rpc CheckDatabase (Empty) returns (CheckDatabaseOut);
|
rpc CheckDatabase (Empty) returns (CheckDatabaseOut);
|
||||||
|
@ -344,9 +377,6 @@ message BackendInput {
|
||||||
Empty restore_trash = 35;
|
Empty restore_trash = 35;
|
||||||
OpenCollectionIn open_collection = 36;
|
OpenCollectionIn open_collection = 36;
|
||||||
CloseCollectionIn close_collection = 37;
|
CloseCollectionIn close_collection = 37;
|
||||||
int64 get_card = 38;
|
|
||||||
Card update_card = 39;
|
|
||||||
Card add_card = 40;
|
|
||||||
Empty abort_media_sync = 46;
|
Empty abort_media_sync = 46;
|
||||||
Empty before_upload = 47;
|
Empty before_upload = 47;
|
||||||
RegisterTagsIn register_tags = 48;
|
RegisterTagsIn register_tags = 48;
|
||||||
|
@ -364,19 +394,12 @@ message BackendInput {
|
||||||
Empty get_notetype_names_and_counts = 63;
|
Empty get_notetype_names_and_counts = 63;
|
||||||
string get_notetype_id_by_name = 64;
|
string get_notetype_id_by_name = 64;
|
||||||
int64 remove_notetype = 65;
|
int64 remove_notetype = 65;
|
||||||
int64 new_note = 66;
|
|
||||||
AddNoteIn add_note = 67;
|
|
||||||
Note update_note = 68;
|
|
||||||
int64 get_note = 69;
|
|
||||||
FieldNamesForNotesIn field_names_for_notes = 78;
|
FieldNamesForNotesIn field_names_for_notes = 78;
|
||||||
FindAndReplaceIn find_and_replace = 79;
|
FindAndReplaceIn find_and_replace = 79;
|
||||||
AfterNoteUpdatesIn after_note_updates = 80;
|
AfterNoteUpdatesIn after_note_updates = 80;
|
||||||
AddNoteTagsIn add_note_tags = 81;
|
|
||||||
UpdateNoteTagsIn update_note_tags = 82;
|
|
||||||
int32 set_local_minutes_west = 83;
|
int32 set_local_minutes_west = 83;
|
||||||
Empty get_preferences = 84;
|
Empty get_preferences = 84;
|
||||||
Preferences set_preferences = 85;
|
Preferences set_preferences = 85;
|
||||||
Note cloze_numbers_in_note = 87;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +411,6 @@ message BackendOutput {
|
||||||
string studied_today = 32;
|
string studied_today = 32;
|
||||||
string congrats_learn_msg = 33;
|
string congrats_learn_msg = 33;
|
||||||
Empty abort_media_sync = 46;
|
Empty abort_media_sync = 46;
|
||||||
ClozeNumbersInNoteOut cloze_numbers_in_note = 87;
|
|
||||||
|
|
||||||
// fallible commands
|
// fallible commands
|
||||||
string add_media_file = 26;
|
string add_media_file = 26;
|
||||||
|
@ -396,9 +418,6 @@ message BackendOutput {
|
||||||
Empty restore_trash = 35;
|
Empty restore_trash = 35;
|
||||||
Empty open_collection = 36;
|
Empty open_collection = 36;
|
||||||
Empty close_collection = 37;
|
Empty close_collection = 37;
|
||||||
GetCardOut get_card = 38;
|
|
||||||
Empty update_card = 39;
|
|
||||||
int64 add_card = 40;
|
|
||||||
Empty before_upload = 47;
|
Empty before_upload = 47;
|
||||||
bool register_tags = 48;
|
bool register_tags = 48;
|
||||||
AllTagsOut all_tags = 50;
|
AllTagsOut all_tags = 50;
|
||||||
|
@ -414,15 +433,9 @@ message BackendOutput {
|
||||||
NoteTypeUseCounts get_notetype_names_and_counts = 63;
|
NoteTypeUseCounts get_notetype_names_and_counts = 63;
|
||||||
int64 get_notetype_id_by_name = 64;
|
int64 get_notetype_id_by_name = 64;
|
||||||
Empty remove_notetype = 65;
|
Empty remove_notetype = 65;
|
||||||
Note new_note = 66;
|
|
||||||
int64 add_note = 67;
|
|
||||||
Empty update_note = 68;
|
|
||||||
Note get_note = 69;
|
|
||||||
FieldNamesForNotesOut field_names_for_notes = 78;
|
FieldNamesForNotesOut field_names_for_notes = 78;
|
||||||
uint32 find_and_replace = 79;
|
uint32 find_and_replace = 79;
|
||||||
Empty after_note_updates = 80;
|
Empty after_note_updates = 80;
|
||||||
uint32 add_note_tags = 81;
|
|
||||||
uint32 update_note_tags = 82;
|
|
||||||
Empty set_local_minutes_west = 83;
|
Empty set_local_minutes_west = 83;
|
||||||
Preferences get_preferences = 84;
|
Preferences get_preferences = 84;
|
||||||
Empty set_preferences = 85;
|
Empty set_preferences = 85;
|
||||||
|
@ -698,11 +711,6 @@ message BuiltinSearchOrder {
|
||||||
bool reverse = 2;
|
bool reverse = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
message GetCardOut {
|
|
||||||
Card card = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CloseCollectionIn {
|
message CloseCollectionIn {
|
||||||
bool downgrade_to_schema11 = 1;
|
bool downgrade_to_schema11 = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -467,4 +467,4 @@ and notes.mid = ? and cards.ord = ?""",
|
||||||
def _availClozeOrds(self, m: NoteType, flds: str, allowEmpty: bool = True) -> List:
|
def _availClozeOrds(self, m: NoteType, flds: str, allowEmpty: bool = True) -> List:
|
||||||
print("_availClozeOrds() is deprecated; use note.cloze_numbers_in_fields()")
|
print("_availClozeOrds() is deprecated; use note.cloze_numbers_in_fields()")
|
||||||
note = anki.rsbackend.BackendNote(fields=[flds])
|
note = anki.rsbackend.BackendNote(fields=[flds])
|
||||||
return self.col.backend.cloze_numbers_in_note(note)
|
return list(self.col.backend.cloze_numbers_in_note(note))
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, List, Optional, Tuple
|
from typing import Any, List, Optional, Sequence, Tuple
|
||||||
|
|
||||||
import anki # pylint: disable=unused-import
|
import anki # pylint: disable=unused-import
|
||||||
from anki import hooks
|
from anki import hooks
|
||||||
|
@ -82,7 +82,7 @@ class Note:
|
||||||
|
|
||||||
_model = property(model)
|
_model = property(model)
|
||||||
|
|
||||||
def cloze_numbers_in_fields(self) -> List[int]:
|
def cloze_numbers_in_fields(self) -> Sequence[int]:
|
||||||
return self.col.backend.cloze_numbers_in_note(self.to_backend_note())
|
return self.col.backend.cloze_numbers_in_note(self.to_backend_note())
|
||||||
|
|
||||||
# Dict interface
|
# Dict interface
|
||||||
|
|
|
@ -306,20 +306,6 @@ class RustBackend:
|
||||||
def _db_command(self, input: Dict[str, Any]) -> Any:
|
def _db_command(self, input: Dict[str, Any]) -> Any:
|
||||||
return orjson.loads(self._backend.db_command(orjson.dumps(input)))
|
return orjson.loads(self._backend.db_command(orjson.dumps(input)))
|
||||||
|
|
||||||
def get_card(self, cid: int) -> Optional[pb.Card]:
|
|
||||||
output = self._run_command(pb.BackendInput(get_card=cid)).get_card
|
|
||||||
if output.HasField("card"):
|
|
||||||
return output.card
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def update_card(self, card: BackendCard) -> None:
|
|
||||||
self._run_command(pb.BackendInput(update_card=card))
|
|
||||||
|
|
||||||
# returns the new card id
|
|
||||||
def add_card(self, card: BackendCard) -> int:
|
|
||||||
return self._run_command(pb.BackendInput(add_card=card)).add_card
|
|
||||||
|
|
||||||
def abort_media_sync(self):
|
def abort_media_sync(self):
|
||||||
self._run_command(pb.BackendInput(abort_media_sync=pb.Empty()))
|
self._run_command(pb.BackendInput(abort_media_sync=pb.Empty()))
|
||||||
|
|
||||||
|
@ -442,23 +428,6 @@ class RustBackend:
|
||||||
def remove_notetype(self, ntid: int) -> None:
|
def remove_notetype(self, ntid: int) -> None:
|
||||||
self._run_command(pb.BackendInput(remove_notetype=ntid), release_gil=True)
|
self._run_command(pb.BackendInput(remove_notetype=ntid), release_gil=True)
|
||||||
|
|
||||||
def new_note(self, ntid: int) -> BackendNote:
|
|
||||||
return self._run_command(pb.BackendInput(new_note=ntid)).new_note
|
|
||||||
|
|
||||||
def add_note(self, note: BackendNote, deck_id: int) -> int:
|
|
||||||
return self._run_command(
|
|
||||||
pb.BackendInput(add_note=pb.AddNoteIn(note=note, deck_id=deck_id))
|
|
||||||
).add_note
|
|
||||||
|
|
||||||
def update_note(self, note: BackendNote) -> None:
|
|
||||||
self._run_command(pb.BackendInput(update_note=note))
|
|
||||||
|
|
||||||
def get_note(self, nid) -> Optional[BackendNote]:
|
|
||||||
try:
|
|
||||||
return self._run_command(pb.BackendInput(get_note=nid)).get_note
|
|
||||||
except NotFoundError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def field_names_for_note_ids(self, nids: List[int]) -> Sequence[str]:
|
def field_names_for_note_ids(self, nids: List[int]) -> Sequence[str]:
|
||||||
return self._run_command(
|
return self._run_command(
|
||||||
pb.BackendInput(field_names_for_notes=pb.FieldNamesForNotesIn(nids=nids)),
|
pb.BackendInput(field_names_for_notes=pb.FieldNamesForNotesIn(nids=nids)),
|
||||||
|
@ -502,22 +471,6 @@ class RustBackend:
|
||||||
release_gil=True,
|
release_gil=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_note_tags(self, nids: List[int], tags: str) -> int:
|
|
||||||
return self._run_command(
|
|
||||||
pb.BackendInput(add_note_tags=pb.AddNoteTagsIn(nids=nids, tags=tags))
|
|
||||||
).add_note_tags
|
|
||||||
|
|
||||||
def update_note_tags(
|
|
||||||
self, nids: List[int], tags: str, replacement: str, regex: bool
|
|
||||||
) -> int:
|
|
||||||
return self._run_command(
|
|
||||||
pb.BackendInput(
|
|
||||||
update_note_tags=pb.UpdateNoteTagsIn(
|
|
||||||
nids=nids, tags=tags, replacement=replacement, regex=regex
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).update_note_tags
|
|
||||||
|
|
||||||
def set_local_minutes_west(self, mins: int) -> None:
|
def set_local_minutes_west(self, mins: int) -> None:
|
||||||
self._run_command(pb.BackendInput(set_local_minutes_west=mins))
|
self._run_command(pb.BackendInput(set_local_minutes_west=mins))
|
||||||
|
|
||||||
|
@ -529,13 +482,6 @@ class RustBackend:
|
||||||
def set_preferences(self, prefs: pb.Preferences) -> None:
|
def set_preferences(self, prefs: pb.Preferences) -> None:
|
||||||
self._run_command(pb.BackendInput(set_preferences=prefs))
|
self._run_command(pb.BackendInput(set_preferences=prefs))
|
||||||
|
|
||||||
def cloze_numbers_in_note(self, note: pb.Note) -> List[int]:
|
|
||||||
return list(
|
|
||||||
self._run_command(
|
|
||||||
pb.BackendInput(cloze_numbers_in_note=note)
|
|
||||||
).cloze_numbers_in_note.numbers
|
|
||||||
)
|
|
||||||
|
|
||||||
def _run_command2(self, method: int, input: Any) -> bytes:
|
def _run_command2(self, method: int, input: Any) -> bytes:
|
||||||
input_bytes = input.SerializeToString()
|
input_bytes = input.SerializeToString()
|
||||||
try:
|
try:
|
||||||
|
@ -731,10 +677,70 @@ class RustBackend:
|
||||||
output.ParseFromString(self._run_command2(27, input))
|
output.ParseFromString(self._run_command2(27, input))
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def get_card(self, cid: int) -> pb.Card:
|
||||||
|
input = pb.CardID(cid=cid)
|
||||||
|
output = pb.Card()
|
||||||
|
output.ParseFromString(self._run_command2(28, input))
|
||||||
|
return output
|
||||||
|
|
||||||
|
def update_card(self, input: pb.Card) -> pb.Empty:
|
||||||
|
output = pb.Empty()
|
||||||
|
output.ParseFromString(self._run_command2(29, input))
|
||||||
|
return output
|
||||||
|
|
||||||
|
def add_card(self, input: pb.Card) -> int:
|
||||||
|
output = pb.CardID()
|
||||||
|
output.ParseFromString(self._run_command2(30, input))
|
||||||
|
return output.cid
|
||||||
|
|
||||||
|
def new_note(self, ntid: int) -> pb.Note:
|
||||||
|
input = pb.NoteTypeID(ntid=ntid)
|
||||||
|
output = pb.Note()
|
||||||
|
output.ParseFromString(self._run_command2(31, input))
|
||||||
|
return output
|
||||||
|
|
||||||
|
def add_note(self, note: pb.Note, deck_id: int) -> int:
|
||||||
|
input = pb.AddNoteIn(note=note, deck_id=deck_id)
|
||||||
|
output = pb.NoteID()
|
||||||
|
output.ParseFromString(self._run_command2(32, input))
|
||||||
|
return output.nid
|
||||||
|
|
||||||
|
def update_note(self, input: pb.Note) -> pb.Empty:
|
||||||
|
output = pb.Empty()
|
||||||
|
output.ParseFromString(self._run_command2(33, input))
|
||||||
|
return output
|
||||||
|
|
||||||
|
def get_note(self, nid: int) -> pb.Note:
|
||||||
|
input = pb.NoteID(nid=nid)
|
||||||
|
output = pb.Note()
|
||||||
|
output.ParseFromString(self._run_command2(34, input))
|
||||||
|
return output
|
||||||
|
|
||||||
|
def add_note_tags(self, nids: Sequence[int], tags: str) -> int:
|
||||||
|
input = pb.AddNoteTagsIn(nids=nids, tags=tags)
|
||||||
|
output = pb.UInt32()
|
||||||
|
output.ParseFromString(self._run_command2(35, input))
|
||||||
|
return output.val
|
||||||
|
|
||||||
|
def update_note_tags(
|
||||||
|
self, nids: Sequence[int], tags: str, replacement: str, regex: bool
|
||||||
|
) -> int:
|
||||||
|
input = pb.UpdateNoteTagsIn(
|
||||||
|
nids=nids, tags=tags, replacement=replacement, regex=regex
|
||||||
|
)
|
||||||
|
output = pb.UInt32()
|
||||||
|
output.ParseFromString(self._run_command2(36, input))
|
||||||
|
return output.val
|
||||||
|
|
||||||
|
def cloze_numbers_in_note(self, input: pb.Note) -> Sequence[int]:
|
||||||
|
output = pb.ClozeNumbersInNoteOut()
|
||||||
|
output.ParseFromString(self._run_command2(37, input))
|
||||||
|
return output.numbers
|
||||||
|
|
||||||
def check_database(self) -> Sequence[str]:
|
def check_database(self) -> Sequence[str]:
|
||||||
input = pb.Empty()
|
input = pb.Empty()
|
||||||
output = pb.CheckDatabaseOut()
|
output = pb.CheckDatabaseOut()
|
||||||
output.ParseFromString(self._run_command2(28, input))
|
output.ParseFromString(self._run_command2(38, input))
|
||||||
return output.problems
|
return output.problems
|
||||||
|
|
||||||
# @@AUTOGEN@@
|
# @@AUTOGEN@@
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from anki.consts import MODEL_CLOZE
|
from anki.consts import MODEL_CLOZE
|
||||||
|
from anki.rsbackend import NotFoundError
|
||||||
from anki.utils import isWin, stripHTML
|
from anki.utils import isWin, stripHTML
|
||||||
from tests.shared import getEmptyCol
|
from tests.shared import getEmptyCol
|
||||||
|
|
||||||
|
@ -323,7 +324,7 @@ def test_modelChange():
|
||||||
try:
|
try:
|
||||||
c1.load()
|
c1.load()
|
||||||
assert 0
|
assert 0
|
||||||
except AssertionError:
|
except NotFoundError:
|
||||||
pass
|
pass
|
||||||
# but we have two cards, as a new one was generated
|
# but we have two cards, as a new one was generated
|
||||||
assert len(f.cards()) == 2
|
assert len(f.cards()) == 2
|
||||||
|
|
|
@ -77,8 +77,16 @@ def get_input_assign(msg):
|
||||||
|
|
||||||
|
|
||||||
def render_method(method, idx):
|
def render_method(method, idx):
|
||||||
|
input_name = method.input_type.name
|
||||||
|
if input_name.endswith("In") or len(method.input_type.fields) < 2:
|
||||||
input_args = get_input_args(method.input_type)
|
input_args = get_input_args(method.input_type)
|
||||||
input_assign = get_input_assign(method.input_type)
|
input_assign = get_input_assign(method.input_type)
|
||||||
|
input_assign_outer = (
|
||||||
|
f"input = pb.{method.input_type.name}({input_assign})\n "
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
input_args = f"self, input: pb.{input_name}"
|
||||||
|
input_assign_outer = ""
|
||||||
name = fix_snakecase(stringcase.snakecase(method.name))
|
name = fix_snakecase(stringcase.snakecase(method.name))
|
||||||
if len(method.output_type.fields) == 1:
|
if len(method.output_type.fields) == 1:
|
||||||
# unwrap single return arg
|
# unwrap single return arg
|
||||||
|
@ -90,8 +98,7 @@ def render_method(method, idx):
|
||||||
return_type = f"pb.{method.output_type.name}"
|
return_type = f"pb.{method.output_type.name}"
|
||||||
return f"""\
|
return f"""\
|
||||||
def {name}({input_args}) -> {return_type}:
|
def {name}({input_args}) -> {return_type}:
|
||||||
input = pb.{method.input_type.name}({input_assign})
|
{input_assign_outer}output = pb.{method.output_type.name}()
|
||||||
output = pb.{method.output_type.name}()
|
|
||||||
output.ParseFromString(self._run_command2({idx+1}, input))
|
output.ParseFromString(self._run_command2({idx+1}, input))
|
||||||
return output{single_field}
|
return output{single_field}
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -166,12 +166,36 @@ impl From<i64> for pb::Int64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u32> for pb::UInt32 {
|
||||||
|
fn from(val: u32) -> Self {
|
||||||
|
pb::UInt32 { val }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<()> for pb::Empty {
|
impl From<()> for pb::Empty {
|
||||||
fn from(_val: ()) -> Self {
|
fn from(_val: ()) -> Self {
|
||||||
pb::Empty {}
|
pb::Empty {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<pb::CardId> for CardID {
|
||||||
|
fn from(cid: pb::CardId) -> Self {
|
||||||
|
CardID(cid.cid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<pb::NoteId> for NoteID {
|
||||||
|
fn from(nid: pb::NoteId) -> Self {
|
||||||
|
NoteID(nid.nid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<pb::NoteTypeId> for NoteTypeID {
|
||||||
|
fn from(ntid: pb::NoteTypeId) -> Self {
|
||||||
|
NoteTypeID(ntid.ntid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BackendService for Backend {
|
impl BackendService for Backend {
|
||||||
// card rendering
|
// card rendering
|
||||||
|
|
||||||
|
@ -486,6 +510,106 @@ impl BackendService for Backend {
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cards
|
||||||
|
//-------------------------------------------------------------------
|
||||||
|
|
||||||
|
fn get_card(&mut self, input: pb::CardId) -> BackendResult<pb::Card> {
|
||||||
|
self.with_col(|col| {
|
||||||
|
col.storage
|
||||||
|
.get_card(input.into())
|
||||||
|
.and_then(|opt| opt.ok_or(AnkiError::NotFound))
|
||||||
|
.map(card_to_pb)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_card(&mut self, input: pb::Card) -> BackendResult<Empty> {
|
||||||
|
let mut card = pbcard_to_native(input)?;
|
||||||
|
self.with_col(|col| {
|
||||||
|
col.transact(None, |ctx| {
|
||||||
|
let orig = ctx
|
||||||
|
.storage
|
||||||
|
.get_card(card.id)?
|
||||||
|
.ok_or_else(|| AnkiError::invalid_input("missing card"))?;
|
||||||
|
ctx.update_card(&mut card, &orig)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_card(&mut self, input: pb::Card) -> BackendResult<pb::CardId> {
|
||||||
|
let mut card = pbcard_to_native(input)?;
|
||||||
|
self.with_col(|col| col.transact(None, |ctx| ctx.add_card(&mut card)))?;
|
||||||
|
Ok(pb::CardId { cid: card.id.0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
// notes
|
||||||
|
//-------------------------------------------------------------------
|
||||||
|
|
||||||
|
fn new_note(&mut self, input: pb::NoteTypeId) -> BackendResult<pb::Note> {
|
||||||
|
self.with_col(|col| {
|
||||||
|
let nt = col.get_notetype(input.into())?.ok_or(AnkiError::NotFound)?;
|
||||||
|
Ok(nt.new_note().into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_note(&mut self, input: pb::AddNoteIn) -> BackendResult<pb::NoteId> {
|
||||||
|
self.with_col(|col| {
|
||||||
|
let mut note: Note = input.note.ok_or(AnkiError::NotFound)?.into();
|
||||||
|
col.add_note(&mut note, DeckID(input.deck_id))
|
||||||
|
.map(|_| pb::NoteId { nid: note.id.0 })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_note(&mut self, input: pb::Note) -> BackendResult<Empty> {
|
||||||
|
self.with_col(|col| {
|
||||||
|
let mut note: Note = input.into();
|
||||||
|
col.update_note(&mut note)
|
||||||
|
})
|
||||||
|
.map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_note(&mut self, input: pb::NoteId) -> BackendResult<pb::Note> {
|
||||||
|
self.with_col(|col| {
|
||||||
|
col.storage
|
||||||
|
.get_note(input.into())?
|
||||||
|
.ok_or(AnkiError::NotFound)
|
||||||
|
.map(Into::into)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_note_tags(&mut self, input: pb::AddNoteTagsIn) -> BackendResult<pb::UInt32> {
|
||||||
|
self.with_col(|col| {
|
||||||
|
col.add_tags_for_notes(&to_nids(input.nids), &input.tags)
|
||||||
|
.map(|n| n as u32)
|
||||||
|
})
|
||||||
|
.map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_note_tags(&mut self, input: pb::UpdateNoteTagsIn) -> BackendResult<pb::UInt32> {
|
||||||
|
self.with_col(|col| {
|
||||||
|
col.replace_tags_for_notes(
|
||||||
|
&to_nids(input.nids),
|
||||||
|
&input.tags,
|
||||||
|
&input.replacement,
|
||||||
|
input.regex,
|
||||||
|
)
|
||||||
|
.map(|n| (n as u32).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cloze_numbers_in_note(
|
||||||
|
&mut self,
|
||||||
|
note: pb::Note,
|
||||||
|
) -> BackendResult<pb::ClozeNumbersInNoteOut> {
|
||||||
|
let mut set = HashSet::with_capacity(4);
|
||||||
|
for field in ¬e.fields {
|
||||||
|
add_cloze_numbers_in_string(field, &mut set);
|
||||||
|
}
|
||||||
|
Ok(pb::ClozeNumbersInNoteOut {
|
||||||
|
numbers: set.into_iter().map(|n| n as u32).collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// media
|
// media
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -669,12 +793,6 @@ impl Backend {
|
||||||
self.close_collection(input.downgrade_to_schema11)?;
|
self.close_collection(input.downgrade_to_schema11)?;
|
||||||
OValue::CloseCollection(Empty {})
|
OValue::CloseCollection(Empty {})
|
||||||
}
|
}
|
||||||
Value::GetCard(cid) => OValue::GetCard(self.get_card(cid)?),
|
|
||||||
Value::UpdateCard(card) => {
|
|
||||||
self.update_card(card)?;
|
|
||||||
OValue::UpdateCard(pb::Empty {})
|
|
||||||
}
|
|
||||||
Value::AddCard(card) => OValue::AddCard(self.add_card(card)?),
|
|
||||||
Value::AbortMediaSync(_) => {
|
Value::AbortMediaSync(_) => {
|
||||||
self.abort_media_sync();
|
self.abort_media_sync();
|
||||||
OValue::AbortMediaSync(pb::Empty {})
|
OValue::AbortMediaSync(pb::Empty {})
|
||||||
|
@ -721,13 +839,6 @@ impl Backend {
|
||||||
self.remove_notetype(id)?;
|
self.remove_notetype(id)?;
|
||||||
OValue::RemoveNotetype(pb::Empty {})
|
OValue::RemoveNotetype(pb::Empty {})
|
||||||
}
|
}
|
||||||
Value::NewNote(ntid) => OValue::NewNote(self.new_note(ntid)?),
|
|
||||||
Value::AddNote(input) => OValue::AddNote(self.add_note(input)?),
|
|
||||||
Value::UpdateNote(note) => {
|
|
||||||
self.update_note(note)?;
|
|
||||||
OValue::UpdateNote(pb::Empty {})
|
|
||||||
}
|
|
||||||
Value::GetNote(nid) => OValue::GetNote(self.get_note(nid)?),
|
|
||||||
Value::FieldNamesForNotes(input) => {
|
Value::FieldNamesForNotes(input) => {
|
||||||
OValue::FieldNamesForNotes(self.field_names_for_notes(input)?)
|
OValue::FieldNamesForNotes(self.field_names_for_notes(input)?)
|
||||||
}
|
}
|
||||||
|
@ -735,8 +846,6 @@ impl Backend {
|
||||||
Value::AfterNoteUpdates(input) => {
|
Value::AfterNoteUpdates(input) => {
|
||||||
OValue::AfterNoteUpdates(self.after_note_updates(input)?)
|
OValue::AfterNoteUpdates(self.after_note_updates(input)?)
|
||||||
}
|
}
|
||||||
Value::AddNoteTags(input) => OValue::AddNoteTags(self.add_note_tags(input)?),
|
|
||||||
Value::UpdateNoteTags(input) => OValue::UpdateNoteTags(self.update_note_tags(input)?),
|
|
||||||
Value::SetLocalMinutesWest(mins) => OValue::SetLocalMinutesWest({
|
Value::SetLocalMinutesWest(mins) => OValue::SetLocalMinutesWest({
|
||||||
self.set_local_mins_west(mins)?;
|
self.set_local_mins_west(mins)?;
|
||||||
pb::Empty {}
|
pb::Empty {}
|
||||||
|
@ -746,9 +855,6 @@ impl Backend {
|
||||||
self.set_preferences(prefs)?;
|
self.set_preferences(prefs)?;
|
||||||
pb::Empty {}
|
pb::Empty {}
|
||||||
}),
|
}),
|
||||||
Value::ClozeNumbersInNote(note) => {
|
|
||||||
OValue::ClozeNumbersInNote(self.cloze_numbers_in_note(note))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -924,32 +1030,6 @@ impl Backend {
|
||||||
self.with_col(|col| db_command_bytes(&col.storage, input))
|
self.with_col(|col| db_command_bytes(&col.storage, input))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_card(&self, cid: i64) -> Result<pb::GetCardOut> {
|
|
||||||
let card = self.with_col(|col| col.storage.get_card(CardID(cid)))?;
|
|
||||||
Ok(pb::GetCardOut {
|
|
||||||
card: card.map(card_to_pb),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_card(&self, pbcard: pb::Card) -> Result<()> {
|
|
||||||
let mut card = pbcard_to_native(pbcard)?;
|
|
||||||
self.with_col(|col| {
|
|
||||||
col.transact(None, |ctx| {
|
|
||||||
let orig = ctx
|
|
||||||
.storage
|
|
||||||
.get_card(card.id)?
|
|
||||||
.ok_or_else(|| AnkiError::invalid_input("missing card"))?;
|
|
||||||
ctx.update_card(&mut card, &orig)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_card(&self, pbcard: pb::Card) -> Result<i64> {
|
|
||||||
let mut card = pbcard_to_native(pbcard)?;
|
|
||||||
self.with_col(|col| col.transact(None, |ctx| ctx.add_card(&mut card)))?;
|
|
||||||
Ok(card.id.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn before_upload(&self) -> Result<()> {
|
fn before_upload(&self) -> Result<()> {
|
||||||
self.with_col(|col| col.before_upload())
|
self.with_col(|col| col.before_upload())
|
||||||
}
|
}
|
||||||
|
@ -1104,39 +1184,6 @@ impl Backend {
|
||||||
self.with_col(|col| col.remove_notetype(NoteTypeID(id)))
|
self.with_col(|col| col.remove_notetype(NoteTypeID(id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_note(&self, ntid: i64) -> Result<pb::Note> {
|
|
||||||
self.with_col(|col| {
|
|
||||||
let nt = col
|
|
||||||
.get_notetype(NoteTypeID(ntid))?
|
|
||||||
.ok_or(AnkiError::NotFound)?;
|
|
||||||
Ok(nt.new_note().into())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_note(&self, input: pb::AddNoteIn) -> Result<i64> {
|
|
||||||
self.with_col(|col| {
|
|
||||||
let mut note: Note = input.note.ok_or(AnkiError::NotFound)?.into();
|
|
||||||
col.add_note(&mut note, DeckID(input.deck_id))
|
|
||||||
.map(|_| note.id.0)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_note(&self, pbnote: pb::Note) -> Result<()> {
|
|
||||||
self.with_col(|col| {
|
|
||||||
let mut note: Note = pbnote.into();
|
|
||||||
col.update_note(&mut note)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_note(&self, nid: i64) -> Result<pb::Note> {
|
|
||||||
self.with_col(|col| {
|
|
||||||
col.storage
|
|
||||||
.get_note(NoteID(nid))?
|
|
||||||
.ok_or(AnkiError::NotFound)
|
|
||||||
.map(Into::into)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field_names_for_notes(
|
fn field_names_for_notes(
|
||||||
&self,
|
&self,
|
||||||
input: pb::FieldNamesForNotesIn,
|
input: pb::FieldNamesForNotesIn,
|
||||||
|
@ -1184,25 +1231,6 @@ impl Backend {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_note_tags(&self, input: pb::AddNoteTagsIn) -> Result<u32> {
|
|
||||||
self.with_col(|col| {
|
|
||||||
col.add_tags_for_notes(&to_nids(input.nids), &input.tags)
|
|
||||||
.map(|n| n as u32)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_note_tags(&self, input: pb::UpdateNoteTagsIn) -> Result<u32> {
|
|
||||||
self.with_col(|col| {
|
|
||||||
col.replace_tags_for_notes(
|
|
||||||
&to_nids(input.nids),
|
|
||||||
&input.tags,
|
|
||||||
&input.replacement,
|
|
||||||
input.regex,
|
|
||||||
)
|
|
||||||
.map(|n| n as u32)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_local_mins_west(&self, mins: i32) -> Result<()> {
|
fn set_local_mins_west(&self, mins: i32) -> Result<()> {
|
||||||
self.with_col(|col| col.transact(None, |col| col.set_local_mins_west(mins)))
|
self.with_col(|col| col.transact(None, |col| col.set_local_mins_west(mins)))
|
||||||
}
|
}
|
||||||
|
@ -1215,16 +1243,6 @@ impl Backend {
|
||||||
self.with_col(|col| col.transact(None, |col| col.set_preferences(prefs)))
|
self.with_col(|col| col.transact(None, |col| col.set_preferences(prefs)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cloze_numbers_in_note(&self, note: pb::Note) -> pb::ClozeNumbersInNoteOut {
|
|
||||||
let mut set = HashSet::with_capacity(4);
|
|
||||||
for field in ¬e.fields {
|
|
||||||
add_cloze_numbers_in_string(field, &mut set);
|
|
||||||
}
|
|
||||||
pb::ClozeNumbersInNoteOut {
|
|
||||||
numbers: set.into_iter().map(|n| n as u32).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_stock_notetype_legacy(&self, kind: i32) -> Result<Vec<u8>> {
|
fn get_stock_notetype_legacy(&self, kind: i32) -> Result<Vec<u8>> {
|
||||||
// fixme: use individual functions instead of full vec
|
// fixme: use individual functions instead of full vec
|
||||||
let mut all = all_stock_notetypes(&self.i18n);
|
let mut all = all_stock_notetypes(&self.i18n);
|
||||||
|
|
Loading…
Reference in a new issue