From 678d3f391b6dce9a0c00955c56ecf537138e5c06 Mon Sep 17 00:00:00 2001 From: Joscha Date: Tue, 15 Aug 2023 23:18:27 +0200 Subject: [PATCH] Show metrics as tree --- ...78ce28a2398b408bbc99ea1299315904d6673.json | 20 ++++ src/server/web/pages/graph.rs | 92 +++++++++++++++++++ src/server/web/pages/graph/util.rs | 24 +++++ templates/pages/graph.html | 26 ++++++ 4 files changed, 162 insertions(+) create mode 100644 .sqlx/query-35324f9148e3e7ce2d2aa62d69378ce28a2398b408bbc99ea1299315904d6673.json diff --git a/.sqlx/query-35324f9148e3e7ce2d2aa62d69378ce28a2398b408bbc99ea1299315904d6673.json b/.sqlx/query-35324f9148e3e7ce2d2aa62d69378ce28a2398b408bbc99ea1299315904d6673.json new file mode 100644 index 0000000..97964f7 --- /dev/null +++ b/.sqlx/query-35324f9148e3e7ce2d2aa62d69378ce28a2398b408bbc99ea1299315904d6673.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "SELECT DISTINCT metric FROM run_measurements ORDER BY metric ASC", + "describe": { + "columns": [ + { + "name": "metric", + "ordinal": 0, + "type_info": "Text" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false + ] + }, + "hash": "35324f9148e3e7ce2d2aa62d69378ce28a2398b408bbc99ea1299315904d6673" +} diff --git a/src/server/web/pages/graph.rs b/src/server/web/pages/graph.rs index 2ee99e3..e52893f 100644 --- a/src/server/web/pages/graph.rs +++ b/src/server/web/pages/graph.rs @@ -20,6 +20,84 @@ use crate::{ somehow, }; +use self::util::MetricFolder; + +#[derive(Template)] +#[template( + ext = "html", + source = " +{% match self %} + {% when MetricTree::File with { name, metric } %} + + {% when MetricTree::Folder with { name, metric, children } %} + {% if let Some(metric) = metric %} + + {% endif %} +
+ {{ name }}/ + {{ children|safe }} +
+{% endmatch %} +" +)] +enum MetricTree { + File { + name: String, + metric: String, + }, + Folder { + name: String, + metric: Option, + children: MetricForest, + }, +} + +#[derive(Template)] +#[template( + ext = "html", + source = " + +" +)] +struct MetricForest { + trees: Vec, +} + +impl MetricForest { + fn from_forest(children: HashMap) -> Self { + let mut children = children.into_iter().collect::>(); + children.sort_unstable_by(|a, b| a.0.cmp(&b.0)); + + let mut trees = vec![]; + for (name, mut folder) in children { + if let Some(file_metric) = folder.metric { + trees.push(MetricTree::File { + name: name.clone(), + metric: file_metric, + }); + } + + let is_folder = !folder.children.is_empty(); + let folder_metric = folder.children.remove("").and_then(|f| f.metric); + if is_folder { + trees.push(MetricTree::Folder { + name, + metric: folder_metric, + children: Self::from_forest(folder.children), + }) + } + } + Self { trees } + } +} + // TODO Metric tree selector in template #[derive(Template)] #[template(path = "pages/graph.html")] @@ -27,17 +105,31 @@ struct Page { link_uplot_css: Link, link_graph_js: Link, base: Base, + + metrics: MetricForest, } pub async fn get_graph( _path: PathGraph, State(config): State<&'static Config>, + State(db): State, ) -> somehow::Result { + let metrics = + sqlx::query_scalar!("SELECT DISTINCT metric FROM run_measurements ORDER BY metric ASC") + .fetch_all(&db) + .await?; + + let metrics = MetricFolder::new(metrics); + assert!(metrics.metric.is_none()); + let metrics = MetricForest::from_forest(metrics.children); + let base = Base::new(config, Tab::Graph); Ok(Page { link_uplot_css: base.link(UPLOT_CSS), link_graph_js: base.link(GRAPH_JS), base, + + metrics, }) } diff --git a/src/server/web/pages/graph/util.rs b/src/server/web/pages/graph/util.rs index 23ea854..3fb58dc 100644 --- a/src/server/web/pages/graph/util.rs +++ b/src/server/web/pages/graph/util.rs @@ -1,5 +1,29 @@ use std::collections::{HashMap, HashSet}; +#[derive(Default)] +pub struct MetricFolder { + pub metric: Option, + pub children: HashMap, +} + +impl MetricFolder { + fn insert(&mut self, metric: String) { + let mut current = self; + for segment in metric.split('/') { + current = current.children.entry(segment.to_string()).or_default(); + } + current.metric = Some(metric); + } + + pub fn new(metrics: Vec) -> Self { + let mut tree = Self::default(); + for metric in metrics { + tree.insert(metric); + } + tree + } +} + /// Sort commits topologically such that parents come before their children. /// /// Assumes that `parent_child_pairs` contains no duplicates and is in the diff --git a/templates/pages/graph.html b/templates/pages/graph.html index ac2dde8..b1e68a3 100644 --- a/templates/pages/graph.html +++ b/templates/pages/graph.html @@ -7,6 +7,32 @@ {% endblock %} +{# {% macro metric_tree(tree) %} +{% match tree %} +{% when File with { name, metric } %} + +{% when Folder with { name, metric, children } %} +
+ {{ name }}/ + {% call metric_forest(children) %} +
+{% endmatch %} +{% endmacro %} + +{% macro metric_forest(trees) %} +
    + {% for tree in forest %} + {% call metric_tree(tree) %} + {% endfor %} +
+{% endmacro %} #} + {% block body %} +

Graph

+ +
+ +{{ metrics|safe }} + {% endblock %}