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 GraphicJson
let 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 json
json.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()
}