Skip to content
Merged
4 changes: 1 addition & 3 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@
});

async function loadComponent(mode) {
if (mode.waitForReady) {
await appReadyPromise;
}
await appReadyPromise;
return mode.component();
}

Expand Down
7 changes: 2 additions & 5 deletions src/blocks/SurveyStats.svelte
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
<script>
import { fetchSampleSizesNationSummary } from '../data';
import { referenceRawNationSignal, refSensor } from '../stores/questions';
import { referenceRawNationSensorLike } from '../stores/questions';
import UiKitHint from '../components/UIKitHint.svelte';
import { formatDateLocal } from '../formats';
import KPIValue from '../components/KPIValue.svelte';

export let className = '';

function fetchOverview() {
return fetchSampleSizesNationSummary({
id: refSensor.id,
signal: referenceRawNationSignal,
});
return fetchSampleSizesNationSummary(referenceRawNationSensorLike);
}

const data = fetchOverview();
Expand Down
11 changes: 3 additions & 8 deletions src/components/DownloadMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { csvFormat } from 'd3-dsv';
import { formatDateISO } from '../formats';
import { modeByID } from '../modes';
import { currentMode, sensorList } from '../stores';
import { currentMode } from '../stores';

export let fileName = 'chart';
/**
Expand Down Expand Up @@ -112,13 +112,8 @@
function exportData() {
// switch to export mode
currentMode.set(modeByID.export);

const knownOne = sensor ? sensorList.find((d) => d.key == sensor.key) : null;
if (knownOne) {
sensor.set(knownOne, true);
} else {
scrollToTop();
}
sensor.set(sensor.value, true);
scrollToTop();
}
</script>

Expand Down
2 changes: 1 addition & 1 deletion src/components/IndicatorPicker.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

export let className = 'grid-3-11';

export let allSensors = groupedSensorList;
export let allSensors = $groupedSensorList;

export let label = 'Indicator';

Expand Down
47 changes: 13 additions & 34 deletions src/data/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { parseAPIDateAndWeek } from './utils';
import type { RegionLevel } from './regions';
import { isCountSignal } from './signals';
import { ALL_TIME_FRAME, TimeFrame } from './TimeFrame';
import { Sensor, units, colorScales, vegaColorScales, yAxis } from './sensor';
import { Sensor, units, colorScales, vegaColorScales, yAxis, EpiDataMetaParsedInfo, SensorLike } from './sensor';
import { formatSpecifiers, formatter } from '../formats';
import { parse as parseMarkDown, parseInline } from 'marked';
import type { EpiWeek } from './EpiWeek';

function toKey(source: string, signal: string) {
return `${source}-${signal}`;
Expand Down Expand Up @@ -40,16 +39,6 @@ function extractStats(signal: string, entry: EpiDataMetaInfo | null, level?: Reg
return [...Object.values(entry.geo_types)][0]!;
}

export interface EpiDataMetaParsedInfo extends EpiDataMetaInfo {
maxIssue: Date;
maxIssueWeek: EpiWeek;
minTime: Date;
minWeek: EpiWeek;
maxTime: Date;
maxWeek: EpiWeek;
timeFrame: TimeFrame;
}

function parse(d: EpiDataMetaInfo): EpiDataMetaParsedInfo {
const minTime = parseAPIDateAndWeek(d.min_time);
const maxTime = parseAPIDateAndWeek(d.max_time);
Expand Down Expand Up @@ -92,11 +81,6 @@ function toValueDomain(
];
}

export interface SensorLike {
id: string;
signal: string;
}

function generateCredits(license: EpiDataMetaSourceInfo['license']) {
if (!license) {
return 'We are happy for you to use this data in products and publications.';
Expand All @@ -108,19 +92,14 @@ function generateCredits(license: EpiDataMetaSourceInfo['license']) {
return parseInline(license);
}

export interface SensorSensor extends Sensor {
meta: EpiDataMetaParsedInfo;
active: boolean;
}

export interface SensorSource
extends Readonly<Omit<EpiDataMetaSourceInfo, 'signals' | 'reference_signal' | 'db_source'>> {
readonly sensors: readonly SensorSensor[];
readonly referenceSensor?: SensorSensor;
readonly sensors: readonly Sensor[];
readonly referenceSensor?: Sensor;
readonly credits: string;
}

function findRawSignal(sensor: SensorSensor, sensors: SensorSensor[]) {
function findRawSignal(sensor: Sensor, sensors: Sensor[]) {
if (!sensor.meta.is_smoothed) {
return undefined;
}
Expand All @@ -135,7 +114,7 @@ function findRawSignal(sensor: SensorSensor, sensors: SensorSensor[]) {
);
return raw;
}
function findRawCumulativeSignal(sensor: SensorSensor, sensors: SensorSensor[]) {
function findRawCumulativeSignal(sensor: Sensor, sensors: Sensor[]) {
if (sensor.meta.is_cumulative) {
return undefined;
}
Expand All @@ -152,16 +131,16 @@ function findRawCumulativeSignal(sensor: SensorSensor, sensors: SensorSensor[])
}

function deriveMetaSensors(metadata: EpiDataMetaSourceInfo[]): {
list: SensorSensor[];
map: Map<string, SensorSensor>;
list: Sensor[];
map: Map<string, Sensor>;
sources: SensorSource[];
} {
const sources = metadata
.map((sm): SensorSource => {
const credits = generateCredits(sm.license);
const sensors: SensorSensor[] = sm.signals.map((m) => {
const sensors: Sensor[] = sm.signals.map((m) => {
const parsed = parse(m);
const s: SensorSensor = {
const s: Sensor = {
key: toKey(m.source, m.signal),
active: m.active,
id: m.source,
Expand Down Expand Up @@ -230,8 +209,8 @@ function deriveMetaSensors(metadata: EpiDataMetaSourceInfo[]): {
}

export class MetaDataManager {
private readonly lookup: ReadonlyMap<string, SensorSensor>;
readonly metaSensors: readonly SensorSensor[];
private readonly lookup: ReadonlyMap<string, Sensor>;
readonly metaSensors: readonly Sensor[];
readonly metaSources: readonly SensorSource[];

constructor(metadata: EpiDataMetaSourceInfo[]) {
Expand All @@ -257,7 +236,7 @@ export class MetaDataManager {
return s ? this.metaSources.find((d) => d.source === s.id) ?? null : null;
}

getSensor(sensor: SensorLike | string): SensorSensor | null {
getSensor(sensor: SensorLike | string): Sensor | null {
const r = this.lookup.get(typeof sensor === 'string' ? sensor : toKey(sensor.id, sensor.signal));
return r ?? null;
}
Expand All @@ -277,7 +256,7 @@ export class MetaDataManager {
return extractStats(sensor.signal, entry, level);
}

getTimeFrame(sensor: SensorLike): TimeFrame {
getTimeFrame(sensor: string | SensorLike): TimeFrame {
const entry = this.getMetaData(sensor);
if (!entry) {
return ALL_TIME_FRAME;
Expand Down
25 changes: 21 additions & 4 deletions src/data/sensor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { formatter, formatSpecifiers } from '../formats';
import { interpolateBuPu, interpolateYlGnBu, interpolateYlOrRd } from 'd3-scale-chromatic';
import { getDataSource } from './dataSourceLookup';
import { defaultCountyRegion, defaultStateRegion, nationInfo, Region, RegionLevel } from './regions';
import type { SignalCategory, SignalFormat, SignalHighValuesAre } from './api';
import type { EpiDataMetaInfo, SignalCategory, SignalFormat, SignalHighValuesAre } from './api';
import { isArray } from './apimodel';
import type { EpiWeek } from './EpiWeek';
import type { TimeFrame } from './TimeFrame';

export const sensorTypes: { id: SignalCategory; label: string }[] = [
{
Expand All @@ -24,7 +26,25 @@ export const sensorTypes: { id: SignalCategory; label: string }[] = [
},
];

export interface SensorLike {
id: string;
signal: string;
}

export interface EpiDataMetaParsedInfo extends EpiDataMetaInfo {
maxIssue: Date;
maxIssueWeek: EpiWeek;
minTime: Date;
minWeek: EpiWeek;
maxTime: Date;
maxWeek: EpiWeek;
timeFrame: TimeFrame;
}

export interface Sensor {
meta: EpiDataMetaParsedInfo;
active: boolean;

readonly key: string; // id:signal
readonly id: string; // data source
readonly signal: string;
Expand Down Expand Up @@ -58,9 +78,6 @@ export interface Sensor {

readonly formatSpecifier: string;
formatValue(v?: number | null, enforceSign?: boolean): string;

readonly highlight?: string[];
readonly linkFrom?: string[];
}

function determineHighValuesAre(sensor: {
Expand Down
44 changes: 24 additions & 20 deletions src/formats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,17 @@ export function formatPopulation(info: { population?: number }): string {
return info.population.toLocaleString();
}

const f = format(',.1f');
const count = format('~s');
const basePercentFormatter = format('.2%');
export const formatSpecifiers = {
raw: ',.2f',
raw_count: '~s',
count: '~s',
fraction: ',.2f',
percent: '.2f',
per100k: ',.1f',
};

const generic = format(',.1f');
const fractionAsPercentFormatter = format('.2%');
const rawFormatter = format(',.2f');

function sign(
Expand All @@ -98,34 +106,30 @@ function sign(
}

export function formatValue(value?: number | null, enforceSign = false): string {
return sign(value, f, enforceSign);
}
export function formatCount(value?: number | null, enforceSign = false): string {
return sign(value, count, enforceSign);
}
export function formatPercentage(value?: number | null, enforceSign = false): string {
return sign(value, basePercentFormatter, enforceSign, 1 / 100);
return sign(value, generic, enforceSign);
}
export function formatFraction(value?: number | null, enforceSign = false): string {
return sign(value, basePercentFormatter, enforceSign);
return sign(value, fractionAsPercentFormatter, enforceSign);
}
export function formatRawValue(value?: number | null, enforceSign = false): string {
return sign(value, rawFormatter, enforceSign);
}

const count = format(formatSpecifiers.count);
const per100Formatter = format(formatSpecifiers.percent);

function formatCount(value?: number | null, enforceSign = false): string {
return sign(value, count, enforceSign);
}
function formatPer100(value?: number | null, enforceSign = false): string {
return sign(value, per100Formatter, enforceSign);
}

export const formatter = {
raw: formatRawValue,
raw_count: formatCount,
count: formatCount,
fraction: formatRawValue,
percent: formatPercentage,
percent: formatPer100,
per100k: formatValue,
};
export const formatSpecifiers = {
raw: ',.2f',
raw_count: '~s',
count: '~s',
fraction: ',.2f',
percent: '.2f',
per100k: ',.1f',
};
3 changes: 1 addition & 2 deletions src/modes/dashboard/configResolver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { parseAPITime } from '../../data';
import type { SensorSensor } from '../../data/meta';
import { getInfoByName } from '../../data/regions';
import { DateParam, RegionLevel, RegionParam, Sensor, SensorParam, TimeFrame } from '../../stores/params';

Expand Down Expand Up @@ -27,7 +26,7 @@ export function resolveSensors(defaultSensor: SensorParam, keys?: readonly strin
if (typeof keys === 'string') {
return [resolveSensor(defaultSensor, keys).value];
}
return keys.map((k) => defaultSensor.manager.getSensor(k)).filter((d): d is SensorSensor => d != null);
return keys.map((k) => defaultSensor.manager.getSensor(k)).filter((d): d is Sensor => d != null);
}

export function resolveRegion(defaultRegion: RegionParam, r?: string): RegionParam {
Expand Down
8 changes: 5 additions & 3 deletions src/modes/dashboard/highlight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import {
nationInfo,
StateInfo,
} from '../../data/regions';
import { sensorMap } from '../../stores';
import { DateParam, Region, RegionLevel, RegionParam, Sensor, SensorParam, TimeFrame } from '../../stores/params';
import { EpiWeek } from '../../data/EpiWeek';
import { metaDataManager } from '../../stores';
import { get } from 'svelte/store';

function isArray<T>(v: T | readonly T[]): v is readonly T[] {
return Array.isArray(v);
Expand Down Expand Up @@ -127,10 +128,11 @@ export class WidgetHighlight {
if (this.sensorIds == null) {
return '*';
}
const m = get(metaDataManager);
if (typeof this.sensorIds === 'string') {
return [sensorMap.get(this.sensorIds)!];
return [m.getSensor(this.sensorIds)!];
}
return [...this.sensorIds].map((d) => sensorMap.get(d)!);
return [...this.sensorIds].map((d) => m.getSensor(d)!);
}

get primarySensor(): Sensor | null {
Expand Down
4 changes: 2 additions & 2 deletions src/modes/dashboard/widgets/SensorTableWidget.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
return Promise.resolve([]);
}
// TODO
return Promise.all(fetcher.fetchNSensors1Region1DateTrend(sensorList, region, date)).then((trends) => {
return sensorList.map((sensor, i) => {
return Promise.all(fetcher.fetchNSensors1Region1DateTrend($sensorList, region, date)).then((trends) => {
return $sensorList.map((sensor, i) => {
const trend = trends[i];
return toRow(sensor.key, sensor.name, sensor, region.value, date.value, trend);
});
Expand Down
1 change: 0 additions & 1 deletion src/modes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export interface Mode {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
component: () => Promise<any>;
anchor?: string;
waitForReady?: boolean;
isGeneric?: boolean;
}

Expand Down
4 changes: 2 additions & 2 deletions src/modes/indicator-status/data.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { timeDay } from 'd3-time';
import { callBackfillAPI, callCoverageAPI, CoverageRow, EpiDataBackfillRow, ParsedCoverageRow } from '../../data/api';
import { GeoPair, SourceSignalPair, TimePair } from '../../data/apimodel';
import type { EpiDataMetaParsedInfo, MetaDataManager, SensorLike, SensorSource } from '../../data/meta';
import type { MetaDataManager, SensorSource } from '../../data/meta';
import { countyInfo, RegionLevel, stateCoreInfo } from '../../data/regions';
import { parseAPIDateAndWeek, parseAPITime, toTimeValue } from '../../data/utils';
import { Sensor, TimeFrame } from '../../stores/params';
import { addNameInfos, EpiDataRow } from '../../data';
import fetchTriple from '../../data/fetchTriple';
import type { RegionInfo } from '../../data/regions';
import { splitDailyWeekly } from '../../data/sensor';
import { EpiDataMetaParsedInfo, SensorLike, splitDailyWeekly } from '../../data/sensor';
import { EpiWeek, weekRange } from '../../data/EpiWeek';

export interface SourceData extends SensorSource {
Expand Down
2 changes: 1 addition & 1 deletion src/modes/indicator/IndicatorCorrelation.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
});
}

$: compareTo = sensorList.filter((d) => d.key !== sensor.key && d.isWeeklySignal === sensor.isWeeklySignal);
$: compareTo = $sensorList.filter((d) => d.key !== sensor.key && d.isWeeklySignal === sensor.isWeeklySignal);
$: otherSensors = buildTableData(sensor, compareTo, region, date);

function switchMode(sensor) {
Expand Down
Loading