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
use {
crate::*,
anyhow::*,
cli_log::*,
chrono::{DateTime, SecondsFormat, Utc},
std::{collections::HashMap, io::Write},
};
#[derive(Debug)]
pub struct ExtractLine {
pub time: DateTime<Utc>,
pub counts: Vec<Option<usize>>,
}
#[derive(Debug)]
pub struct Extract {
pub names: Vec<String>,
pub lines: Vec<ExtractLine>,
}
#[derive(Debug)]
pub(crate) struct Col {
pub idx: usize,
pub name: String,
}
impl Extract {
pub fn write_csv<W: Write>(&self, w: &mut W) -> Result<()> {
write!(w, "time")?;
for name in &self.names {
write!(w, ",{}", name)?;
}
writeln!(w)?;
for line in &self.lines {
write!(w, "{}", line.time.to_rfc3339_opts(SecondsFormat::Secs, true))?;
for count in &line.counts {
if let Some(count) = count {
write!(w, ",{}", count)?;
} else {
write!(w, ",")?;
}
}
writeln!(w)?;
}
w.flush()?;
Ok(())
}
pub fn read(db: &Db, names: Vec<String>) -> Result<Self> {
let mut queries: Vec<UserQuery> = Vec::new();
for (idx, name) in names.iter().enumerate() {
let mut tokens = name.split('/');
let user_id = UserId::new(tokens.next().unwrap()); let query_idx = queries
.iter()
.position(|q| q.user_id == user_id)
.unwrap_or_else(|| {
let idx = queries.len();
queries.push(UserQuery {
user_id,
sum: None,
repos: Vec::new(),
});
idx
});
match tokens.next() {
Some(repo) => {
queries[query_idx].repos.push(Col {
idx,
name: repo.to_string(),
});
}
None => {
queries[query_idx].sum = Some(Col {
idx,
name: name.to_string(),
});
}
}
}
debug!("queries: {:#?}", &queries);
let mut results: HashMap<DateTime<Utc>, ExtractLine> = HashMap::new();
for query in queries {
let repo_names = query.repos.iter().map(|col| col.name.as_str()).collect();
let response_lines = db.extract_user_query(&query.user_id, repo_names)?;
debug!("response_lines: {:#?}", &response_lines);
for response_line in response_lines {
let extract_line =
results
.entry(response_line.time)
.or_insert_with(|| ExtractLine {
time: response_line.time,
counts: vec![None; names.len()],
});
if let Some(col) = query.sum.as_ref() {
extract_line.counts[col.idx] = Some(response_line.sum);
}
for (idx, col) in query.repos.iter().enumerate() {
extract_line.counts[col.idx] = response_line.counts[idx];
}
}
}
let mut lines: Vec<ExtractLine> = results.drain().map(|(_, line)| line).collect();
debug!("lines: {:#?}", &lines);
lines.sort_by_key(|line| line.time);
Ok(Self { names, lines })
}
}