2SABVMY3A2RZDF3KJXZSMJ2UQ4Q5EW422G4DVBJRKK26S2ESGVQAC
U4VCAFXQNTKC7KWWE3B2JJMZMFRGLBOHSZIOXCE6EEXW7WE2Q5NAC
GQVS55HIQLU7KPJNRMF57QUM4EATSWFQRCS7ZEJMJPUXFX2NHSYAC
4MG5JFXTKAE3SOVKGGNKEUTNCKOWEBHTGKVZHJWLWE3PTZTQKHPAC
77SIQZ3EGGV6KSECMLPDKQFGEC7CCFAPWGER7ZARQ5STDKJNU6GQC
LVMGQJGH34VSGZCB74IQEJIVFNJE6OPQQCF7IPS72NPVMIUFBV4AC
CUADTSHQNPGMWHIJCWXKHNNIY4UJOYZ7XBZA5CJ2VVJJAMSNT4BAC
JTX5OHWHO647X4XLQPUDLH34QG2VDP7N7X6OL7XEIZQYVWELV6CAC
//! Deserialize data to be served as JSON to build time-series plots.
//!
//! The `TSSpec::TSPageSpec::TSGraphicSpec` group of data-structures mirrors
//! `ts_spec.keytree`. Its main function is TSSpec::new(PageJson
//!
//! The `PageJson::GraphicJson::SeriesJson` group of datastructures is convertible into Json and
//! served to the client. Its main function is
//!
//! ```
//! PageJson::new(ts_spec: TSSpec, checked_spec: Checked...)
//! ```
}
/// ts_spec:
/// page:
/// data_type: u
/// country: Australia
/// graphic:
/// series:
/// id: AUSURANAA
/// id: AUSURAQS
/// id: AUSURHARMADSMEI
/// id: AUSURHARMMDSMEI
pub fn into_json(
&self,
data_spec: &IndexedCheckedDataSpec,
root_path: &str) -> Result<TSJson, Error>
{
let mut ts_json = TSJson::new();
for page_spec in &self.0 {
let key = page_spec.key();
let mut value: Vec<GraphicJson> = Vec::new();
for graphic in &page_spec.graphics {
let json = graphic.into_json(data_spec, root_path)?;
value.push(json);
};
ts_json.insert(&key, value);
}
Ok(ts_json)
// // We don't want to pass off responsibility to the struct's components, as we need
// // to use the h HashMap, so we loop down to series.
// /// Build from a `TSSpec`.
// pub fn new(ts_spec: TSSpec, data_spec: IndexCheckedDataSpec) -> Self {
// let ts_spec = TSSpec::from_file("ts_spec.keytree");
// // loop through pages in ts_spec
// let page_json = PageJson::new(
// let h: HashMap<String, usize> = HashMap::new();
// for (i, series) in data_spec.0.enumerate() {
// for series
// h.insert(series.id, i);
// }
// let ts:
// let mut builder = TSJson(HashMap::new());
// for ts_page in ts_spec.pages() {
// let page_json = ts_page.to_page_json()
// builder.insert(page_json.key(), page_json);
// }
//
// PageJson(builder)
// }
}
pub (crate) fn into_json(
&self,
data_spec: &IndexedCheckedDataSpec,
root_path: &str) -> Result<GraphicJson, Error>
{
let mut series_json = SeriesJson::new();
for series_id in &self.series_ids {
let ts = data_spec.time_series_data(series_id.clone(), root_path)?;
let meta = data_spec.meta(series_id.to_string(), root_path);
series_json.push(ts, meta);
}
Ok(
GraphicJson {
height: self.height,
series: series_json,
}
)
pub struct SeriesJson {
data: Vec<(RegularTimeSeries<1>, SeriesMetaData)>,
pub struct SeriesJson(Vec<(RegularTimeSeries<1>, SeriesMetaData)>);
impl SeriesJson {
/// Create a new, empty `SeriesJson`.
pub fn new() -> Self {
SeriesJson(Vec::new())
}
/// Push time-series data and associated meta-data onto `SeriesJson`.
pub fn push(&mut self, ts: RegularTimeSeries<1>, meta: SeriesMetaData) {
self.0.push((ts, meta));
}
//! seriess:
//! series:
//! data_type: u
//! country: Australia
//! id: AUSURAMS
//! series:
//! data_type: u
//! country: Australia
//! id: AUSURANAA
//! series:
//! data_type: u
//! country: Australia
//! id: AUSURAQS
//! series:
//! data_type: u
//! country: Australia
//! id: AUSURHARMADSMEI
//! series:
//! data_type: u
//! country: Australia
//! id: AUSURHARMMDSMEI
//! ```
//! ### Step 2. Check and Save Data
//! ### Step 3. Save Data
//!
//! The saved data will look something like
//! ```text
//! seriess:
//! series:
//! data_type: u
//! country: Australia
//! id: AUSURAMS
//! error: ok
//! time_stamp: 2021-06-20 9:33:39
//! series:
//! data_type: u
//! country: Australia
//! id: AUSURANAA
//! error: ok
//! time_stamp: 2021-06-20 9:33:41
//! series:
//! data_type: u
//! country: Australia
//! id: AUSURAQS
//! error: ok
//! time_stamp: 2021-06-20 9:33:42
//! ```
//! ### Step 4. Serve Time-series
//! To generate a generic time-series specification,
//! ```
//! checked_data_spec
//! .from_file("data_spec.keytree")
//! .bootstrap_ts_spec;
//! ```
//! The specification will look something like
//! ```
//! page:
//! country:
//! name: Australia
//! graphic:
//! series:
//! id: AUSURANAA
//! id: AUSURAQS
//! id: AUSURHARMADSMEI
//! id: AUSURHARMMDSMEI
//! ```
//! Once it has been generated with the full set of data, we want to be able to edit it manually.
//!
//! ### Step 3. Serve Time-series
//!
//! The structure of JSON which is served to the client is determined by `Serialize`-able data
//! structures. The top structure is `TSJson`. The `ts_spec.keytree` file specifies the graphics on
//! each page, and what data each graphic uses. The `ts_spec.keytree` file is deserialized into
//! `TSSpec`.
//! The procedure is generally to read in the specification in `ts_spec.keytree`, and to read in the
//! `data_spec.keytree` which specifies data available, and also acts as a gate-keeper for that
//! data.
//! ```
//! // Read in the data specification.
//! let data_spec = CheckedData::from_file("data_spec.keytree").indexed();
//!
//! // Read in the time-series specification.
//! let ts_spec = TSSpec::from_file("ts_spec.keytree");
//!
//! // Load all data to memory. The server uses `ts_json` as a data-store to respond to data
//! // requests.
//! let ts_json = ts_spec.into_json(data_spec);
//! ```
//! Then `TSSpec` uses `IndexedCheckedDataSpec` as an argument to be converted into `TSJson`.
//!
//!
}
}
// Its better for CheckedDataSpec to be a Vec, but we need to index into the Vec, so
// we wrap it with an index.
/// `CheckedDataSpec` wrapped in an index for faster lookup.
pub struct IndexedCheckedDataSpec {
data: CheckedDataSpec,
index: HashMap<String, usize>,
}
impl IndexedCheckedDataSpec {
/// Return the time-series data for `series_id`.
pub fn time_series_data(
&self,
series_id: String,
root_path: &str) -> Result<RegularTimeSeries<1>, Error>
{
let i = match self.index.get(&series_id) {
Some(i) => i,
None => {
println!("Series {} not found in IndexedCheckedDataSpec::index.", series_id);
panic!();
},
};
let checked_series = self.data.series_from_index(*i);
if checked_series.error != "ok" {
return Err(Error::new(
ErrorKind::SpecErrorStatus(series_id, checked_series.error.clone())
))
};
let path = &format!(
"{}/{}/{}/{}.csv",
root_path,
checked_series.data_type,
checked_series.country,
series_id,
);
Ok(
TimeSeries::<1>::from_csv(&path).try_into()
.map_err(|err| Error::new(
ErrorKind::NotRegular(checked_series.id.clone())
))?
)
}
/// Return the meta-data for `series_id`.
pub fn meta(&self, series_id: String, root_path: &str) -> SeriesMetaData {
let i = match self.index.get(&series_id) {
Some(i) => i,
None => {
println!("Series {} not found in IndexedCheckedDataSpec::index.", series_id);
panic!();
},
};
let checked_series = self.data.series_from_index(*i);
let path = &format!(
"{}/{}/{}/{}.csv",
root_path,
checked_series.data_type,
checked_series.country,
series_id,
);
let meta_str = fs::read_to_string(path).unwrap();
let kt = KeyTree::parse(&meta_str).unwrap();
kt.to_ref().try_into().unwrap()
}
/// Wrap CheckedDataSpec with an index into its inner `Vec`.
pub fn into_indexed(self) -> IndexedCheckedDataSpec {
let mut h: HashMap<String, usize> = HashMap::new();
for (i, series) in self.0.iter().enumerate() {
h.insert(series.id.clone(), i);
}
IndexedCheckedDataSpec {
data: self,
index: h
}
impl SeriesMetaData {
///
pub fn from_file(
data_type: DataType,
country: Country,
series_id: String,
root_path: &str) -> Result<Self, Error>
{
let path = &format!(
"{}/{}/{}/{}.meta",
root_path,
data_type,
country,
series_id,
);
let meta_str = fs::read_to_string(&path).unwrap();
let kt = KeyTree::parse(&meta_str).unwrap();
Ok(kt.to_ref().try_into().unwrap())
}
}