5B2HBV3JTNBNJYEJ4BXFQDEP2D552SBJFPY6STKSK7SPFZYSTCFAC TSY4YBBZ4AEW2JTEI4AS5FGTTUZR5WE3TENDHVIOWWY6V2DRT7ZQC SPSFTMLRZE2R4EBAAQKWBUZDRYZ36S2VFTBIJRE6XS7JMKELV4AQC 2SABVMY3A2RZDF3KJXZSMJ2UQ4Q5EW422G4DVBJRKK26S2ESGVQAC GQVS55HIQLU7KPJNRMF57QUM4EATSWFQRCS7ZEJMJPUXFX2NHSYAC TTR5IFSG25VNBQ2F2FNOLUMTEIHVBHFOEXYB2ZWEHWOURUV4GJMQC SAHJYVNBUBBIUBI4ZMAXK4QJFOT54M5UA3W2HQMTNDSP3GGCRX7QC K4CH53V4MO5KCCJOOQUQKI3LEFSUSCNAJS24VUWOZISXTCQD4FZQC 2CCG6KUP6VL2Q7WQKLVNIPPGZWHSWIOWETRCS3APZTV4WSF5GLWAC XI5ALEH6NPTQWB6O62QV62EP4H3K7WSNTHCOGT3LZIIU6I2YDGIQC 4MG5JFXTKAE3SOVKGGNKEUTNCKOWEBHTGKVZHJWLWE3PTZTQKHPAC UUD3CJZLSTAVLMIXIWE4CHN5HQSFM6K3DCPWECJMKGXDOROK4G4AC #[derive(Debug)]pub struct Json(BTreeMap<(Country, DataType, usize), Vec<GraphicJson>>);#[derive(Debug, Serialize)]pub struct GraphicJson {title_opt: Option<String>,graphics: Vec<SeriesJson>,text_spec: TextSpec,}#[derive(Debug, Serialize)]pub struct SeriesJson {rts: RegularTimeSeries<1>,meta: Option<SeriesMetaData>,transforms: Vec<Transform>,}
/// Time-series specification./// ```/// ts_spec:/// page:/// country: Australia/// data_type: u/// index: 0/// graphic:/// series:/// data_type: u/// series_id: AUSURAMS/// series:/// data_type: u/// series_id: AUSURANAA/// ```
#[derive(Debug)]
}pub (crate) fn into_json(&self, root_path: &str) -> Result<Json, Error> {let mut json = BTreeMap::new();for page_spec in &self.0 {let PageSpec {country,data_type,index,seriess,graphics,} = page_spec;// Iterate over graphics.let mut v: Vec<GraphicJson> = Vec::new();for graphic_spec in graphics {let GraphicSpec {title_opt,height_opt,series_ids,text_spec,..} = graphic_spec;// Construct GraphicJsonlet t_opt = match title_opt {Some(title_opt) => Some(title_opt.clone()),None => None,};let mut graphic_json = GraphicJson {title_opt: t_opt,graphics: Vec::new(),text_spec: graphic_spec.text_spec,};for series_id in series_ids {graphic_json.graphics.push(page_spec.into_series_json(series_id, *country, root_path)?);}// Push GraphicJson to temporary Vec.v.push(graphic_json);}// Push key and value to jsonjson.insert((country, data_type, index), v);}Ok(Json(BTreeMap::new()))
impl PageSpec {pub (crate) fn into_series_json(&self,series_id: &SeriesId,country: Country,root_path: &str) -> Result<SeriesJson, Error>{let series_spec = match self.seriess.get(&series_id) {Some(series) => series,None => {return Err(failed_to_reference_series(file!(),line!(),&series_id.to_string() ,))}};let rts = series_spec.data_with_transforms()?;let meta = series_spec.meta(country, root_path);Ok(SeriesJson {rts: rts,meta: Some(meta),transforms: series_spec.transforms.clone(),})}}
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")?,
title_opt: self.opt_value("graphic::title")?,height_opt: self.opt_value("graphic::height")?,series_ids: self.vec_value("graphic::series_id")?,
// This is differentiated from lib::SeriesSpec in that is is just for interacting with///// Component of time-series specification./// ```/// series:/// data_type: u/// series_id: AUSURAMS/// ```#[derive(Debug)]pub struct SeriesSpec {///pub data_type: DataType,///pub series_id: SeriesId,///pub date_range: DateRange,///pub transforms: Vec<Transform>,///pub fid: Option<FID>,}impl<'a> TryInto<SeriesSpec> for KeyTreeRef<'a> {type Error = keytree::Error;fn try_into(self) -> Result<SeriesSpec, keytree::Error> {let first_date = self.opt_value("series::first_date")?;let last_date = self.opt_value("series::last_date")?;let date_range = DateRange::new(&first_date, &last_date);Ok(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")?,})}}impl IntoKeyTree for SeriesSpec {fn keytree(&self) -> KeyTreeString {let mut kt = KeyTreeString::new();kt.push_key(0, "series");kt.push_value(1, "data_type", &self.data_type);kt.push_value(1, "series_id", &self.series_id);if let Some(first_date) = self.date_range.first_date() {kt.push_value(1, "first_date", first_date);}if let Some(last_date) = self.date_range.last_date() {kt.push_value(1, "last_date", last_date);};for f in &self.transforms {kt.push_value(1, "f", f);}if let Some(fid) = &self.fid {kt.push_value(1, "fid", fid);}kt}}
Transform::ToMonthly => write!(f, "to_monthly"),Transform::ToQuarterly => write!(f, "to_quarterly"),Transform::YearOnYear => write!(f, "yoy"),}}}/// 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') },}
Transform::ToMonthly => write!(f, "to_monthly"),Transform::ToQuarterly => write!(f, "to_quarterly"),Transform::YearOnYear => write!(f, "yoy"),
impl FromStr for FID {type Err = Error;fn from_str(s: &str) -> Result<Self, Self::Err> {Ok(FID(String::from(s)))}}impl fmt::Display for FID {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {write!(f, "{}", self.0)}}
///
// We conflate both FRED-facing series and client-facing series. The reason for this is so that// the time_series() functionality is in one place. The down-side is that we need to make// the transforms optional, as they are not used in FRED-facing functionality, and// the country field optional as it is specified at a higher level in JsonSpec.
// We pass country because `series_spec.country` is None./// Return the meta data for a `SeriesSpec`.pub fn meta(&self, country: Country, root_path: &str) -> SeriesMetaData{let path = data_path(root_path,self.data_type,country,self.series_id.clone(),"meta");let meta_str = fs::read_to_string(path).unwrap();let kt = KeyTree::parse(&meta_str).unwrap();kt.to_ref().try_into().unwrap()}
}}pub fn data_with_transforms(&self) -> Result<RegularTimeSeries<1>, Error> {let mut rts = self.data_without_transform()?;// Do transforms before constraining dates.for transform in &self.transforms {match transform {Transform::ToMonthly => rts.to_monthly(0),Transform::ToQuarterly => rts.to_quarterly(0),Transform::YearOnYear => {match rts.to_year_on_year(0) {Ok(rts) => rts,Err(err) => {return Err(keytree_error(file!(),line!(),&err.to_string(),))},}}};
kt.push_opt_value(1, "drop_first", self.drop_first);
if let Some(first_date) = self.date_range.first_date() {kt.push_value(1, "first_date", first_date);}if let Some(last_date) = self.date_range.last_date() {kt.push_value(1, "last_date", last_date);};for f in &self.transforms {kt.push_value(1, "f", f);}if let Some(drop) = &self.drop_first {kt.push_value(1, "drop_first", drop);}
/// Read csv data from file and return a time-series.pub fn data_from_file(country: Country,data_type: DataType,series_id: SeriesId,root_path: &str) -> Result<RegularTimeSeries<1>, Error>{let path = data_path(root_path,data_type,country,series_id.clone(),"csv");match TimeSeries::<1>::from_csv(&path) {Ok(ts) => {match ts.try_into() {Ok(rts) => Ok(rts),Err(err) => {Err(time_series_from_csv_failed(file!(),line!(),&data_type.to_string(),&country.to_string(),&series_id.to_string(),&err.to_string(),))},}},Err(err) => Err(time_series_from_csv_failed(file!(),line!(),&data_type.to_string(),&country.to_string(),&series_id.to_string(),&err.to_string(),))}}/// Return the meta data for a `SeriesSpec`.pub fn meta_from_file(country: Country,data_type: DataType,series_id: SeriesId,root_path: &str) -> SeriesMetaData{let path = data_path(root_path,data_type,country,series_id.clone(),"meta");let meta_str = fs::read_to_string(path).unwrap();let kt = KeyTree::parse(&meta_str).unwrap();kt.to_ref().try_into().unwrap()}