1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use super::{models::Metric, setup_db, Result};
use diesel::prelude::*;
use std::path::Path;
use std::time::Duration;
const SESSION_TIME_GAP_THRESHOLD: Duration = Duration::from_secs(30);
#[derive(Debug)]
pub struct DerivMetric {
pub timestamp: f64,
pub key: String,
pub value: f64,
}
pub struct Session {
pub start_time: f64,
pub end_time: f64,
pub duration: Duration,
}
impl Session {
pub fn new(start_time: f64, end_time: f64) -> Self {
Session {
start_time,
end_time,
duration: Duration::from_secs_f64(end_time - start_time),
}
}
}
pub struct MetricsDb {
db: SqliteConnection,
sessions: Vec<Session>,
}
impl MetricsDb {
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
let db = setup_db(path)?;
let sessions = Self::process_sessions(&db)?;
Ok(MetricsDb { db, sessions })
}
pub fn sessions(&self) -> &[Session] {
&self.sessions
}
fn process_sessions(db: &SqliteConnection) -> Result<Vec<Session>> {
use crate::schema::metrics::dsl::*;
let timestamps = metrics
.select(timestamp)
.order(timestamp.asc())
.load::<f64>(db)?;
let mut sessions: Vec<Session> = Vec::new();
let mut current_start = timestamps[0];
for pair in timestamps.windows(2) {
if pair[1] - pair[0] > SESSION_TIME_GAP_THRESHOLD.as_secs_f64() {
sessions.push(Session::new(current_start, pair[0]));
current_start = pair[1];
}
}
if let Some(last) = timestamps.last() {
if current_start < *last {
sessions.push(Session::new(current_start, *last));
}
}
Ok(sessions)
}
pub fn available_keys(&self) -> Result<Vec<String>> {
use crate::schema::metrics::dsl::*;
let r = metrics.select(key).distinct().load::<String>(&self.db)?;
Ok(r)
}
pub fn metrics_for_key(
&self,
key_name: &str,
session: Option<&Session>,
) -> Result<Vec<Metric>> {
use crate::schema::metrics::dsl::*;
let query = metrics.order(timestamp.asc()).filter(key.eq(key_name));
let r = match session {
Some(session) => query
.filter(timestamp.ge(session.start_time))
.filter(timestamp.le(session.end_time))
.load::<Metric>(&self.db)?,
None => query.load::<Metric>(&self.db)?,
};
Ok(r)
}
pub fn deriv_metrics_for_key(
&self,
key_name: &str,
session: Option<&Session>,
) -> Result<Vec<DerivMetric>> {
let m = self.metrics_for_key(key_name, session)?;
let new_values: Vec<_> = m
.windows(2)
.map(|v| {
let new_value =
(v[1].value - v[0].value) as f64 / (v[1].timestamp - v[0].timestamp);
DerivMetric {
timestamp: v[1].timestamp,
key: format!("{}.deriv", key_name),
value: new_value,
}
})
.collect();
Ok(new_values)
}
#[cfg(feature = "export_csv")]
pub fn export_to_csv<P: AsRef<Path>>(&self, path: P) -> Result<()> {
use crate::schema::metrics::dsl::*;
use std::fs::File;
let out_file = File::create(path)?;
let mut csv_writer = csv::Writer::from_writer(out_file);
let query = metrics.order(timestamp.asc());
for row in query.load::<Metric>(&self.db)? {
csv_writer.serialize(row)?;
}
csv_writer.flush()?;
Ok(())
}
}