Show metrics as tree
This commit is contained in:
parent
82e2385f59
commit
678d3f391b
4 changed files with 162 additions and 0 deletions
20
.sqlx/query-35324f9148e3e7ce2d2aa62d69378ce28a2398b408bbc99ea1299315904d6673.json
generated
Normal file
20
.sqlx/query-35324f9148e3e7ce2d2aa62d69378ce28a2398b408bbc99ea1299315904d6673.json
generated
Normal 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"
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue