mirror of
https://git.alemi.dev/dashboard.git
synced 2024-11-14 11:59:18 +01:00
feat: floating windows to edit stuff
allow editing models via floating windows. Also added weird buttons to create new models. Still need to do relations!
This commit is contained in:
parent
3fcf059095
commit
76772465a3
9 changed files with 265 additions and 121 deletions
|
@ -13,7 +13,7 @@ pub struct Model {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub source_id: i64,
|
pub source_id: i64,
|
||||||
pub query_x: Option<String>,
|
pub query_x: String,
|
||||||
pub query_y: String,
|
pub query_y: String,
|
||||||
pub panel_id: i64,
|
pub panel_id: i64,
|
||||||
pub color: i32,
|
pub color: i32,
|
||||||
|
@ -29,14 +29,8 @@ impl ActiveModelBehavior for ActiveModel {}
|
||||||
impl Model {
|
impl Model {
|
||||||
pub fn extract(&self, value: &serde_json::Value) -> Result<PlotPoint, FetchError> {
|
pub fn extract(&self, value: &serde_json::Value) -> Result<PlotPoint, FetchError> {
|
||||||
let x: f64;
|
let x: f64;
|
||||||
let fallback_query = "".into();
|
if self.query_x.len() > 0 {
|
||||||
let q_x = self.query_x.as_ref().unwrap_or(&fallback_query);
|
x = jql::walker(value, self.query_x.as_str())?
|
||||||
// TODO because of bad design, empty queries are
|
|
||||||
// empty strings in my db. Rather than converting
|
|
||||||
// them right away, I'm putting this jank fix:
|
|
||||||
// checking len
|
|
||||||
if q_x.len() > 0 {
|
|
||||||
x = jql::walker(value, q_x.as_str())?
|
|
||||||
.as_f64()
|
.as_f64()
|
||||||
.ok_or(FetchError::JQLError("X query is null".to_string()))?; // TODO what if it's given to us as a string?
|
.ok_or(FetchError::JQLError("X query is null".to_string()))?; // TODO what if it's given to us as a string?
|
||||||
} else {
|
} else {
|
||||||
|
@ -55,7 +49,7 @@ impl Default for Model {
|
||||||
id: 0,
|
id: 0,
|
||||||
name: "".into(),
|
name: "".into(),
|
||||||
source_id: 0,
|
source_id: 0,
|
||||||
query_x: None,
|
query_x: "".into(),
|
||||||
query_y: "".into(),
|
query_y: "".into(),
|
||||||
panel_id: 0,
|
panel_id: 0,
|
||||||
color: 0,
|
color: 0,
|
||||||
|
|
|
@ -36,8 +36,7 @@ pub fn _metric_display_ui(ui: &mut Ui, metric: &entities::metrics::Model, _width
|
||||||
pub fn metric_edit_ui(ui: &mut Ui, metric: &entities::metrics::Model, panels: Option<&Vec<entities::panels::Model>>, width: f32) {
|
pub fn metric_edit_ui(ui: &mut Ui, metric: &entities::metrics::Model, panels: Option<&Vec<entities::panels::Model>>, width: f32) {
|
||||||
let text_width = width - 195.0;
|
let text_width = width - 195.0;
|
||||||
let mut name = metric.name.clone();
|
let mut name = metric.name.clone();
|
||||||
let def_str = "".into();
|
let mut query_x = metric.query_x.clone();
|
||||||
let mut query_x = metric.query_x.as_ref().unwrap_or(&def_str).clone();
|
|
||||||
let mut query_y = metric.query_y.clone();
|
let mut query_y = metric.query_y.clone();
|
||||||
let mut panel_id = 0;
|
let mut panel_id = 0;
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use eframe::egui::{CentralPanel, Context, SidePanel, TopBottomPanel, Window};
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{data::entities, worker::visualizer::AppStateView};
|
use crate::{data::entities, worker::{visualizer::AppStateView, BackgroundAction}};
|
||||||
use panel::main_content;
|
use panel::main_content;
|
||||||
use scaffold::{
|
use scaffold::{
|
||||||
// confirmation_popup_delete_metric, confirmation_popup_delete_source, footer,
|
// confirmation_popup_delete_metric, confirmation_popup_delete_source, footer,
|
||||||
|
@ -17,7 +17,7 @@ use scaffold::{
|
||||||
};
|
};
|
||||||
use source::source_panel;
|
use source::source_panel;
|
||||||
|
|
||||||
use self::scaffold::footer;
|
use self::scaffold::{footer, popup_edit_ui, EditingModel};
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
view: AppStateView,
|
view: AppStateView,
|
||||||
|
@ -29,11 +29,12 @@ pub struct App {
|
||||||
width_tx: watch::Sender<i64>,
|
width_tx: watch::Sender<i64>,
|
||||||
logger_view: watch::Receiver<Vec<String>>,
|
logger_view: watch::Receiver<Vec<String>>,
|
||||||
|
|
||||||
buffer_panel: entities::panels::Model,
|
// buffer_panel: entities::panels::Model,
|
||||||
buffer_source: entities::sources::Model,
|
buffer_source: entities::sources::Model,
|
||||||
buffer_metric: entities::metrics::Model,
|
// buffer_metric: entities::metrics::Model,
|
||||||
|
|
||||||
edit: bool,
|
edit: bool,
|
||||||
|
editing: Vec<EditingModel>,
|
||||||
sidebar: bool,
|
sidebar: bool,
|
||||||
padding: bool,
|
padding: bool,
|
||||||
// windows: Vec<Window<'open>>,
|
// windows: Vec<Window<'open>>,
|
||||||
|
@ -51,11 +52,10 @@ impl App {
|
||||||
let panels = view.panels.borrow().clone();
|
let panels = view.panels.borrow().clone();
|
||||||
Self {
|
Self {
|
||||||
db_path, interval, panels, width_tx, view, logger_view,
|
db_path, interval, panels, width_tx, view, logger_view,
|
||||||
buffer_panel: entities::panels::Model::default(),
|
|
||||||
buffer_source: entities::sources::Model::default(),
|
buffer_source: entities::sources::Model::default(),
|
||||||
buffer_metric: entities::metrics::Model::default(),
|
|
||||||
last_redraw: 0,
|
last_redraw: 0,
|
||||||
edit: false,
|
edit: false,
|
||||||
|
editing: vec![],
|
||||||
sidebar: true,
|
sidebar: true,
|
||||||
padding: false,
|
padding: false,
|
||||||
// windows: vec![],
|
// windows: vec![],
|
||||||
|
@ -75,6 +75,12 @@ impl App {
|
||||||
error!(target: "app", "Could not request flush: {:?}", e);
|
error!(target: "app", "Could not request flush: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn op(&self, op: BackgroundAction) {
|
||||||
|
if let Err(e) = self.view.op.blocking_send(op) {
|
||||||
|
error!(target: "app", "Could not send operation: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl eframe::App for App {
|
impl eframe::App for App {
|
||||||
|
@ -87,20 +93,9 @@ impl eframe::App for App {
|
||||||
footer(ctx, ui, self.logger_view.clone(), self.db_path.clone(), self.view.points.borrow().len());
|
footer(ctx, ui, self.logger_view.clone(), self.db_path.clone(), self.view.points.borrow().len());
|
||||||
});
|
});
|
||||||
|
|
||||||
let _w = Window::new("a");
|
for m in self.editing.iter_mut() {
|
||||||
|
Window::new(m.id_repr()).show(ctx, |ui| popup_edit_ui(ui, m));
|
||||||
// if let Some(index) = self.deleting_metric {
|
}
|
||||||
// Window::new(format!("Delete Metric #{}?", index))
|
|
||||||
// .show(ctx, |ui| confirmation_popup_delete_metric(self, ui, index));
|
|
||||||
// }
|
|
||||||
// if let Some(index) = self.deleting_source {
|
|
||||||
// Window::new(format!("Delete Source #{}?", index))
|
|
||||||
// .show(ctx, |ui| confirmation_popup_delete_source(self, ui, index));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for window in self.windows {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
if self.sidebar {
|
if self.sidebar {
|
||||||
SidePanel::left("sources-bar")
|
SidePanel::left("sources-bar")
|
||||||
|
@ -113,7 +108,7 @@ impl eframe::App for App {
|
||||||
main_content(self, ctx, ui);
|
main_content(self, ctx, ui);
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(viewsize) = self.panels.iter().map(|p| p.view_size).max() {
|
if let Some(viewsize) = self.panels.iter().map(|p| p.view_size + p.view_offset).max() {
|
||||||
if let Err(e) = self.width_tx.send(viewsize as i64) {
|
if let Err(e) = self.width_tx.send(viewsize as i64) {
|
||||||
error!(target: "app", "Could not update fetch size : {:?}", e);
|
error!(target: "app", "Could not update fetch size : {:?}", e);
|
||||||
}
|
}
|
||||||
|
@ -123,5 +118,13 @@ impl eframe::App for App {
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
self.last_redraw = Utc::now().timestamp();
|
self.last_redraw = Utc::now().timestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for m in self.editing.iter() {
|
||||||
|
if m.should_fetch() {
|
||||||
|
self.op(m.to_msg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.editing.retain(|v| v.modifying());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ pub fn main_content(app: &mut App, ctx: &Context, ui: &mut Ui) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn panel_edit_inline_ui(_ui: &mut Ui, _panel: &entities::panels::Model) {
|
pub fn _panel_edit_inline_ui(_ui: &mut Ui, _panel: &entities::panels::Model) {
|
||||||
// TextEdit::singleline(&mut panel.name)
|
// TextEdit::singleline(&mut panel.name)
|
||||||
// .hint_text("name")
|
// .hint_text("name")
|
||||||
// .desired_width(100.0)
|
// .desired_width(100.0)
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use eframe::{Frame, egui::{collapsing_header::CollapsingState, Context, Ui, Layout, ScrollArea, global_dark_light_mode_switch}, emath::Align};
|
use eframe::{Frame, egui::{collapsing_header::CollapsingState, Context, Ui, Layout, ScrollArea, global_dark_light_mode_switch, TextEdit, Checkbox, Slider}, emath::Align};
|
||||||
|
use sea_orm::{Set, Unchanged, ActiveValue::NotSet};
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
|
|
||||||
use crate::gui::App;
|
use crate::{gui::App, data::entities, util::unpack_color, worker::BackgroundAction};
|
||||||
|
|
||||||
use super::panel::panel_edit_inline_ui;
|
|
||||||
|
|
||||||
// TODO make this not super specific!
|
// TODO make this not super specific!
|
||||||
pub fn _confirmation_popup_delete_metric(_app: &mut App, ui: &mut Ui, _metric_index: usize) {
|
pub fn _confirmation_popup_delete_metric(_app: &mut App, ui: &mut Ui, _metric_index: usize) {
|
||||||
|
@ -58,6 +57,166 @@ pub fn _confirmation_popup_delete_source(_app: &mut App, ui: &mut Ui, _source_in
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct EditingModel {
|
||||||
|
pub id: i64,
|
||||||
|
m: EditingModelType,
|
||||||
|
new: bool,
|
||||||
|
valid: bool,
|
||||||
|
ready: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EditingModel {
|
||||||
|
pub fn id_repr(&self) -> String {
|
||||||
|
let prefix = match self.m {
|
||||||
|
EditingModelType::EditingPanel { panel: _ } => "panel",
|
||||||
|
EditingModelType::EditingSource { source: _ } => "source",
|
||||||
|
EditingModelType::EditingMetric { metric: _ } => "metric",
|
||||||
|
};
|
||||||
|
format!("edit_{}_{}", prefix, self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_fetch(&self) -> bool {
|
||||||
|
return self.ready && self.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modifying(&self) -> bool {
|
||||||
|
return !self.ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_msg(&self) -> BackgroundAction {
|
||||||
|
match &self.m {
|
||||||
|
EditingModelType::EditingPanel { panel } =>
|
||||||
|
BackgroundAction::UpdatePanel {
|
||||||
|
panel: entities::panels::ActiveModel {
|
||||||
|
id: if self.new { NotSet } else { Unchanged(panel.id) },
|
||||||
|
name: Set(panel.name.clone()),
|
||||||
|
view_scroll: Set(panel.view_scroll),
|
||||||
|
view_size: Set(panel.view_size),
|
||||||
|
timeserie: Set(panel.timeserie),
|
||||||
|
height: Set(panel.height),
|
||||||
|
limit_view: Set(panel.limit_view),
|
||||||
|
position: Set(panel.position),
|
||||||
|
reduce_view: Set(panel.reduce_view),
|
||||||
|
view_chunks: Set(panel.view_chunks),
|
||||||
|
shift_view: Set(panel.shift_view),
|
||||||
|
view_offset: Set(panel.view_offset),
|
||||||
|
average_view: Set(panel.average_view),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
EditingModelType::EditingSource { source } =>
|
||||||
|
BackgroundAction::UpdateSource {
|
||||||
|
source: entities::sources::ActiveModel {
|
||||||
|
id: if self.new { NotSet } else { Unchanged(source.id) },
|
||||||
|
name: Set(source.name.clone()),
|
||||||
|
enabled: Set(source.enabled),
|
||||||
|
url: Set(source.url.clone()),
|
||||||
|
interval: Set(source.interval),
|
||||||
|
last_update: Set(source.last_update),
|
||||||
|
position: Set(source.position),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
EditingModelType::EditingMetric { metric } =>
|
||||||
|
BackgroundAction::UpdateMetric {
|
||||||
|
metric: entities::metrics::ActiveModel {
|
||||||
|
id: if self.new { NotSet} else { Unchanged(metric.id) },
|
||||||
|
name: Set(metric.name.clone()),
|
||||||
|
source_id: Set(metric.source_id),
|
||||||
|
color: Set(metric.color),
|
||||||
|
panel_id: Set(metric.panel_id),
|
||||||
|
query_x: Set(metric.query_x.clone()),
|
||||||
|
query_y: Set(metric.query_y.clone()),
|
||||||
|
position: Set(metric.position),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<entities::sources::Model> for EditingModel {
|
||||||
|
fn from(s: entities::sources::Model) -> Self {
|
||||||
|
EditingModel {
|
||||||
|
new: if s.id == 0 { true } else { false },
|
||||||
|
id: s.id, m: EditingModelType::EditingSource { source: s }, valid: false, ready: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<entities::metrics::Model> for EditingModel {
|
||||||
|
fn from(m: entities::metrics::Model) -> Self {
|
||||||
|
EditingModel {
|
||||||
|
new: if m.id == 0 { true } else { false },
|
||||||
|
id: m.id, m: EditingModelType::EditingMetric { metric: m }, valid: false, ready: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<entities::panels::Model> for EditingModel {
|
||||||
|
fn from(p: entities::panels::Model) -> Self {
|
||||||
|
EditingModel {
|
||||||
|
new: if p.id == 0 { true } else { false },
|
||||||
|
id: p.id, m: EditingModelType::EditingPanel { panel: p }, valid: false, ready: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum EditingModelType {
|
||||||
|
EditingPanel { panel : entities::panels::Model },
|
||||||
|
EditingSource { source: entities::sources::Model },
|
||||||
|
EditingMetric { metric: entities::metrics::Model },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn popup_edit_ui(ui: &mut Ui, model: &mut EditingModel) {
|
||||||
|
match &mut model.m {
|
||||||
|
EditingModelType::EditingPanel { panel } => {
|
||||||
|
ui.heading(format!("Edit panel #{}", panel.id));
|
||||||
|
TextEdit::singleline(&mut panel.name)
|
||||||
|
.hint_text("name")
|
||||||
|
.show(ui);
|
||||||
|
},
|
||||||
|
EditingModelType::EditingSource { source } => {
|
||||||
|
ui.heading(format!("Edit source #{}", source.id));
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.add(Checkbox::new(&mut source.enabled, ""));
|
||||||
|
TextEdit::singleline(&mut source.name)
|
||||||
|
.hint_text("name")
|
||||||
|
.show(ui);
|
||||||
|
});
|
||||||
|
TextEdit::singleline(&mut source.url)
|
||||||
|
.hint_text("url")
|
||||||
|
.show(ui);
|
||||||
|
ui.add(Slider::new(&mut source.interval, 1..=3600).text("interval"));
|
||||||
|
},
|
||||||
|
EditingModelType::EditingMetric { metric } => {
|
||||||
|
ui.heading(format!("Edit metric #{}", metric.id));
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.color_edit_button_srgba(&mut unpack_color(metric.color));
|
||||||
|
TextEdit::singleline(&mut metric.name)
|
||||||
|
.hint_text("name")
|
||||||
|
.show(ui);
|
||||||
|
});
|
||||||
|
TextEdit::singleline(&mut metric.query_x)
|
||||||
|
.hint_text("x")
|
||||||
|
.show(ui);
|
||||||
|
TextEdit::singleline(&mut metric.query_y)
|
||||||
|
.hint_text("y")
|
||||||
|
.show(ui);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button(" save ").clicked() {
|
||||||
|
model.valid = true;
|
||||||
|
model.ready = true;
|
||||||
|
}
|
||||||
|
ui.with_layout(Layout::top_down(Align::RIGHT), |ui| {
|
||||||
|
if ui.button(" close ").clicked() {
|
||||||
|
model.valid = false;
|
||||||
|
model.ready = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn header(app: &mut App, ui: &mut Ui, frame: &mut Frame) {
|
pub fn header(app: &mut App, ui: &mut Ui, frame: &mut Frame) {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
global_dark_light_mode_switch(ui);
|
global_dark_light_mode_switch(ui);
|
||||||
|
@ -77,17 +236,29 @@ pub fn header(app: &mut App, ui: &mut Ui, frame: &mut Frame) {
|
||||||
app.refresh_data();
|
app.refresh_data();
|
||||||
}
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.checkbox(&mut app.edit, "edit");
|
if ui.button("new panel").clicked() {
|
||||||
if app.edit {
|
app.editing.push(entities::panels::Model::default().into());
|
||||||
ui.label("+ panel");
|
|
||||||
panel_edit_inline_ui(ui, &mut app.buffer_panel);
|
|
||||||
if ui.button("add").clicked() {
|
|
||||||
// if let Err(e) = app.data.add_panel(&app.input_panel) {
|
|
||||||
// error!(target: "ui", "Failed to add panel: {:?}", e);
|
|
||||||
// };
|
|
||||||
}
|
|
||||||
ui.separator();
|
|
||||||
}
|
}
|
||||||
|
ui.separator();
|
||||||
|
if ui.button("new source").clicked() {
|
||||||
|
app.editing.push(entities::sources::Model::default().into());
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
if ui.button("new metric").clicked() {
|
||||||
|
app.editing.push(entities::metrics::Model::default().into());
|
||||||
|
}
|
||||||
|
// ui.separator();
|
||||||
|
// ui.checkbox(&mut app.edit, "edit");
|
||||||
|
// if app.edit {
|
||||||
|
// ui.label("+ panel");
|
||||||
|
// panel_edit_inline_ui(ui, &mut app.buffer_panel);
|
||||||
|
// if ui.button("add").clicked() {
|
||||||
|
// // if let Err(e) = app.data.add_panel(&app.input_panel) {
|
||||||
|
// // error!(target: "ui", "Failed to add panel: {:?}", e);
|
||||||
|
// // };
|
||||||
|
// }
|
||||||
|
// ui.separator();
|
||||||
|
// }
|
||||||
ui.with_layout(Layout::top_down(Align::RIGHT), |ui| {
|
ui.with_layout(Layout::top_down(Align::RIGHT), |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if ui.small_button("×").clicked() {
|
if ui.small_button("×").clicked() {
|
||||||
|
|
|
@ -3,16 +3,14 @@ use eframe::{
|
||||||
emath::Align,
|
emath::Align,
|
||||||
};
|
};
|
||||||
use rfd::FileDialog;
|
use rfd::FileDialog;
|
||||||
use tracing::error;
|
|
||||||
|
|
||||||
use crate::util::deserialize_values;
|
|
||||||
use crate::gui::App;
|
use crate::gui::App;
|
||||||
use crate::data::entities;
|
use crate::data::entities;
|
||||||
|
|
||||||
use super::metric::metric_edit_ui;
|
use super::metric::metric_edit_ui;
|
||||||
|
|
||||||
pub fn source_panel(app: &mut App, ui: &mut Ui) {
|
pub fn source_panel(app: &mut App, ui: &mut Ui) {
|
||||||
let mut source_to_put_metric_on : Option<i64> = None;
|
let source_to_put_metric_on : Option<i64> = None;
|
||||||
let mut to_swap: Option<usize> = None;
|
let mut to_swap: Option<usize> = None;
|
||||||
let _to_insert: Vec<entities::metrics::Model> = Vec::new();
|
let _to_insert: Vec<entities::metrics::Model> = Vec::new();
|
||||||
// let mut to_delete: Option<usize> = None;
|
// let mut to_delete: Option<usize> = None;
|
||||||
|
@ -48,8 +46,8 @@ pub fn source_panel(app: &mut App, ui: &mut Ui) {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
source_edit_ui(ui, source, remaining_width - 34.0);
|
source_edit_ui(ui, source, remaining_width - 34.0);
|
||||||
if ui.small_button("×").clicked() {
|
if ui.small_button("×").clicked() {
|
||||||
// app.deleting_metric = None;
|
// TODO don't add duplicates
|
||||||
// app.deleting_source = Some(i);
|
app.editing.push(source.clone().into());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let metrics = app
|
let metrics = app
|
||||||
|
@ -83,65 +81,12 @@ pub fn source_panel(app: &mut App, ui: &mut Ui) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ui.small_button("×").clicked() {
|
if ui.small_button("×").clicked() {
|
||||||
// app.deleting_source = None;
|
// TODO don't add duplicates
|
||||||
// app.deleting_metric = Some(j);
|
app.editing.push(metric.clone().into());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui.horizontal(|ui| {
|
|
||||||
metric_edit_ui(
|
|
||||||
ui,
|
|
||||||
&mut app.buffer_metric,
|
|
||||||
None,
|
|
||||||
remaining_width - 53.0,
|
|
||||||
);
|
|
||||||
ui.add_space(2.0);
|
|
||||||
if ui.small_button(" + ").clicked() {
|
|
||||||
source_to_put_metric_on = Some(source.id);
|
|
||||||
}
|
|
||||||
ui.add_space(1.0); // DAMN!
|
|
||||||
if ui.small_button("o").clicked() {
|
|
||||||
let path = FileDialog::new()
|
|
||||||
.add_filter("csv", &["csv"])
|
|
||||||
.pick_file();
|
|
||||||
if let Some(path) = path {
|
|
||||||
match deserialize_values(path) {
|
|
||||||
Ok((_name, _query_x, _query_y, _data)) => {
|
|
||||||
// let mut store = app
|
|
||||||
// .data
|
|
||||||
// .storage
|
|
||||||
// .lock()
|
|
||||||
// .expect("Storage Mutex poisoned");
|
|
||||||
// match store.new_metric(
|
|
||||||
// name.as_str(),
|
|
||||||
// source.id,
|
|
||||||
// query_x.as_str(),
|
|
||||||
// query_y.as_str(),
|
|
||||||
// -1,
|
|
||||||
// Color32::TRANSPARENT,
|
|
||||||
// metrics.len() as i32,
|
|
||||||
// ) {
|
|
||||||
// Ok(verified_metric) => {
|
|
||||||
// store.put_values(verified_metric.id, &data).unwrap ();
|
|
||||||
// *verified_metric.data.write().expect("Values RwLock poisoned") = data;
|
|
||||||
// to_insert.push(verified_metric);
|
|
||||||
// }
|
|
||||||
// Err(e) => {
|
|
||||||
// error!(target: "ui", "could not save metric into archive : {:?}", e);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!(target: "ui", "Could not deserialize metric from file : {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ui.small_button("×").clicked() {
|
|
||||||
app.buffer_metric = entities::metrics::Model::default();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -175,9 +175,9 @@ fn main() {
|
||||||
native_options,
|
native_options,
|
||||||
Box::new(
|
Box::new(
|
||||||
move |cc| {
|
move |cc| {
|
||||||
ctx_tx.send(Some(cc.egui_ctx.clone())).unwrap_or_else(|_| {
|
if let Err(_e) = ctx_tx.send(Some(cc.egui_ctx.clone())) {
|
||||||
error!(target: "launcher", "Could not share reference to egui context (won't be able to periodically refresh window)");
|
error!(target: "launcher", "Could not share reference to egui context (won't be able to periodically refresh window)");
|
||||||
});
|
};
|
||||||
Box::new(
|
Box::new(
|
||||||
App::new(
|
App::new(
|
||||||
cc,
|
cc,
|
||||||
|
|
|
@ -13,9 +13,8 @@ const _PREFIXES: &'static [&'static str] = &["", "k", "M", "G", "T"];
|
||||||
pub fn _serialize_values(values: &Vec<PlotPoint>, metric: &entities::metrics::Model, path: PathBuf) -> Result<(), Box<dyn Error>> {
|
pub fn _serialize_values(values: &Vec<PlotPoint>, metric: &entities::metrics::Model, path: PathBuf) -> Result<(), Box<dyn Error>> {
|
||||||
let mut wtr = csv::Writer::from_writer(std::fs::File::create(path)?);
|
let mut wtr = csv::Writer::from_writer(std::fs::File::create(path)?);
|
||||||
// DAMN! VVVVV
|
// DAMN! VVVVV
|
||||||
let def_q_x = "".into();
|
|
||||||
let name = metric.name.as_str();
|
let name = metric.name.as_str();
|
||||||
let q_x = metric.query_x.as_ref().unwrap_or(&def_q_x).as_str();
|
let q_x = metric.query_x.as_str();
|
||||||
let q_y = metric.query_y.as_str();
|
let q_y = metric.query_y.as_str();
|
||||||
wtr.write_record(&[name, q_x, q_y])?;
|
wtr.write_record(&[name, q_x, q_y])?;
|
||||||
// DAMN! AAAAA
|
// DAMN! AAAAA
|
||||||
|
@ -26,7 +25,7 @@ pub fn _serialize_values(values: &Vec<PlotPoint>, metric: &entities::metrics::Mo
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize_values(path: PathBuf) -> Result<(String, String, String, Vec<PlotPoint>), Box<dyn Error>> {
|
pub fn _deserialize_values(path: PathBuf) -> Result<(String, String, String, Vec<PlotPoint>), Box<dyn Error>> {
|
||||||
let mut values = Vec::new();
|
let mut values = Vec::new();
|
||||||
|
|
||||||
let mut rdr = csv::Reader::from_reader(std::fs::File::open(path)?);
|
let mut rdr = csv::Reader::from_reader(std::fs::File::open(path)?);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use sea_orm::{TransactionTrait, DatabaseConnection, EntityTrait, Condition, ColumnTrait, QueryFilter, Set, QueryOrder, Order};
|
use sea_orm::{TransactionTrait, DatabaseConnection, EntityTrait, Condition, ColumnTrait, QueryFilter, Set, QueryOrder, Order, ActiveModelTrait, ActiveValue::NotSet};
|
||||||
use tokio::sync::{watch, mpsc};
|
use tokio::sync::{watch, mpsc};
|
||||||
use tracing::{info, error};
|
use tracing::{info, error};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -17,7 +17,7 @@ pub struct AppStateView {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppStateView {
|
impl AppStateView {
|
||||||
pub async fn _request_flush(&self) -> bool {
|
pub async fn request_flush(&self) -> bool {
|
||||||
match self.flush.send(()).await {
|
match self.flush.send(()).await {
|
||||||
Ok(_) => true,
|
Ok(_) => true,
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
|
@ -109,17 +109,23 @@ impl AppState {
|
||||||
|
|
||||||
pub async fn fetch(&mut self, db: &DatabaseConnection) -> Result<(), sea_orm::DbErr> {
|
pub async fn fetch(&mut self, db: &DatabaseConnection) -> Result<(), sea_orm::DbErr> {
|
||||||
// TODO parallelize all this stuff
|
// TODO parallelize all this stuff
|
||||||
self.panels = entities::panels::Entity::find().all(db).await?;
|
self.panels = entities::panels::Entity::find()
|
||||||
|
.order_by(entities::panels::Column::Position, Order::Asc)
|
||||||
|
.all(db).await?;
|
||||||
if let Err(e) = self.tx.panels.send(self.panels.clone()) {
|
if let Err(e) = self.tx.panels.send(self.panels.clone()) {
|
||||||
error!(target: "worker", "Could not send panels update: {:?}", e);
|
error!(target: "worker", "Could not send panels update: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sources = entities::sources::Entity::find().all(db).await?;
|
self.sources = entities::sources::Entity::find()
|
||||||
|
.order_by(entities::sources::Column::Position, Order::Asc)
|
||||||
|
.all(db).await?;
|
||||||
if let Err(e) = self.tx.sources.send(self.sources.clone()) {
|
if let Err(e) = self.tx.sources.send(self.sources.clone()) {
|
||||||
error!(target: "worker", "Could not send sources update: {:?}", e);
|
error!(target: "worker", "Could not send sources update: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.metrics = entities::metrics::Entity::find().all(db).await?;
|
self.metrics = entities::metrics::Entity::find()
|
||||||
|
.order_by(entities::metrics::Column::Position, Order::Asc)
|
||||||
|
.all(db).await?;
|
||||||
if let Err(e) = self.tx.metrics.send(self.metrics.clone()) {
|
if let Err(e) = self.tx.metrics.send(self.metrics.clone()) {
|
||||||
error!(target: "worker", "Could not send metrics update: {:?}", e);
|
error!(target: "worker", "Could not send metrics update: {:?}", e);
|
||||||
}
|
}
|
||||||
|
@ -178,6 +184,30 @@ impl AppState {
|
||||||
self.panels = panels;
|
self.panels = panels;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
BackgroundAction::UpdatePanel { panel } => {
|
||||||
|
let op = if panel.id == NotSet { panel.insert(&db) } else { panel.update(&db) };
|
||||||
|
if let Err(e) = op.await {
|
||||||
|
error!(target: "worker", "Could not update panel: {:?}", e);
|
||||||
|
} else {
|
||||||
|
self.view.request_flush().await;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BackgroundAction::UpdateSource { source } => {
|
||||||
|
let op = if source.id == NotSet { source.insert(&db) } else { source.update(&db) };
|
||||||
|
if let Err(e) = op.await {
|
||||||
|
error!(target: "worker", "Could not update source: {:?}", e);
|
||||||
|
} else {
|
||||||
|
self.view.request_flush().await;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BackgroundAction::UpdateMetric { metric } => {
|
||||||
|
let op = if metric.id == NotSet { metric.insert(&db) } else { metric.update(&db) };
|
||||||
|
if let Err(e) = op.await {
|
||||||
|
error!(target: "worker", "Could not update metric: {:?}", e);
|
||||||
|
} else {
|
||||||
|
self.view.request_flush().await;
|
||||||
|
}
|
||||||
|
},
|
||||||
// _ => todo!(),
|
// _ => todo!(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -300,7 +330,10 @@ impl AppState {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum BackgroundAction {
|
pub enum BackgroundAction {
|
||||||
UpdateAllPanels { panels: Vec<entities::panels::Model> },
|
UpdateAllPanels { panels: Vec<entities::panels::Model> },
|
||||||
// UpdatePanel { panel : entities::panels::ActiveModel },
|
UpdatePanel { panel : entities::panels::ActiveModel },
|
||||||
// UpdateSource { source: entities::sources::ActiveModel },
|
UpdateSource { source: entities::sources::ActiveModel },
|
||||||
// UpdateMetric { metric: entities::metrics::ActiveModel },
|
UpdateMetric { metric: entities::metrics::ActiveModel },
|
||||||
|
// InsertPanel { panel : entities::panels::ActiveModel },
|
||||||
|
// InsertSource { source: entities::sources::ActiveModel },
|
||||||
|
// InsertMetric { metric: entities::metrics::ActiveModel },
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue