Skip to content

Commit

Permalink
UseCODAPApi updates units in table.
Browse files Browse the repository at this point in the history
  • Loading branch information
lublagg committed Jan 26, 2024
1 parent 768c385 commit aa5c243
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 152 deletions.
13 changes: 2 additions & 11 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import { adjustStationDataset, getWeatherStations } from "../utils/getWeatherSta
import { addNotificationHandler, createStationsDataset } from "../utils/codapHelpers";
import InfoIcon from "../assets/images/icon-info.svg";
import { useCODAPApi } from "../hooks/use-codap-api";
import { dataTypeStore } from "../utils/noaaDataTypes";
import { composeURL, formatData } from "../utils/noaaApiHelper";
import { IDataType } from "../types";
import { StationDSName } from "../constants";
import { geoLocSearch } from "../utils/geonameSearch";
import { DataReturnWarning } from "./data-return-warning";
Expand Down Expand Up @@ -50,6 +48,7 @@ export const App = () => {
draft.weatherStation = station;
draft.location = {name: locationName, latitude, longitude};
draft.weatherStationDistance = 0;
draft.zoomMap = false;
});
}
}
Expand All @@ -73,14 +72,6 @@ export const App = () => {
});
};

const getSelectedDataTypes = () => {
const { selectedFrequency } = state;
const attributes = state.frequencies[selectedFrequency].attrs.map(attr => attr.name);
return attributes.map((attr) => {
return dataTypeStore.findByName(attr);
}) as IDataType[];
};

const fetchSuccessHandler = async (data: any) => {
const {startDate, endDate, units, selectedFrequency,
weatherStation, timezone} = state;
Expand All @@ -101,7 +92,7 @@ export const App = () => {
const items = Array.isArray(dataRecords) ? dataRecords : [dataRecords];
const filteredItems = filterItems(items);
setStatusMessage("Sending weather records to CODAP");
await createNOAAItems(filteredItems, getSelectedDataTypes()).then(
await createNOAAItems(filteredItems).then(
function (result: any) {
setIsFetching(false);
setStatusMessage(`Retrieved ${filteredItems.length} cases`);
Expand Down
143 changes: 90 additions & 53 deletions src/hooks/use-codap-api.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,76 @@
import { Attribute, Collection, DataContext, IDataType, IItem } from "../types";
import { IResult, codapInterface, createItems, getDataContext } from "@concord-consortium/codap-plugin-api";
import { DSCollection1, DSCollection2, DSName, kStationsCollectionName } from "../constants";
import { useEffect, useState } from "react";
import { useStateContext } from "./use-state";
import { useEffect } from "react";
import { createMap, selectStations } from "../utils/codapHelpers";
import { Attribute, Collection, DataContext, ICODAPItem, IDataType, IItem } from "../types";
import { IResult, codapInterface, createItems, getAllItems, getDataContext } from "@concord-consortium/codap-plugin-api";
import { DSCollection1, DSCollection2, DSName, kStationsCollectionName } from "../constants";
import { clearData, createMap, selectStations } from "../utils/codapHelpers";
import { dataTypeStore } from "../utils/noaaDataTypes";

export const useCODAPApi = () => {
const {state} = useStateContext();
const [ selectedDataTypes, setSelectedDataTypes ] = useState<IDataType[]>([]);
const { frequencies, selectedFrequency, weatherStation, units, isMapOpen, zoomMap } = state;
const { attrs } = frequencies[selectedFrequency];

useEffect(() => {
if (state.weatherStation && state.isMapOpen) {
const zoom = state.zoomMap ? 7 : null;
createMap(kStationsCollectionName, {width: 500, height: 350}, [state.weatherStation.latitude, state.weatherStation.longitude], zoom);
selectStations([state.weatherStation.name]);
if (weatherStation && isMapOpen) {
const zoom = zoomMap ? 7 : null;
createMap(kStationsCollectionName, {width: 500, height: 350}, [weatherStation.latitude, weatherStation.longitude], zoom);
selectStations([weatherStation.name]);
}
}, [state.isMapOpen, state.weatherStation, state.zoomMap]);
}, [isMapOpen, weatherStation, zoomMap]);

useEffect(() => {
const attributes = frequencies[selectedFrequency].attrs.map(attr => attr.name);
const dataTypes = attributes.map((attr) => {
return dataTypeStore.findByName(attr);
}) as IDataType[];
setSelectedDataTypes(dataTypes);
}, [selectedFrequency, frequencies, attrs]);

const createNOAAItems = async (items: IItem[]) => {
await updateWeatherDataset(selectedDataTypes);
// eslint-disable-next-line no-console
console.log("noaa-cdo ... createNOAAItems with " + items.length + " case(s)");
await createItems(DSName, items);
await codapInterface.sendRequest({
"action": "create",
"resource": "component",
"values": {
"type": "caseTable",
"dataContext": DSName,
"horizontalScrollOffset": 500
}
});
};

useEffect(() => {
const updateUnitsInCODAP = async () => {
let doesDataCtxExist = await getDataContext(DSName);
if (!doesDataCtxExist || !doesDataCtxExist.success) {
return;
}
const oldUnits = units === "metric" ? "standard" : "metric";
// fetch existing items in existing dataset
let allItemsRes = await getAllItems(DSName);
let allItems = allItemsRes.values.map((item: {id: string, values: ICODAPItem}) => item.values);
// convert from old units to new units
allItems.forEach(function (item: ICODAPItem) {
Object.keys(item).forEach(function (attrName) {
let dataType = dataTypeStore.findByAbbr(attrName);
if (dataType && dataType.convertUnits) {
item[attrName] = dataType.convertUnits(dataType.units[oldUnits], dataType.units[units], item[attrName]);
}
});
});
// clear dataset
await clearData(DSName);
// insert items
await createNOAAItems(allItems);
};
updateUnitsInCODAP();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [units]);

const getNoaaDataContextSetupObject = () => {
return {
Expand Down Expand Up @@ -69,14 +125,13 @@ export const useCODAPApi = () => {
};

const createAttribute = (datasetName: string, collectionName: string, dataType: IDataType) => {
const {units} = state;
return codapInterface.sendRequest({
action: "create",
resource: "dataContext[" + datasetName + "].collection[" + collectionName + "].attribute",
values: {
name: dataType.name,
unit: dataType.units[units],
description: dataType.description
action: "create",
resource: "dataContext[" + datasetName + "].collection[" + collectionName + "].attribute",
values: {
name: dataType.name,
unit: dataType.units[units],
description: dataType.description
}
});
};
Expand All @@ -101,7 +156,6 @@ export const useCODAPApi = () => {
};

const updateWeatherDataset = async (dataTypes: IDataType[]) => {
const {units} = state;
let result = await getDataContext(DSName);

if (!result || !result.success) {
Expand All @@ -123,34 +177,33 @@ export const useCODAPApi = () => {
const attrDefs: Attribute[] = [];

dataSetDef.collections.forEach(function (collection: Collection) {
collection.attrs.forEach(function (attr) {
attrDefs.push(attr);
});
collection.attrs.forEach(function (attr) {
attrDefs.push(attr);
});
});

const lastCollection = dataSetDef.collections[dataSetDef.collections.length - 1];
const promises = dataTypes.map(function (dataType) {
const attrName = dataType.name;
const attrDef = attrDefs.find(function (ad) {
return ad.name === attrName;
});
if (!attrDef) {
return createAttribute(DSName, lastCollection.name, dataType);
} else {
let unit = dataType.units[units];
if (attrDef.unit !== unit) {
return updateAttributeUnit(dataSetDef, attrName, unit);
} else {
return Promise.resolve("Unknown attribute.");
}
}
const attrName = dataType.name;
const attrDef = attrDefs.find(function (ad) {
return ad.name === attrName;
});
if (!attrDef) {
return createAttribute(DSName, lastCollection.name, dataType);
} else {
let unit = dataType.units[units];
if (attrDef.unit !== unit) {
return updateAttributeUnit(dataSetDef, attrName, unit);
} else {
return Promise.resolve("Unknown attribute.");
}
}
});
return Promise.all(promises);
};

const filterItems = (items: IItem[]) => {
const { selectedFrequency, frequencies } = state;
const { attrs, filters } = frequencies[selectedFrequency];
const { filters } = frequencies[selectedFrequency];
const filteredItems = items.filter((item: IItem) => {
const allFiltersMatch: boolean[] = [];
filters.forEach((filter) => {
Expand Down Expand Up @@ -192,22 +245,6 @@ export const useCODAPApi = () => {
return filteredItems;
};

const createNOAAItems = async (items: IItem[], dataTypes: IDataType[]) => {
await updateWeatherDataset(dataTypes);
// eslint-disable-next-line no-console
console.log("noaa-cdo ... createNOAAItems with " + items.length + " case(s)");
await createItems(DSName, items);
await codapInterface.sendRequest({
"action": "create",
"resource": "component",
"values": {
"type": "caseTable",
"dataContext": DSName,
"horizontalScrollOffset": 500
}
});
};

return {
filterItems,
createNOAAItems
Expand Down
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,7 @@ export interface IRecord {
export interface IItem {
[key: string]: string;
}

export interface ICODAPItem {
[key: string]: any;
}
74 changes: 37 additions & 37 deletions src/utils/codapHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,50 +33,50 @@ const createMap = async (name: string, dimensions: IDimensions, center: ILatLong
}

if (!map) {
let result = await codapInterface.sendRequest({
action: "create", resource: "component", values: {
type: "map",
name,
dimensions,
dataContextName: name,
legendAttributeName: "isActive"
}
}) as IResult;
if (result.success) {
map = result.values;
}
let result = await codapInterface.sendRequest({
action: "create", resource: "component", values: {
type: "map",
name,
dimensions,
dataContextName: name,
legendAttributeName: "isActive"
}
}) as IResult;
if (result.success) {
map = result.values;
}
}
if (map && center && (zoom != null)) {
return centerAndZoomMap(map.id, center, zoom);
return centerAndZoomMap(map.id, center, zoom);
} else {
return selectComponent(map.id);
return selectComponent(map.id);
}
};

const centerAndZoomMap = (mapName: string, center: ILatLong, zoom: number) => {
return new Promise<void>((resolve) => {
setTimeout(function () {
codapInterface.sendRequest({
action: "update",
resource: `component[${mapName}]`,
values: {
center,
zoom: 4
}
});
setTimeout(function () {
codapInterface.sendRequest({
action: "update",
resource: `component[${mapName}]`,
values: {
zoom
}
});

}, 500);
}, 2000);
resolve();
});
return new Promise<void>((resolve) => {
setTimeout(function () {
codapInterface.sendRequest({
action: "update",
resource: `component[${mapName}]`,
values: {
center,
zoom: 4
}
});
setTimeout(function () {
codapInterface.sendRequest({
action: "update",
resource: `component[${mapName}]`,
values: {
zoom
}
});

}, 500);
}, 2000);
resolve();
});
};

const hasMap = async () => {
Expand Down
19 changes: 2 additions & 17 deletions src/utils/noaaApiHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,6 @@ import { IFrequency, IRecord, ITimeZone, IUnits, IWeatherStation } from "../type
import { frequencyToReportTypeMap, nceiBaseURL } from "../constants";
import { dataTypeStore } from "./noaaDataTypes";

export const convertUnits = (fromUnitSystem: IUnits, toUnitSystem: IUnits, data: any) => {
if (fromUnitSystem === toUnitSystem) {
return;
}
data.forEach(function (item: any) {
Object.keys(item).forEach(function (prop) {
let dataType = dataTypeStore.findByName(prop);
if (dataType && dataType.convertUnits) {
item[prop] = dataType.convertUnits(dataType.units[fromUnitSystem], dataType.units[toUnitSystem], item[prop]);
}
});
});
};

interface IFormatData {
data: IRecord[];
units: IUnits;
Expand All @@ -26,7 +12,7 @@ interface IFormatData {
}

export const formatData = (props: IFormatData) => {
const {data, timezone, units, frequency, weatherStation} = props;
const {data, timezone, frequency, weatherStation} = props;
const database = frequencyToReportTypeMap[frequency];
let dataRecords: any[] = [];
data.forEach((r: any) => {
Expand All @@ -39,12 +25,11 @@ export const formatData = (props: IFormatData) => {
aValue["report type"] = frequency;
dataRecords.push(aValue);
});
convertUnits("metric", units, dataRecords);
return dataRecords;
};

export const decodeData = (iField: string, iValue: any, database: string) => {
let dataType = dataTypeStore.findByName(iField);
let dataType = dataTypeStore.findByAbbr(iField);
let decoder = dataType && dataType.decode && dataType.decode[database];
return decoder ? decoder(iValue) : iValue;
};
Expand Down
Loading

0 comments on commit aa5c243

Please sign in to comment.