mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 23:12:21 -04:00
cloze generation
This commit is contained in:
parent
83bcb084fe
commit
15ff279a96
3 changed files with 81 additions and 12 deletions
|
@ -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> {
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue