Add a method for exporting revlogs in binary

This commit is contained in:
Damien Elmes 2023-11-22 11:57:40 +10:00
parent f8e0321ca5
commit a9d73fc3be
2 changed files with 46 additions and 1 deletions

View file

@ -176,7 +176,6 @@ message RevlogEntry {
LEARNING = 0; LEARNING = 0;
REVIEW = 1; REVIEW = 1;
RELEARNING = 2; RELEARNING = 2;
// Recent Anki versions only use this when rescheduling disabled
FILTERED = 3; FILTERED = 3;
MANUAL = 4; MANUAL = 4;
} }
@ -190,3 +189,7 @@ message RevlogEntry {
uint32 taken_millis = 8; uint32 taken_millis = 8;
ReviewKind review_kind = 9; ReviewKind review_kind = 9;
} }
message RevlogEntries {
repeated RevlogEntry entries = 1;
}

View file

@ -1,16 +1,21 @@
// 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
use std::iter; use std::iter;
use std::path::Path;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use anki_io::write_file;
use anki_proto::scheduler::ComputeFsrsWeightsResponse; use anki_proto::scheduler::ComputeFsrsWeightsResponse;
use anki_proto::stats::revlog_entry;
use anki_proto::stats::RevlogEntries;
use fsrs::CombinedProgressState; use fsrs::CombinedProgressState;
use fsrs::FSRSItem; use fsrs::FSRSItem;
use fsrs::FSRSReview; use fsrs::FSRSReview;
use fsrs::ModelEvaluation; use fsrs::ModelEvaluation;
use fsrs::FSRS; use fsrs::FSRS;
use itertools::Itertools; use itertools::Itertools;
use prost::Message;
use crate::prelude::*; use crate::prelude::*;
use crate::revlog::RevlogEntry; use crate::revlog::RevlogEntry;
@ -72,6 +77,23 @@ impl Collection {
.get_revlog_entries_for_searched_cards_in_card_order() .get_revlog_entries_for_searched_cards_in_card_order()
} }
/// Used for exporting revlogs for algorithm research.
pub fn export_revlog_entries_to_protobuf(
&mut self,
min_entries: usize,
target_path: &Path,
) -> Result<()> {
let entries = self.storage.get_all_revlog_entries_in_card_order()?;
if entries.len() < min_entries {
return Err(AnkiError::FsrsInsufficientData);
}
let entries = entries.into_iter().map(revlog_entry_to_proto).collect_vec();
let entries = RevlogEntries { entries };
let data = entries.encode_to_vec();
write_file(target_path, data)?;
Ok(())
}
pub fn evaluate_weights(&mut self, weights: &Weights, search: &str) -> Result<ModelEvaluation> { pub fn evaluate_weights(&mut self, weights: &Weights, search: &str) -> Result<ModelEvaluation> {
let timing = self.timing_today()?; let timing = self.timing_today()?;
let mut anki_progress = self.new_progress_handler::<ComputeWeightsProgress>(); let mut anki_progress = self.new_progress_handler::<ComputeWeightsProgress>();
@ -208,6 +230,26 @@ impl RevlogEntry {
} }
} }
fn revlog_entry_to_proto(e: RevlogEntry) -> anki_proto::stats::RevlogEntry {
anki_proto::stats::RevlogEntry {
id: e.id.0,
cid: e.cid.0,
usn: 0,
button_chosen: e.button_chosen as u32,
interval: e.interval,
last_interval: e.last_interval,
ease_factor: e.ease_factor,
taken_millis: e.taken_millis,
review_kind: match e.review_kind {
RevlogReviewKind::Learning => revlog_entry::ReviewKind::Learning,
RevlogReviewKind::Review => revlog_entry::ReviewKind::Review,
RevlogReviewKind::Relearning => revlog_entry::ReviewKind::Relearning,
RevlogReviewKind::Filtered => revlog_entry::ReviewKind::Filtered,
RevlogReviewKind::Manual => revlog_entry::ReviewKind::Manual,
} as i32,
}
}
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;