phink_lib/cli/ui/
chart.rs1use chrono::DateTime;
2use ratatui::{
3 layout::{
4 Alignment,
5 Constraint,
6 },
7 prelude::Line,
8 style::{
9 Color,
10 Style,
11 Stylize,
12 },
13 symbols::Marker,
14 text::Span,
15 widgets,
16 widgets::{
17 Axis,
18 Block,
19 Chart,
20 Dataset,
21 GraphType,
22 },
23};
24use widgets::LegendPosition;
25
26pub struct ChartManager<'a> {
27 f64_array: &'a [(f64, f64)],
28}
29
30impl<'a> ChartManager<'a> {
31 pub fn new(f64_array: &'a [(f64, f64)]) -> Self {
32 Self { f64_array }
33 }
34
35 fn get_x_values(&self) -> Vec<f64> {
36 self.f64_array.iter().map(|(x, _)| *x).collect()
37 }
38
39 fn get_y_values(&self) -> Vec<f64> {
40 self.f64_array.iter().map(|(_, y)| *y).collect()
41 }
42
43 fn find_max(values: &[f64]) -> f64 {
44 values.iter().cloned().fold(f64::NEG_INFINITY, f64::max)
45 }
46
47 fn find_min(values: &[f64]) -> f64 {
48 values.iter().cloned().fold(f64::INFINITY, f64::min)
49 }
50 fn timestamp_to_str(unix_timestamp: f64) -> String {
51 DateTime::from_timestamp(unix_timestamp as i64, 0)
52 .unwrap()
53 .format("%Y-%m-%d %H:%M:%S")
54 .to_string()
55 }
56 pub fn get_first_x(&self) -> f64 {
57 let x_values = self.get_x_values();
58 Self::find_min(&x_values)
59 }
60
61 pub fn get_middle_x(&self) -> f64 {
62 let x_values = self.get_x_values();
63 let min_x = Self::find_min(&x_values);
64 let max_x = Self::find_max(&x_values);
65 (min_x + max_x) / 2.0
66 }
67
68 pub fn get_max_x(&self) -> f64 {
69 let x_values = self.get_x_values();
70 Self::find_max(&x_values)
71 }
72
73 pub fn get_first_y(&self) -> f64 {
74 let y_values = self.get_y_values();
75 Self::find_min(&y_values)
76 }
77
78 pub fn get_middle_y(&self) -> f64 {
79 let y_values = self.get_y_values();
80 let min_y = Self::find_min(&y_values);
81 let max_y = Self::find_max(&y_values);
82 (min_y + max_y) / 2.0
83 }
84
85 fn get_max_y(&self) -> f64 {
86 let y_values = self.get_y_values();
87 Self::find_max(&y_values)
88 }
89
90 pub fn create_chart(&self) -> Chart {
91 let dataset = vec![Dataset::default()
92 .marker(Marker::Braille)
93 .graph_type(GraphType::Line)
94 .style(Style::default().fg(Color::Green))
95 .data(self.f64_array)];
96
97 Chart::new(dataset)
98 .block(
99 Block::bordered().title_bottom(
100 Line::from("Corpus evolution".bold()).alignment(Alignment::Center),
101 ),
102 )
103 .x_axis(
104 Axis::default()
105 .title("Time")
106 .style(Style::default().fg(Color::Gray).italic())
107 .bounds([self.get_first_x(), self.get_max_x()])
108 .labels([
109 Self::timestamp_to_str(self.get_first_x()).bold(),
110 Span::from(Self::timestamp_to_str(self.get_middle_x())),
111 Self::timestamp_to_str(self.get_max_x()).bold(),
112 ]),
113 )
114 .y_axis(
115 Axis::default()
116 .title("Number of entries")
117 .style(Style::default().fg(Color::Gray).italic())
118 .bounds([self.get_first_y(), self.get_max_y()])
119 .labels([
120 self.get_first_y().to_string().bold(),
121 Span::from(self.get_middle_y().to_string()),
122 self.get_max_y().to_string().bold(),
123 ]),
124 )
125 .legend_position(Some(LegendPosition::TopLeft))
126 .hidden_legend_constraints((Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)))
127 }
128}