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;
AddOrUpdateNotetypeIn add_or_update_notetype = 57;
Empty get_all_decks = 58;
// bytes set_all_decks = 59;
Empty check_database = 59;
Empty all_stock_notetypes = 60;
int64 get_notetype_legacy = 61;
Empty get_notetype_names = 62;
@ -140,7 +140,7 @@ message BackendOutput {
bytes get_changed_notetypes = 56;
int64 add_or_update_notetype = 57;
bytes get_all_decks = 58;
// Empty set_all_decks = 59;
Empty check_database = 59;
bytes get_notetype_legacy = 61;
NoteTypeNames get_notetype_names = 62;
NoteTypeUseCounts get_notetype_names_and_counts = 63;

View file

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

View file

@ -5,7 +5,7 @@ from __future__ import annotations
import copy
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.backend_pb2 as pb
@ -524,37 +524,8 @@ class DeckManager:
# )
# 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:
self._recoverOrphans()
self._checkDeckTree()
def should_deck_be_displayed(
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))
).deck_tree
def check_database(self) -> None:
self._run_command(pb.BackendInput(check_database=pb.Empty()))
def translate_string_in(
key: TR, **kwargs: Union[str, int, float]

View file

@ -356,6 +356,10 @@ impl Backend {
self.remove_deck(did)?;
pb::Empty {}
}),
Value::CheckDatabase(_) => {
self.check_database()?;
OValue::CheckDatabase(pb::Empty {})
}
})
}
@ -1046,6 +1050,10 @@ impl Backend {
fn remove_deck(&self, did: i64) -> Result<()> {
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 {

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 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
@ -80,6 +84,21 @@ impl Collection {
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)]

View file

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