feat: added relations, added window uis to edit them all

This commit is contained in:
əlemi 2022-11-03 02:07:21 +01:00
parent 76772465a3
commit 32d68691a1
Signed by: alemi
GPG key ID: A4895B84D311642C
16 changed files with 435 additions and 78 deletions

View file

@ -2,6 +2,8 @@ pub use sea_orm_migration::prelude::*;
mod m20220101_000001_create_table;
mod m20221030_192706_add_last_update;
mod m20221102_232244_add_join_table;
mod m20221102_232858_remove_unused_columns;
pub struct Migrator;
@ -9,8 +11,10 @@ pub struct Migrator;
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(m20220101_000001_create_table::Migration),
Box::new(m20221030_192706_add_last_update::Migration),
]
Box::new(m20220101_000001_create_table::Migration),
Box::new(m20221030_192706_add_last_update::Migration),
Box::new(m20221102_232244_add_join_table::Migration),
Box::new(m20221102_232858_remove_unused_columns::Migration),
]
}
}

View file

@ -0,0 +1,40 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(PanelMetric::Table)
.if_not_exists()
.col(
ColumnDef::new(PanelMetric::Id)
.big_integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(PanelMetric::PanelId).big_integer().not_null())
.col(ColumnDef::new(PanelMetric::MetricId).big_integer().not_null())
.to_owned(),
).await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(PanelMetric::Table).to_owned())
.await
}
}
#[derive(Iden)]
enum PanelMetric {
Table,
Id,
PanelId,
MetricId,
}

View file

@ -0,0 +1,85 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.
alter_table(
Table::alter()
.table(Panels::Table)
.drop_column(Panels::Width)
.drop_column(Panels::LimitView)
.drop_column(Panels::ShiftView)
.to_owned()
)
.await?;
manager.
alter_table(
Table::alter()
.table(Metrics::Table)
.drop_column(Metrics::PanelId)
.to_owned()
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Panels::Table)
.add_column(
ColumnDef::new(Panels::Width)
.integer()
.not_null()
.default(100)
)
.add_column(
ColumnDef::new(Panels::LimitView)
.boolean()
.not_null()
.default(true)
)
.add_column(
ColumnDef::new(Panels::ShiftView)
.boolean()
.not_null()
.default(false)
)
.to_owned()
)
.await?;
manager
.alter_table(
Table::alter()
.table(Metrics::Table)
.add_column(
ColumnDef::new(Metrics::PanelId)
.big_integer()
.not_null()
.default(0)
)
.to_owned()
)
.await?;
Ok(())
}
}
#[derive(Iden)]
enum Panels {
Table,
Width,
LimitView,
ShiftView,
}
#[derive(Iden)]
enum Metrics {
Table,
PanelId,
}

View file

@ -15,17 +15,43 @@ pub struct Model {
pub source_id: i64,
pub query_x: String,
pub query_y: String,
pub panel_id: i64,
pub color: i32,
pub position: i32,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
pub enum Relation {
#[sea_orm(
belongs_to = "super::sources::Entity",
from = "Column::SourceId",
to = "super::sources::Column::Id"
)]
Source,
#[sea_orm(has_many = "super::points::Entity")]
Point,
}
impl Related<super::sources::Entity> for Entity {
fn to() -> RelationDef { Relation::Source.def() }
}
impl Related<super::points::Entity> for Entity {
fn to() -> RelationDef { Relation::Point.def() }
}
impl Related<super::panels::Entity> for Entity {
fn to() -> RelationDef {
super::panel_metric::Relation::Panel.def()
}
fn via() -> Option<RelationDef> {
Some(super::panel_metric::Relation::Metric.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}
impl Model {
pub fn extract(&self, value: &serde_json::Value) -> Result<PlotPoint, FetchError> {
let x: f64;
@ -51,7 +77,6 @@ impl Default for Model {
source_id: 0,
query_x: "".into(),
query_y: "".into(),
panel_id: 0,
color: 0,
position: 0,
}

View file

@ -2,7 +2,8 @@
pub mod prelude;
pub mod metrics;
pub mod panels;
pub mod panel_metric;
pub mod metrics;
pub mod points;
pub mod sources;

View file

@ -0,0 +1,29 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "panel_metric")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = true)]
pub id: i64,
pub panel_id: i64,
pub metric_id: i64,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::panels::Entity",
from = "Column::PanelId",
to = "super::panels::Column::Id"
)]
Panel,
#[sea_orm(
belongs_to = "super::metrics::Entity",
from = "Column::MetricId",
to = "super::metrics::Column::Id"
)]
Metric,
}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -12,11 +12,9 @@ pub struct Model {
pub view_size: i32,
pub timeserie: bool,
pub height: i32,
pub limit_view: bool,
pub position: i32,
pub reduce_view: bool,
pub view_chunks: i32,
pub shift_view: bool,
pub view_offset: i32,
pub average_view: bool,
}
@ -24,6 +22,16 @@ pub struct Model {
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl Related<super::metrics::Entity> for Entity {
fn to() -> RelationDef {
super::panel_metric::Relation::Metric.def()
}
fn via() -> Option<RelationDef> {
Some(super::panel_metric::Relation::Panel.def().rev())
}
}
impl ActiveModelBehavior for ActiveModel {}
impl Default for Model {
@ -35,11 +43,9 @@ impl Default for Model {
view_size: 1000,
timeserie: true,
height: 100,
limit_view: true,
position: 0,
reduce_view: false,
view_chunks: 10,
shift_view: false,
view_offset: 0,
average_view: true,
}

View file

@ -13,7 +13,18 @@ pub struct Model {
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
pub enum Relation {
#[sea_orm(
belongs_to = "super::metrics::Entity",
from = "Column::MetricId",
to = "super::metrics::Column::Id"
)]
Metric,
}
impl Related<super::metrics::Entity> for Entity {
fn to() -> RelationDef { Relation::Metric.def() }
}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -4,3 +4,4 @@ pub use super::metrics::Entity as Metrics;
pub use super::panels::Entity as Panels;
pub use super::points::Entity as Points;
pub use super::sources::Entity as Sources;
pub use super::panel_metric::Entity as PanelMetric;

View file

@ -15,7 +15,16 @@ pub struct Model {
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
pub enum Relation {
#[sea_orm(has_many = "super::metrics::Entity")]
Metric,
}
impl Related<super::metrics::Entity> for Entity {
fn to() -> RelationDef {
Relation::Metric.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -2,7 +2,7 @@ use eframe::{egui::{Ui, Layout, Sense, color_picker::show_color_at, ComboBox, Te
use crate::{data::entities, util::unpack_color};
fn _color_square(ui: &mut Ui, color:Color32) {
fn color_square(ui: &mut Ui, color:Color32) {
let size = ui.spacing().interact_size;
let (rect, response) = ui.allocate_exact_size(size, Sense::click());
if ui.is_rect_visible(rect) {
@ -19,11 +19,11 @@ fn _color_square(ui: &mut Ui, color:Color32) {
pub fn _metric_display_ui(ui: &mut Ui, metric: &entities::metrics::Model, _width: f32) {
ui.horizontal(|ui| {
_color_square(ui, unpack_color(metric.color));
color_square(ui, unpack_color(metric.color));
ui.label(&metric.name);
ui.with_layout(Layout::top_down(Align::RIGHT), |ui| {
ui.horizontal(|ui| {
ui.label(format!("panel: {}", metric.panel_id));
ui.label("panel: ???");
ui.label(format!("y: {}", metric.query_y));
// if let Some(query_x) = metric.query_x {
// ui.label(format!("x: {}", query_x));
@ -40,7 +40,8 @@ pub fn metric_edit_ui(ui: &mut Ui, metric: &entities::metrics::Model, panels: Op
let mut query_y = metric.query_y.clone();
let mut panel_id = 0;
ui.horizontal(|ui| {
ui.color_edit_button_srgba(&mut unpack_color(metric.color));
// ui.color_edit_button_srgba(&mut unpack_color(metric.color));
color_square(ui, unpack_color(metric.color));
TextEdit::singleline(&mut name)
.interactive(false)
.desired_width(text_width / 2.0)
@ -62,7 +63,7 @@ pub fn metric_edit_ui(ui: &mut Ui, metric: &entities::metrics::Model, panels: Op
if let Some(panels) = panels {
ComboBox::from_id_source(format!("panel-selector-{}", metric.id))
.width(60.0)
.selected_text(format!("panel: {:02}", metric.panel_id))
.selected_text("panel: ???")
.show_ui(ui, |ui| {
ui.selectable_value(&mut panel_id, -1, "None");
for p in panels {

View file

@ -17,7 +17,7 @@ use scaffold::{
};
use source::source_panel;
use self::scaffold::{footer, popup_edit_ui, EditingModel};
use self::scaffold::{footer, EditingModel, popup_edit_ui};
pub struct App {
view: AppStateView,
@ -94,7 +94,9 @@ impl eframe::App for App {
});
for m in self.editing.iter_mut() {
Window::new(m.id_repr()).show(ctx, |ui| popup_edit_ui(ui, m));
Window::new(m.id_repr())
.default_width(150.0)
.show(ctx, |ui| popup_edit_ui(ui, m, &self.view.sources.borrow(), &self.view.metrics.borrow()));
}
if self.sidebar {
@ -121,7 +123,7 @@ impl eframe::App for App {
for m in self.editing.iter() {
if m.should_fetch() {
self.op(m.to_msg());
self.op(m.to_msg(self.view.clone())); // TODO cloning is super wasteful
}
}

View file

@ -8,6 +8,8 @@ use crate::util::{timestamp_to_str, unpack_color};
use crate::gui::App;
use crate::data::entities;
use super::scaffold::EditingModel;
pub fn main_content(app: &mut App, ctx: &Context, ui: &mut Ui) {
let mut _to_swap: Option<usize> = None;
let mut _to_delete: Option<usize> = None;
@ -39,9 +41,9 @@ pub fn main_content(app: &mut App, ctx: &Context, ui: &mut Ui) {
// to_delete = Some(index); // TODO kinda jank but is there a better way?
// }
// ui.separator();
panel_title_ui(ui, panel, app.edit);
panel_title_ui(ui, panel, &mut app.editing, &app.view.metrics.borrow(), &app.view.panel_metric.borrow());
})
.body(|ui| panel_body_ui(ui, panel, &metrics, &app.view.points.borrow()));
.body(|ui| panel_body_ui(ui, panel, &metrics, &app.view.points.borrow(), &app.view.panel_metric.borrow()));
}
});
}
@ -53,10 +55,23 @@ pub fn _panel_edit_inline_ui(_ui: &mut Ui, _panel: &entities::panels::Model) {
// .show(ui);
}
pub fn panel_title_ui(ui: &mut Ui, panel: &mut entities::panels::Model, _edit: bool) { // TODO make edit UI in separate func
pub fn panel_title_ui(
ui: &mut Ui,
panel: &mut entities::panels::Model,
editing: &mut Vec<EditingModel>,
metrics: &Vec<entities::metrics::Model>,
panel_metric: &Vec<entities::panel_metric::Model>,
) { // TODO make edit UI in separate func
ui.horizontal(|ui| {
ui.heading(panel.name.as_str());
ui.separator();
if ui.small_button("#").clicked() {
// TODO don't add duplicates
editing.push(
EditingModel::make_edit_panel(panel.clone(), metrics, panel_metric)
);
}
ui.separator();
ui.add(Slider::new(&mut panel.height, 0..=500).text("height"));
//ui.separator();
//ui.checkbox(&mut panel.timeserie, "timeserie");
@ -93,26 +108,29 @@ pub fn panel_title_ui(ui: &mut Ui, panel: &mut entities::panels::Model, _edit: b
});
}
pub fn panel_body_ui(ui: &mut Ui, panel: &entities::panels::Model, metrics: &Vec<entities::metrics::Model>, points: &Vec<entities::points::Model>) {
pub fn panel_body_ui(
ui: &mut Ui,
panel: &entities::panels::Model,
metrics: &Vec<entities::metrics::Model>,
points: &Vec<entities::points::Model>,
panel_metric: &Vec<entities::panel_metric::Model>,
) {
let mut p = Plot::new(format!("plot-{}", panel.name))
.height(panel.height as f32)
.allow_scroll(false)
.legend(Legend::default().position(Corner::LeftTop));
if panel.limit_view {
if panel.view_scroll {
p = p.set_margin_fraction(Vec2 { x: 0.0, y: 0.1 });
}
if panel.timeserie {
if panel.view_scroll {
let _now = (Utc::now().timestamp() as f64) - (60.0 * panel.view_offset as f64);
p = p.include_x(_now);
if panel.limit_view {
p = p
.include_x(_now + (panel.view_size as f64 * 3.0))
.include_x(_now - (panel.view_size as f64 * 60.0)); // ??? TODO
}
let now = (Utc::now().timestamp() as f64) - (60.0 * panel.view_offset as f64);
p = p.include_x(now)
.include_x(now + (panel.view_size as f64 * 3.0))
.include_x(now - (panel.view_size as f64 * 60.0)); // ??? TODO
}
p = p
.x_axis_formatter(|x, _range| timestamp_to_str(x as i64, true, false))
@ -165,8 +183,9 @@ pub fn panel_body_ui(ui: &mut Ui, panel: &entities::panels::Model, metrics: &Vec
let min_x = now - size - off;
let max_x = now - off;
let chunk_size = if panel.reduce_view { Some(panel.view_chunks) } else { None };
let metric_ids : Vec<i64> = panel_metric.iter().filter(|x| x.panel_id == panel.id).map(|x| x.metric_id).collect();
for metric in metrics {
if metric.panel_id == panel.id {
if metric_ids.contains(&metric.id) {
// let values = metric.values(min_x, max_x, chunk_size, panel.average_view);
let mut values : Vec<[f64;2]> = points
.iter()

View file

@ -1,8 +1,8 @@
use eframe::{Frame, egui::{collapsing_header::CollapsingState, Context, Ui, Layout, ScrollArea, global_dark_light_mode_switch, TextEdit, Checkbox, Slider}, emath::Align};
use eframe::{Frame, egui::{collapsing_header::CollapsingState, Context, Ui, Layout, ScrollArea, global_dark_light_mode_switch, TextEdit, Checkbox, Slider, ComboBox}, emath::Align};
use sea_orm::{Set, Unchanged, ActiveValue::NotSet};
use tokio::sync::watch;
use crate::{gui::App, data::entities, util::unpack_color, worker::BackgroundAction};
use crate::{gui::App, data::entities, util::{unpack_color, repack_color}, worker::{BackgroundAction, AppStateView}};
// TODO make this not super specific!
pub fn _confirmation_popup_delete_metric(_app: &mut App, ui: &mut Ui, _metric_index: usize) {
@ -68,7 +68,7 @@ pub struct EditingModel {
impl EditingModel {
pub fn id_repr(&self) -> String {
let prefix = match self.m {
EditingModelType::EditingPanel { panel: _ } => "panel",
EditingModelType::EditingPanel { panel: _, opts: _ } => "panel",
EditingModelType::EditingSource { source: _ } => "source",
EditingModelType::EditingMetric { metric: _ } => "metric",
};
@ -83,9 +83,30 @@ impl EditingModel {
return !self.ready;
}
pub fn to_msg(&self) -> BackgroundAction {
pub fn make_edit_panel(
panel: entities::panels::Model,
metrics: &Vec<entities::metrics::Model>,
panel_metric: &Vec<entities::panel_metric::Model>
) -> EditingModel {
let metric_ids : Vec<i64> = panel_metric.iter().filter(|x| x.panel_id == panel.id).map(|x| x.metric_id).collect();
let mut opts = vec![false; metrics.len()];
for i in 0..metrics.len() {
if metric_ids.contains(&metrics[i].id) {
opts[i] = true;
}
}
EditingModel {
id: panel.id,
new: if panel.id > 0 { false } else { true },
m: EditingModelType::EditingPanel { panel, opts },
valid: false,
ready: false,
}
}
pub fn to_msg(&self, view:AppStateView) -> BackgroundAction {
match &self.m {
EditingModelType::EditingPanel { panel } =>
EditingModelType::EditingPanel { panel, opts: metrics } =>
BackgroundAction::UpdatePanel {
panel: entities::panels::ActiveModel {
id: if self.new { NotSet } else { Unchanged(panel.id) },
@ -94,14 +115,21 @@ impl EditingModel {
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),
}
},
metrics: view.metrics.borrow().iter()
.enumerate()
.filter(|(i,x)| *metrics.get(*i).unwrap_or(&false))
.map(|(i,m)| entities::panel_metric::ActiveModel {
id: NotSet,
panel_id: Set(panel.id),
metric_id: Set(m.id),
})
.collect(),
},
EditingModelType::EditingSource { source } =>
BackgroundAction::UpdateSource {
@ -122,7 +150,6 @@ impl EditingModel {
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),
@ -154,24 +181,35 @@ 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,
id: p.id, m: EditingModelType::EditingPanel { panel: p , opts: vec![] }, valid: false, ready: false,
}
}
}
pub enum EditingModelType {
EditingPanel { panel : entities::panels::Model },
EditingPanel { panel : entities::panels::Model, opts: Vec<bool> },
EditingSource { source: entities::sources::Model },
EditingMetric { metric: entities::metrics::Model },
}
pub fn popup_edit_ui(ui: &mut Ui, model: &mut EditingModel) {
pub fn popup_edit_ui(
ui: &mut Ui,
model: &mut EditingModel,
sources: &Vec<entities::sources::Model>,
metrics: &Vec<entities::metrics::Model>
) {
match &mut model.m {
EditingModelType::EditingPanel { panel } => {
EditingModelType::EditingPanel { panel, opts } => {
ui.heading(format!("Edit panel #{}", panel.id));
TextEdit::singleline(&mut panel.name)
.hint_text("name")
.show(ui);
for (i, metric) in metrics.iter().enumerate() {
if i >= opts.len() { // TODO safe but jank: always starts with all off
opts.push(false);
}
ui.checkbox(&mut opts[i], &metric.name);
}
},
EditingModelType::EditingSource { source } => {
ui.heading(format!("Edit source #{}", source.id));
@ -189,11 +227,21 @@ pub fn popup_edit_ui(ui: &mut Ui, model: &mut EditingModel) {
EditingModelType::EditingMetric { metric } => {
ui.heading(format!("Edit metric #{}", metric.id));
ui.horizontal(|ui| {
ui.color_edit_button_srgba(&mut unpack_color(metric.color));
let mut color_buf = unpack_color(metric.color);
ui.color_edit_button_srgba(&mut color_buf);
metric.color = repack_color(color_buf);
TextEdit::singleline(&mut metric.name)
.hint_text("name")
.show(ui);
});
ComboBox::from_id_source(format!("source-selector-{}", metric.id))
.selected_text(format!("source: {:02}", metric.source_id))
.show_ui(ui, |ui| {
ui.selectable_value(&mut metric.source_id, -1, "None");
for s in sources.iter() {
ui.selectable_value(&mut metric.source_id, s.id, s.name.as_str());
}
});
TextEdit::singleline(&mut metric.query_x)
.hint_text("x")
.show(ui);

View file

@ -16,6 +16,7 @@ pub fn source_panel(app: &mut App, ui: &mut Ui) {
// let mut to_delete: Option<usize> = None;
let panels = &app.panels;
let panel_width = ui.available_width();
let mut orphaned_metrics = app.view.metrics.borrow().clone();
ScrollArea::vertical()
.max_width(panel_width)
.show(ui, |ui| {
@ -29,16 +30,8 @@ pub fn source_panel(app: &mut App, ui: &mut Ui) {
ui.horizontal(|ui| {
ui.vertical(|ui| {
ui.add_space(10.0);
if ui.small_button("+").clicked() {
if i > 0 {
to_swap = Some(i); // TODO kinda jank but is there a better way?
}
}
if ui.small_button("").clicked() {
if i < sources_count - 1 {
to_swap = Some(i + 1); // TODO kinda jank but is there a better way?
}
}
if ui.small_button("+").clicked() { }
if ui.small_button("").clicked() { }
});
ui.vertical(|ui| { // actual sources list container
let remaining_width = ui.available_width();
@ -56,6 +49,7 @@ pub fn source_panel(app: &mut App, ui: &mut Ui) {
.borrow();
for (_j, metric) in metrics.iter().enumerate() {
if metric.source_id == source.id {
orphaned_metrics.retain(|m| m.id != metric.id);
ui.horizontal(|ui| {
metric_edit_ui(
ui,
@ -91,6 +85,55 @@ pub fn source_panel(app: &mut App, ui: &mut Ui) {
});
});
}
ui.horizontal(|ui| { // 1 more for uncategorized sources
ui.vertical(|ui| {
ui.add_space(10.0);
if ui.small_button("+").clicked() { }
if ui.small_button("").clicked() { }
});
ui.vertical(|ui| { // actual sources list container
let remaining_width = ui.available_width();
ui.group(|ui| {
ui.horizontal(|ui| {
source_edit_ui(ui, &app.buffer_source, remaining_width - 34.0);
if ui.small_button("×").clicked() {
app.buffer_source = entities::sources::Model::default();
}
});
for metric in orphaned_metrics.iter() {
ui.horizontal(|ui| {
metric_edit_ui(
ui,
metric,
Some(&panels),
remaining_width - 53.0,
);
if ui.small_button("s").clicked() {
// let path = FileDialog::new()
// .add_filter("csv", &["csv"])
// .set_file_name(format!("{}-{}.csv", source.name, metric.name).as_str())
// .save_file();
// if let Some(_path) = path {
// // serialize_values(
// // &*metric
// // .data
// // .read()
// // .expect("Values RwLock poisoned"),
// // metric,
// // path,
// // )
// // .expect("Could not serialize data");
// }
}
if ui.small_button("×").clicked() {
// TODO don't add duplicates
app.editing.push(metric.clone().into());
}
});
}
});
});
});
}
if app.edit {
ui.separator();

View file

@ -1,5 +1,5 @@
use chrono::Utc;
use sea_orm::{TransactionTrait, DatabaseConnection, EntityTrait, Condition, ColumnTrait, QueryFilter, Set, QueryOrder, Order, ActiveModelTrait, ActiveValue::NotSet};
use sea_orm::{TransactionTrait, DatabaseConnection, EntityTrait, Condition, ColumnTrait, QueryFilter, Set, QueryOrder, Order, ActiveModelTrait, ActiveValue::{NotSet, self}};
use tokio::sync::{watch, mpsc};
use tracing::{info, error};
use std::collections::VecDeque;
@ -8,12 +8,13 @@ use crate::data::{entities, FetchError};
#[derive(Clone)]
pub struct AppStateView {
pub panels: watch::Receiver<Vec<entities::panels::Model>>,
pub sources: watch::Receiver<Vec<entities::sources::Model>>,
pub metrics: watch::Receiver<Vec<entities::metrics::Model>>,
pub points: watch::Receiver<Vec<entities::points::Model>>,
pub flush: mpsc::Sender<()>,
pub op: mpsc::Sender<BackgroundAction>,
pub panels: watch::Receiver<Vec<entities::panels::Model>>,
pub sources: watch::Receiver<Vec<entities::sources::Model>>,
pub metrics: watch::Receiver<Vec<entities::metrics::Model>>,
pub panel_metric: watch::Receiver<Vec<entities::panel_metric::Model>>,
pub points: watch::Receiver<Vec<entities::points::Model>>,
pub flush: mpsc::Sender<()>,
pub op: mpsc::Sender<BackgroundAction>,
}
impl AppStateView {
@ -26,18 +27,20 @@ impl AppStateView {
}
struct AppStateTransmitters {
panels: watch::Sender<Vec<entities::panels::Model>>,
sources: watch::Sender<Vec<entities::sources::Model>>,
metrics: watch::Sender<Vec<entities::metrics::Model>>,
points: watch::Sender<Vec<entities::points::Model>>,
panels: watch::Sender<Vec<entities::panels::Model>>,
sources: watch::Sender<Vec<entities::sources::Model>>,
metrics: watch::Sender<Vec<entities::metrics::Model>>,
points: watch::Sender<Vec<entities::points::Model>>,
panel_metric: watch::Sender<Vec<entities::panel_metric::Model>>,
}
pub struct AppState {
tx: AppStateTransmitters,
panels: Vec<entities::panels::Model>,
sources: Vec<entities::sources::Model>,
metrics: Vec<entities::metrics::Model>,
panels: Vec<entities::panels::Model>,
sources: Vec<entities::sources::Model>,
metrics: Vec<entities::metrics::Model>,
panel_metric: Vec<entities::panel_metric::Model>,
last_refresh: i64,
points: VecDeque<entities::points::Model>,
@ -70,6 +73,7 @@ impl AppState {
let (source_tx, source_rx) = watch::channel(vec![]);
let (metric_tx, metric_rx) = watch::channel(vec![]);
let (point_tx, point_rx) = watch::channel(vec![]);
let (panel_metric_tx, panel_metric_rx) = watch::channel(vec![]);
// let (view_tx, view_rx) = watch::channel(0);
let (flush_tx, flush_rx) = mpsc::channel(10);
let (op_tx, op_rx) = mpsc::channel(100);
@ -78,6 +82,7 @@ impl AppState {
panels: vec![],
sources: vec![],
metrics: vec![],
panel_metric: vec![],
last_refresh: 0,
points: VecDeque::new(),
last_check: 0,
@ -88,6 +93,7 @@ impl AppState {
sources: source_rx,
metrics: metric_rx,
points: point_rx,
panel_metric: panel_metric_rx,
flush: flush_tx,
op: op_tx,
},
@ -96,6 +102,7 @@ impl AppState {
sources: source_tx,
metrics: metric_tx,
points: point_tx,
panel_metric: panel_metric_tx,
},
width,
interval,
@ -130,6 +137,12 @@ impl AppState {
error!(target: "worker", "Could not send metrics update: {:?}", e);
}
self.panel_metric = entities::panel_metric::Entity::find()
.all(db).await?;
if let Err(e) = self.tx.panel_metric.send(self.panel_metric.clone()) {
error!(target: "worker", "Could not send panel-metric update: {:?}", e);
}
info!(target: "worker", "Updated panels, sources and metrics");
self.last_refresh = chrono::Utc::now().timestamp();
Ok(())
@ -164,11 +177,9 @@ impl AppState {
view_size: Set(v.view_size),
timeserie: Set(v.timeserie),
height: Set(v.height),
limit_view: Set(v.limit_view),
position: Set(v.position),
reduce_view: Set(v.reduce_view),
view_chunks: Set(v.view_chunks),
shift_view: Set(v.shift_view),
view_offset: Set(v.view_offset),
average_view: Set(v.average_view),
}).collect::<Vec<entities::panels::ActiveModel>>()
@ -184,12 +195,34 @@ impl AppState {
self.panels = panels;
}
},
BackgroundAction::UpdatePanel { panel } => {
BackgroundAction::UpdatePanel { panel, metrics } => {
let panel_id = match panel.id {
ActiveValue::Unchanged(pid) => Some(pid),
_ => None,
};
let op = if panel.id == NotSet { panel.insert(&db) } else { panel.update(&db) };
// TODO chained if is trashy
if let Err(e) = op.await {
error!(target: "worker", "Could not update panel: {:?}", e);
} else {
self.view.request_flush().await;
if let Some(panel_id) = panel_id {
if let Err(e) = db.transaction::<_, (), sea_orm::DbErr>(|txn| {
Box::pin(async move {
entities::panel_metric::Entity::delete_many()
.filter(
Condition::all()
.add(entities::panel_metric::Column::PanelId.eq(panel_id))
)
.exec(txn).await?;
entities::panel_metric::Entity::insert_many(metrics).exec(txn).await?;
Ok(())
})
}).await {
error!(target: "worker", "Could not update panels on database: {:?}", e);
}
} else {
self.view.request_flush().await;
}
}
},
BackgroundAction::UpdateSource { source } => {
@ -330,7 +363,7 @@ impl AppState {
#[derive(Debug)]
pub enum BackgroundAction {
UpdateAllPanels { panels: Vec<entities::panels::Model> },
UpdatePanel { panel : entities::panels::ActiveModel },
UpdatePanel { panel : entities::panels::ActiveModel, metrics: Vec<entities::panel_metric::ActiveModel> },
UpdateSource { source: entities::sources::ActiveModel },
UpdateMetric { metric: entities::metrics::ActiveModel },
// InsertPanel { panel : entities::panels::ActiveModel },