2CCG6KUP6VL2Q7WQKLVNIPPGZWHSWIOWETRCS3APZTV4WSF5GLWAC
AZQVIGSM6OJHNHTTWY3X5D6YEYWQ5IXNJ2OM7DDVP2MPJ3R37MTAC
K4CH53V4MO5KCCJOOQUQKI3LEFSUSCNAJS24VUWOZISXTCQD4FZQC
4MG5JFXTKAE3SOVKGGNKEUTNCKOWEBHTGKVZHJWLWE3PTZTQKHPAC
SAHJYVNBUBBIUBI4ZMAXK4QJFOT54M5UA3W2HQMTNDSP3GGCRX7QC
GQVS55HIQLU7KPJNRMF57QUM4EATSWFQRCS7ZEJMJPUXFX2NHSYAC
4QOTH75I2VINLY52J75OR3E3B5CN5ITHJGOAKQYXI6PH3XLJ6DBQC
SPSFTMLRZE2R4EBAAQKWBUZDRYZ36S2VFTBIJRE6XS7JMKELV4AQC
TTR5IFSG25VNBQ2F2FNOLUMTEIHVBHFOEXYB2ZWEHWOURUV4GJMQC
XI5ALEH6NPTQWB6O62QV62EP4H3K7WSNTHCOGT3LZIIU6I2YDGIQC
YJXKWWM6TQLANMJZ2KGCLYUPSHYQXBD6JGZZLGF4BHAPL57YFNAQC
UKQAGL5F5LWZZR7RWFJI3H2MW6LXPRGPXAIGNBZI5IVJLWUFHLGAC
U4VCAFXQNTKC7KWWE3B2JJMZMFRGLBOHSZIOXCE6EEXW7WE2Q5NAC
2SABVMY3A2RZDF3KJXZSMJ2UQ4Q5EW422G4DVBJRKK26S2ESGVQAC
GUXZCEWWPBCHXO26JVWZ74CTSDFDDO775YR7FKY7UGVZCA7GCSYAC
A6ZAYJNBYFXPUTCHTQDGAWUZIYOXNO3IJBN73ZX6PEJ3T4NMNOGQC
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"ident" => Ok(Transform1::Ident),
_ => Err(parse_transform1(file!(), line!(), s)),
}
}
}
/// Transforms from two TimeSeries<1> to a TimeSeries<2>.
pub enum Transform2 {
///
Zip,
}
impl FromStr for Transform2 {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"zip" => Ok(Transform2::Zip),
_ => {
Err(parse_transform2(file!(), line!(), s))
},
}
}
}
// --- Client-facing data-structures --------------------------------------------------------------
/// `(DataType, Country)` key to lookup data or spec.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct PageKey {
///
pub country: Country,
///
pub data_type: DataType,
///
pub index: usize,
}
impl PageKey {
/// Return a new `PageKey`.
pub fn new(country: Country, data_type: DataType, index: usize) -> Self {
PageKey { country, data_type, index }
}
}
impl<'a> TryInto<PageKey> for KeyTreeRef<'a> {
type Error = keytree::Error;
fn try_into(self) -> Result<PageKey, Self::Error> {
Ok(PageKey {
index: self.value("key::index")?,
data_type: self.value("key::data_type")?,
country: self.value("key::country")?,
})
}
}
/// Represents a series of graphics to be plotted on one page. A `PageKey` is a combination of
/// `Country` and `DataType`. It is served to the client as one chunk of JSON.
// GraphicJson. Json holds the JSON serialization as a String. PageJson is the largest component
// that is serializable into JSON.
#[derive(Debug)]
pub struct TSData(pub HashMap<PageKey, String>);
impl TSData {
/// Build time-series data from a time-series specification.
pub fn from_spec(spec: &Spec, root_path: &str) -> Result<Self, Error> {
spec
.into_data(root_path)
}
pub (crate) fn new() -> TSData {
TSData(HashMap::new())
}
pub (crate) fn insert(&mut self, key: &PageKey, value: String) {
match self.0.get_mut(key) {
Some(_) => {
println!("Tried to insert page_key: {:?} twice.", key);
panic!();
},
None => {
self.0.insert(*key, value);
},
}
}
}
/// Converts specification data into `TSData`.
pub struct Json(Vec<PageJson>);
impl Json {
pub (crate) fn into_data(&self) -> Result<TSData, Error> {
let mut ts_data = TSData::new();
for page_json in &self.0 {
let key = PageKey::new(
page_json.country,
page_json.data_type,
page_json.index
);
let value = match serde_json::to_string(&page_json) {
Ok(s) => s,
Err(err) => {
eprintln!("{}", err.to_string());
panic!();
},
};
ts_data.insert(&key, value);
}
Ok(ts_data)
}
pub (crate) fn new() -> Self {
Json(Vec::new())
}
pub (crate) fn push(&mut self, page_json: PageJson) {
self.0.push(page_json);
}
}
#[derive(Debug)]
/// Serializable into JSON data for a time-series HTML page.
pub struct PageJson {
country: Country,
data_type: DataType,
index: usize,
graphics: Vec<GraphicJson>,
}
impl PageJson {
pub (crate) fn new(country: Country, data_type: DataType, index: usize) -> Self {
PageJson {
country, data_type, index, graphics: Vec::new()
}
}
pub (crate) fn push(&mut self, graphic_json: GraphicJson) {
self.graphics.push(graphic_json);
}
pub (crate) fn max(&self) -> f32 {
self.graphics.iter()
.map(|graphic| graphic.max())
.fold(f32::NEG_INFINITY, |a, b| a.max(b))
}
pub (crate) fn min(&self) -> f32 {
self.graphics.iter()
.map(|graphic| graphic.min())
.fold(f32::INFINITY, |a, b| a.min(b))
}
pub (crate) fn first_date(&self) -> MonthlyDate {
match self.graphics.iter()
.map(|graphic| graphic.first_date().0)
.min()
{
Some(date) => MonthlyDate(date),
None => {
// No data in series?
eprintln!("Could not calculate first date.");
panic!()
},
}
}
pub (crate) fn last_date(&self) -> MonthlyDate {
match self.graphics.iter()
.map(|graphic| graphic.last_date().0)
.max()
{
Some(date) => MonthlyDate(date),
None => {
// No data in series?
eprintln!("Could not calculate last date.");
panic!()
},
}
}
}
impl Serialize for PageJson {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut page_json = serializer.serialize_struct("Json", 6)?;
page_json.serialize_field("country", &self.country)?;
page_json.serialize_field("data_type", &self.data_type)?;
page_json.serialize_field("index", &self.index)?;
page_json.serialize_field("max", &self.max())?;
page_json.serialize_field("min", &self.min())?;
page_json.serialize_field("first_date", &self.first_date())?;
page_json.serialize_field("last_date", &self.last_date())?;
page_json.serialize_field("graphics", &self.graphics)?;
page_json.serialize_field("height", &GRAPHIC_HEIGHT)?;
page_json.end()
}
}
/// GraphicJson is the largest component that is serializable into JSON.
#[derive(Debug)]
pub struct GraphicJson {
///
pub height: Option<f32>,
///
pub series: Vec<(RegularTimeSeries<1>, SeriesMetaData)>,
}
impl GraphicJson {
pub (crate) fn new() -> Self {
GraphicJson {
height: None,
series: Vec::new(),
}
}
pub (crate) fn push(&mut self, time_series: RegularTimeSeries<1>, meta: SeriesMetaData) {
self.series.push((time_series, meta))
}
/// Calculate the maximum value of all series in `Self`.
pub (crate) fn max(&self) -> f32 {
self.series.iter()
.map(|(ts, _)| ts.max(0))
.fold(f32::NEG_INFINITY, |a, b| a.max(b))
}
/// Calculate the minimum value of all series in `Self`.
pub (crate) fn min(&self) -> f32 {
self.series.iter()
.map(|(ts, _)| ts.min(0))
.fold(f32::INFINITY, |a, b| a.min(b))
}
/// Return first date in all series.
pub (crate) fn first_date(&self) -> MonthlyDate {
match self.series.iter()
.map(|(ts, _)| ts.first_date())
.min()
{
Some(date) => MonthlyDate(date),
None => {
// No data in series?
eprintln!("Could not calculate first date.");
panic!()
}
}
}
/// Return last date of in all series.
pub (crate) fn last_date(&self) -> MonthlyDate {
match self.series.iter()
.map(|(ts, _)| ts.last_date())
.max()
{
Some(date) => MonthlyDate(date),
None => {
// No data in series?
eprintln!("Could not calculate first date.");
panic!()
}
}
}
pub (crate) fn height(&self) -> f32 {
match self.height {
Some(h) => h,
None => GRAPHIC_HEIGHT,
}
}
}
impl Serialize for GraphicJson {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut graphic_json = serializer.serialize_struct("GraphicJson", 6)?;
graphic_json.serialize_field("height", &self.height())?;
graphic_json.serialize_field("series", &self.series)?;
graphic_json.serialize_field("max", &self.max())?;
graphic_json.serialize_field("min", &self.min())?;
graphic_json.serialize_field("first_date", &self.first_date())?;
graphic_json.serialize_field("last_date", &self.last_date())?;
graphic_json.end()
}
}
// The Spec components do not map well to Json components.
// 1. Json is not serializable - it is a HashMap which maps PageKeys to Strings to serve.
// 2. PageJson does not exists. PageSpec.into_json creates a Vec<GraphicJson>.
pub (crate) fn into_data(&self, root_path: &str) -> Result<TSData, Error> {
let mut json = Json::new();
for page_spec in &self.0 {
let mut page_json = PageJson::new(
page_spec.country,
page_spec.data_type,
page_spec.index,
);
for graphic_spec in &page_spec.graphics {
let mut graphic_json = GraphicJson::new();
for series_spec in &graphic_spec.seriess {
let rts = data_from_file(
page_spec.country,
series_spec.data_type,
series_spec.series_id.clone(),
root_path,
)?;
let meta = meta_from_file(
page_spec.country,
series_spec.data_type,
series_spec.series_id.clone(),
root_path,
);
graphic_json.push(rts, meta);
}
page_json.push(graphic_json);
}
json.push(page_json);
}
// Now we have a Json datastructure which we can iterate over the pages, serialize, generate
// keys, and build a TSData Hashmap.
json.into_data()
}
}
}
/// Specifies what text a graphic displays.
pub enum TextSpec {
/// Specifies that graphic displays Links to series.
Link,
/// Specifies that graphics displays metadata.
Meta,
/// Specifies that graphics do not display text.
None,
}
impl FromStr for TextSpec {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"link" => Ok(TextSpec::Link),
"meta" => Ok(TextSpec::Meta),
_ => Err(keytree_error(file!(), line!(), "Failed to parse to Text")),
}
pub (crate) fn push(&mut self, series_spec: self::SeriesSpec) {
self.seriess.push(series_spec)
}
// pub (crate) fn push(
// &mut self,
// title: Option<Title>,
// height: Option<f32>,
// transform_spec: Vec<TransformSpec>)
// {
// let graph_spec = GraphSpec {
// title,
// height,
// transform_spec,
// };
// self.seriess.push(series_spec)
// }
GraphicSpec{
height: self.opt_value("graphic::height")?,
seriess: self.vec_at("graphic::series")?,
GraphicSpec {
title: self.opt_value("graphic::title")?,
height: self.opt_value("graphic::height")?,
series_id: self.vec_value("graphic::series_id")?,
fid: self.vec_value("graphic::fid")?,
text: self.value("graphic::text")?,
SeriesSpec{
data_type: self.value("series::data_type")?,
series_id: self.value("series::series_id")?,
SeriesSpec {
data_type: self.value("series::data_type")?,
series_id: self.value("series::series_id")?,
date_range,
transforms: self.vec_value("series::transform")?,
fid: self.opt_value("series::fid")?,
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"to_monthly" => Ok(Transform::ToMonthly),
"to_quarterly" => Ok(Transform::ToQuarterly),
"yoy" => Ok(Transform::YearOnYear),
_ => Err(parse_transform1(file!(), line!(), s)),
}
}
}
/// SeriesIdentifierAfterTransform
#[derive(Debug)]
pub struct FID(String);
impl FID {
pub (crate) fn new(series_id: &SeriesId, f: Vec<Transform>) -> Self {
let mut s = format!(
"{}_",
series_id,
);
for transform in f {
match transform {
Transform::ToMonthly => { s.push('m') },
Transform::ToQuarterly => { s.push('q') },
Transform::YearOnYear => { s.push('y') },
}
}
FID(s)
}
}
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(FID(String::from(s)))
}
}
impl MonthlyDate {
/// Create a time_series::DateRange.
pub fn range(
date_opt1: &Option<MonthlyDate>,
date_opt2: &Option<MonthlyDate>) -> time_series::DateRange
{
time_series::DateRange::new(
date_opt1.as_ref().map(|date| date.0),
date_opt2.as_ref().map(|date| date.0),
)
}
}
/// Create a time_series::DateRange.
pub fn new(
first_date: &Option<MonthlyDate>,
last_date: &Option<MonthlyDate>) -> DateRange
{
DateRange(
time_series::DateRange::new(
&first_date.clone().map(|d| d.0),
&last_date.clone().map(|d| d.0),
)
)
}
///
pub fn first_date(&self) -> Option<MonthlyDate> {
self.0.first_date().map(|date| MonthlyDate(date))
}
///
pub fn last_date(&self) -> Option<MonthlyDate> {
self.0.last_date().map(|date| MonthlyDate(date))
}
}
for series_spec in series_specs {
let ts_series_spec = ts::SeriesSpec::new(
series_spec.data_type,
series_spec.series_id.clone()
);
ts_graphic_spec.push(ts_series_spec);
};
// let series_id = series_spec.series_id;
// let f = Vec::new();
// for series_spec in series_specs {
// let series_spec = ts::SeriesSpec::new(
// series_spec.data_type,
// series_id,
// DateRange::new(&None, &None),
// f,
// FID::new(series_id, f),
// );
// graphic_spec.push(series_spec);
// };