forked from github-starred/komodo
build monthly stats and build versions
This commit is contained in:
@@ -1,13 +1,17 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Context;
|
||||
use async_timing_util::unix_timestamp_ms;
|
||||
use async_trait::async_trait;
|
||||
use futures::TryStreamExt;
|
||||
use monitor_types::{
|
||||
entities::{
|
||||
build::{Build, BuildActionState},
|
||||
PermissionLevel,
|
||||
Operation, PermissionLevel, update::UpdateStatus,
|
||||
},
|
||||
requests::read::*,
|
||||
};
|
||||
use mungos::mongodb::bson::doc;
|
||||
use mungos::mongodb::{bson::doc, options::FindOptions};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{auth::RequestUser, resource::Resource, state::State};
|
||||
@@ -87,3 +91,130 @@ impl Resolve<GetBuildsSummary, RequestUser> for State {
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
const ONE_DAY_MS: i64 = 86400000;
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<GetBuildMonthlyStats, RequestUser> for State {
|
||||
async fn resolve(
|
||||
&self,
|
||||
GetBuildMonthlyStats { page }: GetBuildMonthlyStats,
|
||||
_: RequestUser,
|
||||
) -> anyhow::Result<GetBuildMonthlyStatsResponse> {
|
||||
let curr_ts = unix_timestamp_ms() as i64;
|
||||
let next_day = curr_ts - curr_ts % ONE_DAY_MS + ONE_DAY_MS;
|
||||
|
||||
let close_ts = next_day - page as i64 * 30 * ONE_DAY_MS;
|
||||
let open_ts = close_ts - 30 * ONE_DAY_MS;
|
||||
|
||||
let mut build_updates = self
|
||||
.db
|
||||
.updates
|
||||
.collection
|
||||
.find(
|
||||
doc! {
|
||||
"start_ts": {
|
||||
"$gte": open_ts,
|
||||
"$lt": close_ts
|
||||
},
|
||||
"operation": Operation::RunBuild.to_string(),
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to get updates cursor")?;
|
||||
|
||||
let mut days = HashMap::<i64, BuildStatsDay>::with_capacity(32);
|
||||
|
||||
let mut curr = open_ts;
|
||||
|
||||
while curr < close_ts {
|
||||
let stats = BuildStatsDay {
|
||||
ts: curr as f64,
|
||||
..Default::default()
|
||||
};
|
||||
days.insert(curr, stats);
|
||||
curr += ONE_DAY_MS;
|
||||
}
|
||||
|
||||
while let Some(update) = build_updates.try_next().await? {
|
||||
if let Some(end_ts) = update.end_ts {
|
||||
let day = update.start_ts - update.start_ts % ONE_DAY_MS;
|
||||
let mut entry = days.entry(day).or_default();
|
||||
entry.count += 1.0;
|
||||
entry.time += ms_to_hour(end_ts - update.start_ts);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(GetBuildMonthlyStatsResponse::new(
|
||||
days.into_values().collect(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
const MS_TO_HOUR_DIVISOR: f64 = 1000.0 * 60.0 * 60.0;
|
||||
fn ms_to_hour(duration: i64) -> f64 {
|
||||
duration as f64 / MS_TO_HOUR_DIVISOR
|
||||
}
|
||||
|
||||
const NUM_VERSIONS_PER_PAGE: u64 = 10;
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<GetBuildVersions, RequestUser> for State {
|
||||
async fn resolve(
|
||||
&self,
|
||||
GetBuildVersions {
|
||||
id,
|
||||
page,
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
}: GetBuildVersions,
|
||||
user: RequestUser,
|
||||
) -> anyhow::Result<Vec<BuildVersionResponseItem>> {
|
||||
let _: Build = self
|
||||
.get_resource_check_permissions(&id, &user, PermissionLevel::Read)
|
||||
.await?;
|
||||
|
||||
let mut filter = doc! {
|
||||
"target": {
|
||||
"type": "Build",
|
||||
"id": id
|
||||
},
|
||||
"operation": Operation::RunBuild.to_string(),
|
||||
"status": UpdateStatus::Complete.to_string(),
|
||||
"success": true
|
||||
};
|
||||
if let Some(major) = major {
|
||||
filter.insert("version.major", major);
|
||||
}
|
||||
if let Some(minor) = minor {
|
||||
filter.insert("version.minor", minor);
|
||||
}
|
||||
if let Some(patch) = patch {
|
||||
filter.insert("version.patch", patch);
|
||||
}
|
||||
let versions = self
|
||||
.db
|
||||
.updates
|
||||
.get_some(
|
||||
filter,
|
||||
FindOptions::builder()
|
||||
.sort(doc! { "_id": -1 })
|
||||
.limit(NUM_VERSIONS_PER_PAGE as i64)
|
||||
.skip(page as u64 * NUM_VERSIONS_PER_PAGE)
|
||||
.build(),
|
||||
)
|
||||
.await
|
||||
.context("failed to pull versions from mongo")?
|
||||
.into_iter()
|
||||
.map(|u| (u.version, u.start_ts))
|
||||
.filter(|(v, _)| !v.is_none())
|
||||
.map(|(version, ts)| BuildVersionResponseItem {
|
||||
version,
|
||||
ts,
|
||||
})
|
||||
.collect();
|
||||
Ok(versions)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,8 @@ enum ReadRequest {
|
||||
GetBuild(GetBuild),
|
||||
ListBuilds(ListBuilds),
|
||||
GetBuildActionState(GetBuildActionState),
|
||||
GetBuildMonthlyStats(GetBuildMonthlyStats),
|
||||
GetBuildVersions(GetBuildVersions),
|
||||
|
||||
// ==== REPO ====
|
||||
GetReposSummary(GetReposSummary),
|
||||
|
||||
@@ -42,6 +42,8 @@ export type ReadResponses = {
|
||||
GetBuild: Types.Build;
|
||||
ListBuilds: Types.BuildListItem[];
|
||||
GetBuildActionState: Types.BuildActionState;
|
||||
GetBuildMonthlyStats: Types.GetBuildMonthlyStatsResponse;
|
||||
GetBuildVersions: Types.BuildVersionResponseItem[];
|
||||
|
||||
// ==== REPO ====
|
||||
GetReposSummary: Types.GetReposSummaryResponse;
|
||||
|
||||
@@ -767,6 +767,35 @@ export interface GetBuildsSummaryResponse {
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface GetBuildMonthlyStats {
|
||||
page?: number;
|
||||
}
|
||||
|
||||
export interface BuildStatsDay {
|
||||
time: number;
|
||||
count: number;
|
||||
ts: number;
|
||||
}
|
||||
|
||||
export interface GetBuildMonthlyStatsResponse {
|
||||
total_time: number;
|
||||
total_count: number;
|
||||
days: BuildStatsDay[];
|
||||
}
|
||||
|
||||
export interface GetBuildVersions {
|
||||
id: string;
|
||||
page?: number;
|
||||
major?: number;
|
||||
minor?: number;
|
||||
patch?: number;
|
||||
}
|
||||
|
||||
export interface BuildVersionResponseItem {
|
||||
version: Version;
|
||||
ts: I64;
|
||||
}
|
||||
|
||||
export interface GetBuilder {
|
||||
id: string;
|
||||
}
|
||||
@@ -1231,6 +1260,8 @@ export type ReadRequest =
|
||||
| { type: "GetBuild", params: GetBuild }
|
||||
| { type: "ListBuilds", params: ListBuilds }
|
||||
| { type: "GetBuildActionState", params: GetBuildActionState }
|
||||
| { type: "GetBuildMonthlyStats", params: GetBuildMonthlyStats }
|
||||
| { type: "GetBuildVersions", params: GetBuildVersions }
|
||||
| { type: "GetReposSummary", params: GetReposSummary }
|
||||
| { type: "GetRepo", params: GetRepo }
|
||||
| { type: "ListRepos", params: ListRepos }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use resolver_api::derive::Request;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
@@ -59,3 +61,73 @@ pub struct GetBuildsSummary {}
|
||||
pub struct GetBuildsSummaryResponse {
|
||||
pub total: u32,
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Request)]
|
||||
#[response(GetBuildMonthlyStatsResponse)]
|
||||
pub struct GetBuildMonthlyStats {
|
||||
#[serde(default)]
|
||||
pub page: u32,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct GetBuildMonthlyStatsResponse {
|
||||
pub total_time: f64, // in hours
|
||||
pub total_count: f64, // number of builds
|
||||
pub days: Vec<BuildStatsDay>,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct BuildStatsDay {
|
||||
pub time: f64,
|
||||
pub count: f64,
|
||||
pub ts: f64,
|
||||
}
|
||||
|
||||
impl GetBuildMonthlyStatsResponse {
|
||||
pub fn new(mut days: Vec<BuildStatsDay>) -> GetBuildMonthlyStatsResponse {
|
||||
days.sort_by(|a, b| {
|
||||
if a.ts < b.ts {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
});
|
||||
let mut total_time = 0.0;
|
||||
let mut total_count = 0.0;
|
||||
for day in &days {
|
||||
total_time += day.time;
|
||||
total_count += day.count;
|
||||
}
|
||||
GetBuildMonthlyStatsResponse {
|
||||
total_time,
|
||||
total_count,
|
||||
days,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, Request)]
|
||||
#[response(Vec<BuildVersionResponseItem>)]
|
||||
pub struct GetBuildVersions {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub page: u32,
|
||||
pub major: Option<i32>,
|
||||
pub minor: Option<i32>,
|
||||
pub patch: Option<i32>,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct BuildVersionResponseItem {
|
||||
pub version: Version,
|
||||
pub ts: I64,
|
||||
}
|
||||
Reference in New Issue
Block a user