GQVS55HIQLU7KPJNRMF57QUM4EATSWFQRCS7ZEJMJPUXFX2NHSYAC
LVMGQJGH34VSGZCB74IQEJIVFNJE6OPQQCF7IPS72NPVMIUFBV4AC
4MG5JFXTKAE3SOVKGGNKEUTNCKOWEBHTGKVZHJWLWE3PTZTQKHPAC
77SIQZ3EGGV6KSECMLPDKQFGEC7CCFAPWGER7ZARQ5STDKJNU6GQC
GUXZCEWWPBCHXO26JVWZ74CTSDFDDO775YR7FKY7UGVZCA7GCSYAC
AT753JPOOWZCIYNKAG27LZSZZ72ZILWVENG42Y6U2S34JD3B6ZZQC
5POF332LJEBGWEJUGI34P33Q4BQP7CAQNV5ODQT3PWG7FI7VNLOQC
AIFRDCG2GLASIMF3WZYAQBHIGZZDGPMP2WBYH7HXIYORKAK2SUYAC
UCSU3QE4352YC4VOVI7GCSTNNA5FAHYURNJLJQEHY32BB6S3CZVAC
CUADTSHQNPGMWHIJCWXKHNNIY4UJOYZ7XBZA5CJ2VVJJAMSNT4BAC
//! Deserialize data to be served as JSON to build Unemployment/Inflation scatter-plots.
use std::convert::TryInto;
use std::collections::HashMap;
use serde::{Serialize};
use countries::Country;
use keytree::{
KeyTreeRef,
Error,
ErrorKind,
};
use time_series::{
DateRange,
MonthlyDate,
RegularTimeSeries,
};
use crate::{
DataType,
};
use crate::ts::{
data_from_csv,
SeriesMetaData,
};
/// ```
/// ui:
/// ui_graphic:
/// time_series:
/// data_type: u
/// series: AUSURAMS_a
/// transform: ident
/// time_series:
/// data_type: i
/// series: _
/// transform: ident
/// x: 0
/// y: 1
/// ```
pub struct UISpec(pub HashMap<Country, Vec<UIGraphicSpec>>);
impl<'a> TryInto<UISpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<UISpec, Error> {
let countries: Vec<CountrySpec> = self.vec("ui::country")?;
let mut h = HashMap::new();
for country in countries {
h.insert(country.key, country.value.0);
}
Ok(UISpec(h))
}
}
pub struct CountrySpec {
key: Country,
value: ValueSpec,
}
impl<'a> TryInto<CountrySpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<CountrySpec, Error> {
let country_str: String = self.at("country::key")?;
let country = Country::from_str(&country_str).unwrap();
Ok(
CountrySpec {
key: country,
value: self.at("country::value")?,
}
)
}
}
pub struct ValueSpec(Vec<UIGraphicSpec>);
impl<'a> TryInto<ValueSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<ValueSpec, Error> {
Ok(ValueSpec(self.vec("value::graphic")?))
}
}
/// An integer reference to a series, for x and y coordinates.
/// ```
/// ui:
/// country:
/// key: Australia
/// value:
/// graphic:
/// title: Unemployment and Inflation for Australia
/// time_series:
/// data_type: u
/// series: AUSURAMS_a
/// transform: ident
/// time_series:
/// data_type: i
/// series: _
/// transform: ident
/// line:
/// x: 0
/// y: 1
/// ```
/// For instance in this example, x refers to the first time-series AUSURAMS_a, and y refers to the
/// second time-series.
pub struct UILineSpec {
x: String,
y: String,
start_date: Option<MonthlyDate>,
end_date: Option<MonthlyDate>,
}
impl<'a> TryInto<UILineSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<UILineSpec, Error> {
let start_date_str: Option<String> = self.op("line::start_date")?;
let start_date_opt = match start_date_str {
Some(date) => Some(parse_date(&date)?),
None => None,
};
let end_date_str: Option<String> = self.op("line::end_date")?;
let end_date_opt = match end_date_str {
Some(date) => Some(parse_date(&date)?),
None => None,
};
Ok(
UILineSpec {
x: self.at("line::x")?,
y: self.at("line::y")?,
start_date: start_date_opt,
end_date: end_date_opt,
}
)
}
}
fn parse_date(s: &str) -> Result<MonthlyDate, Error> {
let year = s[..=4].parse::<usize>().map_err(|err| keytree::Error(Box::new(ErrorKind::User(err.to_string()))))?;
let month = s[5..].parse::<usize>().map_err(|err| keytree::Error(Box::new(ErrorKind::User(err.to_string()))))?;
Ok(MonthlyDate::ym(year, month))
}
#[test]
fn test_parse_date() {
let s = "1999-03";
assert!(
parse_date(s),
MonthlyDate(1999, 3),
)
}
// Structs UIJson, UIGraphJson and UILineJson encapsulate data to be converted to JSON.
/// `UIJson` are a collection of `LineJson` convertible to JSON for a ui graphic.
#[derive(Serialize)]
pub struct UIJson {
country: Country,
graphics: Vec<UIGraphicJson>,
}
impl UIJson {
/// Build a new `UIJson` from a `UISpec` specification.
pub fn new(ui_spec: &UISpec, country: Country) -> Self {
let ui_country_spec = ui_spec.0.get(&country).unwrap();
let mut builder = UIJson {
country: country,
graphics: Vec::new(),
};
for graphic_spec in ui_country_spec {
builder.graphics.push(UIGraphicJson::new(graphic_spec, country));
}
builder
}
}
/// `UIGraphicJson` is a JSON representation of a UI graph.
#[derive(Serialize)]
pub struct UIGraphicJson {
title: String,
lines: Vec<UILineJson>,
}
impl UIGraphicJson {
pub fn new(graphic_spec: &UIGraphicSpec, country: Country) -> Self {
let mut builder = UIGraphicJson {
title: graphic_spec.title.clone(),
lines: Vec::new(),
};
for line_spec in &graphic_spec.line {
builder.lines.push(UILineJson::new(&line_spec, country));
};
builder
}
}
/// `UILineJson` are two time-series
#[derive(Serialize)]
pub struct UILineJson {
x: RegularTimeSeries<1>,
y: RegularTimeSeries<1>,
x_meta: SeriesMetaData,
y_meta: SeriesMetaData,
}
impl UILineJson {
/// Build a new `UILineJson` from a `UILineSpec` specification.
pub fn new(line_spec: &UILineSpec, country: Country) -> Self {
let (mut x_data, x_meta) = data_from_csv(&line_spec.x, country, DataType::U);
let range = DateRange::new(
line_spec.start_date,
line_spec.end_date,
);
x_data.with_range(&range);
let (mut y_data, y_meta) = data_from_csv(&line_spec.y, country, DataType::Inf);
y_data.with_range(&range);
UILineJson {
x: x_data,
y: y_data,
x_meta: x_meta,
y_meta: y_meta,
}
}
}
/// ```
/// ui:
/// country:
/// key: Australia
/// value:
/// graphic:
/// title: Unemployment and Inflation for Australia
/// line:
/// x: AUSURAMS_a
/// y: _
/// start_date: _
/// end_date: _
/// line:
/// ```
pub struct UIGraphicSpec {
pub title: String,
pub line: Vec<UILineSpec>,
}
impl<'a> TryInto<UIGraphicSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<UIGraphicSpec, Error> {
Ok(
UIGraphicSpec {
title: self.at("ui_graphic::title")?,
line: self.vec("ui_graphic::line")?,
}
)
}
}
//! Use a specification to fetch and save data to disk.
use std::convert::TryInto;
use std::fs;
use std::path::PathBuf;
use countries::Country;
use fred_api::Fred;
use keytree::{KeyTree, KeyTreeRef};
use crate::ts::SourceSpec;
/// Read the specification, pull the data down from FRED and save to disk.
/// ```
/// save_data(spec: "source_spec.keytree", "/path/test_data");
/// ```
pub fn save_data(spec: &str, full_path: &str) {
let spec = fs::read_to_string(&spec).unwrap();
let kt = KeyTree::parse(&spec).unwrap();
let source_spec: SourceSpec = kt.to_ref().try_into().unwrap();
for series_id in &source_spec.all_series() {
save_series_meta(series_id, full_path);
save_data_csv(series_id, full_path);
}
}
/// Save FRED series metadata including title to disk as json.
/// ```
/// save_series_meta("LRUNTTTTSIQ156S");
/// ```
pub fn save_series_meta(series_id: &str, full_path: &str) {
dbg!(&series_id);
let data = match Fred::series(series_id) {
Ok(series) => series.to_string(),
Err(err) => {
println!("{}", err);
panic!();
},
};
let file_path = &format!(
"{}/{}.meta",
full_path,
series_id,
);
if PathBuf::from(file_path).exists() {
println!("Cannot write data as filepath {} already exists.", file_path);
panic!();
} else {
fs::write(file_path, &data).unwrap();
};
}
/// Save FRED data to disk as csv, using full path. Will fail if an existing filepath is
/// encountered.
/// ```
/// save_data_csv("LRUNTTTTSIQ156S");
/// ```
/// To handle breaks the data is saved in a collection of files "LRUNTTTTSIQ156S_a.csv",
/// "LRUNTTTTSIQ156S_b.csv" etc.
///
pub fn save_data_csv(series_id: &str, path: &str) {
let alphabet = "abcdefghijklmnopqrstuvwxyz";
let mut chars = alphabet.chars();
let mut contains_data = false;
let mut data = String::new();
// Select the observation data from the FRED data.
let observations = match Fred::series_observations(series_id) {
Ok(series_obs) => series_obs.observations,
Err(err) => {
println!("{}", err);
panic! ();
},
};
for obs in observations.iter() {
let value: Result<f32, _> = obs.value.parse();
dbg!(&value);
match value {
Ok(_) => {
contains_data = true;
data.push_str(&obs.to_string());
data.push('\n');
},
Err(_) => {
if contains_data {
// Remove last linefeed.
data.pop();
fs::create_dir_all(&path).unwrap();
let file_name = &format!(
"{}_{}",
series_id,
chars.next().expect("Too many breaks"),
);
let file_path = &format!(
"{}/{}.csv",
path,
file_name,
);
println!(" graphic:");
println!(
" data: {}",
&file_name,
);
if PathBuf::from(file_path).exists() {
println!("Cannot write data as filepath {} already exists.", file_path);
panic!();
} else {
fs::write(file_path, &data).unwrap();
};
data = String::new();
contains_data = false;
}
},
}
}
if contains_data {
fs::create_dir_all(&path).unwrap();
let file_name = &format!(
"{}_{}",
series_id,
chars.next().expect("Too many breaks"),
);
let file_path = &format!(
"{}/{}.csv",
path,
file_name,
);
println!(" graphic:");
println!(
" data: {}",
&file_name,
);
if PathBuf::from(file_path).exists() {
println!("Cannot write data as filepath {} already exists.", file_path);
panic!();
} else {
fs::write(file_path, &data).unwrap();
};
}
}
//! Builds generic specifications.
use std::convert::TryInto;
use std::fs;
use countries::Country;
use fred_api::{
Fred,
SeriesItems,
};
use keytree::{
KeyTree,
KeyTreeRef,
};
use keytree::Error;
use keytree::serialize::{
IntoKeyTree,
KeyTreeString,
};
use crate::DataType;
/// Return all the countries with good data as a `Vec`.
pub fn countries_with_data() -> Vec<Country> {
vec!(
Country::Australia,
Country::Austria,
Country::Belgium,
Country::Canada,
Country::Chile,
Country::CzechRepublic,
Country::Denmark,
Country::Estonia,
Country::Finland,
Country::France,
Country::Germany,
Country::Greece,
Country::Ireland,
Country::Israel,
Country::Italy,
Country::Japan,
Country::Latvia,
Country::Netherlands,
Country::NewZealand,
Country::Norway,
Country::Poland,
Country::Serbia,
Country::SouthKorea,
Country::Spain,
Country::Sweden,
Country::Switzerland,
Country::UnitedKingdom,
Country::UnitedStates,
)
}
/// Returns relevant CPI series for a country, by pre-selecting series titles for each country.
///
/// ```
/// println!("{}", generic_cpi_series_spec(&Country::NewZealand, "cpi_series.keytree"));
/// ```
pub fn generic_cpi_series_spec(country: &Country, path: &str) -> SeriesItems {
let titles = match country {
Country::Australia => {
vec!(
"Consumer Price Index: All items: Total: Total for Australia",
"Consumer Price Index: All Items for Australia",
"Consumer Price Index in Australia (DISCONTINUED)",
"Consumer Price Index: Total All Items for Australia",
"Consumer Price Index for Australia",
"Inflation, consumer prices for Australia",
)
}
Country::Austria => {
vec!(
"Consumer Price Index for Austria",
"Consumer Price Index: All items: Total: Total for Austria",
"Consumer Price Index: All Items for Austria",
"Consumer Price Index in Austria (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for Austria",
"Consumer Price Index: Harmonized Prices: Total All Items for Austria // (DISCONTINUED)",
"Consumer Price Index: Total All Items for Austria",
"Harmonized Index of Consumer Prices: All Items for Austria",
"Harmonized Index of Consumer Prices in Austria (DISCONTINUED)",
"Inflation, consumer prices for Austria",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for // Austria",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Austria (DISCONTINUED)",
)
}
Country::Belgium => {
vec!(
"Consumer Price Index for Belgium",
"Consumer Price Index: Harmonized Prices: Total All Items for Belgium",
"Consumer Price Index: Harmonized Prices: Total All Items for Belgium (DISCONTINUED)",
"Consumer Price Index: Total All Items for Belgium",
"Harmonized Index of Consumer Prices: All Items for Belgium",
"Harmonized Index of Consumer Prices in Belgium (DISCONTINUED)",
"Consumer Price Index: All Items for Belgium",
"Consumer Price Index in Belgium (DISCONTINUED)",
"Consumer Price Index: All items: Total: Total for Belgium",
)
}
Country::Canada => {
vec!(
"Inflation, consumer prices for Canada",
"Consumer Price Index for Canada",
"Consumer Price Index: Total All Items for Canada",
"Consumer Price Index in Canada (DISCONTINUED)",
"Consumer Price Index of All Items in Canada",
"Consumer Price Index: All items: Total: Total for Canada",
)
}
Country::Chile => {
vec!(
"Consumer Price Index for Chile",
"Inflation, consumer prices for Chile",
"Consumer Price Index: Total All Items for Chile",
"Consumer Price Index: All Items for Chile",
"Consumer Price Index: All items: Total: Total for Chile",
)
}
Country::CzechRepublic => {
vec!(
"Consumer Price Index for Czech Republic",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Czech Republic",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Czech Republic (DISCONTINUED)",
"Consumer Price Index: All Items for Czech Republic",
"Consumer Price Index: All items: Total: Total for the Czech Republic",
"Consumer Price Index: Harmonized Prices: Total All Items for the Czech Republic",
"Consumer Price Index: Harmonized Prices: Total All Items for the Czech Republic (DISCONTINUED)",
"Consumer Price Index: Total All Items for the Czech Republic",
"Harmonized Index of Consumer Prices: All Items for Czech Republic",
)
}
Country::Denmark => {
vec!(
"Inflation, consumer prices for Denmark",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Denmark",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Denmark (DISCONTINUED)",
"Harmonized Index of Consumer Prices in Denmark (DISCONTINUED)",
"Consumer Price Index: All Items for Denmark",
"Consumer Price Index in Denmark (DISCONTINUED)",
"Consumer Price Index: All items: Total: Total for Denmark",
"Consumer Price Index for Denmark",
"Consumer Price Index: Total All Items for Denmark",
"Harmonized Index of Consumer Prices: All Items for Denmark",
)
}
Country::Estonia => {
vec!(
"Inflation, consumer prices for Estonia",
"Consumer Price Index: All items: Total: Total for Estonia",
"Consumer Price Index for Estonia",
"Consumer Price Index: Harmonized Prices: Total All Items for Estonia",
"Consumer Price Index: Harmonized Prices: Total All Items for Estonia (DISCONTINUED)",
"Consumer Price Index: Total All Items for Estonia",
"Harmonized Index of Consumer Prices: All Items for Estonia",
)
}
Country::Finland => {
vec!(
"Inflation, consumer prices for Finland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Finland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Finland (DISCONTINUED)",
"Consumer Price Index: All Items for Finland",
"Consumer Price Index: OECD Groups: Services: Total for Finland",
"Consumer Price Index: All items: Total: Total for Finland",
"Consumer Price Index: Harmonized Prices: Total All Items for Finland (DISCONTINUED)",
"Consumer Price Index for Finland",
"Consumer Price Index: Harmonized Prices: Total All Items for Finland",
"Consumer Price Index: Total All Items for Finland",
"Harmonized Index of Consumer Prices: All Items for Finland",
)
}
Country::France => {
vec!(
"Harmonized Index of Consumer Prices in France (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for France (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for France",
"Consumer Price Index in France (DISCONTINUED)",
"Consumer Price Index of All Items in France",
"Consumer Price Index: All items: Total: Total for France",
"Inflation, consumer prices for France",
"Consumer Price Index for France",
"Consumer Price Index: Harmonized Prices: Total All Items for France (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for France",
"Consumer Price Index: Total All Items for France",
)
}
Country::Germany => {
vec!(
"Inflation, consumer prices for Germany",
"Harmonized Index of Consumer Prices in Germany (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Germany (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Germany",
"Consumer Price Index in Germany (DISCONTINUED)",
"Consumer Price Index of All Items in Germany",
"Consumer Price Index: All items: Total: Total for Germany",
"Consumer Price Index for Germany",
"Consumer Price Index: Harmonized Prices: Total All Items for Germany (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for Germany",
"Consumer Price Index: Total All Items for Germany",
"Harmonized Index of Consumer Prices: All Items for Germany (including former GDR from 1991)",
)
}
Country::Greece => {
vec!(
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Greece",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Greece (DISCONTINUED)",
"Consumer Price Index: All Items for Greece",
"Consumer Price Index: All items: Total: Total for Greece",
"Inflation, consumer prices for Greece",
"Consumer Price Index for Greece",
"Consumer Price Index: Harmonized Prices: Total All Items for Greece",
"Consumer Price Index: Harmonized Prices: Total All Items for Greece (DISCONTINUED)",
"Consumer Price Index: Total All Items for Greece",
"Harmonized Index of Consumer Prices: All Items for Greece",
)
}
Country::Ireland => {
vec!(
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Ireland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Ireland (DISCONTINUED)",
"Consumer Price Index: OECD Groups: Services: Total for Ireland",
// "Consumer Price Index: All Items for Ireland", // Does not parse
// "Consumer Price Index: All items: Total: Total for Ireland", // Does not parse
"Consumer Price Index: Harmonized Prices: Total All Items for Ireland (DISCONTINUED)",
"Consumer Price Index for Ireland",
"Consumer Price Index: Harmonized Prices: Total All Items for Ireland",
"Harmonized Index of Consumer Prices: All Items for Ireland",
)
}
Country::Israel => {
vec!(
"Consumer Price Index: All Items for Israel",
"Consumer Price Index: All items: Total: Total for Israel",
"Consumer Price Index for Israel",
"Inflation, consumer prices for Israel",
"Consumer Price Index: Total All Items for Israel",
)
}
Country::Italy => {
vec!(
"Harmonized Index of Consumer Prices in Italy (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Italy (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Italy",
"Consumer Price Index: Food for Italy",
"Consumer Price Index in Italy (DISCONTINUED)",
"Consumer Price Index of All Items in Italy",
"Consumer Price Index: All items: Total: Total for Italy",
"Inflation, consumer prices for Italy",
"Consumer Price Index for Italy",
"Consumer Price Index: Harmonized Prices: Total All Items for Italy (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for Italy",
"Consumer Price Index: Total All Items for Italy",
"Harmonized Index of Consumer Prices: All Items for Italy",
)
}
Country::Japan => {
vec!(
"Harmonized Index of Consumer Prices in Japan (DISCONTINUED)",
"Consumer Price Index in Japan (DISCONTINUED)",
"Consumer Price Index of All Items in Japan",
"Consumer Price Index: All items: Total: Total for Japan",
"Inflation, consumer prices for Japan",
"Not Seasonally Adjusted",
)
}
Country::Latvia => {
vec!(
"Consumer Price Index: All items: Total: Total for Latvia",
"Inflation, consumer prices for Latvia",
"Harmonized Index of Consumer Prices: Unprocessed Food for Latvia",
"Consumer Price Index for Latvia",
)
}
Country::Netherlands => {
vec!(
"Harmonized Index of Consumer Prices in Netherlands (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Netherlands (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Netherlands",
"Consumer Price Index in Netherlands (DISCONTINUED)",
"Consumer Price Index: All Items for Netherlands",
"Consumer Price Index: All items: Total: Total for the Netherlands",
"Consumer Price Index for Netherlands",
"Consumer Price Index: Harmonized Prices: Total All Items for the Netherlands",
"Consumer Price Index: Harmonized Prices: Total All Items for the Netherlands (DISCONTINUED)",
"Consumer Price Index: Total All Items for the Netherlands",
"Harmonized Index of Consumer Prices: All Items for Netherlands",
)
}
Country::NewZealand => {
vec!(
"Consumer Price Index: All Items for New Zealand",
"Consumer Price Index: All Items Excluding Food and Energy for New Zealand",
"Consumer Price Index: All items: Total: Total for New Zealand",
"Consumer Price Index for New Zealand",
"Inflation, consumer prices for New Zealand",
"Consumer Price Index: Total All Items for New Zealand",
)
}
Country::Norway => {
vec!(
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Norway",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Norway (DISCONTINUED)",
"Consumer Price Index: All Items for Norway",
"Consumer Price Index in Norway (DISCONTINUED)",
"Consumer Price Index: All items: Total: Total for Norway",
"Consumer Price Index: Harmonized Prices: Total All Items for Norway (DISCONTINUED)",
"Consumer Price Index for Norway",
"Consumer Price Index: Harmonized Prices: Total All Items for Norway",
"Consumer Price Index: Total All Items for Norway",
"Harmonized Index of Consumer Prices: All Items for Norway",
)
}
Country::Poland => {
vec!(
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Poland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Poland (DISCONTINUED)",
"Consumer Price Index: Food for Poland",
"Consumer Price Index: All Items for Poland",
"Consumer Price Index: All items: Total: Total for Poland",
"Inflation, consumer prices for Poland",
"Consumer Price Index for Poland",
"Consumer Price Index: Total All Items for Poland",
"Harmonized Index of Consumer Prices: All Items for Poland",
)
}
Country::Serbia => {
vec!(
"Consumer Price Index for Serbia",
"Inflation, consumer prices for Serbia",
)
}
Country::SouthKorea => {
vec!(
"Consumer Price Index: All Items for Korea",
"Consumer Price Index: All items: Total: Total for the Republic of Korea",
"Inflation, consumer prices for the Republic of Korea",
"Consumer Price Index for Republic of Korea",
"Consumer Price Index: Total All Items for the Republic of Korea",
)
}
Country::Spain => {
vec!(
"Inflation, consumer prices for Spain",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Spain (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Spain",
"Consumer Price Index in Spain (DISCONTINUED)",
"Consumer Price Index: All Items for Spain",
"Consumer Price Index: All items: Total: Total for Spain",
"Consumer Price Index for Spain",
"Consumer Price Index: Harmonized Prices: Total All Items for Spain (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for Spain",
"Consumer Price Index: Total All Items for Spain",
"Harmonized Index of Consumer Prices: All Items for Spain",
)
}
Country::Sweden => {
vec!(
"Harmonized Index of Consumer Prices in Sweden (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Sweden",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Sweden (DISCONTINUED)",
"Consumer Price Index: All Items for Sweden",
"Consumer Price Index in Sweden (DISCONTINUED)",
"Consumer Price Index: All items: Total: Total for Sweden",
"Consumer Price Index: Harmonized Prices: Total All Items for Sweden",
"Consumer Price Index: Harmonized Prices: Total All Items for Sweden (DISCONTINUED)",
"Consumer Price Index for Sweden",
"Consumer Price Index: Total, Net All Items for Sweden (DISCONTINUED)",
"Consumer Price Index: Total All Items for Sweden",
"Harmonized Index of Consumer Prices: All Items for Sweden",
)
}
Country::Switzerland => {
vec!(
"Consumer Price Index: Harmonized Prices: Total All Items for Switzerland",
"Consumer Price Index: Harmonized Prices: Total All Items for Switzerland (DISCONTINUED)",
"Consumer Price Index for Switzerland",
"Consumer Price Index: Total All Items for Switzerland",
"Harmonized Index of Consumer Prices: All Items for Switzerland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Switzerland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Switzerland (DISCONTINUED)",
)
}
Country::UnitedKingdom => {
vec!(
"Harmonized Index of Consumer Prices in the United Kingdom (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for United Kingdom (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for United Kingdom",
"Consumer Price Index in the United Kingdom (DISCONTINUED)",
"Consumer Price Index of All Items in the United Kingdom",
"Consumer Price Index: All Items for United Kingdom",
"Consumer Price Index: All items: Total: Total for the United Kingdom",
"Inflation, consumer prices for the United Kingdom",
"Consumer Price Index for United Kingdom",
"Consumer Price Index in the United Kingdom",
"Consumer Price Inflation in the United Kingdom",
"Consumer Price Index: Harmonized Prices: Total All Items for the United Kingdom (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for the United Kingdom",
"Consumer Price Index: Total All Items for the United Kingdom",
"Harmonized Index of Consumer Prices: All Items for United Kingdom",
)
}
Country::UnitedStates => {
vec!(
"Harmonized Index of Consumer Prices: All Items for United States",
"Consumer Price Index: Total All Items for the United States",
"Consumer Price Index: Harmonized Prices: Total All Items for the United States",
"Consumer Price Index: Harmonized Prices: Total All Items for the United States (DISCONTINUED)",
"Research Consumer Price Index: All Items",
"Consumer Price Index for United States",
"Flexible Price Consumer Price Index",
"Inflation, consumer prices for the United States",
"Consumer Price Index, All Items for United States",
"Rate of Change (6 Month Span at Annual Rate), Consumer Price Index, All Items (Centered) for United States",
"Median Consumer Price Index",
"Sticky Price Consumer Price Index",
"16% Trimmed-Mean Consumer Price Index",
"Consumer Price Index: All Items for the United States",
"Consumer Price Index of All Items in United States",
"Consumer Price Index in the United States (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for the United States",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for the United States (DISCONTINUED)",
"Harmonized Index of Consumer Prices in the United States (DISCONTINUED)",
)
}
_ => panic!(),
};
match country {
Country::UnitedStates => {
match Fred::tags_series("cpi;usa") {
Ok(tags_series) => {
tags_series.seriess
.equals_one_of(titles)
},
Err(json_error) => {
println!("{}", json_error);
panic!();
},
}
},
_ => {
match Fred::tags_series(&to_tag("cpi", &country)) {
Ok(tags_series) => {
tags_series.seriess
.equals_one_of(titles)
},
Err(json_error) => {
println!("{}", json_error);
panic!();
},
}
},
}
}
fn to_tag(tag: &str, country: &Country) -> String {
format!(
"{};{}",
tag,
country.to_string().to_lowercase()
)
}
/// Return relevant unemployment rate series for a country.
/// ```
/// println!("{}", unemployment_series(&Country::Canada, "u_series.keytree"));
/// ```
pub fn generic_u_series_spec(country: &Country) -> SeriesItems {
if let Country::UnitedStates = country {
// Need to use a different search technique for US data.
let exclude_phrase = vec!(
"Male",
"Female",
"Men",
"Women",
"Youth",
);
let one_of = vec!(
"Unemployment Rate for United States",
"Unemployment Rate: Aged 15 and Over: All Persons for the United States",
"Unemployment Rate: Aged 15-74: All Persons for the United States",
"Harmonized Unemployment Rate: Total: All Persons for the United States",
"Unemployment Rate - 18 Years and Over",
);
let tag_series = Fred::tags_series("unemployment;rate;usa;nation").unwrap().seriess;
tag_series
.exclude_phrases(exclude_phrase)
.equals_one_of(one_of)
} else {
let (exclude_phrase, include_phrase) = match country {
Country::Australia => {
(
vec!(
"Male",
"Female",
"55-64",
"25-54",
"15-24",
"20 to 24",
"Youth",
"Women",
"Teenagers",
),
vec!(
"Rate"
),
)
}
Country::Austria => {
(
vec!(
"Male",
"Female",
"55-64",
"25-54",
"15-24",
"15-64", // series includes 15-74
"20 to 24",
"Youth",
"Women",
"Teenagers",
),
vec!(
"Rate"
),
)
}
Country::Belgium => {
(
vec!(
"Male",
"Female",
"55-64",
"25-54",
"15-24",
"15-64", // series includes 15-74
"20 to 24",
"Youth",
"Women",
"Teenagers",
),
vec!(
"Rate"
),
)
}
Country::Canada => {
(
vec!(
"Male",
"Female",
"15-64",
"55-64",
"25-54",
"15-24",
"20 to 24",
"Youth",
"Women",
"Teenagers",
),
vec!(
"Rate"
),
)
}
Country::Chile => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate"
),
)
}
Country::CzechRepublic => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate"
),
)
}
Country::Denmark => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate"
),
)
}
Country::Estonia => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Finland => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::France => {
(
vec!(
"Male",
"Men",
"Female",
"Women",
"Youth",
"Teenagers",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Germany => {
(
vec!(
"Male",
"Men",
"Female",
"Youth",
"Women",
"Teenagers",
"20 to 24",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Greece => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Ireland => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Israel => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Italy => {
(
vec!(
"Male",
"Female",
"Youth",
"Men",
"Women",
"Teenagers",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Japan => {
(
vec!(
"Male",
"Female",
"Youth",
"Men",
"Women",
"Teenagers",
"20 to 24",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Latvia => {
(
vec!(
"Youth",
"Male",
"Female",
"25 and over",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"nemployment",
),
)
}
Country::Netherlands => {
(
vec!(
"Male",
"Female",
"Youth",
"Women",
"Teenagers",
"Men",
"20 to 24",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::NewZealand => {
(
vec!(
"Male",
"Female",
"55-64",
"25-54",
"15-24",
"Youth",
),
vec!(
"Rate",
),
)
}
Country::Norway => {
(
vec!(
"Male",
"Female",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Poland => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Serbia => {
(
vec!(
),
vec!(
"",
),
)
}
Country::SouthKorea => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Spain => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Sweden => {
(
vec!(
"Male",
"Female",
"Youth",
"Men",
"Women",
"Teenagers",
"15-24",
"15-64",
"25-54",
"55-64",
"20 to 24",
),
vec!(
"Rate",
),
)
}
Country::Switzerland => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::UnitedKingdom => {
(
vec!(
"Male",
"Female",
"Youth",
"Men",
"Women",
"Teenagers",
"20 to 24",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
_ => panic!(),
};
match Fred::tags_series(&to_tag("unemployment", country)) {
Ok(tags_series) => {
tags_series.seriess
.exclude_phrases(exclude_phrase)
.only_include(include_phrase)
},
Err(err) => {
println!("{}", err);
panic!();
},
}
}
}
/// ```
/// seriess:
/// series:
/// data_type: u
/// country: Australia
/// id: AUSURAMS
/// ```
pub fn build_data_spec() -> DataSpec {
let mut seriess = DataSpec(Vec::new());
let data_type = DataType::U;
for country in countries_with_data() {
println!("{}", country);
for series_item in generic_u_series_spec(&country).iter() {
seriess.0.push(
SourceSeries {
data_type,
country,
id: series_item.id.clone(),
}
)
}
}
seriess
}
/// ```
/// series:
/// data_type: u
/// country: United States
/// id: LRUNTTTTUSQ156S
/// ```
pub struct DataSpec(pub Vec<SourceSeries>);
impl DataSpec {
/// Read in keytree data from file.
pub fn from_file(path: &str) -> Self {
let source_spec = fs::read_to_string(path).unwrap();
let kt = KeyTree::parse(&source_spec).unwrap();
kt.to_ref().try_into().unwrap()
}
/// Read in source_data.keytree, download series, and return
/// `CheckedSourceSeries`.
pub fn update(&self) {
for series in &self.0 {
let data = match Fred::series(&series.id) {
Ok(data) => println!("{}", data.keytree()),
Err(err) => println!("{:?}", err),
};
}
}
}
impl<'a> TryInto<DataSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<DataSpec, Error> {
Ok(
DataSpec(self.vec("seriess:series")?)
)
}
}
impl IntoKeyTree for DataSpec {
fn keytree(&self) -> KeyTreeString {
let mut kt = KeyTreeString::new();
kt.push_key(0, "seriess");
for series in &self.0 {
kt.push_keytree(1, series.keytree());
}
kt
}
}
pub struct SourceSeries {
pub data_type: DataType,
pub country: Country,
pub id: String,
}
impl IntoKeyTree for SourceSeries {
fn keytree(&self) -> KeyTreeString {
let mut kt = KeyTreeString::new();
kt.push_key(0, "series");
kt.push_keyvalue(1, "data_type", &self.data_type.to_string());
kt.push_keyvalue(1, "country", &self.country.to_string());
kt.push_keyvalue(1, "id", &self.id);
kt
}
}
impl<'a> TryInto<SourceSeries> for KeyTreeRef<'a> {
type Error = keytree::Error;
fn try_into(self) -> Result<SourceSeries, keytree::Error> {
let data_type_str: String = self.at("series::data_type")?;
let data_type = DataType::from_str(&data_type_str).unwrap();
let country_str: String = self.at("series::country")?;
let country = Country::from_str(&country_str).unwrap();
Ok(
SourceSeries{
data_type,
country,
id: self.at("series::id")?,
}
)
}
}
//! Deserialize data to be served as JSON to build time-series plots.
use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::env;
use std::fs;
use serde::{Serialize};
use countries::Country;
use keytree::{KeyTree, KeyTreeRef};
use keytree::Error;
use time_series::{
RegularTimeSeries,
TimeSeries,
};
use crate::DataType;
/// The top level struct that holds all the source data.
pub struct SourceData(pub HashMap<PageKey, SourceJson>);
impl SourceData {
// Load the source data. Will panic on error.
pub fn new() -> Self {
let source_spec_file = fs::read_to_string("source_spec.keytree").unwrap();
let kt = match KeyTree::parse(&source_spec_file) {
Ok(kt) => kt,
Err(err) => {
println!("{}", err);
panic!();
},
};
let source_spec: SourceSpec = match kt.to_ref().try_into() {
Ok(source) => source,
Err(e) => {
println!("{}", e);
panic!();
}
};
let mut builder = HashMap::new();
for page_key in source_spec.0.keys() {
let source_json = SourceJson::new(page_key.data_type, page_key.country, &source_spec);
builder.insert(*page_key, source_json);
}
SourceData(builder)
}
}
/// The top-level specification for source data.
///
/// "source_spec.keytree" file.
/// ```text
/// source:
/// html_page:
/// key:
/// data_type: u
/// country: Australia
/// value:
/// graphic:
/// data: AUSURAMS_a
/// graphic:
/// data: AUSURANAA_a
/// ```
pub struct SourceSpec(pub HashMap<PageKey, PageValue>);
impl<'a> TryInto<SourceSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<SourceSpec, Error> {
let html_pages: Vec<SourceHtmlPage> = self.vec("source::html_page")?;
let mut h = HashMap::new();
for html_page in html_pages {
h.insert(html_page.key, html_page.value);
}
Ok(SourceSpec(h))
}
}
impl SourceSpec {
/// Return a `Vec` of all series ids in the spec.
pub fn all_series(&self) -> Vec<String> {
let mut v = Vec::new();
for (_, page_value) in self.0.iter() {
for graphic_spec in &page_value.0 {
for series_id in &graphic_spec.series_ids {
v.push(series_id.clone())
}
}
}
v
}
}
/// A container for the specification for a particular datatype and country.
/// ```text
/// html_page:
/// key:
/// data_type: u
/// country: Australia
/// value:
/// graphic:
/// data: AUSURAMS_a
/// graphic:
/// data: AUSURANAA_a
/// ```
pub struct SourceHtmlPage {
pub key: PageKey,
pub value: PageValue,
}
// impl HtmlPage {
// json_data() -> String
// get csv data from file
//
// }
impl<'a> TryInto<SourceHtmlPage> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<SourceHtmlPage, Error> {
Ok(SourceHtmlPage {
key: self.at("html_page::key")?,
value: self.at("html_page::value")?,
})
}
}
/// `(DataType, Country)` key to lookup data or spec.
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct PageKey {
pub data_type: DataType,
pub country: Country,
}
impl<'a> TryInto<PageKey> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<PageKey, Error> {
let data_type_str: String = self.at("key::data_type")?;
// All KeyTree strings are internal, so if this fails it is a bug.
let data_type = DataType::from_str(&data_type_str).unwrap();
let country_str: String = self.at("key::country")?;
let country = Country::from_str(&country_str).unwrap();
Ok(PageKey {
data_type: data_type,
country: country,
})
}
}
/// Collection of graphics specification.
pub struct PageValue(pub Vec<GraphicSpec>);
impl<'a> TryInto<PageValue> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<PageValue, Error> {
Ok(PageValue(self.vec("value::graphic")?))
}
}
/// Specifies the data files required for a graphic in the '/source' route.
pub struct GraphicSpec {
pub height: Option<f32>,
// e.g. AUSURHARMADSMEI, ...
pub series_ids: Vec<String>,
}
impl<'a> TryInto<GraphicSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<GraphicSpec, Error> {
Ok(
GraphicSpec {
height: self.op("graphic::height")?,
series_ids: self.vec("graphic::data")?,
}
)
}
}
/// `SourceJson` is a collection of time-series convertible to JSON.
#[derive(Serialize)]
pub struct SourceJson {
graphs: Vec<SeriesJson>,
}
impl SourceJson {
/// Given `data_type`, `country` and source specification, return Self. `data_path` is the full
/// path to the root of the data files.
pub fn new(data_type: DataType, country: Country, spec: &SourceSpec) -> Self {
let page_key = PageKey {
data_type: data_type,
country: country,
};
let page_value = spec.0.get(&page_key).unwrap();
let mut v = Vec::new();
for graphic_spec in &page_value.0 {
v.push(SeriesJson::new(&graphic_spec, country, data_type));
}
SourceJson { graphs: v }
}
}
/// `SeriesJson` is a time-series with meta-data convertible to JSON.
#[derive(Serialize)]
pub struct SeriesJson {
data: Vec<(RegularTimeSeries<1>, SeriesMetaData)>,
}
impl SeriesJson {
pub fn new(graphic_spec: &GraphicSpec, country: Country, data_type: DataType) -> Self {
let mut v = Vec::new();
for series_id in &graphic_spec.series_ids {
let (time_series, meta) = data_from_csv(series_id, country, data_type);
v.push((time_series, meta));
}
SeriesJson { data: v }
}
// We want to break new() down into something that just takes a series_id and gets the date. Once
// we've done that we can use it for source or ui or anything. The common data-structure is
// (series_id) ??
}
fn no_label(filename: &str) -> String {
filename[..filename.len() - 2].into()
}
/// Source meta-data is stored in a file as a String:
///
/// ```text
/// series:
/// realtime_start: 2021-06-03
/// realtime_end: 2021-06-03
/// series_items:
/// series_item:
/// realtime: 2021-06-03
/// id: AUSCPALTT01IXNBQ
/// title: Consumer Price Index: All items: Total: Total for Australia
/// observation_start: 1960-01-01
/// observation_end: 2021-01-01
/// frequency: Quarterly
/// seasonal_adjustment: Not Seasonally Adjusted
/// notes: (see JSON data for notes)
/// ```
#[derive(Serialize)]
pub struct SeriesMetaData {
realtime: String,
id: String,
title: String,
observation_start: String,
observation_end: String,
frequency: String,
seasonal_adjustment: String,
}
impl<'a> TryInto<SeriesMetaData> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<SeriesMetaData, Error> {
Ok(
SeriesMetaData {
realtime: self.at("series::series_items::series_item::realtime_start")?,
id: self.at("series::series_items::series_item::id")?,
title: self.at("series::series_items::series_item::title")?,
observation_start: self.at("series::series_items::series_item::observation_start")?,
observation_end: self.at("series::series_items::series_item::observation_end")?,
frequency: self.at("series::series_items::series_item::frequency")?,
seasonal_adjustment: self.at("series::series_items::series_item::seasonal_adjustment")?,
}
)
}
}
/// Read a series from file.
pub fn data_from_csv(
series_id: &str,
country: Country,
data_type: DataType) -> (RegularTimeSeries<1>, SeriesMetaData)
{
let root = env::current_dir().unwrap();
let csv_path = &format!(
"{}/contents/data/{}/{}/{}.csv",
root.display(),
data_type,
country.as_path(),
series_id,
);
let meta_path = &format!(
"{}/contents/data/{}/{}/{}.meta",
root.display(),
data_type,
country.as_path(),
no_label(&series_id),
);
let time_series = match RegularTimeSeries::try_from(
TimeSeries::<1>::from_csv(csv_path)
) {
Ok(ts) => ts,
Err(_) => {
println!("country: {}", country);
println!("data_type: {}", data_type);
println!("series_id: {}", series_id);
println!("Data not contiguous.");
panic!();
},
};
let meta_str = fs::read_to_string(meta_path).unwrap();
let kt = KeyTree::parse(&meta_str).unwrap();
let meta: SeriesMetaData = kt.to_ref().try_into().unwrap();
(time_series, meta)
}
//! Deserialize data to be served as JSON to build Unemployment/Inflation scatter-plots.
use std::convert::TryInto;
use std::collections::HashMap;
use serde::{Serialize};
use countries::Country;
use keytree::{
KeyTreeRef,
Error,
ErrorKind,
};
use time_series::{
DateRange,
MonthlyDate,
RegularTimeSeries,
};
use crate::{
DataType,
};
use crate::check_data::{
SeriesMetaData,
};
use crate::serve_ts::{
data_from_csv,
};
/// ```
/// ui:
/// ui_graphic:
/// time_series:
/// data_type: u
/// series: AUSURAMS_a
/// transform: ident
/// time_series:
/// data_type: i
/// series: _
/// transform: ident
/// x: 0
/// y: 1
/// ```
pub struct UISpec(pub HashMap<Country, Vec<UIGraphicSpec>>);
impl<'a> TryInto<UISpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<UISpec, Error> {
let countries: Vec<CountrySpec> = self.vec("ui::country")?;
let mut h = HashMap::new();
for country in countries {
h.insert(country.key, country.value.0);
}
Ok(UISpec(h))
}
}
pub struct CountrySpec {
key: Country,
value: ValueSpec,
}
impl<'a> TryInto<CountrySpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<CountrySpec, Error> {
let country_str: String = self.at("country::key")?;
let country = Country::from_str(&country_str).unwrap();
Ok(
CountrySpec {
key: country,
value: self.at("country::value")?,
}
)
}
}
pub struct ValueSpec(Vec<UIGraphicSpec>);
impl<'a> TryInto<ValueSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<ValueSpec, Error> {
Ok(ValueSpec(self.vec("value::graphic")?))
}
}
/// An integer reference to a series, for x and y coordinates.
/// ```
/// ui:
/// country:
/// key: Australia
/// value:
/// graphic:
/// title: Unemployment and Inflation for Australia
/// time_series:
/// data_type: u
/// series: AUSURAMS_a
/// transform: ident
/// time_series:
/// data_type: i
/// series: _
/// transform: ident
/// line:
/// x: 0
/// y: 1
/// ```
/// For instance in this example, x refers to the first time-series AUSURAMS_a, and y refers to the
/// second time-series.
pub struct UILineSpec {
x: String,
y: String,
start_date: Option<MonthlyDate>,
end_date: Option<MonthlyDate>,
}
impl<'a> TryInto<UILineSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<UILineSpec, Error> {
let start_date_str: Option<String> = self.op("line::start_date")?;
let start_date_opt = match start_date_str {
Some(date) => Some(parse_date(&date)?),
None => None,
};
let end_date_str: Option<String> = self.op("line::end_date")?;
let end_date_opt = match end_date_str {
Some(date) => Some(parse_date(&date)?),
None => None,
};
Ok(
UILineSpec {
x: self.at("line::x")?,
y: self.at("line::y")?,
start_date: start_date_opt,
end_date: end_date_opt,
}
)
}
}
fn parse_date(s: &str) -> Result<MonthlyDate, Error> {
let year = s[..=4].parse::<usize>().map_err(|err| keytree::Error(Box::new(ErrorKind::User(err.to_string()))))?;
let month = s[5..].parse::<usize>().map_err(|err| keytree::Error(Box::new(ErrorKind::User(err.to_string()))))?;
Ok(MonthlyDate::ym(year, month))
}
#[test]
fn test_parse_date() {
let s = "1999-03";
assert!(
parse_date(s),
MonthlyDate(1999, 3),
)
}
// Structs UIJson, UIGraphJson and UILineJson encapsulate data to be converted to JSON.
/// `UIJson` are a collection of `LineJson` convertible to JSON for a ui graphic.
#[derive(Serialize)]
pub struct UIJson {
country: Country,
graphics: Vec<UIGraphicJson>,
}
impl UIJson {
/// Build a new `UIJson` from a `UISpec` specification.
pub fn new(ui_spec: &UISpec, country: Country) -> Self {
let ui_country_spec = ui_spec.0.get(&country).unwrap();
let mut builder = UIJson {
country: country,
graphics: Vec::new(),
};
for graphic_spec in ui_country_spec {
builder.graphics.push(UIGraphicJson::new(graphic_spec, country));
}
builder
}
}
/// `UIGraphicJson` is a JSON representation of a UI graph.
#[derive(Serialize)]
pub struct UIGraphicJson {
title: String,
lines: Vec<UILineJson>,
}
impl UIGraphicJson {
pub fn new(graphic_spec: &UIGraphicSpec, country: Country) -> Self {
let mut builder = UIGraphicJson {
title: graphic_spec.title.clone(),
lines: Vec::new(),
};
for line_spec in &graphic_spec.line {
builder.lines.push(UILineJson::new(&line_spec, country));
};
builder
}
}
/// `UILineJson` are two time-series
#[derive(Serialize)]
pub struct UILineJson {
x: RegularTimeSeries<1>,
y: RegularTimeSeries<1>,
x_meta: SeriesMetaData,
y_meta: SeriesMetaData,
}
impl UILineJson {
/// Build a new `UILineJson` from a `UILineSpec` specification.
pub fn new(line_spec: &UILineSpec, country: Country) -> Self {
let (mut x_data, x_meta) = data_from_csv(&line_spec.x, country, DataType::U);
let range = DateRange::new(
line_spec.start_date,
line_spec.end_date,
);
x_data.with_range(&range);
let (mut y_data, y_meta) = data_from_csv(&line_spec.y, country, DataType::Inf);
y_data.with_range(&range);
UILineJson {
x: x_data,
y: y_data,
x_meta: x_meta,
y_meta: y_meta,
}
}
}
/// ```
/// ui:
/// country:
/// key: Australia
/// value:
/// graphic:
/// title: Unemployment and Inflation for Australia
/// line:
/// x: AUSURAMS_a
/// y: _
/// start_date: _
/// end_date: _
/// line:
/// ```
pub struct UIGraphicSpec {
pub title: String,
pub line: Vec<UILineSpec>,
}
impl<'a> TryInto<UIGraphicSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<UIGraphicSpec, Error> {
Ok(
UIGraphicSpec {
title: self.at("ui_graphic::title")?,
line: self.vec("ui_graphic::line")?,
}
)
}
}
//! Deserialize data to be served as JSON to build time-series plots.
//!
//! The `TSPageSpec::TSCountrySpec::TSGraphicSpec` group of data-structures mirrors
//! `ts_spec.keytree`. Its main function is TSPageSpec::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: TSPageSpec, checked_spec: Checked...)
//! ```
use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::env;
use std::fs;
use serde::Serialize;
use countries::Country;
use keytree::{KeyTree, KeyTreeRef};
use keytree::Error;
use keytree::serialize::{
KeyTreeString,
IntoKeyTree,
};
use time_series::{
RegularTimeSeries,
TimeSeries,
};
use crate::DataType;
use crate::check_data::{
CheckedDataSpec,
SeriesMetaData,
};
// -- "ts_spec.keytree" ---------------------------------------------------------------------------
pub struct TSPageSpec(pub Vec<TSCountrySpec>);
impl TSPageSpec {
pub fn new() -> TSPageSpec {
TSPageSpec(Vec::new())
}
pub fn push(&mut self, item: TSCountrySpec) {
self.0.push(item)
}
}
impl IntoKeyTree for TSPageSpec {
fn keytree(&self) -> KeyTreeString {
let mut kt = KeyTreeString::new();
kt.push_key(0, "page");
for country in &self.0 {
kt.push_keytree(1, country.keytree());
}
kt
}
}
impl<'a> TryInto<TSPageSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<TSPageSpec, Error> {
let country_vec: Vec<TSCountrySpec> = self.vec("page::country")?;
Ok(TSPageSpec(country_vec))
}
}
pub struct TSCountrySpec {
pub name: Country,
pub graphics: Vec<TSGraphicSpec>,
}
impl TSCountrySpec {
pub fn new(country: Country, graphics: Vec<TSGraphicSpec>) -> Self {
TSCountrySpec {
name: country,
graphics: graphics,
}
}
}
impl IntoKeyTree for TSCountrySpec {
fn keytree(&self) -> KeyTreeString {
let mut kt = KeyTreeString::new();
kt.push_key(0, "country");
kt.push_keyvalue(1, "name", &self.name.to_string());
for graphic in &self.graphics {
kt.push_keytree(1, graphic.keytree());
}
kt
}
}
impl<'a> TryInto<TSCountrySpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<TSCountrySpec, Error> {
let country_str: String = self.at("country::name")?;
let country = Country::from_str(&country_str).unwrap();
Ok(TSCountrySpec {
name: country,
graphics: self.vec("country::graphic")?,
})
}
}
/// Specifies the data files required for a graphic in the '/source' route.
pub struct TSGraphicSpec {
pub height: Option<f32>,
// e.g. AUSURHARMADSMEI, ...
pub series_ids: Vec<String>,
}
impl TSGraphicSpec {
pub fn new() -> Self {
TSGraphicSpec {
height: None,
series_ids: Vec::new(),
}
}
pub fn push(&mut self, id: &str) {
self.series_ids.push(id.to_string())
}
}
impl IntoKeyTree for TSGraphicSpec {
fn keytree(&self) -> KeyTreeString {
let mut kt = KeyTreeString::new();
kt.push_key(0, "graphic");
if let Some(height) = self.height {
kt.push_keyvalue(1, "height", &height.to_string());
};
kt.push_key(1, "series");
for id in &self.series_ids {
kt.push_keyvalue(2, "id", &id);
}
kt
}
}
impl<'a> TryInto<TSGraphicSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<TSGraphicSpec, Error> {
Ok(
TSGraphicSpec {
height: self.op("graphic::height")?,
series_ids: self.vec("graphic::data")?,
}
)
}
}
// --- Client-facing data-structures --------------------------------------------------------------
/// 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.
pub struct PageJson(HashMap<PageKey, Vec<GraphicJson>>);
/// Respresents a graphic to be served to the client as JSON.
#[derive(Serialize)]
pub struct GraphicJson {
pub height: Option<f32>,
pub series: SeriesJson,
}
/// `SeriesJson` is a time-series to be served to the client as JSON.
#[derive(Serialize)]
pub struct SeriesJson {
data: Vec<(RegularTimeSeries<1>, SeriesMetaData)>,
}
/// `(DataType, Country)` key to lookup data or spec.
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct PageKey {
pub data_type: DataType,
pub country: Country,
}
impl<'a> TryInto<PageKey> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<PageKey, Error> {
let data_type_str: String = self.at("key::data_type")?;
// All KeyTree strings are internal, so if this fails it is a bug.
let data_type = DataType::from_str(&data_type_str).unwrap();
let country_str: String = self.at("key::country")?;
let country = Country::from_str(&country_str).unwrap();
Ok(PageKey {
data_type: data_type,
country: country,
})
}
}
//! Use the specification from step 3 to build or update data files.
//! We want to serve data to clients in a particular way depending on the client (time-series or
//! scatter plots). Each client-facing data-structure has its own specification. The two
//! specifications interact.
/// Read in source_data.keytree, download series, and return
/// `CheckedSourceSeries`.
pub fn update(&self) {
/// Save FRED data to disk as csv, using full path. Will fail if an existing filepath is
/// encountered.
/// ```
/// save_data_csv("LRUNTTTTSIQ156S");
/// ```
/// To path from root is "/{data_type}/{country}/LRUNTTTTSIQ156S.csv"
///
pub fn write(&mut self, root_path: &str) {
for series in &mut self.0 {
series.write_data(root_path);
series.write_meta(root_path);
}
println!("{}", self.keytree())
}
/// Takes `checked_data.keytree` and builds a collection of graphics,
/// each plotting 1 time-series.
pub fn generate_ts_spec(&self) {
let mut h: HashMap<Country, TSCountrySpec> = HashMap::new();
let data = match Fred::series(&series.id) {
Ok(data) => println!("{}", data.keytree()),
Err(err) => println!("{:?}", err),
// Check that there are no errors in the CheckedDataSpec series.
if series.error.to_lowercase() != "ok" {
println!("{}", series.keytree());
panic!();
// Insert the series into the TSGraphicSpec.
match h.get_mut(&series.country) {
Some(country_spec) => {
country_spec.graphics[0].push(&series.id);
},
None => {
let mut ts_graphic_spec = TSGraphicSpec::new();
ts_graphic_spec.push(&series.id);
let ts_country_spec = TSCountrySpec::new(series.country, vec!(ts_graphic_spec));
h.insert(series.country, ts_country_spec);
}
}
data_type: DataType,
country: Country,
id: String,
error: String,
pub data_type: DataType,
pub country: Country,
pub id: String,
pub error: String,
pub time_stamp: Option<time::PrimitiveDateTime>,
}
impl CheckedSourceSeries {
/// Fetches data as specified in `checked_data.keytree` and saves to disk.
pub fn write_data(&mut self, root_path: &str) {
// Select the observation data from the FRED data.
let observations = match Fred::series_observations(&self.id) {
Ok(series_obs) => series_obs.observations,
Err(err) => {
println!("{}", err);
panic! ();
},
};
let mut data = String::new();
for obs in observations.iter() {
data.push_str(&obs.to_string());
data.push('\n');
}
if &self.error == "ok" {
let dir_path = &format!(
"{}/{}/{}",
root_path,
self.data_type,
self.country.as_path(),
);
let filename = &format!(
"{}/{}.csv",
dir_path,
self.id,
);
self.time_stamp = Some(PrimitiveDateTime::now());
fs::create_dir_all(&dir_path).unwrap();
fs::write(filename, &data).unwrap();
}
}
/// Fetches data as specified in `checked_data.keytree` and saves meta_data to disk.
pub fn write_meta(&mut self, root_path: &str) {
let meta = match Fred::series(&self.id) {
Ok(series) => {
let series_item = series.seriess.iter().next().unwrap();
SeriesMetaData {
realtime: series.realtime_start.clone(),
id: self.id.clone(),
title: series_item.title.clone(),
observation_start: series_item.observation_start.clone(),
observation_end: series_item.observation_end.clone(),
frequency: series_item.frequency.clone(),
seasonal_adjustment: series_item.seasonal_adjustment.clone(),
}
},
Err(err) => {
println!("{}", err);
panic! ();
},
};
let dir_path = &format!(
"{}/{}/{}",
root_path,
self.data_type,
self.country.as_path(),
);
let filename = &format!(
"{}/{}.meta",
dir_path,
self.id,
);
fs::create_dir_all(&dir_path).unwrap();
fs::write(filename, &meta.keytree().to_string()).unwrap();
}
/// Source meta-data is stored in a file as a String:
///
/// ```text
/// series:
/// realtime_start: 2021-06-03
/// realtime_end: 2021-06-03
/// series_items:
/// series_item:
/// realtime: 2021-06-03
/// id: AUSCPALTT01IXNBQ
/// title: Consumer Price Index: All items: Total: Total for Australia
/// observation_start: 1960-01-01
/// observation_end: 2021-01-01
/// frequency: Quarterly
/// seasonal_adjustment: Not Seasonally Adjusted
/// notes: (see JSON data for notes)
/// ```
#[derive(Serialize)]
pub struct SeriesMetaData {
realtime: String,
id: String,
title: String,
observation_start: String,
observation_end: String,
frequency: String,
seasonal_adjustment: String,
}
impl<'a> TryInto<SeriesMetaData> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<SeriesMetaData, Error> {
Ok(
SeriesMetaData {
realtime: self.at("series_meta::realtime")?,
id: self.at("series_meta::id")?,
title: self.at("series_meta::title")?,
observation_start: self.at("series_meta::observation_start")?,
observation_end: self.at("series_meta::observation_end")?,
frequency: self.at("series_meta::frequency")?,
seasonal_adjustment: self.at("series_meta::seasonal_adjustment")?,
}
)
}
}
impl IntoKeyTree for SeriesMetaData {
fn keytree(&self) -> KeyTreeString {
let mut kt = KeyTreeString::new();
kt.push_key(0, "series_meta");
kt.push_keyvalue(1, "realtime", &self.realtime);
kt.push_keyvalue(1, "id", &self.id);
kt.push_keyvalue(1, "title", &self.title);
kt.push_keyvalue(1, "observation_start", &self.observation_start);
kt.push_keyvalue(1, "observation_end", &self.observation_end);
kt.push_keyvalue(1, "frequency", &self.frequency);
kt.push_keyvalue(1, "seasonal_adjustment", &self.seasonal_adjustment);
kt
}
}
//! Builds generic specifications.
use std::convert::TryInto;
use std::fs;
use countries::Country;
use fred_api::{
Fred,
SeriesItems,
};
use keytree::{
KeyTree,
KeyTreeRef,
};
use keytree::Error;
use keytree::serialize::{
IntoKeyTree,
KeyTreeString,
};
use crate::DataType;
/// Return all the countries with good data as a `Vec`.
pub fn countries_with_data() -> Vec<Country> {
vec!(
Country::Australia,
Country::Austria,
Country::Belgium,
Country::Canada,
Country::Chile,
Country::CzechRepublic,
Country::Denmark,
Country::Estonia,
Country::Finland,
Country::France,
Country::Germany,
Country::Greece,
Country::Ireland,
Country::Israel,
Country::Italy,
Country::Japan,
Country::Latvia,
Country::Netherlands,
Country::NewZealand,
Country::Norway,
Country::Poland,
Country::Serbia,
Country::SouthKorea,
Country::Spain,
Country::Sweden,
Country::Switzerland,
Country::UnitedKingdom,
Country::UnitedStates,
)
}
/// Returns relevant CPI series for a country, by pre-selecting series titles for each country.
///
/// ```
/// println!("{}", generic_cpi_series_spec(&Country::NewZealand, "cpi_series.keytree"));
/// ```
pub fn generic_cpi_series_spec(country: &Country) -> SeriesItems {
let titles = match country {
Country::Australia => {
vec!(
"Consumer Price Index: All items: Total: Total for Australia",
"Consumer Price Index: All Items for Australia",
"Consumer Price Index in Australia (DISCONTINUED)",
"Consumer Price Index: Total All Items for Australia",
"Consumer Price Index for Australia",
"Inflation, consumer prices for Australia",
)
}
Country::Austria => {
vec!(
"Consumer Price Index for Austria",
"Consumer Price Index: All items: Total: Total for Austria",
"Consumer Price Index: All Items for Austria",
"Consumer Price Index in Austria (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for Austria",
"Consumer Price Index: Harmonized Prices: Total All Items for Austria // (DISCONTINUED)",
"Consumer Price Index: Total All Items for Austria",
"Harmonized Index of Consumer Prices: All Items for Austria",
"Harmonized Index of Consumer Prices in Austria (DISCONTINUED)",
"Inflation, consumer prices for Austria",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for // Austria",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Austria (DISCONTINUED)",
)
}
Country::Belgium => {
vec!(
"Consumer Price Index for Belgium",
"Consumer Price Index: Harmonized Prices: Total All Items for Belgium",
"Consumer Price Index: Harmonized Prices: Total All Items for Belgium (DISCONTINUED)",
"Consumer Price Index: Total All Items for Belgium",
"Harmonized Index of Consumer Prices: All Items for Belgium",
"Harmonized Index of Consumer Prices in Belgium (DISCONTINUED)",
"Consumer Price Index: All Items for Belgium",
"Consumer Price Index in Belgium (DISCONTINUED)",
"Consumer Price Index: All items: Total: Total for Belgium",
)
}
Country::Canada => {
vec!(
"Inflation, consumer prices for Canada",
"Consumer Price Index for Canada",
"Consumer Price Index: Total All Items for Canada",
"Consumer Price Index in Canada (DISCONTINUED)",
"Consumer Price Index of All Items in Canada",
"Consumer Price Index: All items: Total: Total for Canada",
)
}
Country::Chile => {
vec!(
"Consumer Price Index for Chile",
"Inflation, consumer prices for Chile",
"Consumer Price Index: Total All Items for Chile",
"Consumer Price Index: All Items for Chile",
"Consumer Price Index: All items: Total: Total for Chile",
)
}
Country::CzechRepublic => {
vec!(
"Consumer Price Index for Czech Republic",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Czech Republic",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Czech Republic (DISCONTINUED)",
"Consumer Price Index: All Items for Czech Republic",
"Consumer Price Index: All items: Total: Total for the Czech Republic",
"Consumer Price Index: Harmonized Prices: Total All Items for the Czech Republic",
"Consumer Price Index: Harmonized Prices: Total All Items for the Czech Republic (DISCONTINUED)",
"Consumer Price Index: Total All Items for the Czech Republic",
"Harmonized Index of Consumer Prices: All Items for Czech Republic",
)
}
Country::Denmark => {
vec!(
"Inflation, consumer prices for Denmark",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Denmark",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Denmark (DISCONTINUED)",
"Harmonized Index of Consumer Prices in Denmark (DISCONTINUED)",
"Consumer Price Index: All Items for Denmark",
"Consumer Price Index in Denmark (DISCONTINUED)",
"Consumer Price Index: All items: Total: Total for Denmark",
"Consumer Price Index for Denmark",
"Consumer Price Index: Total All Items for Denmark",
"Harmonized Index of Consumer Prices: All Items for Denmark",
)
}
Country::Estonia => {
vec!(
"Inflation, consumer prices for Estonia",
"Consumer Price Index: All items: Total: Total for Estonia",
"Consumer Price Index for Estonia",
"Consumer Price Index: Harmonized Prices: Total All Items for Estonia",
"Consumer Price Index: Harmonized Prices: Total All Items for Estonia (DISCONTINUED)",
"Consumer Price Index: Total All Items for Estonia",
"Harmonized Index of Consumer Prices: All Items for Estonia",
)
}
Country::Finland => {
vec!(
"Inflation, consumer prices for Finland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Finland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Finland (DISCONTINUED)",
"Consumer Price Index: All Items for Finland",
"Consumer Price Index: OECD Groups: Services: Total for Finland",
"Consumer Price Index: All items: Total: Total for Finland",
"Consumer Price Index: Harmonized Prices: Total All Items for Finland (DISCONTINUED)",
"Consumer Price Index for Finland",
"Consumer Price Index: Harmonized Prices: Total All Items for Finland",
"Consumer Price Index: Total All Items for Finland",
"Harmonized Index of Consumer Prices: All Items for Finland",
)
}
Country::France => {
vec!(
"Harmonized Index of Consumer Prices in France (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for France (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for France",
"Consumer Price Index in France (DISCONTINUED)",
"Consumer Price Index of All Items in France",
"Consumer Price Index: All items: Total: Total for France",
"Inflation, consumer prices for France",
"Consumer Price Index for France",
"Consumer Price Index: Harmonized Prices: Total All Items for France (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for France",
"Consumer Price Index: Total All Items for France",
)
}
Country::Germany => {
vec!(
"Inflation, consumer prices for Germany",
"Harmonized Index of Consumer Prices in Germany (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Germany (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Germany",
"Consumer Price Index in Germany (DISCONTINUED)",
"Consumer Price Index of All Items in Germany",
"Consumer Price Index: All items: Total: Total for Germany",
"Consumer Price Index for Germany",
"Consumer Price Index: Harmonized Prices: Total All Items for Germany (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for Germany",
"Consumer Price Index: Total All Items for Germany",
"Harmonized Index of Consumer Prices: All Items for Germany (including former GDR from 1991)",
)
}
Country::Greece => {
vec!(
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Greece",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Greece (DISCONTINUED)",
"Consumer Price Index: All Items for Greece",
"Consumer Price Index: All items: Total: Total for Greece",
"Inflation, consumer prices for Greece",
"Consumer Price Index for Greece",
"Consumer Price Index: Harmonized Prices: Total All Items for Greece",
"Consumer Price Index: Harmonized Prices: Total All Items for Greece (DISCONTINUED)",
"Consumer Price Index: Total All Items for Greece",
"Harmonized Index of Consumer Prices: All Items for Greece",
)
}
Country::Ireland => {
vec!(
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Ireland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Ireland (DISCONTINUED)",
"Consumer Price Index: OECD Groups: Services: Total for Ireland",
// "Consumer Price Index: All Items for Ireland", // Does not parse
// "Consumer Price Index: All items: Total: Total for Ireland", // Does not parse
"Consumer Price Index: Harmonized Prices: Total All Items for Ireland (DISCONTINUED)",
"Consumer Price Index for Ireland",
"Consumer Price Index: Harmonized Prices: Total All Items for Ireland",
"Harmonized Index of Consumer Prices: All Items for Ireland",
)
}
Country::Israel => {
vec!(
"Consumer Price Index: All Items for Israel",
"Consumer Price Index: All items: Total: Total for Israel",
"Consumer Price Index for Israel",
"Inflation, consumer prices for Israel",
"Consumer Price Index: Total All Items for Israel",
)
}
Country::Italy => {
vec!(
"Harmonized Index of Consumer Prices in Italy (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Italy (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Italy",
"Consumer Price Index: Food for Italy",
"Consumer Price Index in Italy (DISCONTINUED)",
"Consumer Price Index of All Items in Italy",
"Consumer Price Index: All items: Total: Total for Italy",
"Inflation, consumer prices for Italy",
"Consumer Price Index for Italy",
"Consumer Price Index: Harmonized Prices: Total All Items for Italy (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for Italy",
"Consumer Price Index: Total All Items for Italy",
"Harmonized Index of Consumer Prices: All Items for Italy",
)
}
Country::Japan => {
vec!(
"Harmonized Index of Consumer Prices in Japan (DISCONTINUED)",
"Consumer Price Index in Japan (DISCONTINUED)",
"Consumer Price Index of All Items in Japan",
"Consumer Price Index: All items: Total: Total for Japan",
"Inflation, consumer prices for Japan",
"Not Seasonally Adjusted",
)
}
Country::Latvia => {
vec!(
"Consumer Price Index: All items: Total: Total for Latvia",
"Inflation, consumer prices for Latvia",
"Harmonized Index of Consumer Prices: Unprocessed Food for Latvia",
"Consumer Price Index for Latvia",
)
}
Country::Netherlands => {
vec!(
"Harmonized Index of Consumer Prices in Netherlands (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Netherlands (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Netherlands",
"Consumer Price Index in Netherlands (DISCONTINUED)",
"Consumer Price Index: All Items for Netherlands",
"Consumer Price Index: All items: Total: Total for the Netherlands",
"Consumer Price Index for Netherlands",
"Consumer Price Index: Harmonized Prices: Total All Items for the Netherlands",
"Consumer Price Index: Harmonized Prices: Total All Items for the Netherlands (DISCONTINUED)",
"Consumer Price Index: Total All Items for the Netherlands",
"Harmonized Index of Consumer Prices: All Items for Netherlands",
)
}
Country::NewZealand => {
vec!(
"Consumer Price Index: All Items for New Zealand",
"Consumer Price Index: All Items Excluding Food and Energy for New Zealand",
"Consumer Price Index: All items: Total: Total for New Zealand",
"Consumer Price Index for New Zealand",
"Inflation, consumer prices for New Zealand",
"Consumer Price Index: Total All Items for New Zealand",
)
}
Country::Norway => {
vec!(
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Norway",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Norway (DISCONTINUED)",
"Consumer Price Index: All Items for Norway",
"Consumer Price Index in Norway (DISCONTINUED)",
"Consumer Price Index: All items: Total: Total for Norway",
"Consumer Price Index: Harmonized Prices: Total All Items for Norway (DISCONTINUED)",
"Consumer Price Index for Norway",
"Consumer Price Index: Harmonized Prices: Total All Items for Norway",
"Consumer Price Index: Total All Items for Norway",
"Harmonized Index of Consumer Prices: All Items for Norway",
)
}
Country::Poland => {
vec!(
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Poland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Poland (DISCONTINUED)",
"Consumer Price Index: Food for Poland",
"Consumer Price Index: All Items for Poland",
"Consumer Price Index: All items: Total: Total for Poland",
"Inflation, consumer prices for Poland",
"Consumer Price Index for Poland",
"Consumer Price Index: Total All Items for Poland",
"Harmonized Index of Consumer Prices: All Items for Poland",
)
}
Country::Serbia => {
vec!(
"Consumer Price Index for Serbia",
"Inflation, consumer prices for Serbia",
)
}
Country::SouthKorea => {
vec!(
"Consumer Price Index: All Items for Korea",
"Consumer Price Index: All items: Total: Total for the Republic of Korea",
"Inflation, consumer prices for the Republic of Korea",
"Consumer Price Index for Republic of Korea",
"Consumer Price Index: Total All Items for the Republic of Korea",
)
}
Country::Spain => {
vec!(
"Inflation, consumer prices for Spain",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Spain (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Spain",
"Consumer Price Index in Spain (DISCONTINUED)",
"Consumer Price Index: All Items for Spain",
"Consumer Price Index: All items: Total: Total for Spain",
"Consumer Price Index for Spain",
"Consumer Price Index: Harmonized Prices: Total All Items for Spain (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for Spain",
"Consumer Price Index: Total All Items for Spain",
"Harmonized Index of Consumer Prices: All Items for Spain",
)
}
Country::Sweden => {
vec!(
"Harmonized Index of Consumer Prices in Sweden (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Sweden",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Sweden (DISCONTINUED)",
"Consumer Price Index: All Items for Sweden",
"Consumer Price Index in Sweden (DISCONTINUED)",
"Consumer Price Index: All items: Total: Total for Sweden",
"Consumer Price Index: Harmonized Prices: Total All Items for Sweden",
"Consumer Price Index: Harmonized Prices: Total All Items for Sweden (DISCONTINUED)",
"Consumer Price Index for Sweden",
"Consumer Price Index: Total, Net All Items for Sweden (DISCONTINUED)",
"Consumer Price Index: Total All Items for Sweden",
"Harmonized Index of Consumer Prices: All Items for Sweden",
)
}
Country::Switzerland => {
vec!(
"Consumer Price Index: Harmonized Prices: Total All Items for Switzerland",
"Consumer Price Index: Harmonized Prices: Total All Items for Switzerland (DISCONTINUED)",
"Consumer Price Index for Switzerland",
"Consumer Price Index: Total All Items for Switzerland",
"Harmonized Index of Consumer Prices: All Items for Switzerland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Switzerland",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for Switzerland (DISCONTINUED)",
)
}
Country::UnitedKingdom => {
vec!(
"Harmonized Index of Consumer Prices in the United Kingdom (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for United Kingdom (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for United Kingdom",
"Consumer Price Index in the United Kingdom (DISCONTINUED)",
"Consumer Price Index of All Items in the United Kingdom",
"Consumer Price Index: All Items for United Kingdom",
"Consumer Price Index: All items: Total: Total for the United Kingdom",
"Inflation, consumer prices for the United Kingdom",
"Consumer Price Index for United Kingdom",
"Consumer Price Index in the United Kingdom",
"Consumer Price Inflation in the United Kingdom",
"Consumer Price Index: Harmonized Prices: Total All Items for the United Kingdom (DISCONTINUED)",
"Consumer Price Index: Harmonized Prices: Total All Items for the United Kingdom",
"Consumer Price Index: Total All Items for the United Kingdom",
"Harmonized Index of Consumer Prices: All Items for United Kingdom",
)
}
Country::UnitedStates => {
vec!(
"Harmonized Index of Consumer Prices: All Items for United States",
"Consumer Price Index: Total All Items for the United States",
"Consumer Price Index: Harmonized Prices: Total All Items for the United States",
"Consumer Price Index: Harmonized Prices: Total All Items for the United States (DISCONTINUED)",
"Research Consumer Price Index: All Items",
"Consumer Price Index for United States",
"Flexible Price Consumer Price Index",
"Inflation, consumer prices for the United States",
"Consumer Price Index, All Items for United States",
"Rate of Change (6 Month Span at Annual Rate), Consumer Price Index, All Items (Centered) for United States",
"Median Consumer Price Index",
"Sticky Price Consumer Price Index",
"16% Trimmed-Mean Consumer Price Index",
"Consumer Price Index: All Items for the United States",
"Consumer Price Index of All Items in United States",
"Consumer Price Index in the United States (DISCONTINUED)",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for the United States",
"Consumer Price Index: All Items (Harmonized Index of Consumer Prices) for the United States (DISCONTINUED)",
"Harmonized Index of Consumer Prices in the United States (DISCONTINUED)",
)
}
_ => panic!(),
};
match country {
Country::UnitedStates => {
match Fred::tags_series("cpi;usa") {
Ok(tags_series) => {
tags_series.seriess
.equals_one_of(titles)
},
Err(json_error) => {
println!("{}", json_error);
panic!();
},
}
},
_ => {
match Fred::tags_series(&to_tag("cpi", &country)) {
Ok(tags_series) => {
tags_series.seriess
.equals_one_of(titles)
},
Err(json_error) => {
println!("{}", json_error);
panic!();
},
}
},
}
}
fn to_tag(tag: &str, country: &Country) -> String {
format!(
"{};{}",
tag,
country.to_string().to_lowercase()
)
}
/// Return relevant unemployment rate series for a country.
/// ```
/// println!("{}", unemployment_series(&Country::Canada, "u_series.keytree"));
/// ```
pub fn generic_u_series_spec(country: &Country) -> SeriesItems {
if let Country::UnitedStates = country {
// Need to use a different search technique for US data.
let exclude_phrase = vec!(
"Male",
"Female",
"Men",
"Women",
"Youth",
);
let one_of = vec!(
"Unemployment Rate for United States",
"Unemployment Rate: Aged 15 and Over: All Persons for the United States",
"Unemployment Rate: Aged 15-74: All Persons for the United States",
"Harmonized Unemployment Rate: Total: All Persons for the United States",
"Unemployment Rate - 18 Years and Over",
);
let tag_series = Fred::tags_series("unemployment;rate;usa;nation").unwrap().seriess;
tag_series
.exclude_phrases(exclude_phrase)
.equals_one_of(one_of)
} else {
let (exclude_phrase, include_phrase) = match country {
Country::Australia => {
(
vec!(
"Male",
"Female",
"55-64",
"25-54",
"15-24",
"20 to 24",
"Youth",
"Women",
"Teenagers",
),
vec!(
"Rate"
),
)
}
Country::Austria => {
(
vec!(
"Male",
"Female",
"55-64",
"25-54",
"15-24",
"15-64", // series includes 15-74
"20 to 24",
"Youth",
"Women",
"Teenagers",
),
vec!(
"Rate"
),
)
}
Country::Belgium => {
(
vec!(
"Male",
"Female",
"55-64",
"25-54",
"15-24",
"15-64", // series includes 15-74
"20 to 24",
"Youth",
"Women",
"Teenagers",
),
vec!(
"Rate"
),
)
}
Country::Canada => {
(
vec!(
"Male",
"Female",
"15-64",
"55-64",
"25-54",
"15-24",
"20 to 24",
"Youth",
"Women",
"Teenagers",
),
vec!(
"Rate"
),
)
}
Country::Chile => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate"
),
)
}
Country::CzechRepublic => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate"
),
)
}
Country::Denmark => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate"
),
)
}
Country::Estonia => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Finland => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::France => {
(
vec!(
"Male",
"Men",
"Female",
"Women",
"Youth",
"Teenagers",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Germany => {
(
vec!(
"Male",
"Men",
"Female",
"Youth",
"Women",
"Teenagers",
"20 to 24",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Greece => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Ireland => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Israel => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Italy => {
(
vec!(
"Male",
"Female",
"Youth",
"Men",
"Women",
"Teenagers",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Japan => {
(
vec!(
"Male",
"Female",
"Youth",
"Men",
"Women",
"Teenagers",
"20 to 24",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Latvia => {
(
vec!(
"Youth",
"Male",
"Female",
"25 and over",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"nemployment",
),
)
}
Country::Netherlands => {
(
vec!(
"Male",
"Female",
"Youth",
"Women",
"Teenagers",
"Men",
"20 to 24",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::NewZealand => {
(
vec!(
"Male",
"Female",
"55-64",
"25-54",
"15-24",
"Youth",
),
vec!(
"Rate",
),
)
}
Country::Norway => {
(
vec!(
"Male",
"Female",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Poland => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Serbia => {
(
vec!(
),
vec!(
"",
),
)
}
Country::SouthKorea => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Spain => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::Sweden => {
(
vec!(
"Male",
"Female",
"Youth",
"Men",
"Women",
"Teenagers",
"15-24",
"15-64",
"25-54",
"55-64",
"20 to 24",
),
vec!(
"Rate",
),
)
}
Country::Switzerland => {
(
vec!(
"Male",
"Female",
"Youth",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
Country::UnitedKingdom => {
(
vec!(
"Male",
"Female",
"Youth",
"Men",
"Women",
"Teenagers",
"20 to 24",
"15-24",
"15-64",
"25-54",
"55-64",
),
vec!(
"Rate",
),
)
}
_ => panic!(),
};
match Fred::tags_series(&to_tag("unemployment", country)) {
Ok(tags_series) => {
tags_series.seriess
.exclude_phrases(exclude_phrase)
.only_include(include_phrase)
},
Err(err) => {
println!("{}", err);
panic!();
},
}
}
}
/// ```
/// seriess:
/// series:
/// data_type: u
/// country: Australia
/// id: AUSURAMS
/// ```
pub fn build_data_spec() -> DataSpec {
let mut seriess = DataSpec(Vec::new());
let data_type = DataType::U;
for country in countries_with_data() {
println!("{}", country);
for series_item in generic_u_series_spec(&country).iter() {
seriess.0.push(
SourceSeries {
data_type,
country,
id: series_item.id.clone(),
}
)
}
}
seriess
}
/// ```
/// series:
/// data_type: u
/// country: United States
/// id: LRUNTTTTUSQ156S
/// ```
pub struct DataSpec(pub Vec<SourceSeries>);
impl DataSpec {
/// Read in keytree data from file.
pub fn from_file(path: &str) -> Self {
let source_spec = fs::read_to_string(path).unwrap();
let kt = KeyTree::parse(&source_spec).unwrap();
kt.to_ref().try_into().unwrap()
}
// /// Read in source_data.keytree, download series, and return
// /// `CheckedSourceSeries`.
// pub fn update(&self) {
// for series in &self.0 {
// let data = match Fred::series(&series.id) {
// Ok(data) => println!("{}", data.keytree()),
// Err(err) => println!("{:?}", err),
// };
// }
// }
}
impl<'a> TryInto<DataSpec> for KeyTreeRef<'a> {
type Error = Error;
fn try_into(self) -> Result<DataSpec, Error> {
Ok(
DataSpec(self.vec("seriess:series")?)
)
}
}
impl IntoKeyTree for DataSpec {
fn keytree(&self) -> KeyTreeString {
let mut kt = KeyTreeString::new();
kt.push_key(0, "seriess");
for series in &self.0 {
kt.push_keytree(1, series.keytree());
}
kt
}
}
pub struct SourceSeries {
pub data_type: DataType,
pub country: Country,
pub id: String,
}
impl IntoKeyTree for SourceSeries {
fn keytree(&self) -> KeyTreeString {
let mut kt = KeyTreeString::new();
kt.push_key(0, "series");
kt.push_keyvalue(1, "data_type", &self.data_type.to_string());
kt.push_keyvalue(1, "country", &self.country.to_string());
kt.push_keyvalue(1, "id", &self.id);
kt
}
}
impl<'a> TryInto<SourceSeries> for KeyTreeRef<'a> {
type Error = keytree::Error;
fn try_into(self) -> Result<SourceSeries, keytree::Error> {
let data_type_str: String = self.at("series::data_type")?;
let data_type = DataType::from_str(&data_type_str).unwrap();
let country_str: String = self.at("series::country")?;
let country = Country::from_str(&country_str).unwrap();
Ok(
SourceSeries{
data_type,
country,
id: self.at("series::id")?,
}
)
}
}