1
1
use crate :: util:: std_dev;
2
+ use crate :: TICK_RATE_MS ;
2
3
use chrono:: prelude:: * ;
4
+ use directories:: ProjectDirs ;
3
5
use itertools:: Itertools ;
4
6
use std:: fs:: OpenOptions ;
5
7
use std:: io:: { self , Write } ;
@@ -27,36 +29,35 @@ pub struct Thok {
27
29
pub wpm_coords : Vec < ( f64 , f64 ) > ,
28
30
pub cursor_pos : usize ,
29
31
pub started_at : Option < SystemTime > ,
30
- // How much time is left
31
- pub time_remaining : Option < f64 > ,
32
- // The duration of the test
33
- pub test_duration : Option < f64 > ,
32
+ pub seconds_remaining : Option < f64 > ,
33
+ pub number_of_secs : Option < f64 > ,
34
+ pub number_of_words : usize ,
34
35
pub wpm : f64 ,
35
36
pub accuracy : f64 ,
36
37
pub std_dev : f64 ,
37
38
}
38
39
39
40
impl Thok {
40
- pub fn new ( prompt_string : String , duration : Option < usize > ) -> Self {
41
- let duration = duration. map ( |d| d as f64 ) ;
42
-
41
+ pub fn new ( prompt : String , number_of_words : usize , number_of_secs : Option < f64 > ) -> Self {
43
42
Self {
44
- prompt : prompt_string ,
43
+ prompt,
45
44
input : vec ! [ ] ,
46
45
raw_coords : vec ! [ ] ,
47
46
wpm_coords : vec ! [ ] ,
48
47
cursor_pos : 0 ,
49
48
started_at : None ,
50
- time_remaining : duration,
51
- test_duration : duration,
49
+ number_of_secs,
50
+ number_of_words,
51
+ seconds_remaining : number_of_secs,
52
52
wpm : 0.0 ,
53
53
accuracy : 0.0 ,
54
54
std_dev : 0.0 ,
55
55
}
56
56
}
57
57
58
58
pub fn on_tick ( & mut self ) {
59
- self . time_remaining = Some ( self . time_remaining . unwrap ( ) - 0.1 ) ;
59
+ self . seconds_remaining =
60
+ Some ( self . seconds_remaining . unwrap ( ) - ( TICK_RATE_MS as f64 / 1000_f64 ) ) ;
60
61
}
61
62
62
63
pub fn get_expected_char ( & self , idx : usize ) -> char {
@@ -75,63 +76,17 @@ impl Thok {
75
76
}
76
77
}
77
78
78
- pub fn save_results ( & self ) -> io:: Result < ( ) > {
79
- let project_dir = directories:: ProjectDirs :: from ( "" , "" , "thokr" ) . unwrap ( ) ;
80
- let config_dir = project_dir. config_dir ( ) ;
81
- let log_path = config_dir. join ( "log.csv" ) ;
82
- dbg ! ( & log_path) ;
83
-
84
- // Make sure the directory exists. There's no reason to check if it exists before doing
85
- // this, as this std::fs does that anyways.
86
- std:: fs:: create_dir_all ( config_dir) ?;
87
-
88
- // If the config file doesn't exist, we need to emit a header
89
- let needs_header = !log_path. exists ( ) ;
90
-
91
- let mut log_file = OpenOptions :: new ( )
92
- . write ( true )
93
- . append ( true )
94
- . create ( true )
95
- . open ( log_path) ?;
96
-
97
- if needs_header {
98
- writeln ! (
99
- log_file,
100
- "time, wpm, accuracy, standard deviation, words, duration"
101
- ) ?;
102
- }
103
-
104
- writeln ! (
105
- log_file,
106
- "{},{},{},{},{},{}" ,
107
- Local :: now( ) ,
108
- self . wpm,
109
- self . accuracy,
110
- self . std_dev,
111
- // The number of words in the prompt
112
- // TODO: it may be best to pre-calculate this, but it's not super important'
113
- // as the prompt will likely be replaced on the next test
114
- self . prompt. split_whitespace( ) . count( ) ,
115
- // Don't log anything if duration is None. Log the float otherwise
116
- self . test_duration. map_or( String :: new( ) , |f| f. to_string( ) )
117
- ) ?;
118
-
119
- Ok ( ( ) )
120
- }
121
-
122
79
pub fn calc_results ( & mut self ) {
123
- let elapsed = self . started_at . unwrap ( ) . elapsed ( ) ;
124
-
125
80
let correct_chars = self
126
81
. input
127
82
. clone ( )
128
83
. into_iter ( )
129
84
. filter ( |i| i. outcome == Outcome :: Correct )
130
85
. collect :: < Vec < Input > > ( ) ;
131
86
132
- let total_time = elapsed. unwrap ( ) . as_millis ( ) as f64 / 1000.0 ;
87
+ let elapsed_secs = self . started_at . unwrap ( ) . elapsed ( ) . unwrap ( ) . as_millis ( ) as f64 ;
133
88
134
- let whole_second_limit = total_time . floor ( ) ;
89
+ let whole_second_limit = elapsed_secs . floor ( ) ;
135
90
136
91
let correct_chars_per_sec: Vec < ( f64 , f64 ) > = correct_chars
137
92
. clone ( )
@@ -141,8 +96,7 @@ impl Thok {
141
96
. timestamp
142
97
. duration_since ( self . started_at . unwrap ( ) )
143
98
. unwrap ( )
144
- . as_millis ( ) as f64
145
- / 1000.0 ;
99
+ . as_secs_f64 ( ) ;
146
100
147
101
if num_secs == 0.0 {
148
102
num_secs = 1. ;
@@ -154,7 +108,7 @@ impl Thok {
154
108
num_secs = num_secs. ceil ( )
155
109
}
156
110
} else {
157
- num_secs = total_time ;
111
+ num_secs = elapsed_secs ;
158
112
}
159
113
160
114
* map. entry ( num_secs. to_string ( ) ) . or_insert ( 0 ) += 1 ;
@@ -236,6 +190,48 @@ impl Thok {
236
190
237
191
pub fn has_finished ( & self ) -> bool {
238
192
( self . input . len ( ) == self . prompt . len ( ) )
239
- || ( self . time_remaining . is_some ( ) && self . time_remaining . unwrap ( ) <= 0.0 )
193
+ || ( self . seconds_remaining . is_some ( ) && self . seconds_remaining . unwrap ( ) <= 0.0 )
194
+ }
195
+
196
+ pub fn save_results ( & self ) -> io:: Result < ( ) > {
197
+ if let Some ( proj_dirs) = ProjectDirs :: from ( "" , "" , "thokr" ) {
198
+ let config_dir = proj_dirs. config_dir ( ) ;
199
+ let log_path = config_dir. join ( "log.csv" ) ;
200
+
201
+ std:: fs:: create_dir_all ( config_dir) ?;
202
+
203
+ // If the config file doesn't exist, we need to emit a header
204
+ let needs_header = !log_path. exists ( ) ;
205
+
206
+ let mut log_file = OpenOptions :: new ( )
207
+ . write ( true )
208
+ . append ( true )
209
+ . create ( true )
210
+ . open ( log_path) ?;
211
+
212
+ if needs_header {
213
+ writeln ! (
214
+ log_file,
215
+ "date,num_words,num_secs,elapsed_secs,wpm,accuracy,std_dev"
216
+ ) ?;
217
+ }
218
+
219
+ let elapsed_secs = self . started_at . unwrap ( ) . elapsed ( ) . unwrap ( ) . as_secs_f64 ( ) ;
220
+
221
+ writeln ! (
222
+ log_file,
223
+ "{},{},{},{:.2},{},{},{:.2}" ,
224
+ Local :: now( ) . format( "%c" ) ,
225
+ self . number_of_words,
226
+ self . number_of_secs
227
+ . map_or( String :: from( "" ) , |ns| format!( "{:.2}" , ns) ) ,
228
+ elapsed_secs,
229
+ self . wpm, // already rounded, no need to round to two decimal places
230
+ self . accuracy, // already rounded, no need to round to two decimal places
231
+ self . std_dev,
232
+ ) ?;
233
+ }
234
+
235
+ Ok ( ( ) )
240
236
}
241
237
}
0 commit comments