Compare commits
4 Commits
37ce63df82
...
7255dc7bf5
Author | SHA1 | Date |
---|---|---|
David Holland | 7255dc7bf5 | |
David Holland | a6568bbe7b | |
David Holland | 7c9490f689 | |
David Holland | 6297df6f28 |
File diff suppressed because it is too large
Load Diff
17
Cargo.toml
17
Cargo.toml
|
@ -7,7 +7,6 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
calamine = "0.18.0"
|
||||
chrono = { version = "0.4.23", features = ["wasmbind"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
fast-float = "0.2.0"
|
||||
|
@ -16,24 +15,12 @@ plotly = { version = "0.8.1", features = ["wasm"] }
|
|||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
toml = "0.5.9"
|
||||
indicatif = "0.17.3"
|
||||
dialoguer = "0.10.3"
|
||||
notify-rust = "4.7.0"
|
||||
crossterm = "0.25.0"
|
||||
rfd = "0.10.0"
|
||||
|
||||
egui = { git = "https://github.com/emilk/egui", branch = "master" }
|
||||
eframe = { git = "https://github.com/emilk/egui", branch = "master" }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
tracing-subscriber = "0.3"
|
||||
|
||||
# web:
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
console_error_panic_hook = "0.1.6"
|
||||
tracing-wasm = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
|
||||
dioxus = "0.3.2"
|
||||
dioxus-web = "0.3.2"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 2 # fast and small wasm
|
||||
|
|
|
@ -37,25 +37,9 @@ pub struct Config {
|
|||
pub height: u32,
|
||||
}
|
||||
|
||||
pub const WGHT_SHEET: usize = 0;
|
||||
pub const FLOW_SHEET: usize = 1;
|
||||
|
||||
pub const TIME_COL: usize = 0;
|
||||
pub const WGHT_COL: usize = 5;
|
||||
pub const FLOW_COL: usize = 2;
|
||||
|
||||
pub struct Data {
|
||||
pub weight: Vec<(f64, f64)>,
|
||||
pub flowrate: Vec<(f64, f64)>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_file(path: &str) -> Self {
|
||||
let config_file = fs::read_to_string(&path).expect("Can't read config.toml");
|
||||
toml::from_str(&config_file).expect("Can't deserialize config.toml")
|
||||
}
|
||||
|
||||
pub fn from_default() -> Self {
|
||||
Self::from_file("config.toml")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ impl Database {
|
|||
Database::from_file(&format!("{}/{}", &config.data_dir, &config.main_json))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn bean_names(&self) -> Vec<String> {
|
||||
self.beans
|
||||
.iter()
|
||||
|
@ -81,6 +82,7 @@ impl Database {
|
|||
.collect()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn bean_names_with_uuids(&self) -> Vec<(String, String)> {
|
||||
self.beans
|
||||
.iter()
|
||||
|
@ -94,12 +96,14 @@ impl Database {
|
|||
.find(|bean| bean.config.uuid == uuid.to_owned())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn brew_for_uuid(&self, uuid: &str) -> Option<&Brew> {
|
||||
self.brews
|
||||
.iter()
|
||||
.find(|brew| brew.config.uuid == uuid.to_owned())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn brews_for_uuids(&self, uuids: &Vec<String>) -> Vec<&Brew> {
|
||||
self.brews
|
||||
.iter()
|
||||
|
@ -110,6 +114,7 @@ impl Database {
|
|||
.collect()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn bean_for_brew(&self, brew: &Brew) -> Option<&Bean> {
|
||||
self.beans
|
||||
.iter()
|
||||
|
@ -137,12 +142,13 @@ impl Bean {
|
|||
}
|
||||
|
||||
impl Brew {
|
||||
#[allow(dead_code)]
|
||||
pub fn time(&self) -> String {
|
||||
unix_to_human_time(self.config.unix_timestamp.to_owned())
|
||||
}
|
||||
|
||||
pub fn date_time(&self, database: &Database) -> String {
|
||||
if let Some(bean) = &database.bean_for_uuid(&self.bean) {
|
||||
if let Some(_bean) = &database.bean_for_uuid(&self.bean) {
|
||||
unix_to_human_date_time(self.config.unix_timestamp.to_owned())
|
||||
} else {
|
||||
String::default()
|
||||
|
|
|
@ -66,6 +66,7 @@ impl FlowProfile {
|
|||
brew
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn preprocess_json_mut(&mut self) {
|
||||
self.data_collection = self.process_json();
|
||||
}
|
||||
|
|
36
src/main.rs
36
src/main.rs
|
@ -2,35 +2,21 @@ mod config;
|
|||
mod database;
|
||||
mod flow_profile;
|
||||
mod plot;
|
||||
mod sheets;
|
||||
mod time;
|
||||
mod ui;
|
||||
|
||||
use ui::Ui;
|
||||
|
||||
use crate::plot::database_plot_selected_tui;
|
||||
use crate::plot::generate_plots;
|
||||
use dioxus::prelude::*;
|
||||
|
||||
extern crate console_error_panic_hook;
|
||||
use std::panic;
|
||||
|
||||
use eframe::egui;
|
||||
|
||||
fn main() -> Result<(), eframe::Error> {
|
||||
panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
|
||||
// generate_plots();
|
||||
|
||||
// database_plot_selected_tui();
|
||||
let options = eframe::NativeOptions {
|
||||
drag_and_drop_support: true,
|
||||
min_window_size: Some(egui::vec2(640.0, 360.0)),
|
||||
initial_window_size: Some(egui::vec2(640.0, 360.0)),
|
||||
..Default::default()
|
||||
};
|
||||
eframe::run_native(
|
||||
"RustyBeans",
|
||||
options,
|
||||
Box::new(|_cc| Box::new(Ui::default())),
|
||||
)
|
||||
fn main() {
|
||||
dioxus_web::launch(test);
|
||||
}
|
||||
|
||||
fn test(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
"Hello, world!"
|
||||
}
|
||||
})
|
||||
}
|
332
src/plot.rs
332
src/plot.rs
|
@ -1,23 +1,15 @@
|
|||
use crate::config::Config;
|
||||
use crate::database::{Brew, Database};
|
||||
use crate::flow_profile::FlowProfile;
|
||||
use crate::sheets::load_data;
|
||||
use crate::time::{unix_to_human_date, unix_to_human_date_time, unix_to_machine_date};
|
||||
|
||||
use dialoguer::{theme::ColorfulTheme, MultiSelect};
|
||||
|
||||
use egui::plot::{PlotPoint, PlotPoints};
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||
|
||||
use notify_rust::Notification;
|
||||
|
||||
use plotly::{
|
||||
common::{Mode, Title},
|
||||
Layout, Plot, Scatter,
|
||||
};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub type RawPlotPoints = Vec<PlotPoint>;
|
||||
|
@ -31,72 +23,6 @@ pub struct LoadingProgress {
|
|||
pub percentage: f32,
|
||||
}
|
||||
|
||||
pub fn generate_plots() -> Vec<(String, Plot)> {
|
||||
let config_file = fs::read_to_string("config.toml").expect("Can't read config.toml");
|
||||
let config: Config = toml::from_str(&config_file).expect("Can't deserialize config.toml");
|
||||
|
||||
let mut result: Vec<(String, Plot)> = Vec::with_capacity(config.charts.len());
|
||||
|
||||
for chart in config.charts {
|
||||
// println!("Chart: {}\n", chart.1.title);
|
||||
|
||||
let filename = format!("{}/{}.html", &config.output_dir, &chart.1.title);
|
||||
let mut plot = Plot::new();
|
||||
|
||||
let _shot_count = chart.1.shots.len();
|
||||
|
||||
for shot_nr in chart.1.shots {
|
||||
if let Some(shot) = config.shots.get(&shot_nr.to_string()) {
|
||||
// println!("\tShot: {}n", shot.title);
|
||||
|
||||
if let Some(shot_json) = &shot.json {
|
||||
let brew = FlowProfile::from_file(
|
||||
&format!("{}/{}_flow_profile.json", config.brew_dir, shot_json),
|
||||
shot.cutoff,
|
||||
)
|
||||
.preprocess_json();
|
||||
|
||||
let (x, y): (Vec<_>, Vec<_>) = brew
|
||||
.data_collection
|
||||
.unwrap_or_else(|| {
|
||||
panic!("No data_collection present for shot_json: {}", shot_json)
|
||||
})
|
||||
.weight
|
||||
.iter()
|
||||
.cloned()
|
||||
.unzip();
|
||||
let trace = Scatter::new(x, y).name(&shot.title).mode(Mode::Lines);
|
||||
plot.add_trace(trace);
|
||||
} else if let Some(shot_filename) = &shot.filename {
|
||||
if let Some(data) = load_data(
|
||||
&format!("{}/{}", config.shot_dir, shot_filename),
|
||||
shot.cutoff,
|
||||
) {
|
||||
if let Some(disable) = shot.disable {
|
||||
if disable {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let (x, y): (Vec<_>, Vec<_>) = data.weight.into_iter().unzip();
|
||||
let trace = Scatter::new(x, y).name(&shot.title).mode(Mode::Lines);
|
||||
plot.add_trace(trace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let layout = Layout::new().title(Title::new(&chart.1.title));
|
||||
plot.set_layout(layout);
|
||||
|
||||
plot.use_local_plotly();
|
||||
plot.write_html(filename);
|
||||
result.push((chart.1.title, plot));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn plot_points_to_owned(plot_points: &RawPlotPoints) -> PlotPoints {
|
||||
PlotPoints::Owned(plot_points.to_owned())
|
||||
}
|
||||
|
@ -152,190 +78,74 @@ pub fn database_plot_entries(
|
|||
plot_entries
|
||||
}
|
||||
|
||||
pub fn database_plot_selected(uuids: Vec<String>, config: &Config) -> Vec<(String, Plot)> {
|
||||
let mut result: Vec<(String, Plot)> = Vec::with_capacity(config.charts.len());
|
||||
|
||||
let database = Database::from_config(&config);
|
||||
|
||||
let bean_names = database.bean_names();
|
||||
|
||||
if !uuids.is_empty() {
|
||||
for single_selection in uuids {
|
||||
if let Some(bean) = &database.bean_for_uuid(&single_selection) {
|
||||
let bean_timestamp = bean.config.unix_timestamp.to_owned();
|
||||
|
||||
let title = format!("{} from {}", &bean.name, unix_to_human_date(bean_timestamp));
|
||||
let filename = format!(
|
||||
"{}/{}_from_{}.html",
|
||||
&config.output_dir,
|
||||
&bean.name,
|
||||
unix_to_machine_date(bean_timestamp)
|
||||
)
|
||||
.replace(" ", "_");
|
||||
let mut plot = Plot::new();
|
||||
|
||||
let brews = &database.brews_for_bean(&bean);
|
||||
|
||||
for brew in brews {
|
||||
if let Some(flow_profile) = &brew.flow_profile {
|
||||
if !&flow_profile.is_empty() {
|
||||
let brew_title =
|
||||
unix_to_human_date_time(brew.config.unix_timestamp.to_owned());
|
||||
|
||||
let brew = FlowProfile::from_file(
|
||||
&format!("{}/{}", &config.data_dir, &flow_profile),
|
||||
None,
|
||||
)
|
||||
.preprocess_json();
|
||||
|
||||
let (x, y): (Vec<_>, Vec<_>) = brew
|
||||
.data_collection
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"No data_collection present for flow_profile: {}",
|
||||
&flow_profile
|
||||
)
|
||||
})
|
||||
.weight
|
||||
.iter()
|
||||
.cloned()
|
||||
.unzip();
|
||||
let trace = Scatter::new(x, y).name(&brew_title).mode(Mode::Lines);
|
||||
plot.add_trace(trace);
|
||||
|
||||
// progress_brews.inc(..);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let layout = Layout::new().title(Title::new(&title));
|
||||
plot.set_layout(layout);
|
||||
|
||||
plot.use_local_plotly();
|
||||
plot.write_html(filename);
|
||||
|
||||
result.push((title.to_owned(), plot));
|
||||
}
|
||||
|
||||
// progress_beans.inc(..);
|
||||
}
|
||||
}
|
||||
|
||||
Notification::new()
|
||||
.summary("RustyBeans finished")
|
||||
.body("Successfully generated all selected bean charts automatically, according to the database.")
|
||||
.timeout(5000)
|
||||
.show()
|
||||
.expect("Couldn't show desktop notification");
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn database_plot_selected_tui() -> Vec<(String, Plot)> {
|
||||
let config = Config::from_default();
|
||||
|
||||
let mut result: Vec<(String, Plot)> = Vec::with_capacity(config.charts.len());
|
||||
|
||||
let database = Database::from_config(&config);
|
||||
|
||||
let bean_names = database.bean_names();
|
||||
|
||||
let selection = MultiSelect::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Select the Beans you want to automatically generate charts for:")
|
||||
.items(&bean_names[..])
|
||||
.interact()
|
||||
.expect("You need to select at least one bean to proceed");
|
||||
|
||||
if !selection.is_empty() {
|
||||
let multi_progress = MultiProgress::new();
|
||||
multi_progress.println("Generating brew charts.\nCheck the specified output directory and open the corresponding .html files to view.").unwrap();
|
||||
|
||||
let progress_style = ProgressStyle::with_template(
|
||||
"[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}",
|
||||
)
|
||||
.expect("Can't generate progress bar style");
|
||||
let progress_beans = multi_progress.add(
|
||||
ProgressBar::new(selection.len() as u64)
|
||||
.with_style(progress_style.to_owned())
|
||||
.with_message("Beans"),
|
||||
);
|
||||
let mut progress_brews: ProgressBar = ProgressBar::new(0);
|
||||
|
||||
for single_selection in selection {
|
||||
if let Some(bean) = &database.beans.get(single_selection) {
|
||||
let bean_timestamp = bean.config.unix_timestamp.to_owned();
|
||||
|
||||
let title = format!("{} from {}", &bean.name, unix_to_human_date(bean_timestamp));
|
||||
let filename = format!(
|
||||
"{}/{}_from_{}.html",
|
||||
&config.output_dir,
|
||||
&bean.name,
|
||||
unix_to_machine_date(bean_timestamp)
|
||||
)
|
||||
.replace(" ", "_");
|
||||
let mut plot = Plot::new();
|
||||
|
||||
let brews = &database.brews_for_bean(&bean);
|
||||
|
||||
progress_brews = multi_progress.insert_after(
|
||||
&progress_beans,
|
||||
ProgressBar::new(brews.len() as u64)
|
||||
.with_style(progress_style.to_owned())
|
||||
.with_message("Brews"),
|
||||
);
|
||||
|
||||
for brew in brews {
|
||||
if let Some(flow_profile) = &brew.flow_profile {
|
||||
if !&flow_profile.is_empty() {
|
||||
let brew_title =
|
||||
unix_to_human_date_time(brew.config.unix_timestamp.to_owned());
|
||||
|
||||
let brew = FlowProfile::from_file(
|
||||
&format!("{}/{}", &config.data_dir, &flow_profile),
|
||||
None,
|
||||
)
|
||||
.preprocess_json();
|
||||
|
||||
let (x, y): (Vec<_>, Vec<_>) = brew
|
||||
.data_collection
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"No data_collection present for flow_profile: {}",
|
||||
&flow_profile
|
||||
)
|
||||
})
|
||||
.weight
|
||||
.iter()
|
||||
.cloned()
|
||||
.unzip();
|
||||
let trace = Scatter::new(x, y).name(&brew_title).mode(Mode::Lines);
|
||||
plot.add_trace(trace);
|
||||
|
||||
progress_brews.inc(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let layout = Layout::new().title(Title::new(&title));
|
||||
plot.set_layout(layout);
|
||||
|
||||
plot.use_local_plotly();
|
||||
plot.write_html(filename);
|
||||
|
||||
result.push((title.to_owned(), plot));
|
||||
}
|
||||
|
||||
progress_beans.inc(1);
|
||||
multi_progress.remove(&progress_brews);
|
||||
}
|
||||
}
|
||||
|
||||
Notification::new()
|
||||
.summary("RustyBeans finished")
|
||||
.body("Successfully generated all selected bean charts automatically, according to the database.")
|
||||
.timeout(5000)
|
||||
.show()
|
||||
.expect("Couldn't show desktop notification");
|
||||
|
||||
result
|
||||
}
|
||||
// pub fn database_plot_selected(uuids: Vec<String>, config: &Config) -> Vec<(String, Plot)> {
|
||||
// let mut result: Vec<(String, Plot)> = Vec::with_capacity(config.charts.len());
|
||||
//
|
||||
// let database = Database::from_config(&config);
|
||||
//
|
||||
// let bean_names = database.bean_names();
|
||||
//
|
||||
// if !uuids.is_empty() {
|
||||
// for single_selection in uuids {
|
||||
// if let Some(bean) = &database.bean_for_uuid(&single_selection) {
|
||||
// let bean_timestamp = bean.config.unix_timestamp.to_owned();
|
||||
//
|
||||
// let title = format!("{} from {}", &bean.name, unix_to_human_date(bean_timestamp));
|
||||
// let filename = format!(
|
||||
// "{}/{}_from_{}.html",
|
||||
// &config.output_dir,
|
||||
// &bean.name,
|
||||
// unix_to_machine_date(bean_timestamp)
|
||||
// )
|
||||
// .replace(" ", "_");
|
||||
// let mut plot = Plot::new();
|
||||
//
|
||||
// let brews = &database.brews_for_bean(&bean);
|
||||
//
|
||||
// for brew in brews {
|
||||
// if let Some(flow_profile) = &brew.flow_profile {
|
||||
// if !&flow_profile.is_empty() {
|
||||
// let brew_title =
|
||||
// unix_to_human_date_time(brew.config.unix_timestamp.to_owned());
|
||||
//
|
||||
// let brew = FlowProfile::from_file(
|
||||
// &format!("{}/{}", &config.data_dir, &flow_profile),
|
||||
// None,
|
||||
// )
|
||||
// .preprocess_json();
|
||||
//
|
||||
// let (x, y): (Vec<_>, Vec<_>) = brew
|
||||
// .data_collection
|
||||
// .unwrap_or_else(|| {
|
||||
// panic!(
|
||||
// "No data_collection present for flow_profile: {}",
|
||||
// &flow_profile
|
||||
// )
|
||||
// })
|
||||
// .weight
|
||||
// .iter()
|
||||
// .cloned()
|
||||
// .unzip();
|
||||
// let trace = Scatter::new(x, y).name(&brew_title).mode(Mode::Lines);
|
||||
// plot.add_trace(trace);
|
||||
//
|
||||
// // progress_brews.inc(..);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// let layout = Layout::new().title(Title::new(&title));
|
||||
// plot.set_layout(layout);
|
||||
//
|
||||
// plot.use_local_plotly();
|
||||
// plot.write_html(filename);
|
||||
//
|
||||
// result.push((title.to_owned(), plot));
|
||||
// }
|
||||
//
|
||||
// // progress_beans.inc(..);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// result
|
||||
// }
|
|
@ -1,75 +0,0 @@
|
|||
use crate::config::{Data, FLOW_COL, FLOW_SHEET, TIME_COL, WGHT_COL, WGHT_SHEET};
|
||||
use crate::time::{cell_to_naivetime, deltatime};
|
||||
|
||||
use calamine::{open_workbook, Reader, Xlsx};
|
||||
|
||||
use chrono::NaiveTime;
|
||||
|
||||
pub fn process_sheet(
|
||||
path: &str,
|
||||
worksheet: usize,
|
||||
time_col: usize,
|
||||
data_col: usize,
|
||||
) -> Vec<(f64, f64)> {
|
||||
let mut workbook: Xlsx<_> =
|
||||
open_workbook(path).unwrap_or_else(|_| panic!("Cannot open file at path \"{}\"", path));
|
||||
|
||||
if let Some(Ok(range)) = workbook.worksheet_range_at(worksheet) {
|
||||
let starting_time: NaiveTime = cell_to_naivetime(range[(1, time_col)].get_string());
|
||||
|
||||
let time_range = range.range(
|
||||
(1, time_col as u32),
|
||||
(range.height() as u32 - 1, time_col as u32),
|
||||
);
|
||||
let weight_range = range.range(
|
||||
(1, data_col as u32),
|
||||
(range.height() as u32 - 1, data_col as u32),
|
||||
);
|
||||
|
||||
// println!("time column cells: {:?}", time_range.cells().next());
|
||||
// println!("time column strings: {:?}", time_range.cells().map(|c| c.2.get_string().unwrap()).collect::<Vec<&str>>());
|
||||
|
||||
let map_time_range = time_range.cells().map(|c| {
|
||||
let timestamp = cell_to_naivetime(c.2.get_string());
|
||||
let deltatime = deltatime(timestamp, starting_time);
|
||||
let std_duration = deltatime.to_std().unwrap();
|
||||
|
||||
std_duration.as_secs_f32() as f64
|
||||
});
|
||||
|
||||
let map_weight_range = weight_range.cells().map(|c| {
|
||||
c.2.get_float().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Can't get float value of weight column at position ({},{})",
|
||||
c.0, c.1
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
map_time_range.zip(map_weight_range).collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_data(path: &str, cutoff: Option<f64>) -> Option<Data> {
|
||||
let mut w = process_sheet(path, WGHT_SHEET, TIME_COL, WGHT_COL);
|
||||
let mut fr = process_sheet(path, FLOW_SHEET, TIME_COL, FLOW_COL);
|
||||
|
||||
if let Some(cutoff_val) = cutoff {
|
||||
if cutoff_val != -1.0 {
|
||||
w.retain(|x| x.0 < cutoff_val);
|
||||
fr.retain(|x| x.0 < cutoff_val);
|
||||
}
|
||||
}
|
||||
|
||||
if !w.is_empty() && !fr.is_empty() {
|
||||
let data = Data {
|
||||
weight: w,
|
||||
flowrate: fr,
|
||||
};
|
||||
Some(data)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
12
src/time.rs
12
src/time.rs
|
@ -1,17 +1,16 @@
|
|||
use chrono::{Duration, NaiveDateTime, NaiveTime};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn str_to_naivetime(unix_str: &str) -> NaiveTime {
|
||||
NaiveTime::parse_from_str(unix_str, "%T%.3f").expect("Couldn't parse timestamp")
|
||||
}
|
||||
|
||||
pub fn cell_to_naivetime(cell: Option<&str>) -> NaiveTime {
|
||||
str_to_naivetime(cell.expect("Timestamp is not a string!"))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn deltatime(time: NaiveTime, start: NaiveTime) -> Duration {
|
||||
time - start
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn unix_to_naivetime(unix_timestamp: i64) -> Option<NaiveTime> {
|
||||
if let Some(date_time) = NaiveDateTime::from_timestamp_millis(unix_timestamp) {
|
||||
Some(date_time.time())
|
||||
|
@ -20,10 +19,12 @@ pub fn unix_to_naivetime(unix_timestamp: i64) -> Option<NaiveTime> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_same_day(time_1: NaiveTime, time_2: NaiveTime) -> bool {
|
||||
time_1.format("%Y:%m:%d").to_string() == time_2.format("%Y:%m:%d").to_string()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn unix_to_human_date_time(unix_timestamp: i64) -> String {
|
||||
if let Some(date_time) = NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
|
||||
date_time.format("%b %d, %Y %I:%M %P").to_string()
|
||||
|
@ -32,6 +33,7 @@ pub fn unix_to_human_date_time(unix_timestamp: i64) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn unix_to_human_date(unix_timestamp: i64) -> String {
|
||||
if let Some(date_time) = NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
|
||||
date_time.format("%b %d, %Y").to_string()
|
||||
|
@ -40,6 +42,7 @@ pub fn unix_to_human_date(unix_timestamp: i64) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn unix_to_human_time(unix_timestamp: i64) -> String {
|
||||
if let Some(date_time) = NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
|
||||
date_time.format("%I:%M %P").to_string()
|
||||
|
@ -48,6 +51,7 @@ pub fn unix_to_human_time(unix_timestamp: i64) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn unix_to_machine_date(unix_timestamp: i64) -> String {
|
||||
if let Some(date_time) = NaiveDateTime::from_timestamp_opt(unix_timestamp, 0) {
|
||||
date_time.format("%F").to_string()
|
||||
|
|
88
src/ui.rs
88
src/ui.rs
|
@ -6,8 +6,8 @@ use egui::{
|
|||
plot::{Legend, Line, Plot},
|
||||
Align, Layout, ProgressBar,
|
||||
};
|
||||
use plotly::layout::Center;
|
||||
use rfd::FileDialog;
|
||||
|
||||
// use rfd::FileDialog;
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
|
@ -64,6 +64,7 @@ pub struct Ui {
|
|||
}
|
||||
|
||||
impl Ui {
|
||||
#[allow(dead_code)]
|
||||
pub fn get_selected_brew_uuids(&self) -> Vec<String> {
|
||||
self.brew_checkboxes
|
||||
.iter()
|
||||
|
@ -74,12 +75,13 @@ impl Ui {
|
|||
.collect::<Vec<String>>()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_selected_brews(&self) -> Vec<&Brew> {
|
||||
self.database
|
||||
.brews_for_uuids(&self.get_selected_brew_uuids())
|
||||
}
|
||||
|
||||
pub fn reload(&mut self, ctx: &egui::Context) {
|
||||
pub fn reload(&mut self, _ctx: &egui::Context) {
|
||||
if let Some(loader_thread) = &self.loader_thread {
|
||||
if loader_thread.is_finished() {
|
||||
self.data_loaded = false;
|
||||
|
@ -94,12 +96,6 @@ impl Ui {
|
|||
}
|
||||
|
||||
impl eframe::App for Ui {
|
||||
fn on_close_event(&mut self) -> bool {
|
||||
self.modal = true;
|
||||
self.show_confirmation_dialog = true;
|
||||
self.allowed_to_close
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
if self.continuous_mode {
|
||||
ctx.request_repaint();
|
||||
|
@ -111,31 +107,31 @@ impl eframe::App for Ui {
|
|||
});
|
||||
|
||||
egui::TopBottomPanel::top("dark_light").show(ctx, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
egui::menu::bar(ui, |ui| {
|
||||
let reload_button = ui.button("Reload");
|
||||
ui.vertical(|ui| {
|
||||
egui::menu::bar(ui, |ui| {
|
||||
let reload_button = ui.button("Reload");
|
||||
|
||||
if reload_button.clicked() {
|
||||
self.reload(ctx);
|
||||
}
|
||||
if reload_button.clicked() {
|
||||
self.reload(ctx);
|
||||
}
|
||||
|
||||
if reload_button.hovered() {
|
||||
if let Some(loader_thread) = &self.loader_thread {
|
||||
if !loader_thread.is_finished() {
|
||||
if reload_button.hovered() {
|
||||
if let Some(loader_thread) = &self.loader_thread {
|
||||
if !loader_thread.is_finished() {
|
||||
egui::show_tooltip(ctx, egui::Id::new("reload_tooltip"), |ui| {
|
||||
ui.label("Loading is still in progress.\nTo reload, please wait until previous loading has finished!");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
|
||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
||||
ui.heading("RustyBeans");
|
||||
});
|
||||
ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
|
||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
||||
ui.heading("RustyBeans");
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
if self.modal {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
|
@ -172,7 +168,7 @@ impl eframe::App for Ui {
|
|||
ui.horizontal(|ui| {
|
||||
if ui.button("Yes").clicked() {
|
||||
self.allowed_to_close = true;
|
||||
frame.close();
|
||||
// frame.close();
|
||||
}
|
||||
|
||||
if ui.button("No").clicked() {
|
||||
|
@ -417,7 +413,7 @@ impl eframe::App for Ui {
|
|||
Line::new(plot_points_to_owned(
|
||||
&plot_points.1,
|
||||
))
|
||||
.name(plot_points.0.to_owned()),
|
||||
.name(plot_points.0.to_owned()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -428,21 +424,21 @@ impl eframe::App for Ui {
|
|||
} else {
|
||||
ui.label("Select the config.toml to start");
|
||||
|
||||
if ui.button("Open file").clicked() {
|
||||
if let Some(path) = FileDialog::new()
|
||||
.add_filter("toml", &["toml"])
|
||||
.set_directory(
|
||||
match &env::current_dir() {
|
||||
Ok(path) => path.to_str(),
|
||||
Err(_) => None,
|
||||
}
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.pick_file()
|
||||
{
|
||||
self.picked_path = Some(path.display().to_string());
|
||||
}
|
||||
}
|
||||
// if ui.button("Open file").clicked() {
|
||||
// if let Some(path) = FileDialog::new()
|
||||
// .add_filter("toml", &["toml"])
|
||||
// .set_directory(
|
||||
// match &env::current_dir() {
|
||||
// Ok(path) => path.to_str(),
|
||||
// Err(_) => None,
|
||||
// }
|
||||
// .unwrap_or_default(),
|
||||
// )
|
||||
// .pick_file()
|
||||
// {
|
||||
// self.picked_path = Some(path.display().to_string());
|
||||
// }
|
||||
// }
|
||||
|
||||
if ui.button("Open default config.toml").clicked() {
|
||||
self.picked_path = Some(String::from("config.toml"));
|
||||
|
@ -454,4 +450,10 @@ impl eframe::App for Ui {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
// fn on_close_event(&mut self) -> bool {
|
||||
// self.modal = true;
|
||||
// self.show_confirmation_dialog = true;
|
||||
// self.allowed_to_close
|
||||
// }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue