Skip to content

Commit

Permalink
User can select attributes and create CODAP table.
Browse files Browse the repository at this point in the history
  • Loading branch information
lublagg committed Sep 16, 2023
1 parent b5d6406 commit 1b40224
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 136 deletions.
14 changes: 3 additions & 11 deletions src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import classnames from "classnames";
import { Information } from "./information";
import { attributeOptions, categories, defaultSelectedOptions, yearsOptions } from "./constants";

Check warning on line 5 in src/components/app.tsx

View workflow job for this annotation

GitHub Actions / Build and Run Jest Tests

'yearsOptions' is defined but never used
import { IStateOptions } from "./types";
import { createQueryFromSelections } from "../scripts/api";
import { createTableFromSelections } from "../scripts/api";
import { connect } from "../scripts/connect";


Expand Down Expand Up @@ -43,16 +43,8 @@ function App() {
setShowInfo(true);
};

const handleGetData = () => {
createQueryFromSelections(selectedOptions);

const makeDataSetAndTable = async () => {
const dS = await connect.guaranteeDataset(selectedOptions.geographicLevel);
if (dS.success) {
await connect.makeCaseTableAppear();
}
};
makeDataSetAndTable();
const handleGetData = async () => {
await createTableFromSelections(selectedOptions);
};

return (
Expand Down
2 changes: 1 addition & 1 deletion src/components/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const cropUnitOptions: IAttrOptions = {
options: ["Area Harvested", "Yield"],
instructions: "(Choose units)"
};
const cropOptions: IAttrOptions = {
export const cropOptions: IAttrOptions = {
key: "crops",
label: null,
options: ["Corn", "Cotton", "Grapes", "Grasses", "Oats", "Soybeans", "Wheat"],
Expand Down
42 changes: 42 additions & 0 deletions src/components/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,45 @@ export interface IAttrOptions {
options: string[],
instructions: string|null
}

export interface IResData {
"CV (%)": string;
Value: string;
agg_level_desc: string;
asd_code: string;
asd_desc: string;
begin_code: string;
class_desc: string;
commodity_desc: string;
congr_district_code: string;
country_code: string;
country_name: string;
county_ansi: string;
county_code: string;
county_name: string;
domain_desc: string;
domaincat_desc: string;
end_code: string;
freq_desc: string;
group_desc: string;
load_time: string;
location_desc: string;
prodn_practice_desc: string;
reference_period_desc: string;
region_desc: string;
sector_desc: string;
short_desc: string;
source_desc: string;
state_alpha: string;
state_ansi: string;
state_fips_code: string;
state_name: string;
statisticcat_desc: string;
unit_desc: string;
util_practice_desc: string;
watershed_code: string;
watershed_desc: string;
week_ending: string;
year: number;
zip_5: string;
};

Check failure on line 61 in src/components/types.ts

View workflow job for this annotation

GitHub Actions / Build and Run Jest Tests

Unnecessary semicolon

Check warning on line 61 in src/components/types.ts

View workflow job for this annotation

GitHub Actions / Build and Run Jest Tests

Newline required at end of file but not found
185 changes: 123 additions & 62 deletions src/scripts/api.ts
Original file line number Diff line number Diff line change
@@ -1,94 +1,155 @@
import fetchJsonp from "fetch-jsonp";
import { queryData } from "./query-headers";
import { ICropDataItem, queryData } from "./query-headers";
import { IStateOptions } from "../components/types";
import { connect } from "./connect";
import { cropOptions } from "../components/constants";

const baseURL = `https://quickstats.nass.usda.gov/api/api_GET/?key=9ED0BFB8-8DDD-3609-9940-A2341ED6A9E3`;

export const createRequest = (attribute: string, geoLevel: string, location: string, year: string) => {
interface IRequestParams {
attribute: string,
geographicLevel: string,
location: string,
year: string,
cropCategory?: keyof ICropDataItem
}

interface IGetAttrDataParams {
attribute: string,
geographicLevel: string,
cropUnits: string,
state: string,
year: string
}

export const createRequest = ({attribute, geographicLevel, location, year, cropCategory}: IRequestParams) => {
const queryParams = queryData.find((d) => d.plugInAttribute === attribute);
const {sector, group, commodity, category, domains, dataItem} = queryParams!;

const baseReq = `${baseURL}&sect_desc=${sector}&group_desc=${group}&commodity_desc=${commodity}&statisticcat_desc=${category}&domain_desc=${domains}&agg_level_desc=${geoLevel}&state_name=${location}&year=${year}`;

const reqs = [];
let item;
let cat;
if (cropCategory) {
const cropDataItem = queryParams?.dataItem as ICropDataItem;
const cropCat = queryParams?.category as ICropDataItem;
item = cropDataItem[cropCategory];
cat = cropCat[cropCategory];
} else {
item = dataItem;
cat = category;
}

const baseReq = `${baseURL}&sect_desc=${sector}&group_desc=${group}&commodity_desc=${commodity}&statisticcat_desc=${cat}&domain_desc=${domains}&agg_level_desc=${geographicLevel}&state_name=${location}&year=${year}`;
let req = baseReq;
if (Array.isArray(dataItem)) {
dataItem.forEach(item => {
reqs.push(`${baseReq}&short_desc=${item}`);
dataItem.forEach(dItem => {
req = req + `&short_desc=${dItem}`;
});
} else {
reqs.push(`${baseReq}&short_desc=${dataItem}`);
req = req + `&short_desc=${item}`;
}

return reqs;
return req;
};

export const createQueryFromSelections = (selectedOptions: IStateOptions) => {
const {geographicLevel, states, years, ...subOptions} = selectedOptions;
export const createTableFromSelections = async (selectedOptions: IStateOptions) => {
const {geographicLevel, states, cropUnits, years, ...subOptions} = selectedOptions;
await connect.getNewDataContext();
await connect.createTopCollection();

// need to change this - instead of creating based on UI names, create based on dataItems in queryParams
const allAttrs: Array<string|ICropDataItem> = ["Year"];
for (const key in subOptions) {
const selections = subOptions[key as keyof typeof subOptions];
for (const attribute of selections) {
const queryParams = queryData.find((d) => d.plugInAttribute === attribute);
const {dataItem} = queryParams!;
if (Array.isArray(dataItem)) {
allAttrs.push(...dataItem);
} else {
allAttrs.push(dataItem);
}
}
}
await connect.createSubCollection(allAttrs);
const items = await getItems(selectedOptions);
await connect.createItems(items);
await connect.makeCaseTableAppear();
}

const getItems = async (selectedOptions: IStateOptions) => {
const {states, years} = selectedOptions;
const multipleStatesSelected = states.length > 1 || states[0] === "All States";
const multipleYearsSelected = years.length > 1;

const items = [];
if (multipleStatesSelected) {
states.forEach((state) => {
for (const state of states) {
if (multipleYearsSelected) {
years.forEach(year => {
// do something
console.log("multiple years and mulitple states selected");
});
for (const year of years) {
const item = await getDataForSingleYearAndState(selectedOptions, state, year);
items.push(item);
}
} else {
console.log("multiple states selected with one year");
const item = await getDataForSingleYearAndState(selectedOptions, state, years[0]);
items.push(item);
}
});
}
} else {
for (const key in subOptions) {
const value = subOptions[key as keyof typeof subOptions];
console.log("current value", value);
if (value && Array.isArray(value) && value.length > 1) {
console.log("you selected more than one value from a sub-category");
} else if (value && Array.isArray(value) && value.length === 1) {
console.log("you selected only one value from a sub-category and it is this value", value);
const reqArray = createRequest(value[0], geographicLevel, states[0], years[0]);
console.log("REQUEST", reqArray[0]);
getDataAndCreateCodapTable(reqArray);
const item = await getDataForSingleYearAndState(selectedOptions, states[0], years[0]);
items.push(item);
}

return items;
};

const getDataForSingleYearAndState = async (selectedOptions: IStateOptions, state: string, year: string) => {
const {geographicLevel, states, years, cropUnits, ...subOptions} = selectedOptions;

let item: any = {
"State": state,
"Year": year,
}

for (const key in subOptions) {
const value = subOptions[key as keyof typeof subOptions];
if (value && Array.isArray(value)) {
for (const attribute of value) {
const attrData = await getAttrData({attribute, geographicLevel, state, year, cropUnits});
item = {...item, ...attrData};
}
}
}
};

export const getDataAndCreateCodapTable = (reqs: string[]) => {
reqs.forEach((req) => {
fetchJsonp(req)
.then(function(response) {
return response.json();
}).then(function(json) {
console.log("parsed json", json);
const formattedData = formatDataForCODAP(json);
}).catch(function(ex) {
console.log("parsing failed", ex);
});
});
return item;
};

const formatDataForCODAP = (res: any) => {
console.log({res});
return res;
};
const getAttrData = async (params: IGetAttrDataParams) => {
const {attribute, geographicLevel, state, year, cropUnits} = params;
const reqParams: IRequestParams = {attribute, geographicLevel, location: state, year};
if (cropOptions.options.includes(attribute) && cropUnits) {
reqParams.cropCategory = cropUnits as keyof ICropDataItem;
}
const req = createRequest(reqParams);
const res = await fetchData(req);
const values: any = {};
if (res) {
const {data} = res;
data.forEach((dataItem: any) => {
values[dataItem.short_desc] = dataItem.Value;
})
} else {
console.log("error");
}
return values;
}

// export const runTestQuery = () => {
// const request1 = createRequest("Total Farmers", "STATE", "CALIFORNIA", "2017")[0];
// const request2 = createRequest("Total Farmers", "STATE", "ARKANSAS", "2017")[0];
// const request3 = createRequest("Total Farmers", "STATE", "ALABAMA", "2017")[0];
// const request4 = createRequest("Total Farmers", "STATE", "MONTANA", "2017")[0];
// const requests = [request1, request2, request3, request4];
// requests.forEach((req) => {
// fetchJsonp(req)
// .then(function(response) {
// return response.json();
// }).then(function(json) {
// console.log("parsed json", json);
// }).catch(function(ex) {
// console.log("parsing failed", ex);
// })
// })
// };
export const fetchData = async (req: string) => {
try {
const response = await fetchJsonp(req);
const json = await response.json();
return json;
} catch (error) {
console.log("parsing failed", error);
throw error;
}
};
Loading

0 comments on commit 1b40224

Please sign in to comment.