Import/export feature for config and data
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::keyboard::display::BACKSPACE;
|
||||
use crate::session::drill::DrillState;
|
||||
use crate::session::input::KeystrokeEvent;
|
||||
|
||||
@@ -52,17 +53,50 @@ impl DrillResult {
|
||||
ranked: bool,
|
||||
partial: bool,
|
||||
) -> Self {
|
||||
let per_key_times: Vec<KeyTime> = events
|
||||
.windows(2)
|
||||
.map(|pair| {
|
||||
let dt = pair[1].timestamp.duration_since(pair[0].timestamp);
|
||||
KeyTime {
|
||||
key: pair[1].expected,
|
||||
time_ms: dt.as_secs_f64() * 1000.0,
|
||||
correct: pair[1].correct,
|
||||
let mut per_key_times: Vec<KeyTime> = Vec::new();
|
||||
let mut pending_backspace = false;
|
||||
for pair in events.windows(2) {
|
||||
let prev = &pair[0];
|
||||
let curr = &pair[1];
|
||||
let dt = curr.timestamp.duration_since(prev.timestamp).as_secs_f64() * 1000.0;
|
||||
|
||||
// Track per-key expected-char timing/accuracy for normal typing keys.
|
||||
// Backspace attempts are tracked separately below.
|
||||
if curr.actual != BACKSPACE {
|
||||
per_key_times.push(KeyTime {
|
||||
key: curr.expected,
|
||||
time_ms: dt,
|
||||
correct: curr.correct,
|
||||
});
|
||||
}
|
||||
|
||||
// Backspace attempt tracking:
|
||||
// - Any incorrect non-backspace key creates a pending backspace need.
|
||||
// - While pending, every next key press is a backspace attempt.
|
||||
// - Backspace press = correct attempt; anything else = incorrect attempt
|
||||
// and the requirement stays pending.
|
||||
if pending_backspace {
|
||||
if curr.actual == BACKSPACE {
|
||||
per_key_times.push(KeyTime {
|
||||
key: BACKSPACE,
|
||||
time_ms: dt,
|
||||
correct: true,
|
||||
});
|
||||
pending_backspace = false;
|
||||
} else {
|
||||
per_key_times.push(KeyTime {
|
||||
key: BACKSPACE,
|
||||
time_ms: dt,
|
||||
correct: false,
|
||||
});
|
||||
pending_backspace = true;
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
if curr.actual != BACKSPACE && !curr.correct {
|
||||
pending_backspace = true;
|
||||
}
|
||||
}
|
||||
|
||||
let total_chars = drill.target.len();
|
||||
let typo_count = drill.typo_flags.len();
|
||||
@@ -89,3 +123,65 @@ impl DrillResult {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn ev(expected: char, actual: char, ms: u64, correct: bool, start: Instant) -> KeystrokeEvent {
|
||||
KeystrokeEvent {
|
||||
expected,
|
||||
actual,
|
||||
timestamp: start + Duration::from_millis(ms),
|
||||
correct,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tracks_backspace_success_after_incorrect_key() {
|
||||
let drill = DrillState::new("ab");
|
||||
let t0 = Instant::now();
|
||||
let events = vec![
|
||||
ev('a', 'a', 0, true, t0),
|
||||
ev('b', 'x', 100, false, t0),
|
||||
ev(BACKSPACE, BACKSPACE, 220, true, t0),
|
||||
ev('b', 'b', 350, true, t0),
|
||||
];
|
||||
|
||||
let result = DrillResult::from_drill(&drill, &events, "adaptive", true, false);
|
||||
let backspace: Vec<&KeyTime> = result
|
||||
.per_key_times
|
||||
.iter()
|
||||
.filter(|kt| kt.key == BACKSPACE)
|
||||
.collect();
|
||||
assert_eq!(backspace.len(), 1);
|
||||
assert!(backspace[0].correct);
|
||||
assert!((backspace[0].time_ms - 120.0).abs() < 0.1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tracks_backspace_error_until_user_backspaces() {
|
||||
let drill = DrillState::new("abc");
|
||||
let t0 = Instant::now();
|
||||
let events = vec![
|
||||
ev('a', 'a', 0, true, t0),
|
||||
ev('b', 'x', 100, false, t0),
|
||||
ev('c', 'c', 220, true, t0),
|
||||
ev(BACKSPACE, BACKSPACE, 400, true, t0),
|
||||
];
|
||||
|
||||
let result = DrillResult::from_drill(&drill, &events, "adaptive", true, false);
|
||||
let backspace: Vec<&KeyTime> = result
|
||||
.per_key_times
|
||||
.iter()
|
||||
.filter(|kt| kt.key == BACKSPACE)
|
||||
.collect();
|
||||
assert_eq!(backspace.len(), 2);
|
||||
assert!(!backspace[0].correct);
|
||||
assert!(backspace[1].correct);
|
||||
assert!((backspace[0].time_ms - 120.0).abs() < 0.1);
|
||||
assert!((backspace[1].time_ms - 180.0).abs() < 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user