mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
Add notes immediately instaed of preparing
This commit is contained in:
parent
b9c19273fc
commit
94a6cdd6ed
4 changed files with 55 additions and 44 deletions
|
@ -7,6 +7,7 @@ use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{self},
|
io::{self},
|
||||||
mem,
|
mem,
|
||||||
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use sha1::Sha1;
|
use sha1::Sha1;
|
||||||
|
@ -35,7 +36,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>,
|
||||||
existing_notes: HashSet<NoteId>,
|
remapped_notes: HashMap<NoteId, NoteId>,
|
||||||
data: ExchangeData,
|
data: ExchangeData,
|
||||||
usn: Usn,
|
usn: Usn,
|
||||||
/// Map of source media files, that do not already exist in the target.
|
/// Map of source media files, that do not already exist in the target.
|
||||||
|
@ -44,6 +45,7 @@ struct Context<'a> {
|
||||||
/// 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)>,
|
||||||
conflicting_notes: HashSet<String>,
|
conflicting_notes: HashSet<String>,
|
||||||
|
normalize_notes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -61,10 +63,6 @@ impl NoteMeta {
|
||||||
notetype_id,
|
notetype_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_note(note: &Note) -> Self {
|
|
||||||
Self::new(note.id, note.mtime, note.notetype_id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SafeMediaEntry {
|
impl SafeMediaEntry {
|
||||||
|
@ -95,8 +93,8 @@ impl Collection {
|
||||||
|
|
||||||
let mut ctx = Context::new(archive, self, search, with_scheduling)?;
|
let mut ctx = Context::new(archive, self, search, with_scheduling)?;
|
||||||
ctx.prepare_media()?;
|
ctx.prepare_media()?;
|
||||||
ctx.prepare_notetypes()?;
|
ctx.import_notetypes()?;
|
||||||
ctx.prepare_notes()?;
|
ctx.import_notes()?;
|
||||||
|
|
||||||
ctx.target_col.insert_data(&ctx.data)?;
|
ctx.target_col.insert_data(&ctx.data)?;
|
||||||
ctx.copy_media()?;
|
ctx.copy_media()?;
|
||||||
|
@ -136,18 +134,19 @@ impl<'a> Context<'a> {
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let data = ExchangeData::gather_from_archive(&mut archive, search, with_scheduling)?;
|
let data = ExchangeData::gather_from_archive(&mut archive, search, with_scheduling)?;
|
||||||
let guid_map = target_col.storage.note_guid_map()?;
|
let guid_map = target_col.storage.note_guid_map()?;
|
||||||
let existing_notes = target_col.storage.get_all_note_ids()?;
|
|
||||||
let usn = target_col.usn()?;
|
let usn = target_col.usn()?;
|
||||||
|
let normalize_notes = target_col.get_config_bool(BoolKey::NormalizeNoteText);
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
target_col,
|
target_col,
|
||||||
archive,
|
archive,
|
||||||
data,
|
data,
|
||||||
guid_map,
|
guid_map,
|
||||||
existing_notes,
|
|
||||||
usn,
|
usn,
|
||||||
conflicting_notes: HashSet::new(),
|
conflicting_notes: HashSet::new(),
|
||||||
remapped_notetypes: HashMap::new(),
|
remapped_notetypes: HashMap::new(),
|
||||||
|
remapped_notes: HashMap::new(),
|
||||||
used_media_entries: HashMap::new(),
|
used_media_entries: HashMap::new(),
|
||||||
|
normalize_notes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +168,7 @@ impl<'a> Context<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_notetypes(&mut self) -> Result<()> {
|
fn import_notetypes(&mut self) -> Result<()> {
|
||||||
for mut notetype in std::mem::take(&mut self.data.notetypes) {
|
for mut notetype in std::mem::take(&mut self.data.notetypes) {
|
||||||
if let Some(existing) = self.target_col.storage.get_notetype(notetype.id)? {
|
if let Some(existing) = self.target_col.storage.get_notetype(notetype.id)? {
|
||||||
self.merge_or_remap_notetype(&mut notetype, existing)?;
|
self.merge_or_remap_notetype(&mut notetype, existing)?;
|
||||||
|
@ -204,7 +203,6 @@ impl<'a> Context<'a> {
|
||||||
|
|
||||||
fn update_notetype(&mut self, notetype: &mut Notetype, original: Notetype) -> Result<()> {
|
fn update_notetype(&mut self, notetype: &mut Notetype, original: Notetype) -> Result<()> {
|
||||||
notetype.usn = self.usn;
|
notetype.usn = self.usn;
|
||||||
// TODO: make undoable
|
|
||||||
self.target_col
|
self.target_col
|
||||||
.add_or_update_notetype_with_existing_id_inner(notetype, Some(original), self.usn, true)
|
.add_or_update_notetype_with_existing_id_inner(notetype, Some(original), self.usn, true)
|
||||||
}
|
}
|
||||||
|
@ -218,46 +216,56 @@ impl<'a> Context<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_notes(&mut self) -> Result<()> {
|
fn import_notes(&mut self) -> Result<()> {
|
||||||
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) {
|
||||||
todo!("ignore");
|
self.conflicting_notes.insert(note.guid);
|
||||||
|
// TODO: Log ignore
|
||||||
} else {
|
} else {
|
||||||
note.notetype_id = *notetype_id;
|
note.notetype_id = *notetype_id;
|
||||||
self.prepare_new_note(note)?;
|
self.add_note(&mut note)?;
|
||||||
}
|
}
|
||||||
} else if let Some(&meta) = self.guid_map.get(¬e.guid) {
|
} else if let Some(&meta) = self.guid_map.get(¬e.guid) {
|
||||||
self.prepare_existing_note(note, meta)?;
|
self.maybe_update_note(note, meta)?;
|
||||||
} else {
|
} else {
|
||||||
self.prepare_new_note(note)?;
|
self.add_note(&mut note)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_prepared_note(&mut self, mut note: Note) -> Result<()> {
|
fn add_note(&mut self, mut note: &mut Note) -> Result<()> {
|
||||||
self.munge_media(&mut note)?;
|
|
||||||
note.usn = self.usn;
|
|
||||||
self.data.notes.push(note);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepare_new_note(&mut self, mut note: Note) -> Result<()> {
|
|
||||||
self.to_next_available_note_id(&mut note.id);
|
|
||||||
self.existing_notes.insert(note.id);
|
|
||||||
self.guid_map
|
|
||||||
.insert(note.guid.clone(), NoteMeta::from_note(¬e));
|
|
||||||
self.add_prepared_note(note)
|
|
||||||
// TODO: Log add
|
// TODO: Log add
|
||||||
|
self.munge_media(note)?;
|
||||||
|
self.target_col.canonify_note_tags(note, self.usn)?;
|
||||||
|
let notetype = self.get_expected_notetype(note.notetype_id)?;
|
||||||
|
note.prepare_for_update(¬etype, self.normalize_notes)?;
|
||||||
|
note.usn = self.usn;
|
||||||
|
let old_id = std::mem::take(&mut note.id);
|
||||||
|
self.target_col.add_note_only_undoable(note)?;
|
||||||
|
self.remapped_notes.insert(old_id, note.id);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_existing_note(&mut self, mut note: Note, meta: NoteMeta) -> Result<()> {
|
fn get_expected_notetype(&mut self, ntid: NotetypeId) -> Result<Arc<Notetype>> {
|
||||||
|
self.target_col
|
||||||
|
.get_notetype(ntid)?
|
||||||
|
.ok_or(AnkiError::NotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_expected_note(&mut self, nid: NoteId) -> Result<Note> {
|
||||||
|
self.target_col
|
||||||
|
.storage
|
||||||
|
.get_note(nid)?
|
||||||
|
.ok_or(AnkiError::NotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maybe_update_note(&mut self, mut note: 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 {
|
||||||
note.id = meta.id;
|
note.id = meta.id;
|
||||||
self.add_prepared_note(note)?;
|
self.update_note(&mut note)?;
|
||||||
// TODO: Log update
|
|
||||||
} else {
|
} else {
|
||||||
self.conflicting_notes.insert(note.guid);
|
self.conflicting_notes.insert(note.guid);
|
||||||
// TODO: Log ignore
|
// TODO: Log ignore
|
||||||
|
@ -268,6 +276,22 @@ impl<'a> Context<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_note(&mut self, note: &mut Note) -> Result<()> {
|
||||||
|
// TODO: Log update
|
||||||
|
self.munge_media(note)?;
|
||||||
|
let original = self.get_expected_note(note.id)?;
|
||||||
|
let notetype = self.get_expected_notetype(note.notetype_id)?;
|
||||||
|
self.target_col.update_note_inner_without_cards(
|
||||||
|
note,
|
||||||
|
&original,
|
||||||
|
¬etype,
|
||||||
|
self.usn,
|
||||||
|
true,
|
||||||
|
self.normalize_notes,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn munge_media(&mut self, note: &mut Note) -> Result<()> {
|
fn munge_media(&mut self, note: &mut Note) -> Result<()> {
|
||||||
for field in note.fields_mut() {
|
for field in note.fields_mut() {
|
||||||
if let Some(new_field) = self.replace_media_refs(field) {
|
if let Some(new_field) = self.replace_media_refs(field) {
|
||||||
|
@ -295,12 +319,6 @@ impl<'a> Context<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_next_available_note_id(&self, note_id: &mut NoteId) {
|
|
||||||
while self.existing_notes.contains(note_id) {
|
|
||||||
note_id.0 += 999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
|
|
|
@ -320,7 +320,7 @@ fn invalid_char_for_field(c: char) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Collection {
|
impl Collection {
|
||||||
fn canonify_note_tags(&mut self, note: &mut Note, usn: Usn) -> Result<()> {
|
pub(crate) fn canonify_note_tags(&mut self, note: &mut Note, usn: Usn) -> Result<()> {
|
||||||
if !note.tags.is_empty() {
|
if !note.tags.is_empty() {
|
||||||
let tags = std::mem::take(&mut note.tags);
|
let tags = std::mem::take(&mut note.tags);
|
||||||
note.tags = self.canonify_tags(tags, usn)?.0;
|
note.tags = self.canonify_tags(tags, usn)?.0;
|
||||||
|
|
|
@ -83,7 +83,7 @@ impl Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a note, not adding any cards.
|
/// Add a note, not adding any cards.
|
||||||
pub(super) fn add_note_only_undoable(&mut self, note: &mut Note) -> Result<(), AnkiError> {
|
pub(crate) fn add_note_only_undoable(&mut self, note: &mut Note) -> Result<(), AnkiError> {
|
||||||
self.storage.add_note(note)?;
|
self.storage.add_note(note)?;
|
||||||
self.save_undo(UndoableNoteChange::Added(Box::new(note.clone())));
|
self.save_undo(UndoableNoteChange::Added(Box::new(note.clone())));
|
||||||
|
|
||||||
|
|
|
@ -42,13 +42,6 @@ impl super::SqliteStorage {
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_note_ids(&self) -> Result<HashSet<NoteId>> {
|
|
||||||
self.db
|
|
||||||
.prepare("SELECT id FROM notes")?
|
|
||||||
.query_and_then([], |row| Ok(row.get(0)?))?
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If fields have been modified, caller must call note.prepare_for_update() prior to calling this.
|
/// If fields have been modified, caller must call note.prepare_for_update() prior to calling this.
|
||||||
pub(crate) fn update_note(&self, note: &Note) -> Result<()> {
|
pub(crate) fn update_note(&self, note: &Note) -> Result<()> {
|
||||||
assert!(note.id.0 != 0);
|
assert!(note.id.0 != 0);
|
||||||
|
|
Loading…
Reference in a new issue