diff --git a/proto/anki/notetypes.proto b/proto/anki/notetypes.proto index 4065e34ab..9bf0c7bb0 100644 --- a/proto/anki/notetypes.proto +++ b/proto/anki/notetypes.proto @@ -107,6 +107,7 @@ message Notetype { message AddOrUpdateNotetypeRequest { bytes json = 1; bool preserve_usn_and_mtime = 2; + bool skip_checks = 3; } message StockNotetype { diff --git a/pylib/anki/importing/anki2.py b/pylib/anki/importing/anki2.py index b295df974..cddacd787 100644 --- a/pylib/anki/importing/anki2.py +++ b/pylib/anki/importing/anki2.py @@ -235,7 +235,7 @@ class Anki2Importer(Importer): model = srcModel.copy() model["id"] = mid model["usn"] = self.col.usn() - self.dst.models.update(model) + self.dst.models.update(model, skip_checks=True) break # there's an existing model; do the schemas match? dstModel = self.dst.models.get(mid) @@ -246,7 +246,7 @@ class Anki2Importer(Importer): model = srcModel.copy() model["id"] = mid model["usn"] = self.col.usn() - self.dst.models.update(model) + self.dst.models.update(model, skip_checks=True) break # as they don't match, try next id mid = NotetypeId(mid + 1) diff --git a/pylib/anki/models.py b/pylib/anki/models.py index 889d749bd..932b461c1 100644 --- a/pylib/anki/models.py +++ b/pylib/anki/models.py @@ -527,12 +527,19 @@ and notes.mid = ? and cards.ord = ?""", pass # @deprecated(replaced_by=update_dict) - def update(self, notetype: NotetypeDict, preserve_usn: bool = True) -> None: + def update( + self, + notetype: NotetypeDict, + preserve_usn: bool = True, + skip_checks: bool = False, + ) -> None: "Add or update an existing model. Use .update_dict() instead." self._remove_from_cache(notetype["id"]) self.ensure_name_unique(notetype) notetype["id"] = self.col._backend.add_or_update_notetype( - json=to_json_bytes(notetype), preserve_usn_and_mtime=preserve_usn + json=to_json_bytes(notetype), + preserve_usn_and_mtime=preserve_usn, + skip_checks=skip_checks, ) self.set_current(notetype) self._mutate_after_write(notetype) diff --git a/rslib/src/backend/notetypes.rs b/rslib/src/backend/notetypes.rs index 9dfe9ab44..3e2e85047 100644 --- a/rslib/src/backend/notetypes.rs +++ b/rslib/src/backend/notetypes.rs @@ -17,7 +17,7 @@ impl NotetypesService for Backend { let mut notetype: Notetype = input.into(); self.with_col(|col| { Ok(col - .add_notetype(&mut notetype)? + .add_notetype(&mut notetype, false)? .map(|_| notetype.id.0) .into()) }) @@ -25,7 +25,7 @@ impl NotetypesService for Backend { fn update_notetype(&self, input: pb::Notetype) -> Result { let mut notetype: Notetype = input.into(); - self.with_col(|col| col.update_notetype(&mut notetype)) + self.with_col(|col| col.update_notetype(&mut notetype, false)) .map(Into::into) } @@ -34,7 +34,7 @@ impl NotetypesService for Backend { let mut notetype: Notetype = legacy.into(); self.with_col(|col| { Ok(col - .add_notetype(&mut notetype)? + .add_notetype(&mut notetype, false)? .map(|_| notetype.id.0) .into()) }) @@ -43,7 +43,7 @@ impl NotetypesService for Backend { fn update_notetype_legacy(&self, input: pb::Json) -> Result { let legacy: NotetypeSchema11 = serde_json::from_slice(&input.json)?; let mut notetype: Notetype = legacy.into(); - self.with_col(|col| col.update_notetype(&mut notetype)) + self.with_col(|col| col.update_notetype(&mut notetype, false)) .map(Into::into) } @@ -58,11 +58,11 @@ impl NotetypesService for Backend { nt.set_modified(col.usn()?); } if nt.id.0 == 0 { - col.add_notetype(&mut nt)?; + col.add_notetype(&mut nt, input.skip_checks)?; } else if !input.preserve_usn_and_mtime { - col.update_notetype(&mut nt)?; + col.update_notetype(&mut nt, input.skip_checks)?; } else { - col.add_or_update_notetype_with_existing_id(&mut nt)?; + col.add_or_update_notetype_with_existing_id(&mut nt, input.skip_checks)?; } Ok(pb::NotetypeId { ntid: nt.id.0 }) }) diff --git a/rslib/src/dbcheck.rs b/rslib/src/dbcheck.rs index 92d50e22e..46fb95c3a 100644 --- a/rslib/src/dbcheck.rs +++ b/rslib/src/dbcheck.rs @@ -273,7 +273,7 @@ impl Collection { // one note type exists if self.storage.get_all_notetype_names()?.is_empty() { let mut nt = all_stock_notetypes(&self.tr).remove(0); - self.add_notetype_inner(&mut nt, usn)?; + self.add_notetype_inner(&mut nt, usn, true)?; } if out.card_ords_duplicated > 0 @@ -367,7 +367,7 @@ impl Collection { for n in 0..extra_cards_required { basic.add_template(&format!("Card {}", n + 2), &qfmt, &afmt); } - self.add_notetype(&mut basic)?; + self.add_notetype(&mut basic, true)?; Ok(Arc::new(basic)) } diff --git a/rslib/src/notetype/mod.rs b/rslib/src/notetype/mod.rs index 1220372b3..82b59d910 100644 --- a/rslib/src/notetype/mod.rs +++ b/rslib/src/notetype/mod.rs @@ -117,11 +117,15 @@ impl Notetype { impl Collection { /// Add a new notetype, and allocate it an ID. - pub fn add_notetype(&mut self, notetype: &mut Notetype) -> Result> { + pub fn add_notetype( + &mut self, + notetype: &mut Notetype, + skip_checks: bool, + ) -> Result> { self.transact(Op::AddNotetype, |col| { let usn = col.usn()?; notetype.set_modified(usn); - col.add_notetype_inner(notetype, usn) + col.add_notetype_inner(notetype, usn, skip_checks) }) } @@ -130,7 +134,11 @@ impl Collection { /// /// This does not assign ordinals to the provided notetype, so if you wish /// to make use of template_idx, the notetype must be fetched again. - pub fn update_notetype(&mut self, notetype: &mut Notetype) -> Result> { + pub fn update_notetype( + &mut self, + notetype: &mut Notetype, + skip_checks: bool, + ) -> Result> { self.transact(Op::UpdateNotetype, |col| { let original = col .storage @@ -138,7 +146,12 @@ impl Collection { .ok_or(AnkiError::NotFound)?; let usn = col.usn()?; notetype.set_modified(usn); - col.add_or_update_notetype_with_existing_id_inner(notetype, Some(original), usn) + col.add_or_update_notetype_with_existing_id_inner( + notetype, + Some(original), + usn, + skip_checks, + ) }) } @@ -147,11 +160,12 @@ impl Collection { pub fn add_or_update_notetype_with_existing_id( &mut self, notetype: &mut Notetype, + skip_checks: bool, ) -> Result<()> { self.transact_no_undo(|col| { let usn = col.usn()?; let existing = col.storage.get_notetype(notetype.id)?; - col.add_or_update_notetype_with_existing_id_inner(notetype, existing, usn) + col.add_or_update_notetype_with_existing_id_inner(notetype, existing, usn, skip_checks) }) } @@ -419,7 +433,11 @@ impl Notetype { self.templates.push(CardTemplate::new(name, qfmt, afmt)); } - pub(crate) fn prepare_for_update(&mut self, existing: Option<&Notetype>) -> Result<()> { + pub(crate) fn prepare_for_update( + &mut self, + existing: Option<&Notetype>, + skip_checks: bool, + ) -> Result<()> { if self.fields.is_empty() { return Err(AnkiError::invalid_input("1 field required")); } @@ -450,9 +468,11 @@ impl Notetype { } } self.config.reqs = reqs; - self.ensure_template_fronts_unique()?; - self.ensure_valid_parsed_templates(&parsed_templates)?; - self.ensure_cloze_if_cloze_notetype(&parsed_templates)?; + if !skip_checks { + self.ensure_template_fronts_unique()?; + self.ensure_valid_parsed_templates(&parsed_templates)?; + self.ensure_cloze_if_cloze_notetype(&parsed_templates)?; + } Ok(()) } @@ -610,8 +630,13 @@ impl Collection { } /// Caller must set notetype as modified if appropriate. - pub(crate) fn add_notetype_inner(&mut self, notetype: &mut Notetype, usn: Usn) -> Result<()> { - notetype.prepare_for_update(None)?; + pub(crate) fn add_notetype_inner( + &mut self, + notetype: &mut Notetype, + usn: Usn, + skip_checks: bool, + ) -> Result<()> { + notetype.prepare_for_update(None, skip_checks)?; self.ensure_notetype_name_unique(notetype, usn)?; self.add_notetype_undoable(notetype)?; self.set_current_notetype_id(notetype.id) @@ -624,9 +649,10 @@ impl Collection { notetype: &mut Notetype, original: Option, usn: Usn, + skip_checks: bool, ) -> Result<()> { let normalize = self.get_config_bool(BoolKey::NormalizeNoteText); - notetype.prepare_for_update(original.as_ref())?; + notetype.prepare_for_update(original.as_ref(), skip_checks)?; self.ensure_notetype_name_unique(notetype, usn)?; if let Some(original) = original { @@ -671,7 +697,7 @@ impl Collection { let all = self.storage.get_all_notetype_names()?; if all.is_empty() { let mut nt = all_stock_notetypes(&self.tr).remove(0); - self.add_notetype_inner(&mut nt, self.usn()?)?; + self.add_notetype_inner(&mut nt, self.usn()?, true)?; self.set_current_notetype_id(nt.id) } else { self.set_current_notetype_id(all[0].0) diff --git a/rslib/src/notetype/notetypechange.rs b/rslib/src/notetype/notetypechange.rs index a0904dfb9..b173af8d3 100644 --- a/rslib/src/notetype/notetypechange.rs +++ b/rslib/src/notetype/notetypechange.rs @@ -394,7 +394,7 @@ mod test { basic.add_field("Text"); // 4 basic.add_field("idx5"); // re-fetch to get ordinals - col.update_notetype(&mut basic)?; + col.update_notetype(&mut basic, false)?; let basic = col.get_notetype(basic.id)?.unwrap(); // if names match, assignments are out of order; unmatched entries diff --git a/rslib/src/notetype/schemachange.rs b/rslib/src/notetype/schemachange.rs index b940f401f..88e065005 100644 --- a/rslib/src/notetype/schemachange.rs +++ b/rslib/src/notetype/schemachange.rs @@ -258,13 +258,13 @@ mod test { col.add_note(&mut note, DeckId(1))?; nt.add_field("three"); - col.update_notetype(&mut nt)?; + col.update_notetype(&mut nt, false)?; let note = col.storage.get_note(note.id)?.unwrap(); assert_eq!(note.fields(), &["one".to_string(), "two".into(), "".into()]); nt.fields.remove(1); - col.update_notetype(&mut nt)?; + col.update_notetype(&mut nt, false)?; let note = col.storage.get_note(note.id)?.unwrap(); assert_eq!(note.fields(), &["one".to_string(), "".into()]); @@ -281,13 +281,13 @@ mod test { .unwrap(); nt.templates[0].config.q_format += "\n{{#Front}}{{some:Front}}{{Back}}{{/Front}}"; nt.fields[0].name = "Test".into(); - col.update_notetype(&mut nt)?; + col.update_notetype(&mut nt, false)?; assert_eq!( &nt.templates[0].config.q_format, "{{Test}}\n{{#Test}}{{some:Test}}{{Back}}{{/Test}}" ); nt.fields.remove(0); - col.update_notetype(&mut nt)?; + col.update_notetype(&mut nt, false)?; assert_eq!(&nt.templates[0].config.q_format, "\n{{Back}}"); Ok(()) @@ -313,7 +313,7 @@ mod test { // add an extra card template nt.add_template("card 2", "{{Front}}2", ""); - col.update_notetype(&mut nt)?; + col.update_notetype(&mut nt, false)?; assert_eq!( col.search_cards(note.id, SortMode::NoOrder).unwrap().len(), diff --git a/rslib/src/notetype/stock.rs b/rslib/src/notetype/stock.rs index 268ec485d..58619889b 100644 --- a/rslib/src/notetype/stock.rs +++ b/rslib/src/notetype/stock.rs @@ -15,7 +15,7 @@ use crate::{ impl SqliteStorage { pub(crate) fn add_stock_notetypes(&self, tr: &I18n) -> Result<()> { for (idx, mut nt) in all_stock_notetypes(tr).into_iter().enumerate() { - nt.prepare_for_update(None)?; + nt.prepare_for_update(None, true)?; self.add_notetype(&mut nt)?; if idx == Kind::Basic as usize { self.set_config_entry(&ConfigEntry::boxed( diff --git a/rslib/src/sync/mod.rs b/rslib/src/sync/mod.rs index c802f2bc2..e6c538326 100644 --- a/rslib/src/sync/mod.rs +++ b/rslib/src/sync/mod.rs @@ -1386,7 +1386,7 @@ mod test { // and a new notetype let mut nt = all_stock_notetypes(&col1.tr).remove(0); nt.name = "new".into(); - col1.add_notetype(&mut nt)?; + col1.add_notetype(&mut nt, false)?; // add another note+card+tag let mut note = nt.new_note(); @@ -1502,7 +1502,7 @@ mod test { let mut nt = col2.storage.get_notetype(nt.id)?.unwrap(); nt.name = "newer".into(); - col2.update_notetype(&mut nt)?; + col2.update_notetype(&mut nt, false)?; // sync the changes back let out = ctx.normal_sync(&mut col2).await;