Show metrics as tree

This commit is contained in:
Joscha 2023-08-15 23:18:27 +02:00
parent 82e2385f59
commit 678d3f391b
4 changed files with 162 additions and 0 deletions

View file

@ -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"
}

View file

@ -20,6 +20,84 @@ use crate::{
somehow, somehow,
}; };
use self::util::MetricFolder;
#[derive(Template)]
#[template(
ext = "html",
source = "
{% match self %}
{% when MetricTree::File with { name, metric } %}
<label>
<input type=\"checkbox\" name=\"{{ metric }}\">
{{ name }}
</label>
{% when MetricTree::Folder with { name, metric, children } %}
{% if let Some(metric) = metric %}
<input type=\"checkbox\" name=\"{{ metric }}\">
{% endif %}
<details>
<summary>{{ name }}/</summary>
{{ children|safe }}
</details>
{% endmatch %}
"
)]
enum MetricTree {
File {
name: String,
metric: String,
},
Folder {
name: String,
metric: Option<String>,
children: MetricForest,
},
}
#[derive(Template)]
#[template(
ext = "html",
source = "
<ul>
{% for tree in trees %}
<li>{{ tree|safe }}</li>
{% endfor %}
</ul>
"
)]
struct MetricForest {
trees: Vec<MetricTree>,
}
impl MetricForest {
fn from_forest(children: HashMap<String, MetricFolder>) -> Self {
let mut children = children.into_iter().collect::<Vec<_>>();
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 // TODO Metric tree selector in template
#[derive(Template)] #[derive(Template)]
#[template(path = "pages/graph.html")] #[template(path = "pages/graph.html")]
@ -27,17 +105,31 @@ struct Page {
link_uplot_css: Link, link_uplot_css: Link,
link_graph_js: Link, link_graph_js: Link,
base: Base, base: Base,
metrics: MetricForest,
} }
pub async fn get_graph( pub async fn get_graph(
_path: PathGraph, _path: PathGraph,
State(config): State<&'static Config>, State(config): State<&'static Config>,
State(db): State<SqlitePool>,
) -> somehow::Result<impl IntoResponse> { ) -> somehow::Result<impl IntoResponse> {
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); let base = Base::new(config, Tab::Graph);
Ok(Page { Ok(Page {
link_uplot_css: base.link(UPLOT_CSS), link_uplot_css: base.link(UPLOT_CSS),
link_graph_js: base.link(GRAPH_JS), link_graph_js: base.link(GRAPH_JS),
base, base,
metrics,
}) })
} }

View file

@ -1,5 +1,29 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
#[derive(Default)]
pub struct MetricFolder {
pub metric: Option<String>,
pub children: HashMap<String, MetricFolder>,
}
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<String>) -> Self {
let mut tree = Self::default();
for metric in metrics {
tree.insert(metric);
}
tree
}
}
/// Sort commits topologically such that parents come before their children. /// Sort commits topologically such that parents come before their children.
/// ///
/// Assumes that `parent_child_pairs` contains no duplicates and is in the /// Assumes that `parent_child_pairs` contains no duplicates and is in the

View file

@ -7,6 +7,32 @@
<script type="module" src="{{ link_graph_js }}"></script> <script type="module" src="{{ link_graph_js }}"></script>
{% endblock %} {% endblock %}
{# {% macro metric_tree(tree) %}
{% match tree %}
{% when File with { name, metric } %}
<label><input type="checkbox" name="{{ metric }}"> {{ name }}</label>
{% when Folder with { name, metric, children } %}
<details>
<summary>{{ name }}/</summary>
{% call metric_forest(children) %}
</details>
{% endmatch %}
{% endmacro %}
{% macro metric_forest(trees) %}
<ul>
{% for tree in forest %}
{% call metric_tree(tree) %}
{% endfor %}
</ul>
{% endmacro %} #}
{% block body %} {% block body %}
<h2>Graph</h2> <h2>Graph</h2>
<div id="plot"></div>
{{ metrics|safe }}
{% endblock %} {% endblock %}