1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crate::model::{AcsGeoidQuery, AcsType};
use itertools::Itertools;
/// parameters for running an ACS call.
#[derive(Debug, Clone)]
pub struct AcsApiQueryParams {
/// optional override URL for ACS query. otherwise defaults to [`super::constants::BASE_URL`]
pub base_url: Option<String>,
/// ACS dataset yera
pub year: u64,
/// one or five-year ACS cadence
pub acs_type: AcsType,
/// list of fields to retrieve
pub get_query: Vec<String>,
/// geographic scope of request
pub for_query: AcsGeoidQuery,
/// optional API token in case of rate limiting issues
pub api_token: Option<String>,
}
impl AcsApiQueryParams {
pub fn new(
base_url: Option<String>,
year: u64,
acs_type: AcsType,
get_query: Vec<String>,
for_query: AcsGeoidQuery,
api_token: Option<String>,
) -> AcsApiQueryParams {
AcsApiQueryParams {
base_url,
year,
acs_type,
get_query,
for_query,
api_token,
}
}
pub fn acs_dataset_url(&self) -> String {
let base = self
.base_url
.clone()
.unwrap_or(String::from(super::constants::BASE_URL));
let type_s = self.acs_type.to_directory_name();
format!("{}/{}/acs/{}", base, self.year, type_s)
}
/// builds an ACS REST query URL from application parameters.
///
/// # Examples
///
/// Example 1. Get 2022 Five-Year ACS state-level population estimates for all states, returning
/// their state name, population value, and state FIPS code.
///
/// ```rust
/// use bamcensus_core::model::identifier::{fips, GeoidType};
/// use bamcensus_acs::model::{
/// AcsType,
/// AcsGeoidQuery,
/// AcsApiQueryParams
/// };
///
/// let base_url = String::from("https://api.census.gov/data");
/// let acs_year: u64 = 2022;
/// let acs_type: AcsType = AcsType::FiveYear;
/// let queries = vec![String::from("NAME"), String::from("B01001_001E")];
/// let acs_geoid_query: AcsGeoidQuery = AcsGeoidQuery::new(None, Some(GeoidType::State)).unwrap();
/// let api_query_params = AcsApiQueryParams::new(Some(base_url), acs_year, acs_type, queries, acs_geoid_query, None);
/// let api_url = api_query_params.build_url().unwrap();
/// assert_eq!(api_url, String::from("https://api.census.gov/data/2022/acs/acs5?get=NAME,B01001_001E&for=state:*"))
/// ```
/// Example 2. Get 2022 Five-Year ACS state-level population estimates for all counties in Colorado, returning
/// their state name, population value, and state FIPS code.
///
/// ```rust
/// use bamcensus_core::model::identifier::{fips, GeoidType, Geoid};
/// use bamcensus_acs::model::{
/// AcsType,
/// AcsGeoidQuery,
/// AcsApiQueryParams
/// };
///
/// let base_url = String::from("https://api.census.gov/data");
/// let acs_year: u64 = 2022;
/// let acs_type: AcsType = AcsType::FiveYear;
/// let queries = vec![String::from("NAME"), String::from("B01001_001E")];
/// let acs_geoid_query: AcsGeoidQuery = AcsGeoidQuery::new(Some(Geoid::State(fips::State(08))), Some(GeoidType::County)).unwrap();
/// let api_query_params = AcsApiQueryParams::new(Some(base_url), acs_year, acs_type, queries, acs_geoid_query, None);
/// let api_url = api_query_params.build_url().unwrap();
/// assert_eq!(api_url, String::from("https://api.census.gov/data/2022/acs/acs5?get=NAME,B01001_001E&for=county:*&in=state:08"))
/// ```
pub fn build_url(&self) -> Result<String, String> {
let dataset_url = self.acs_dataset_url();
let get_query = self.get_query.iter().join(",");
let for_query = self.for_query.to_query_key();
let token_query = match &self.api_token {
Some(k) => format!("&key={k}"),
None => String::from(""),
};
let query = format!("{dataset_url}?get={get_query}{for_query}{token_query}",);
Ok(query)
}
/// in order to deconstruct an API response, we need the list of
/// column names in the order that they would appear in the array-
/// shaped ACS response object.
pub fn column_names(&self) -> Vec<String> {
let mut cols = self
.get_query
.iter()
// .map(|s| String::from(s))
.cloned()
.collect_vec();
cols.extend(self.for_query.response_column_names());
cols
}
pub fn output_filename(&self) -> String {
let get_query = self.get_query.join("&");
let for_query = self.for_query.to_query_key();
format!(
"{}-{}-{}-{}.csv",
self.acs_type, self.year, get_query, for_query
)
}
}