mirror of
https://github.com/ankitects/anki.git
synced 2025-12-01 00:47:12 -05:00
Refactor card and revlog importing
This commit is contained in:
parent
8348240f80
commit
a402879e72
2 changed files with 45 additions and 52 deletions
|
|
@ -11,14 +11,14 @@ use crate::{
|
||||||
card::{CardQueue, CardType},
|
card::{CardQueue, CardType},
|
||||||
config::SchedulerVersion,
|
config::SchedulerVersion,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
revlog::RevlogEntry,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CardContext<'a> {
|
struct CardContext<'a> {
|
||||||
target_col: &'a mut Collection,
|
target_col: &'a mut Collection,
|
||||||
usn: Usn,
|
usn: Usn,
|
||||||
|
|
||||||
conflicting_notes: &'a HashSet<NoteId>,
|
imported_notes: &'a HashMap<NoteId, NoteId>,
|
||||||
remapped_notes: &'a HashMap<NoteId, NoteId>,
|
|
||||||
remapped_decks: &'a HashMap<DeckId, DeckId>,
|
remapped_decks: &'a HashMap<DeckId, DeckId>,
|
||||||
|
|
||||||
/// The number of days the source collection is ahead of the target collection
|
/// The number of days the source collection is ahead of the target collection
|
||||||
|
|
@ -37,9 +37,8 @@ impl<'c> CardContext<'c> {
|
||||||
usn: Usn,
|
usn: Usn,
|
||||||
days_elapsed: u32,
|
days_elapsed: u32,
|
||||||
target_col: &'a mut Collection,
|
target_col: &'a mut Collection,
|
||||||
remapped_notes: &'a HashMap<NoteId, NoteId>,
|
imported_notes: &'a HashMap<NoteId, NoteId>,
|
||||||
remapped_decks: &'a HashMap<DeckId, DeckId>,
|
remapped_decks: &'a HashMap<DeckId, DeckId>,
|
||||||
conflicting_notes: &'a HashSet<NoteId>,
|
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let targets = target_col.storage.all_cards_as_nid_and_ord()?;
|
let targets = target_col.storage.all_cards_as_nid_and_ord()?;
|
||||||
let collection_delta = target_col.collection_delta(days_elapsed)?;
|
let collection_delta = target_col.collection_delta(days_elapsed)?;
|
||||||
|
|
@ -48,8 +47,7 @@ impl<'c> CardContext<'c> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
target_col,
|
target_col,
|
||||||
usn,
|
usn,
|
||||||
conflicting_notes,
|
imported_notes,
|
||||||
remapped_notes,
|
|
||||||
remapped_decks,
|
remapped_decks,
|
||||||
targets,
|
targets,
|
||||||
collection_delta,
|
collection_delta,
|
||||||
|
|
@ -68,34 +66,54 @@ impl Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
pub(super) fn import_cards(&mut self) -> Result<HashMap<CardId, CardId>> {
|
pub(super) fn import_cards_and_revlog(&mut self) -> Result<()> {
|
||||||
let mut ctx = CardContext::new(
|
let mut ctx = CardContext::new(
|
||||||
self.usn,
|
self.usn,
|
||||||
self.data.days_elapsed,
|
self.data.days_elapsed,
|
||||||
self.target_col,
|
self.target_col,
|
||||||
&self.remapped_notes,
|
&self.imported_notes,
|
||||||
&self.remapped_decks,
|
&self.remapped_decks,
|
||||||
&self.conflicting_notes,
|
|
||||||
)?;
|
)?;
|
||||||
ctx.import_cards(mem::take(&mut self.data.cards))?;
|
ctx.import_cards(mem::take(&mut self.data.cards))?;
|
||||||
Ok(ctx.imported_cards)
|
ctx.import_revlog(mem::take(&mut self.data.revlog))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CardContext<'_> {
|
impl CardContext<'_> {
|
||||||
pub(super) fn import_cards(&mut self, mut cards: Vec<Card>) -> Result<()> {
|
fn import_cards(&mut self, mut cards: Vec<Card>) -> Result<()> {
|
||||||
for card in &mut cards {
|
for card in &mut cards {
|
||||||
if !self.conflicting_notes.contains(&card.note_id) {
|
if self.map_to_imported_note(card) && !self.target_already_exists(card) {
|
||||||
self.remap_note_id(card);
|
|
||||||
if !self.targets.contains(&(card.note_id, card.template_idx)) {
|
|
||||||
self.add_card(card)?;
|
self.add_card(card)?;
|
||||||
}
|
}
|
||||||
// TODO: maybe update
|
// TODO: could update existing card
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_revlog(&mut self, revlog: Vec<RevlogEntry>) -> Result<()> {
|
||||||
|
for mut entry in revlog {
|
||||||
|
if let Some(cid) = self.imported_cards.get(&entry.cid) {
|
||||||
|
entry.cid = *cid;
|
||||||
|
entry.usn = self.usn;
|
||||||
|
self.target_col.add_revlog_entry_if_unique_undoable(entry)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn map_to_imported_note(&self, card: &mut Card) -> bool {
|
||||||
|
if let Some(nid) = self.imported_notes.get(&card.note_id) {
|
||||||
|
card.note_id = *nid;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_already_exists(&self, card: &Card) -> bool {
|
||||||
|
self.targets.contains(&(card.note_id, card.template_idx))
|
||||||
|
}
|
||||||
|
|
||||||
fn add_card(&mut self, card: &mut Card) -> Result<()> {
|
fn add_card(&mut self, card: &mut Card) -> Result<()> {
|
||||||
card.usn = self.usn;
|
card.usn = self.usn;
|
||||||
self.remap_deck_id(card);
|
self.remap_deck_id(card);
|
||||||
|
|
@ -110,12 +128,6 @@ impl CardContext<'_> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remap_note_id(&self, card: &mut Card) {
|
|
||||||
if let Some(nid) = self.remapped_notes.get(&card.note_id) {
|
|
||||||
card.note_id = *nid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn uniquify_card_id(&mut self, card: &mut Card) -> CardId {
|
fn uniquify_card_id(&mut self, card: &mut Card) -> CardId {
|
||||||
let original = card.id;
|
let original = card.id;
|
||||||
while self.target_ids.contains(&card.id) {
|
while self.target_ids.contains(&card.id) {
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ struct Context<'a> {
|
||||||
archive: ZipArchive<File>,
|
archive: ZipArchive<File>,
|
||||||
guid_map: HashMap<String, NoteMeta>,
|
guid_map: HashMap<String, NoteMeta>,
|
||||||
remapped_notetypes: HashMap<NotetypeId, NotetypeId>,
|
remapped_notetypes: HashMap<NotetypeId, NotetypeId>,
|
||||||
remapped_notes: HashMap<NoteId, NoteId>,
|
imported_notes: HashMap<NoteId, NoteId>,
|
||||||
existing_notes: HashSet<NoteId>,
|
existing_notes: HashSet<NoteId>,
|
||||||
remapped_decks: HashMap<DeckId, DeckId>,
|
remapped_decks: HashMap<DeckId, DeckId>,
|
||||||
data: ExchangeData,
|
data: ExchangeData,
|
||||||
|
|
@ -50,10 +50,6 @@ struct Context<'a> {
|
||||||
/// original, normalized file name → (refererenced on import material,
|
/// original, normalized file name → (refererenced on import material,
|
||||||
/// entry with possibly remapped file name)
|
/// entry with possibly remapped file name)
|
||||||
used_media_entries: HashMap<String, (bool, SafeMediaEntry)>,
|
used_media_entries: HashMap<String, (bool, SafeMediaEntry)>,
|
||||||
/// Source notes that cannot be imported, because notes with the same guid
|
|
||||||
/// exist in the target, but their notetypes don't match.
|
|
||||||
conflicting_notes: HashSet<NoteId>,
|
|
||||||
remapped_cards: HashMap<CardId, CardId>,
|
|
||||||
normalize_notes: bool,
|
normalize_notes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,12 +135,10 @@ impl<'a> Context<'a> {
|
||||||
data,
|
data,
|
||||||
guid_map,
|
guid_map,
|
||||||
usn,
|
usn,
|
||||||
conflicting_notes: HashSet::new(),
|
|
||||||
remapped_notetypes: HashMap::new(),
|
remapped_notetypes: HashMap::new(),
|
||||||
remapped_notes: HashMap::new(),
|
imported_notes: HashMap::new(),
|
||||||
existing_notes,
|
existing_notes,
|
||||||
remapped_decks: HashMap::new(),
|
remapped_decks: HashMap::new(),
|
||||||
remapped_cards: HashMap::new(),
|
|
||||||
used_media_entries: HashMap::new(),
|
used_media_entries: HashMap::new(),
|
||||||
normalize_notes,
|
normalize_notes,
|
||||||
})
|
})
|
||||||
|
|
@ -156,8 +150,7 @@ impl<'a> Context<'a> {
|
||||||
self.import_notes()?;
|
self.import_notes()?;
|
||||||
self.import_deck_configs()?;
|
self.import_deck_configs()?;
|
||||||
self.import_decks()?;
|
self.import_decks()?;
|
||||||
self.import_cards()?;
|
self.import_cards_and_revlog()?;
|
||||||
self.import_revlog()?;
|
|
||||||
self.copy_media()
|
self.copy_media()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,7 +226,6 @@ impl<'a> Context<'a> {
|
||||||
for mut note in mem::take(&mut self.data.notes) {
|
for mut note in mem::take(&mut self.data.notes) {
|
||||||
if let Some(notetype_id) = self.remapped_notetypes.get(¬e.notetype_id) {
|
if let Some(notetype_id) = self.remapped_notetypes.get(¬e.notetype_id) {
|
||||||
if self.guid_map.contains_key(¬e.guid) {
|
if self.guid_map.contains_key(¬e.guid) {
|
||||||
self.conflicting_notes.insert(note.id);
|
|
||||||
// TODO: Log ignore
|
// TODO: Log ignore
|
||||||
} else {
|
} else {
|
||||||
note.notetype_id = *notetype_id;
|
note.notetype_id = *notetype_id;
|
||||||
|
|
@ -255,21 +247,21 @@ impl<'a> Context<'a> {
|
||||||
let notetype = self.get_expected_notetype(note.notetype_id)?;
|
let notetype = self.get_expected_notetype(note.notetype_id)?;
|
||||||
note.prepare_for_update(¬etype, self.normalize_notes)?;
|
note.prepare_for_update(¬etype, self.normalize_notes)?;
|
||||||
note.usn = self.usn;
|
note.usn = self.usn;
|
||||||
self.uniquify_note_id(note);
|
let old_id = self.uniquify_note_id(note);
|
||||||
|
|
||||||
self.target_col.add_note_only_with_id_undoable(note)?;
|
self.target_col.add_note_only_with_id_undoable(note)?;
|
||||||
self.existing_notes.insert(note.id);
|
self.existing_notes.insert(note.id);
|
||||||
|
self.imported_notes.insert(old_id, note.id);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uniquify_note_id(&mut self, note: &mut Note) {
|
fn uniquify_note_id(&mut self, note: &mut Note) -> NoteId {
|
||||||
let original = note.id;
|
let original = note.id;
|
||||||
while self.existing_notes.contains(¬e.id) {
|
while self.existing_notes.contains(¬e.id) {
|
||||||
note.id.0 += 999;
|
note.id.0 += 999;
|
||||||
}
|
}
|
||||||
if original != note.id {
|
original
|
||||||
self.remapped_notes.insert(original, note.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_expected_notetype(&mut self, ntid: NotetypeId) -> Result<Arc<Notetype>> {
|
fn get_expected_notetype(&mut self, ntid: NotetypeId) -> Result<Arc<Notetype>> {
|
||||||
|
|
@ -288,15 +280,15 @@ impl<'a> Context<'a> {
|
||||||
fn maybe_update_note(&mut self, note: &mut Note, meta: NoteMeta) -> Result<()> {
|
fn maybe_update_note(&mut self, note: &mut Note, meta: NoteMeta) -> Result<()> {
|
||||||
if meta.mtime < note.mtime {
|
if meta.mtime < note.mtime {
|
||||||
if meta.notetype_id == note.notetype_id {
|
if meta.notetype_id == note.notetype_id {
|
||||||
self.remapped_notes.insert(note.id, meta.id);
|
self.imported_notes.insert(note.id, meta.id);
|
||||||
note.id = meta.id;
|
note.id = meta.id;
|
||||||
self.update_note(note)?;
|
self.update_note(note)?;
|
||||||
} else {
|
} else {
|
||||||
self.conflicting_notes.insert(note.id);
|
|
||||||
// TODO: Log ignore
|
// TODO: Log ignore
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: Log duplicate
|
// TODO: Log duplicate
|
||||||
|
self.imported_notes.insert(note.id, meta.id);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -396,17 +388,6 @@ impl<'a> Context<'a> {
|
||||||
.get_deck_by_name(deck.name.as_native_str())
|
.get_deck_by_name(deck.name.as_native_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_revlog(&mut self) -> Result<()> {
|
|
||||||
for mut entry in mem::take(&mut self.data.revlog) {
|
|
||||||
if let Some(cid) = self.remapped_cards.get(&entry.cid) {
|
|
||||||
entry.cid = *cid;
|
|
||||||
entry.usn = self.usn;
|
|
||||||
self.target_col.add_revlog_entry_if_unique_undoable(entry)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn copy_media(&mut self) -> Result<()> {
|
fn copy_media(&mut self) -> Result<()> {
|
||||||
for (used, entry) in self.used_media_entries.values() {
|
for (used, entry) in self.used_media_entries.values() {
|
||||||
if *used {
|
if *used {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue