add missing decks in backend

- need to compare parents with unicode case folding
- duplicate check enforced by the DB
This commit is contained in:
Damien Elmes 2020-05-03 12:24:18 +10:00
parent 67421e02ec
commit 5fb5338d97
8 changed files with 49 additions and 33 deletions

View file

@ -71,7 +71,7 @@ message BackendInput {
int32 get_changed_notetypes = 56; int32 get_changed_notetypes = 56;
AddOrUpdateNotetypeIn add_or_update_notetype = 57; AddOrUpdateNotetypeIn add_or_update_notetype = 57;
Empty get_all_decks = 58; Empty get_all_decks = 58;
// bytes set_all_decks = 59; Empty check_database = 59;
Empty all_stock_notetypes = 60; Empty all_stock_notetypes = 60;
int64 get_notetype_legacy = 61; int64 get_notetype_legacy = 61;
Empty get_notetype_names = 62; Empty get_notetype_names = 62;
@ -140,7 +140,7 @@ message BackendOutput {
bytes get_changed_notetypes = 56; bytes get_changed_notetypes = 56;
int64 add_or_update_notetype = 57; int64 add_or_update_notetype = 57;
bytes get_all_decks = 58; bytes get_all_decks = 58;
// Empty set_all_decks = 59; Empty check_database = 59;
bytes get_notetype_legacy = 61; bytes get_notetype_legacy = 61;
NoteTypeNames get_notetype_names = 62; NoteTypeNames get_notetype_names = 62;
NoteTypeUseCounts get_notetype_names_and_counts = 63; NoteTypeUseCounts get_notetype_names_and_counts = 63;

View file

@ -958,6 +958,8 @@ and type=0 and queue!=4""",
# models # models
if self.models.ensureNotEmpty(): if self.models.ensureNotEmpty():
problems.append("Added missing note type.") problems.append("Added missing note type.")
# misc other
self.backend.check_database()
# and finally, optimize # and finally, optimize
self.optimize() self.optimize()
newSize = os.stat(self.path)[stat.ST_SIZE] newSize = os.stat(self.path)[stat.ST_SIZE]

View file

@ -5,7 +5,7 @@ from __future__ import annotations
import copy import copy
import unicodedata import unicodedata
from typing import Any, Dict, List, Optional, Set, Tuple, Union from typing import Any, Dict, List, Optional, Tuple, Union
import anki # pylint: disable=unused-import import anki # pylint: disable=unused-import
import anki.backend_pb2 as pb import anki.backend_pb2 as pb
@ -524,37 +524,8 @@ class DeckManager:
# ) # )
# self.col.db.mod = mod # self.col.db.mod = mod
def _checkDeckTree(self) -> None:
decks = self.all()
decks.sort(key=self.key)
names: Set[str] = set()
for deck in decks:
# two decks with the same name?
if deck["name"] in names:
self.col.log("fix duplicate deck name", deck["name"])
deck["name"] += "%d" % intTime(1000)
self.save(deck)
# ensure no sections are blank
if not all(self.path(deck["name"])):
self.col.log("fix deck with missing sections", deck["name"])
deck["name"] = "recovered%d" % intTime(1000)
self.save(deck)
# immediate parent must exist
if "::" in deck["name"]:
immediateParent = self.immediate_parent(deck["name"])
if immediateParent not in names:
self.col.log("fix deck with missing parent", deck["name"])
self._ensureParents(deck["name"])
names.add(immediateParent)
names.add(deck["name"])
def checkIntegrity(self) -> None: def checkIntegrity(self) -> None:
self._recoverOrphans() self._recoverOrphans()
self._checkDeckTree()
def should_deck_be_displayed( def should_deck_be_displayed(
self, deck, force_default: bool = True, assume_no_child: bool = False self, deck, force_default: bool = True, assume_no_child: bool = False

View file

@ -735,6 +735,9 @@ class RustBackend:
pb.BackendInput(deck_tree=pb.DeckTreeIn(include_counts=include_counts)) pb.BackendInput(deck_tree=pb.DeckTreeIn(include_counts=include_counts))
).deck_tree ).deck_tree
def check_database(self) -> None:
self._run_command(pb.BackendInput(check_database=pb.Empty()))
def translate_string_in( def translate_string_in(
key: TR, **kwargs: Union[str, int, float] key: TR, **kwargs: Union[str, int, float]

View file

@ -356,6 +356,10 @@ impl Backend {
self.remove_deck(did)?; self.remove_deck(did)?;
pb::Empty {} pb::Empty {}
}), }),
Value::CheckDatabase(_) => {
self.check_database()?;
OValue::CheckDatabase(pb::Empty {})
}
}) })
} }
@ -1046,6 +1050,10 @@ impl Backend {
fn remove_deck(&self, did: i64) -> Result<()> { fn remove_deck(&self, did: i64) -> Result<()> {
self.with_col(|col| col.remove_deck_and_child_decks(DeckID(did))) self.with_col(|col| col.remove_deck_and_child_decks(DeckID(did)))
} }
fn check_database(&self) -> Result<()> {
self.with_col(|col| col.transact(None, |col| col.check_database()))
}
} }
fn translate_arg_to_fluent_val(arg: &pb::TranslateArgValue) -> FluentValue { fn translate_arg_to_fluent_val(arg: &pb::TranslateArgValue) -> FluentValue {

12
rslib/src/dbcheck.rs Normal file
View file

@ -0,0 +1,12 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use crate::{collection::Collection, err::Result};
impl Collection {
pub(crate) fn check_database(&mut self) -> Result<()> {
let names = self.storage.get_all_deck_names()?;
self.add_missing_decks(&names)?;
Ok(())
}
}

View file

@ -3,7 +3,11 @@
use super::Deck; use super::Deck;
use crate::{backend_proto::DeckTreeNode, collection::Collection, decks::DeckID, err::Result}; use crate::{backend_proto::DeckTreeNode, collection::Collection, decks::DeckID, err::Result};
use std::{collections::HashMap, iter::Peekable}; use std::{
collections::{HashMap, HashSet},
iter::Peekable,
};
use unicase::UniCase;
// fixme: handle mixed case of parents // fixme: handle mixed case of parents
@ -80,6 +84,21 @@ impl Collection {
Ok(tree) Ok(tree)
} }
pub(crate) fn add_missing_decks(&mut self, names: &[(DeckID, String)]) -> Result<()> {
let mut parents = HashSet::new();
for (_id, name) in names {
parents.insert(UniCase::new(name.as_str()));
if let Some(immediate_parent) = name.rsplitn(2, "::").nth(1) {
let immediate_parent_uni = UniCase::new(immediate_parent);
if !parents.contains(&immediate_parent_uni) {
self.get_or_create_normal_deck(immediate_parent)?;
parents.insert(immediate_parent_uni);
}
}
}
Ok(())
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -14,6 +14,7 @@ pub mod card;
pub mod cloze; pub mod cloze;
pub mod collection; pub mod collection;
pub mod config; pub mod config;
pub mod dbcheck;
pub mod deckconf; pub mod deckconf;
pub mod decks; pub mod decks;
pub mod err; pub mod err;