cloze generation

This commit is contained in:
Damien Elmes 2020-04-21 13:49:40 +10:00
parent 83bcb084fe
commit 15ff279a96
3 changed files with 81 additions and 12 deletions

View file

@ -110,13 +110,18 @@ pub fn expand_clozes_to_reveal_latex(text: &str) -> String {
}
pub fn cloze_numbers_in_string(html: &str) -> HashSet<u16> {
let mut hash = HashSet::with_capacity(4);
for cap in CLOZE.captures_iter(html) {
let mut set = HashSet::with_capacity(4);
add_cloze_numbers_in_string(html, &mut set);
set
}
#[allow(clippy::implicit_hasher)]
pub fn add_cloze_numbers_in_string(field: &str, set: &mut HashSet<u16>) {
for cap in CLOZE.captures_iter(field) {
if let Ok(n) = cap[1].parse() {
hash.insert(n);
set.insert(n);
}
}
hash
}
fn strip_html_inside_mathjax(text: &str) -> Cow<str> {

View file

@ -219,7 +219,24 @@ mod test {
1
);
// fixme: add nt cache, refcount
let nt = col.get_notetype_by_name("cloze")?.unwrap();
let mut note = nt.new_note();
// cloze cards without any cloze deletions are allowed
col.add_note(&mut note).unwrap();
let existing = col.storage.existing_cards_for_note(note.id)?;
assert_eq!(existing.len(), 1);
assert_eq!(existing[0].ord, 0);
// fixme
// assert_eq!(existing[0].original_deck_id, DeckID(1));
note.fields[0] = "{{c1::foo}} {{c2::bar}} {{c3::baz}} {{c0::quux}} {{c501::over}}".into();
col.update_note(&mut note)?;
let existing = col.storage.existing_cards_for_note(note.id)?;
let mut ords = existing.iter().map(|a| a.ord).collect::<Vec<_>>();
ords.sort();
assert_eq!(ords, vec![0, 1, 2, 499]);
Ok(())
}
}

View file

@ -4,10 +4,12 @@
use super::NoteType;
use crate::{
card::{Card, CardID},
cloze::add_cloze_numbers_in_string,
collection::Collection,
decks::DeckID,
err::Result,
notes::{Note, NoteID},
notetype::NoteTypeKind,
template::ParsedTemplate,
types::Usn,
};
@ -15,6 +17,7 @@ use itertools::Itertools;
use std::collections::HashSet;
/// Info about an existing card required when generating new cards
#[derive(Debug, PartialEq)]
pub(crate) struct AlreadyGeneratedCardInfo {
pub id: CardID,
pub nid: NoteID,
@ -82,16 +85,20 @@ impl CardGenContext<'_> {
note: &Note,
existing: &[AlreadyGeneratedCardInfo],
) -> Vec<CardToGenerate> {
let nonempty_fields = note.nonempty_fields(&self.notetype.fields);
let extracted = extract_data_from_existing_cards(existing);
self.new_cards_required_inner(&nonempty_fields, &extracted)
match self.notetype.config.kind() {
NoteTypeKind::Normal => self.new_cards_required_normal(note, &extracted),
NoteTypeKind::Cloze => self.new_cards_required_cloze(note, &extracted),
}
}
fn new_cards_required_inner(
fn new_cards_required_normal(
&self,
nonempty_fields: &HashSet<&str>,
note: &Note,
extracted: &ExtractedCardInfo,
) -> Vec<CardToGenerate> {
let nonempty_fields = note.nonempty_fields(&self.notetype.fields);
self.cards
.iter()
.enumerate()
@ -110,6 +117,43 @@ impl CardGenContext<'_> {
})
.collect()
}
fn new_cards_required_cloze(
&self,
note: &Note,
extracted: &ExtractedCardInfo,
) -> Vec<CardToGenerate> {
// gather all cloze numbers
let mut set = HashSet::with_capacity(4);
for field in note.fields() {
add_cloze_numbers_in_string(field, &mut set);
}
let cards: Vec<_> = set
.into_iter()
.filter_map(|cloze_ord| {
let card_ord = cloze_ord.saturating_sub(1).min(499);
if extracted.existing_ords.contains(&(card_ord as u32)) {
None
} else {
Some(CardToGenerate {
ord: card_ord as u32,
did: extracted.deck_id,
due: extracted.due,
})
}
})
.collect();
if cards.is_empty() && extracted.existing_ords.is_empty() {
// if no cloze deletions are found, we add a card with ord 0
vec![CardToGenerate {
ord: 0,
did: extracted.deck_id,
due: extracted.due,
}]
} else {
cards
}
}
}
// this could be reworked in the future to avoid the extra vec allocation
@ -173,8 +217,11 @@ impl Collection {
let existing_cards = self.storage.existing_cards_for_notetype(ctx.notetype.id)?;
let by_note = group_generated_cards_by_note(existing_cards);
for (nid, existing_cards) in by_note {
if existing_cards.len() == ctx.notetype.templates.len() {
// nothing to do
if ctx.notetype.config.kind() == NoteTypeKind::Normal
&& existing_cards.len() == ctx.notetype.templates.len()
{
// in a normal note type, if card count matches template count, we don't need
// to load the note contents to know if all cards have been generated
continue;
}
let note = self.storage.get_note(nid)?.unwrap();
@ -208,4 +255,4 @@ impl Collection {
}
// fixme: deal with case where invalid deck pointed to
// fixme: cloze cards, & avoid template count comparison for cloze
// fixme: make sure we don't orphan notes