Factor out card importing

This commit is contained in:
RumovZ 2022-04-10 10:36:42 +02:00
parent e1899152be
commit 8348240f80
2 changed files with 164 additions and 87 deletions

View file

@ -0,0 +1,162 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use std::{
collections::{HashMap, HashSet},
mem,
};
use super::Context;
use crate::{
card::{CardQueue, CardType},
config::SchedulerVersion,
prelude::*,
};
struct CardContext<'a> {
target_col: &'a mut Collection,
usn: Usn,
conflicting_notes: &'a HashSet<NoteId>,
remapped_notes: &'a HashMap<NoteId, NoteId>,
remapped_decks: &'a HashMap<DeckId, DeckId>,
/// The number of days the source collection is ahead of the target collection
collection_delta: i32,
scheduler_version: SchedulerVersion,
/// Cards in the target collection as (nid, ord)
targets: HashSet<(NoteId, u16)>,
/// All card ids existing in the target collection
target_ids: HashSet<CardId>,
imported_cards: HashMap<CardId, CardId>,
}
impl<'c> CardContext<'c> {
fn new<'a: 'c>(
usn: Usn,
days_elapsed: u32,
target_col: &'a mut Collection,
remapped_notes: &'a HashMap<NoteId, NoteId>,
remapped_decks: &'a HashMap<DeckId, DeckId>,
conflicting_notes: &'a HashSet<NoteId>,
) -> Result<Self> {
let targets = target_col.storage.all_cards_as_nid_and_ord()?;
let collection_delta = target_col.collection_delta(days_elapsed)?;
let scheduler_version = target_col.scheduler_info()?.version;
let target_ids = target_col.storage.get_all_card_ids()?;
Ok(Self {
target_col,
usn,
conflicting_notes,
remapped_notes,
remapped_decks,
targets,
collection_delta,
scheduler_version,
target_ids,
imported_cards: HashMap::new(),
})
}
}
impl Collection {
/// How much `days_elapsed` is ahead of this collection.
fn collection_delta(&mut self, days_elapsed: u32) -> Result<i32> {
Ok(days_elapsed as i32 - self.timing_today()?.days_elapsed as i32)
}
}
impl<'a> Context<'a> {
pub(super) fn import_cards(&mut self) -> Result<HashMap<CardId, CardId>> {
let mut ctx = CardContext::new(
self.usn,
self.data.days_elapsed,
self.target_col,
&self.remapped_notes,
&self.remapped_decks,
&self.conflicting_notes,
)?;
ctx.import_cards(mem::take(&mut self.data.cards))?;
Ok(ctx.imported_cards)
}
}
impl CardContext<'_> {
pub(super) fn import_cards(&mut self, mut cards: Vec<Card>) -> Result<()> {
for card in &mut cards {
if !self.conflicting_notes.contains(&card.note_id) {
self.remap_note_id(card);
if !self.targets.contains(&(card.note_id, card.template_idx)) {
self.add_card(card)?;
}
// TODO: maybe update
}
}
Ok(())
}
fn add_card(&mut self, card: &mut Card) -> Result<()> {
card.usn = self.usn;
self.remap_deck_id(card);
card.shift_collection_relative_dates(self.collection_delta);
card.maybe_remove_from_filtered_deck(self.scheduler_version);
let old_id = self.uniquify_card_id(card);
self.target_col.add_card_if_unique_undoable(card)?;
self.target_ids.insert(card.id);
self.imported_cards.insert(old_id, card.id);
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 {
let original = card.id;
while self.target_ids.contains(&card.id) {
card.id.0 += 999;
}
original
}
fn remap_deck_id(&self, card: &mut Card) {
if let Some(did) = self.remapped_decks.get(&card.deck_id) {
card.deck_id = *did;
}
}
}
impl Card {
/// `delta` is the number days the card's source collection is ahead of the
/// target collection.
fn shift_collection_relative_dates(&mut self, delta: i32) {
if self.due_in_days_since_collection_creation() {
self.due -= delta;
}
if self.original_due_in_days_since_collection_creation() && self.original_due != 0 {
self.original_due -= delta;
}
}
fn due_in_days_since_collection_creation(&self) -> bool {
matches!(self.queue, CardQueue::Review | CardQueue::DayLearn)
|| self.ctype == CardType::Review
}
fn original_due_in_days_since_collection_creation(&self) -> bool {
self.ctype == CardType::Review
}
fn maybe_remove_from_filtered_deck(&mut self, version: SchedulerVersion) {
if self.is_filtered() {
// instead of moving between decks, the deck is converted to a regular one
self.original_deck_id = self.deck_id;
self.remove_from_filtered_deck_restoring_queue(version);
}
}
}

View file

@ -1,6 +1,8 @@
// Copyright: Ankitects Pty Ltd and contributors // Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
mod cards;
use std::{ use std::{
borrow::Cow, borrow::Cow,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
@ -15,9 +17,7 @@ use tempfile::NamedTempFile;
use zip::ZipArchive; use zip::ZipArchive;
use crate::{ use crate::{
card::{CardQueue, CardType},
collection::CollectionBuilder, collection::CollectionBuilder,
config::SchedulerVersion,
decks::NormalDeck, decks::NormalDeck,
import_export::{ import_export::{
gather::ExchangeData, gather::ExchangeData,
@ -54,7 +54,6 @@ struct Context<'a> {
/// exist in the target, but their notetypes don't match. /// exist in the target, but their notetypes don't match.
conflicting_notes: HashSet<NoteId>, conflicting_notes: HashSet<NoteId>,
remapped_cards: HashMap<CardId, CardId>, remapped_cards: HashMap<CardId, CardId>,
existing_cards: HashSet<CardId>,
normalize_notes: bool, normalize_notes: bool,
} }
@ -134,7 +133,6 @@ impl<'a> Context<'a> {
let usn = target_col.usn()?; let usn = target_col.usn()?;
let normalize_notes = target_col.get_config_bool(BoolKey::NormalizeNoteText); let normalize_notes = target_col.get_config_bool(BoolKey::NormalizeNoteText);
let existing_notes = target_col.storage.get_all_note_ids()?; let existing_notes = target_col.storage.get_all_note_ids()?;
let existing_cards = target_col.storage.get_all_card_ids()?;
Ok(Self { Ok(Self {
target_col, target_col,
archive, archive,
@ -147,7 +145,6 @@ impl<'a> Context<'a> {
existing_notes, existing_notes,
remapped_decks: HashMap::new(), remapped_decks: HashMap::new(),
remapped_cards: HashMap::new(), remapped_cards: HashMap::new(),
existing_cards,
used_media_entries: HashMap::new(), used_media_entries: HashMap::new(),
normalize_notes, normalize_notes,
}) })
@ -399,58 +396,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_cards(&mut self) -> Result<()> {
let existing = self.target_col.storage.all_cards_as_nid_and_ord()?;
let collection_delta = self.collection_delta()?;
let version = self.target_col.scheduler_info()?.version;
for mut card in mem::take(&mut self.data.cards) {
if !self.conflicting_notes.contains(&card.note_id) {
self.remap_note_id(&mut card);
if !existing.contains(&(card.note_id, card.template_idx)) {
self.remap_deck_id(&mut card);
card.shift_collection_relative_dates(collection_delta);
card.maybe_remove_from_filtered_deck(version);
self.add_card(&mut card)?;
}
// TODO: maybe update
}
}
Ok(())
}
/// The number of days the source collection is ahead of the target collection.
fn collection_delta(&mut self) -> Result<i32> {
Ok(self.data.days_elapsed as i32 - self.target_col.timing_today()?.days_elapsed as i32)
}
fn add_card(&mut self, card: &mut Card) -> Result<()> {
card.usn = self.usn;
self.uniquify_card_id(card);
self.target_col.add_card_if_unique_undoable(card)?;
self.existing_cards.insert(card.id);
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) {
let original = card.id;
while self.existing_cards.contains(&card.id) {
card.id.0 += 999;
}
self.remapped_cards.insert(original, card.id);
}
fn remap_deck_id(&self, card: &mut Card) {
if let Some(did) = self.remapped_decks.get(&card.deck_id) {
card.deck_id = *did;
}
}
fn import_revlog(&mut self) -> Result<()> { fn import_revlog(&mut self) -> Result<()> {
for mut entry in mem::take(&mut self.data.revlog) { for mut entry in mem::take(&mut self.data.revlog) {
if let Some(cid) = self.remapped_cards.get(&entry.cid) { if let Some(cid) = self.remapped_cards.get(&entry.cid) {
@ -516,33 +461,3 @@ impl Notetype {
hasher.digest().bytes() hasher.digest().bytes()
} }
} }
impl Card {
/// `delta` is the number days the card's source collection is ahead of the
/// target collection.
fn shift_collection_relative_dates(&mut self, delta: i32) {
if self.due_in_days_since_collection_creation() {
self.due -= delta;
}
if self.original_due_in_days_since_collection_creation() && self.original_due != 0 {
self.original_due -= delta;
}
}
fn due_in_days_since_collection_creation(&self) -> bool {
matches!(self.queue, CardQueue::Review | CardQueue::DayLearn)
|| self.ctype == CardType::Review
}
fn original_due_in_days_since_collection_creation(&self) -> bool {
self.ctype == CardType::Review
}
fn maybe_remove_from_filtered_deck(&mut self, version: SchedulerVersion) {
if self.is_filtered() {
// instead of moving between decks, the deck is converted to a regular one
self.original_deck_id = self.deck_id;
self.remove_from_filtered_deck_restoring_queue(version);
}
}
}