Add a backend method to add notes in bulk (#2659)
* Add a backend method to add notes in bulk * i -> idx * Remove duplicate assignment * Allow add_notes to work with multiple deck IDs * Rename note_deck_id to requests
This commit is contained in:
parent
8b0d663fd0
commit
6f0bf58d49
|
@ -15,6 +15,7 @@ import "anki/cards.proto";
|
|||
service NotesService {
|
||||
rpc NewNote(notetypes.NotetypeId) returns (Note);
|
||||
rpc AddNote(AddNoteRequest) returns (AddNoteResponse);
|
||||
rpc AddNotes(AddNotesRequest) returns (AddNotesResponse);
|
||||
rpc DefaultsForAdding(DefaultsForAddingRequest) returns (DeckAndNotetype);
|
||||
rpc DefaultDeckForNotetype(notetypes.NotetypeId) returns (decks.DeckId);
|
||||
rpc UpdateNotes(UpdateNotesRequest) returns (collection.OpChanges);
|
||||
|
@ -62,6 +63,15 @@ message AddNoteResponse {
|
|||
int64 note_id = 2;
|
||||
}
|
||||
|
||||
message AddNotesRequest {
|
||||
repeated AddNoteRequest requests = 1;
|
||||
}
|
||||
|
||||
message AddNotesResponse {
|
||||
collection.OpChanges changes = 1;
|
||||
repeated int64 nids = 2;
|
||||
}
|
||||
|
||||
message UpdateNotesRequest {
|
||||
repeated Note notes = 1;
|
||||
bool skip_undo_entry = 2;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Generator, Literal, Sequence, Union, cast
|
||||
from typing import Any, Generator, Iterable, Literal, Sequence, Union, cast
|
||||
|
||||
from anki import (
|
||||
ankiweb_pb2,
|
||||
|
@ -14,6 +14,7 @@ from anki import (
|
|||
image_occlusion_pb2,
|
||||
import_export_pb2,
|
||||
links_pb2,
|
||||
notes_pb2,
|
||||
search_pb2,
|
||||
stats_pb2,
|
||||
sync_pb2,
|
||||
|
@ -127,6 +128,12 @@ class CardIdsLimit:
|
|||
ExportLimit = Union[DeckIdLimit, NoteIdsLimit, CardIdsLimit, None]
|
||||
|
||||
|
||||
@dataclass
|
||||
class AddNoteRequest:
|
||||
note: Note
|
||||
deck_id: DeckId
|
||||
|
||||
|
||||
class Collection(DeprecatedNamesMixin):
|
||||
sched: V1Scheduler | V2Scheduler | V3Scheduler
|
||||
|
||||
|
@ -580,6 +587,22 @@ class Collection(DeprecatedNamesMixin):
|
|||
note.id = NoteId(out.note_id)
|
||||
return out.changes
|
||||
|
||||
def add_notes(self, requests: Iterable[AddNoteRequest]) -> OpChanges:
|
||||
for request in requests:
|
||||
hooks.note_will_be_added(self, request.note, request.deck_id)
|
||||
out = self._backend.add_notes(
|
||||
requests=[
|
||||
notes_pb2.AddNoteRequest(
|
||||
note=request.note._to_backend_note(), deck_id=request.deck_id
|
||||
)
|
||||
for request in requests
|
||||
]
|
||||
)
|
||||
for idx, request in enumerate(requests):
|
||||
request.note.id = NoteId(out.nids[idx])
|
||||
|
||||
return out.changes
|
||||
|
||||
def remove_notes(self, note_ids: Sequence[NoteId]) -> OpChangesWithCount:
|
||||
hooks.notes_will_be_deleted(self, note_ids)
|
||||
return self._backend.remove_notes(note_ids=note_ids, card_ids=[])
|
||||
|
|
|
@ -13,7 +13,6 @@ use anki_proto::image_occlusion::GetImageOcclusionNoteResponse;
|
|||
use regex::Regex;
|
||||
|
||||
use crate::media::MediaManager;
|
||||
use crate::notetype::CardGenContext;
|
||||
use crate::prelude::*;
|
||||
|
||||
impl Collection {
|
||||
|
@ -66,11 +65,7 @@ impl Collection {
|
|||
note.set_field(2, header)?;
|
||||
note.set_field(3, back_extra)?;
|
||||
note.tags = tags;
|
||||
|
||||
let last_deck = col.get_last_deck_added_to_for_notetype(note.notetype_id);
|
||||
let ctx = CardGenContext::new(nt.as_ref(), last_deck, col.usn()?);
|
||||
let norm = col.get_config_bool(BoolKey::NormalizeNoteText);
|
||||
col.add_note_inner(&ctx, &mut note, current_deck.id, norm)?;
|
||||
col.add_note_inner(&mut note, current_deck.id)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
@ -15,6 +15,9 @@ use sha1::Sha1;
|
|||
|
||||
use crate::cloze::contains_cloze;
|
||||
use crate::define_newtype;
|
||||
use crate::error;
|
||||
use crate::error::AnkiError;
|
||||
use crate::error::OrInvalid;
|
||||
use crate::notetype::CardGenContext;
|
||||
use crate::notetype::NoteField;
|
||||
use crate::ops::StateChanges;
|
||||
|
@ -66,16 +69,35 @@ impl Note {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AddNoteRequest {
|
||||
pub note: Note,
|
||||
pub deck_id: DeckId,
|
||||
}
|
||||
|
||||
impl TryFrom<anki_proto::notes::AddNoteRequest> for AddNoteRequest {
|
||||
type Error = AnkiError;
|
||||
|
||||
fn try_from(request: anki_proto::notes::AddNoteRequest) -> error::Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
note: request.note.or_invalid("no note provided")?.into(),
|
||||
deck_id: DeckId(request.deck_id),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
pub fn add_note(&mut self, note: &mut Note, did: DeckId) -> Result<OpOutput<()>> {
|
||||
self.transact(Op::AddNote, |col| col.add_note_inner(note, did))
|
||||
}
|
||||
|
||||
pub fn add_notes(&mut self, requests: &mut [AddNoteRequest]) -> Result<OpOutput<()>> {
|
||||
self.transact(Op::AddNote, |col| {
|
||||
let nt = col
|
||||
.get_notetype(note.notetype_id)?
|
||||
.or_invalid("missing note type")?;
|
||||
let last_deck = col.get_last_deck_added_to_for_notetype(note.notetype_id);
|
||||
let ctx = CardGenContext::new(nt.as_ref(), last_deck, col.usn()?);
|
||||
let norm = col.get_config_bool(BoolKey::NormalizeNoteText);
|
||||
col.add_note_inner(&ctx, note, did, norm)
|
||||
for request in requests {
|
||||
col.add_note_inner(&mut request.note, request.deck_id)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -333,18 +355,18 @@ impl Collection {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn add_note_inner(
|
||||
&mut self,
|
||||
ctx: &CardGenContext<&Notetype>,
|
||||
note: &mut Note,
|
||||
did: DeckId,
|
||||
normalize_text: bool,
|
||||
) -> Result<()> {
|
||||
pub(crate) fn add_note_inner(&mut self, note: &mut Note, did: DeckId) -> Result<()> {
|
||||
let nt = self
|
||||
.get_notetype(note.notetype_id)?
|
||||
.or_invalid("missing note type")?;
|
||||
let last_deck = self.get_last_deck_added_to_for_notetype(note.notetype_id);
|
||||
let ctx = CardGenContext::new(nt.as_ref(), last_deck, self.usn()?);
|
||||
let normalize_text = self.get_config_bool(BoolKey::NormalizeNoteText);
|
||||
self.canonify_note_tags(note, ctx.usn)?;
|
||||
note.prepare_for_update(ctx.notetype, normalize_text)?;
|
||||
note.set_modified(ctx.usn);
|
||||
self.add_note_only_undoable(note)?;
|
||||
self.generate_cards_for_new_note(ctx, note, did)?;
|
||||
self.generate_cards_for_new_note(&ctx, note, did)?;
|
||||
self.set_last_deck_for_notetype(note.notetype_id, did)?;
|
||||
self.set_last_notetype_for_deck(did, note.notetype_id)?;
|
||||
self.set_current_notetype_id(note.notetype_id)
|
||||
|
|
|
@ -6,8 +6,10 @@ use crate::cloze::add_cloze_numbers_in_string;
|
|||
use crate::collection::Collection;
|
||||
use crate::decks::DeckId;
|
||||
use crate::error;
|
||||
use crate::error::AnkiError;
|
||||
use crate::error::OrInvalid;
|
||||
use crate::error::OrNotFound;
|
||||
use crate::notes::AddNoteRequest;
|
||||
use crate::notes::Note;
|
||||
use crate::notes::NoteId;
|
||||
use crate::prelude::IntoNewtypeVec;
|
||||
|
@ -39,6 +41,22 @@ impl crate::services::NotesService for Collection {
|
|||
})
|
||||
}
|
||||
|
||||
fn add_notes(
|
||||
&mut self,
|
||||
input: anki_proto::notes::AddNotesRequest,
|
||||
) -> error::Result<anki_proto::notes::AddNotesResponse> {
|
||||
let mut requests = input
|
||||
.requests
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<error::Result<Vec<AddNoteRequest>, AnkiError>>()?;
|
||||
let changes = self.add_notes(&mut requests)?;
|
||||
Ok(anki_proto::notes::AddNotesResponse {
|
||||
nids: requests.iter().map(|r| r.note.id.0).collect(),
|
||||
changes: Some(changes.into()),
|
||||
})
|
||||
}
|
||||
|
||||
fn defaults_for_adding(
|
||||
&mut self,
|
||||
input: anki_proto::notes::DefaultsForAddingRequest,
|
||||
|
|
Loading…
Reference in New Issue