Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fd4aacc
Add persistence to API import forms
rzats Sep 17, 2024
a4ab17f
Merge branch 'dev' into rzatserkovnyi/persistent-forms
rzats Sep 24, 2024
2383b5d
storage: local -> session
rzats Oct 1, 2024
cfb6395
Merge branch 'rzatserkovnyi/persistent-forms' of https://github.com/c…
rzats Oct 1, 2024
0b5ef2c
Merge branch 'dev' into rzatserkovnyi/persistent-forms
rzats Oct 1, 2024
f9af742
session storage
rzats Oct 1, 2024
3f0a535
Add persistence to API keys
rzats Oct 1, 2024
f50c93e
store preference in localhost
rzats Oct 1, 2024
faf4ec5
Update to single API key
rzats Oct 9, 2024
91dc867
Review fixes
rzats Oct 9, 2024
8048bc8
Merge latest dev changes
rzats Nov 21, 2024
0b75ca7
Merge remote-tracking branch 'origin/dev' into rzatserkovnyi/persiste…
rzats Nov 21, 2024
b14e25b
Review fixes
rzats Nov 21, 2024
f673766
Review fixes #2
rzats Nov 27, 2024
f6c12ab
Merge pull request #82 from cmu-delphi/bot/sync-main-dev
melange396 Dec 9, 2024
20fd21f
Remove unnecessary variables
rzats Dec 11, 2024
a8394f3
Review fixes
rzats Dec 11, 2024
6b6fe3b
Merge pull request #72 from cmu-delphi/rzatserkovnyi/persistent-apikeys
rzats Dec 18, 2024
9c78def
Merge remote-tracking branch 'origin/dev' into rzatserkovnyi/persiste…
rzats Dec 18, 2024
f9a3a19
Merge pull request #67 from cmu-delphi/rzatserkovnyi/persistent-forms
rzats Dec 18, 2024
f053b86
Enable reactivity in covidcast form (#85)
rzats Feb 5, 2025
d3f935a
Improve behavior on errors when importing a dataset (#74)
rzats Feb 6, 2025
866c146
chore: release v2.1.6
melange396 Feb 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "www-epivis",
"version": "2.1.5",
"version": "2.1.6",
"private": true,
"license": "MIT",
"description": "",
Expand Down
53 changes: 46 additions & 7 deletions src/api/EpiData.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import UIkit from 'uikit';
import { appendIssueToTitle } from '../components/dialogs/utils';
import {
Expand Down Expand Up @@ -43,11 +44,23 @@ const ENDPOINT = process.env.EPIDATA_ENDPOINT_URL;

export const fetchOptions: RequestInit = process.env.NODE_ENV === 'development' ? { cache: 'force-cache' } : {};

function processResponse<T>(response: Response): Promise<T> {
if (response.ok) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return response.json();
}
return response.text().then((text) => {
throw new Error(`[${response.status}] ${text}`);
});
}

export function fetchImpl<T>(url: URL): Promise<T> {
const urlGetS = url.toString();
if (urlGetS.length < 4096) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return fetch(url.toString(), fetchOptions).then((d) => d.json());
return fetch(url.toString(), fetchOptions).then((d) => {
return processResponse(d);
});
}
const params = new URLSearchParams(url.searchParams);
url.searchParams.forEach((d) => url.searchParams.delete(d));
Expand All @@ -56,7 +69,9 @@ export function fetchImpl<T>(url: URL): Promise<T> {
...fetchOptions,
method: 'POST',
body: params,
}).then((d) => d.json());
}).then((d) => {
return processResponse(d);
});
}

// generic epidata loader
Expand Down Expand Up @@ -147,26 +162,50 @@ export function loadDataSet(
url.searchParams.set('format', 'json');
return fetchImpl<Record<string, unknown>[]>(url)
.then((res) => {
const data = loadEpidata(title, res, columns, columnRenamings, { _endpoint: endpoint, ...params });
if (data.datasets.length == 0) {
try {
const data = loadEpidata(title, res, columns, columnRenamings, { _endpoint: endpoint, ...params });
if (data.datasets.length == 0) {
return UIkit.modal
.alert(
`
<div class="uk-alert uk-alert-error">
<a href="${url.href}">API Link</a> returned no data, which suggests that the API has no available information for the selected location.
</div>`,
)
.then(() => null);
}
return data;
} catch (error) {
console.warn('failed loading data', error);
// EpiData API error - JSON with "message" property
if ('message' in res) {
return UIkit.modal
.alert(
`
<div class="uk-alert uk-alert-error">
[f01] Failed to fetch API data from <a href="${url.href}">API Link</a>:<br/><i>${res['message']}</i>
</div>`,
)
.then(() => null);
}
// Fallback for generic error
return UIkit.modal
.alert(
`
<div class="uk-alert uk-alert-error">
<a href="${url.href}">API Link</a> returned no data.
[f02] Failed to fetch API data from <a href="${url.href}">API Link</a>:<br/><i>${error}</i>
</div>`,
)
.then(() => null);
}
return data;
})
.catch((error) => {
console.warn('failed fetching data', error);
return UIkit.modal
.alert(
`
<div class="uk-alert uk-alert-error">
Failed to fetch API data from <a href="${url.href}">API Link</a>.
[f03] Failed to fetch API data from <a href="${url.href}">API Link</a>:<br/><i>${error}</i>
</div>`,
)
.then(() => null);
Expand Down
193 changes: 130 additions & 63 deletions src/components/dialogs/ImportAPIDialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,14 @@
import NowCast from './dataSources/Nowcast.svelte';
import CovidHosp from './dataSources/COVIDHosp.svelte';
import CoviDcast from './dataSources/COVIDcast.svelte';
import { navMode } from '../../store';
import { navMode, storeApiKeys } from '../../store';
import { NavMode } from '../chartUtils';
import { formSelections } from '../../store';

const dispatch = createEventDispatcher();

const id = randomId();

let dataSource:
| 'fluview'
| 'flusurv'
| 'gft'
| 'ght'
| 'twitter'
| 'wiki'
| 'cdc'
| 'quidel'
| 'nidss_flu'
| 'nidss_dengue'
| 'sensors'
| 'nowcast'
| 'covidcast'
| 'covid_hosp' = 'fluview';

let loading = false;
let handler: unknown = null;

Expand Down Expand Up @@ -71,68 +56,134 @@
<div class="uk-form-label">Data Source</div>
<div class="uk-form-controls uk-form-controls-text">
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="fluview" />
><input
class="uk-radio"
type="radio"
name="dataSource"
bind:group={$formSelections.dataSource}
value="fluview"
/>
ILINet (aka FluView) (source:
<a target="_blank" href="https://gis.cdc.gov/grasp/fluview/fluportaldashboard.html">cdc.gov</a>)
</label>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="flusurv" /> FluSurv
(source: <a target="_blank" href="https://gis.cdc.gov/GRASP/Fluview/FluHospRates.html">cdc.gov</a>)</label
><input
class="uk-radio"
type="radio"
name="dataSource"
bind:group={$formSelections.dataSource}
value="flusurv"
/>
FluSurv (source:
<a target="_blank" href="https://gis.cdc.gov/GRASP/Fluview/FluHospRates.html">cdc.gov</a>)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="gft" /> Google Flu
Trends (source: <a target="_blank" href="https://www.google.org/flutrends/">google.com</a>)</label
><input class="uk-radio" type="radio" name="dataSource" bind:group={$formSelections.dataSource} value="gft" />
Google Flu Trends (source: <a target="_blank" href="https://www.google.org/flutrends/">google.com</a>)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="ght" /> Google Health Trends
(source: private Google API)</label
><input class="uk-radio" type="radio" name="dataSource" bind:group={$formSelections.dataSource} value="ght" />
Google Health Trends (source: private Google API)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="twitter" />
><input
class="uk-radio"
type="radio"
name="dataSource"
bind:group={$formSelections.dataSource}
value="twitter"
/>
HealthTweets (source: <a target="_blank" href="http://www.healthtweets.org/">healthtweets.org</a>)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="wiki" /> Wikipedia
Access (source:
><input
class="uk-radio"
type="radio"
name="dataSource"
bind:group={$formSelections.dataSource}
value="wiki"
/>
Wikipedia Access (source:
<a target="_blank" href="https://dumps.wikimedia.org/other/pagecounts-raw/">dumps.wikimedia.org</a>)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="cdc" /> CDC Page Hits (source:
private CDC dataset)</label
><input class="uk-radio" type="radio" name="dataSource" bind:group={$formSelections.dataSource} value="cdc" />
CDC Page Hits (source: private CDC dataset)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="quidel" /> Quidel Data (source:
private Quidel dataset)</label
><input
class="uk-radio"
type="radio"
name="dataSource"
bind:group={$formSelections.dataSource}
value="quidel"
/> Quidel Data (source: private Quidel dataset)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="nidss_flu" /> NIDSS -
Influenza (source:
><input
class="uk-radio"
type="radio"
name="dataSource"
bind:group={$formSelections.dataSource}
value="nidss_flu"
/>
NIDSS - Influenza (source:
<a target="_blank" href="http://nidss.cdc.gov.tw/en/CDCWNH01.aspx?dc=wnh">nidss.cdc.gov.tw</a>)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="nidss_dengue" /> NIDSS
- Dengue (source:
><input
class="uk-radio"
type="radio"
name="dataSource"
bind:group={$formSelections.dataSource}
value="nidss_dengue"
/>
NIDSS - Dengue (source:
<a
target="_blank"
href="http://nidss.cdc.gov.tw/en/SingleDisease.aspx?dc=1&amp;dt=4&amp;disease=061&amp;position=1"
>nidss.cdc.gov.tw</a
>)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="sensors" /> Delphi
Sensors (source: <a target="_blank" href="https://delphi.cmu.edu/">delphi.cmu.edu</a>)</label
><input
class="uk-radio"
type="radio"
name="dataSource"
bind:group={$formSelections.dataSource}
value="sensors"
/>
Delphi Sensors (source: <a target="_blank" href="https://delphi.cmu.edu/">delphi.cmu.edu</a>)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="nowcast" /> Delphi
Nowcast (source: <a target="_blank" href="https://delphi.cmu.edu/">delphi.cmu.edu</a>)</label
><input
class="uk-radio"
type="radio"
name="dataSource"
bind:group={$formSelections.dataSource}
value="nowcast"
/>
Delphi Nowcast (source: <a target="_blank" href="https://delphi.cmu.edu/">delphi.cmu.edu</a>)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="covidcast" /> Delphi
COVIDcast (source: <a target="_blank" href="https://delphi.cmu.edu/">delphi.cmu.edu</a>)</label
><input
class="uk-radio"
type="radio"
name="dataSource"
bind:group={$formSelections.dataSource}
value="covidcast"
/>
Delphi COVIDcast (source: <a target="_blank" href="https://delphi.cmu.edu/">delphi.cmu.edu</a>)</label
>
<label
><input class="uk-radio" type="radio" name="dataSource" bind:group={dataSource} value="covid_hosp" /> COVID
Hospitalization (source:
><input
class="uk-radio"
type="radio"
name="dataSource"
bind:group={$formSelections.dataSource}
value="covid_hosp"
/>
COVID Hospitalization (source:
<a
target="_blank"
href="https://healthdata.gov/dataset/covid-19-reported-patient-impact-and-hospital-capacity-state-timeseries"
Expand All @@ -142,41 +193,57 @@
</div>
</div>

{#if dataSource === 'fluview'}
{#if $formSelections.dataSource === 'fluview'}
<FluView {id} bind:this={handler} />
{:else if dataSource === 'flusurv'}
{:else if $formSelections.dataSource === 'flusurv'}
<FluSurv {id} bind:this={handler} />
{:else if dataSource === 'gft'}
{:else if $formSelections.dataSource === 'gft'}
<Gft {id} bind:this={handler} />
{:else if dataSource === 'ght'}
{:else if $formSelections.dataSource === 'ght'}
<GHT {id} bind:this={handler} />
{:else if dataSource === 'twitter'}
{:else if $formSelections.dataSource === 'twitter'}
<Twitter {id} bind:this={handler} />
{:else if dataSource === 'wiki'}
{:else if $formSelections.dataSource === 'wiki'}
<Wiki {id} bind:this={handler} />
{:else if dataSource === 'quidel'}
{:else if $formSelections.dataSource === 'quidel'}
<Quidel {id} bind:this={handler} />
{:else if dataSource === 'nidss_dengue'}
{:else if $formSelections.dataSource === 'nidss_dengue'}
<NidssDengue {id} bind:this={handler} />
{:else if dataSource === 'nidss_flu'}
{:else if $formSelections.dataSource === 'nidss_flu'}
<NidssFlu {id} bind:this={handler} />
{:else if dataSource === 'cdc'}
{:else if $formSelections.dataSource === 'cdc'}
<Cdc {id} bind:this={handler} />
{:else if dataSource === 'sensors'}
{:else if $formSelections.dataSource === 'sensors'}
<Sensors {id} bind:this={handler} />
{:else if dataSource === 'nowcast'}
{:else if $formSelections.dataSource === 'nowcast'}
<NowCast {id} bind:this={handler} />
{:else if dataSource === 'covid_hosp'}
{:else if $formSelections.dataSource === 'covid_hosp'}
<CovidHosp {id} bind:this={handler} />
{:else if dataSource === 'covidcast'}
{:else if $formSelections.dataSource === 'covidcast'}
<CoviDcast {id} bind:this={handler} />
{/if}
</form>

<button slot="footer" class="uk-button uk-button-primary" type="submit" form={id} disabled={loading}>
Fetch Data
{#if loading}
<div uk-spinner />
{/if}
</button>
<div slot="footer">
<div class="uk-form-controls uk-form-controls-text container">
<button class="uk-button uk-button-primary" type="submit" form={id} disabled={loading}>
Fetch Data
{#if loading}
<div uk-spinner />
{/if}
</button>
<label
><input class="uk-checkbox" type="checkbox" bind:checked={$storeApiKeys} />
Save API key (auth token) between visits</label
>
</div>
</div>
</Dialog>

<style>
.container {
display: flex;
align-items: center;
column-gap: 2em;
}
</style>
Loading