diff --git a/.sqlx/query-039485fead98beb9d088c5bb35679f2ab0409e7e6b51607869ec30ef35240504.json b/.sqlx/query-039485fead98beb9d088c5bb35679f2ab0409e7e6b51607869ec30ef35240504.json new file mode 100644 index 0000000..19b79db --- /dev/null +++ b/.sqlx/query-039485fead98beb9d088c5bb35679f2ab0409e7e6b51607869ec30ef35240504.json @@ -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" +} diff --git a/.sqlx/query-4e2f2b25a13b3728db01b7c218e7250441098c60725c081215489bd2ad944fe0.json b/.sqlx/query-4e2f2b25a13b3728db01b7c218e7250441098c60725c081215489bd2ad944fe0.json new file mode 100644 index 0000000..1d961bb --- /dev/null +++ b/.sqlx/query-4e2f2b25a13b3728db01b7c218e7250441098c60725c081215489bd2ad944fe0.json @@ -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" +} diff --git a/Cargo.toml b/Cargo.toml index 28909e4..bb41109 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ anyhow = "1.0.72" askama = { version = "0.12.0", features = ["with-axum"] } askama_axum = "0.3.0" 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" clap = { version = "4.3.19", features = ["derive", "deprecated"] } directories = "5.0.1" diff --git a/migrations/20230805101911_commits.sql b/migrations/20230805101911_commits.sql index 208e488..fbd2f62 100644 --- a/migrations/20230805101911_commits.sql +++ b/migrations/20230805101911_commits.sql @@ -26,5 +26,8 @@ CREATE TABLE refs ( FOREIGN KEY (hash) REFERENCES commits (hash) ON DELETE CASCADE ) STRICT; +CREATE INDEX idx_commits_committer_date_hash +ON commits (committer_date, hash); + CREATE INDEX idx_commit_links_parent_child -ON commit_links(parent, child); +ON commit_links (parent, child); diff --git a/migrations/20230809141314_runs.sql b/migrations/20230809141314_runs.sql index a75750a..652dcea 100644 --- a/migrations/20230809141314_runs.sql +++ b/migrations/20230809141314_runs.sql @@ -32,3 +32,6 @@ CREATE TABLE run_output ( PRIMARY KEY (id, idx), FOREIGN KEY (id) REFERENCES runs (id) ON DELETE CASCADE ) STRICT; + +CREATE INDEX idx_run_measurements_name_id_value +ON run_measurements (name, id, value); diff --git a/src/server/web.rs b/src/server/web.rs index 760e380..1040e93 100644 --- a/src/server/web.rs +++ b/src/server/web.rs @@ -22,7 +22,7 @@ use self::{ }, pages::{ commit::get_commit_by_hash, - graph::get_graph, + graph::{get_graph, get_graph_data}, index::get_index, queue::{get_queue, get_queue_delete, get_queue_inner}, 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_commit_by_hash) .typed_get(get_graph) + .typed_get(get_graph_data) .typed_get(get_index) .typed_get(get_queue) .typed_get(get_queue_delete) diff --git a/src/server/web/pages/graph.rs b/src/server/web/pages/graph.rs index 1ea49ac..9293ffb 100644 --- a/src/server/web/pages/graph.rs +++ b/src/server/web/pages/graph.rs @@ -1,11 +1,16 @@ +use std::collections::HashMap; + 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::{ config::Config, server::web::{ base::{Base, Link, Tab}, - paths::PathGraph, + paths::{PathGraph, PathGraphData}, r#static::GRAPH_JS, }, somehow, @@ -28,3 +33,74 @@ pub async fn get_graph( base, }) } + +#[derive(Deserialize)] +pub struct QueryGraphData { + metric: Vec, +} + +#[derive(Serialize)] +struct GraphData { + hashes: Vec, + times: Vec, + metrics: HashMap>>, +} + +pub async fn get_graph_data( + _path: PathGraphData, + State(db): State, + Query(form): Query, +) -> somehow::Result { + 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, + })) +} diff --git a/src/server/web/paths.rs b/src/server/web/paths.rs index 7c4fbcb..205f38d 100644 --- a/src/server/web/paths.rs +++ b/src/server/web/paths.rs @@ -13,6 +13,10 @@ pub struct PathIndex {} #[typed_path("/graph/")] pub struct PathGraph {} +#[derive(Deserialize, TypedPath)] +#[typed_path("/graph/data")] +pub struct PathGraphData {} + #[derive(Deserialize, TypedPath)] #[typed_path("/queue/")] pub struct PathQueue {}