use super::pagination::Page;
use crate::{
dao::pagination::Pages,
entities::iteration::{self, Entity as Iteration},
};
use anyhow::{self, Context};
use sea_orm::*;
use sea_query::{Expr, Query};
#[derive(DerivePartialModel, FromQueryResult)]
#[sea_orm(entity = "Iteration")]
pub struct RunId {
pub run_id: i32,
}
pub async fn fetch_runs_all(
scenarios: &Vec<String>,
page: Option<Page>,
db: &DatabaseConnection,
) -> anyhow::Result<(Vec<iteration::Model>, Pages)> {
let query = iteration::Entity::find()
.filter(iteration::Column::ScenarioName.is_in(scenarios))
.order_by_desc(iteration::Column::StartTime);
match page {
Some(page) => {
let count = query
.clone()
.distinct_on([iteration::Column::RunId])
.count(db)
.await?;
let page_count = (count as f64 / page.size as f64).ceil() as u64;
let res = query.paginate(db, page.size).fetch_page(page.num).await?;
Ok((res, Pages::Required(page_count)))
}
None => {
let res = query.all(db).await?;
Ok((res, Pages::NotRequired))
}
}
}
pub async fn fetch_runs_in_range(
scenarios: &Vec<String>,
from: i64,
to: i64,
page: Option<Page>,
db: &DatabaseConnection,
) -> anyhow::Result<(Vec<iteration::Model>, Pages)> {
let query = iteration::Entity::find()
.filter(
Condition::all()
.add(iteration::Column::ScenarioName.is_in(scenarios))
.add(iteration::Column::StopTime.gt(from))
.add(iteration::Column::StartTime.lt(to)),
)
.order_by_desc(iteration::Column::StartTime);
match page {
Some(page) => {
let count = query.clone().count(db).await?;
let page_count = (count as f64 / page.size as f64).ceil() as u64;
let res = query.paginate(db, page.size).fetch_page(page.num).await?;
Ok((res, Pages::Required(page_count)))
}
None => {
let res = query.all(db).await?;
Ok((res, Pages::NotRequired))
}
}
}
pub async fn fetch_runs_last_n(
scenarios: &Vec<String>,
last_n: u64,
page: Option<Page>,
db: &DatabaseConnection,
) -> anyhow::Result<(Vec<iteration::Model>, Pages)> {
if scenarios.is_empty() {
return Err(anyhow::anyhow!("Cannot get runs for no scenarios!"));
}
match page {
Some(page) => {
if scenarios.is_empty() {
return Err(anyhow::anyhow!(
"Unable to paginate over runs if multiple scenarios are selected!"
));
}
let scenario = scenarios.first().unwrap();
let sub_query = Query::select()
.expr(Expr::col(iteration::Column::RunId))
.from(iteration::Entity)
.cond_where(iteration::Column::ScenarioName.eq(scenario))
.group_by_col(iteration::Column::RunId)
.order_by(iteration::Column::StartTime, Order::Desc)
.limit(last_n)
.to_owned();
let query = iteration::Entity::find().filter(
Condition::all()
.add(iteration::Column::ScenarioName.eq(scenario))
.add(iteration::Column::RunId.in_subquery(sub_query)),
);
let count = query.clone().count(db).await?;
let page_count = (count as f64 / page.size as f64).ceil() as u64;
let res = query.paginate(db, page.size).fetch_page(page.num).await?;
Ok((res, Pages::Required(page_count)))
}
None => {
let mut res = vec![];
for scenario in scenarios {
let sub_query = Query::select()
.expr(Expr::col(iteration::Column::RunId))
.from(iteration::Entity)
.cond_where(iteration::Column::ScenarioName.eq(scenario))
.group_by_col(iteration::Column::RunId)
.order_by(iteration::Column::StartTime, Order::Desc)
.limit(last_n)
.to_owned();
let query = iteration::Entity::find().filter(
Condition::all()
.add(iteration::Column::ScenarioName.eq(scenario))
.add(iteration::Column::RunId.in_subquery(sub_query)),
);
let mut iterations = query.all(db).await?;
res.append(&mut iterations);
}
Ok((res, Pages::NotRequired))
}
}
}
pub async fn fetch_live(run_id: i32, db: &DatabaseConnection) -> anyhow::Result<iteration::Model> {
iteration::Entity::find()
.filter(iteration::Column::RunId.eq(run_id))
.one(db)
.await?
.context(format!("Unable to find live iteration for run {}", run_id))
}
#[cfg(test)]
mod tests {
use crate::{dao, db_connect, db_migrate, tests::setup_fixtures};
#[tokio::test]
async fn fetch_iterations_of_last_n_runs_for_schema() -> anyhow::Result<()> {
let db = db_connect("sqlite::memory:", None).await?;
db_migrate(&db).await?;
setup_fixtures(&["./fixtures/runs.sql", "./fixtures/iterations.sql"], &db).await?;
let (scenario_iterations, _) =
dao::iteration::fetch_runs_last_n(&vec!["scenario_1".to_string()], 1, None, &db)
.await?;
let run_ids = scenario_iterations
.iter()
.map(|run| run.run_id)
.collect::<Vec<_>>();
assert_eq!(run_ids, vec![1]);
let iterations = scenario_iterations
.iter()
.map(|run| run.count)
.collect::<Vec<_>>();
assert_eq!(iterations, vec![1]);
let (scenario_iterations, _) =
dao::iteration::fetch_runs_last_n(&vec!["scenario_3".to_string()], 2, None, &db)
.await?;
let run_ids = scenario_iterations
.iter()
.map(|run| run.run_id)
.collect::<Vec<_>>();
assert_eq!(run_ids, vec![2, 2, 2, 3, 3, 3]);
let iterations = scenario_iterations
.iter()
.map(|run| run.count)
.collect::<Vec<_>>();
assert_eq!(iterations, vec![1, 2, 3, 1, 2, 3]);
Ok(())
}
}