Return graph data at /graph/data

This commit is contained in:
Joscha 2023-08-14 23:23:36 +02:00
parent 3cf444ffc2
commit 3c9fc1ce75
8 changed files with 138 additions and 5 deletions

View file

@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "SELECT hash, committer_date AS \"committer_date: time::OffsetDateTime\" FROM commits ORDER BY committer_date ASC, hash ASC ",
"describe": {
"columns": [
{
"name": "hash",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "committer_date: time::OffsetDateTime",
"ordinal": 1,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false
]
},
"hash": "039485fead98beb9d088c5bb35679f2ab0409e7e6b51607869ec30ef35240504"
}

View file

@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "WITH measurements AS ( SELECT hash, value, MAX(start) FROM runs JOIN run_measurements USING (id) WHERE name = ? GROUP BY hash ) SELECT value FROM commits LEFT JOIN measurements USING (hash) ORDER BY committer_date ASC, hash ASC ",
"describe": {
"columns": [
{
"name": "value",
"ordinal": 0,
"type_info": "Float"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true
]
},
"hash": "4e2f2b25a13b3728db01b7c218e7250441098c60725c081215489bd2ad944fe0"
}

View file

@ -8,7 +8,7 @@ anyhow = "1.0.72"
askama = { version = "0.12.0", features = ["with-axum"] } askama = { version = "0.12.0", features = ["with-axum"] }
askama_axum = "0.3.0" askama_axum = "0.3.0"
axum = { version = "0.6.19", features = ["macros", "headers"] } axum = { version = "0.6.19", features = ["macros", "headers"] }
axum-extra = { version = "0.7.7", features = ["typed-routing"] } axum-extra = { version = "0.7.7", features = ["typed-routing", "query"] }
bytes = "1.4.0" bytes = "1.4.0"
clap = { version = "4.3.19", features = ["derive", "deprecated"] } clap = { version = "4.3.19", features = ["derive", "deprecated"] }
directories = "5.0.1" directories = "5.0.1"

View file

@ -26,5 +26,8 @@ CREATE TABLE refs (
FOREIGN KEY (hash) REFERENCES commits (hash) ON DELETE CASCADE FOREIGN KEY (hash) REFERENCES commits (hash) ON DELETE CASCADE
) STRICT; ) STRICT;
CREATE INDEX idx_commits_committer_date_hash
ON commits (committer_date, hash);
CREATE INDEX idx_commit_links_parent_child CREATE INDEX idx_commit_links_parent_child
ON commit_links (parent, child); ON commit_links (parent, child);

View file

@ -32,3 +32,6 @@ CREATE TABLE run_output (
PRIMARY KEY (id, idx), PRIMARY KEY (id, idx),
FOREIGN KEY (id) REFERENCES runs (id) ON DELETE CASCADE FOREIGN KEY (id) REFERENCES runs (id) ON DELETE CASCADE
) STRICT; ) STRICT;
CREATE INDEX idx_run_measurements_name_id_value
ON run_measurements (name, id, value);

View file

@ -22,7 +22,7 @@ use self::{
}, },
pages::{ pages::{
commit::get_commit_by_hash, commit::get_commit_by_hash,
graph::get_graph, graph::{get_graph, get_graph_data},
index::get_index, index::get_index,
queue::{get_queue, get_queue_delete, get_queue_inner}, queue::{get_queue, get_queue_delete, get_queue_inner},
run::get_run_by_id, run::get_run_by_id,
@ -44,6 +44,7 @@ pub async fn run(server: Server) -> somehow::Result<()> {
.typed_get(get_api_worker_repo_by_hash_tree_tar_gz) .typed_get(get_api_worker_repo_by_hash_tree_tar_gz)
.typed_get(get_commit_by_hash) .typed_get(get_commit_by_hash)
.typed_get(get_graph) .typed_get(get_graph)
.typed_get(get_graph_data)
.typed_get(get_index) .typed_get(get_index)
.typed_get(get_queue) .typed_get(get_queue)
.typed_get(get_queue_delete) .typed_get(get_queue_delete)

View file

@ -1,11 +1,16 @@
use std::collections::HashMap;
use askama::Template; use askama::Template;
use axum::{extract::State, response::IntoResponse}; use axum::{extract::State, response::IntoResponse, Json};
use axum_extra::extract::Query;
use serde::{Deserialize, Serialize};
use sqlx::{Acquire, SqlitePool};
use crate::{ use crate::{
config::Config, config::Config,
server::web::{ server::web::{
base::{Base, Link, Tab}, base::{Base, Link, Tab},
paths::PathGraph, paths::{PathGraph, PathGraphData},
r#static::GRAPH_JS, r#static::GRAPH_JS,
}, },
somehow, somehow,
@ -28,3 +33,74 @@ pub async fn get_graph(
base, base,
}) })
} }
#[derive(Deserialize)]
pub struct QueryGraphData {
metric: Vec<String>,
}
#[derive(Serialize)]
struct GraphData {
hashes: Vec<String>,
times: Vec<i64>,
metrics: HashMap<String, Vec<Option<f64>>>,
}
pub async fn get_graph_data(
_path: PathGraphData,
State(db): State<SqlitePool>,
Query(form): Query<QueryGraphData>,
) -> somehow::Result<impl IntoResponse> {
let mut tx = db.begin().await?;
let conn = tx.acquire().await?;
let rows = sqlx::query!(
"\
SELECT \
hash, \
committer_date AS \"committer_date: time::OffsetDateTime\" \
FROM commits \
ORDER BY committer_date ASC, hash ASC \
"
)
.fetch_all(&mut *conn)
.await?;
let mut hashes = Vec::with_capacity(rows.len());
let mut times = Vec::with_capacity(rows.len());
for row in rows {
hashes.push(row.hash);
times.push(row.committer_date.unix_timestamp());
}
let mut metrics = HashMap::new();
for metric in form.metric {
let values = sqlx::query_scalar!(
"\
WITH \
measurements AS ( \
SELECT hash, value, MAX(start) \
FROM runs \
JOIN run_measurements USING (id) \
WHERE name = ? \
GROUP BY hash \
) \
SELECT value \
FROM commits \
LEFT JOIN measurements USING (hash) \
ORDER BY committer_date ASC, hash ASC \
",
metric,
)
.fetch_all(&mut *conn)
.await?;
metrics.insert(metric, values);
}
Ok(Json(GraphData {
hashes,
times,
metrics,
}))
}

View file

@ -13,6 +13,10 @@ pub struct PathIndex {}
#[typed_path("/graph/")] #[typed_path("/graph/")]
pub struct PathGraph {} pub struct PathGraph {}
#[derive(Deserialize, TypedPath)]
#[typed_path("/graph/data")]
pub struct PathGraphData {}
#[derive(Deserialize, TypedPath)] #[derive(Deserialize, TypedPath)]
#[typed_path("/queue/")] #[typed_path("/queue/")]
pub struct PathQueue {} pub struct PathQueue {}