Files
portal/src/report.rs
T

54 lines
2.2 KiB
Rust
Raw Normal View History

//! What the storylines did, as numbers and nothing else.
//!
//! `GET /report` answers with a count per bucket, per storyline, per
//! state: how many cases each storyline brought in and where they got
//! to. That is the whole of what the storylines are compared on. No
//! record, no field, no address, no time leaves the site: a storyline
//! is assigned evenly, so the counts are the comparison, and the
//! trainer (`portal-trainer report`) reads them to say which words
//! worked. Public, since a count is what a public list already shows.
#![cfg(feature = "ssr")]
use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
/// The report: the site's storylines, and per bucket the count of
/// records by storyline and state.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Report {
pub storylines: usize,
/// bucket -> storyline (as a string, JSON's way) -> state -> count
pub buckets: BTreeMap<String, BTreeMap<String, BTreeMap<String, u64>>>,
}
pub async fn gather(state: &crate::server::AppState) -> Report {
let mut report = Report { storylines: state.questions.load().arity(), buckets: BTreeMap::new() };
let aggregates = state.aggregates.load();
for bucket in aggregates.keys() {
// The runtime's own buckets are not the site's offers.
if bucket.starts_with("portal_") {
continue;
}
let Ok(store) = state.jetstream.get_key_value(bucket).await else { continue };
let keys: Vec<String> = {
use futures::TryStreamExt;
match store.keys().await {
Ok(keys) => keys.try_collect().await.unwrap_or_default(),
Err(_) => continue,
}
};
let counts = report.buckets.entry(bucket.clone()).or_default();
for key in keys {
let Ok(Some(bytes)) = store.get(&key).await else { continue };
let Ok(answer) = serde_json::from_slice::<crate::answers::Answer>(&bytes) else { continue };
*counts.entry(answer.storyline.to_string()).or_default().entry(answer.state).or_default() += 1;
}
}
report
}
pub async fn handler(axum::extract::State(state): axum::extract::State<crate::server::AppState>) -> axum::Json<Report> {
axum::Json(gather(&state).await)
}