Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

Commit

Permalink
feat(COR-1705): Infection Radar graph (#4844)
Browse files Browse the repository at this point in the history
* feat(COR-1705): Added the redirects and first parts of the graph

* feat(COR-1705): Second graph implemented

* feat(COR-1705): Finalized the graph settings

* feat(COR-1705): PR comment resolution

* feat(COR-1705): PR contingency work

* feat(COR-1705): Corrected import for infection-radar-per-age-group

* feat(COR-1705): Fixed key mutations issues

* feat(COR-1705): Lokalize key issue fix

* feat(COR-1705): PR comment resolution

* feat(COR-1705): Added timeline series for the Infection Radar graph

* feat(COR-1705): key-mutations updated

* feat(COR-1705): Added Lokalize keys

* feat(COR-1705): Deleted batch of keys that don't show up in CMS

* feat(COR-1705): Screenreader key update
  • Loading branch information
VWSCoronaDashboard30 authored Aug 25, 2023
1 parent 48d1fc2 commit 089307c
Show file tree
Hide file tree
Showing 15 changed files with 348 additions and 16 deletions.
4 changes: 4 additions & 0 deletions packages/app/schema/nl/__index.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"name",
"proto_name",
"self_test_overall",
"infectionradar_symptoms_trend_per_age_group_weekly",
"sewer",
"vaccine_campaigns",
"vaccine_administered",
Expand Down Expand Up @@ -116,6 +117,9 @@
},
"self_test_overall": {
"$ref": "self_test_overall.json"
},
"infectionradar_symptoms_trend_per_age_group_weekly": {
"$ref": "infectionradar_symptoms_trend_per_age_group_weekly.json"
}
},
"$defs": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "nl_infectionradar_symptoms_trend_per_age_group_weekly",
"type": "object",
"properties": {
"values": {
"type": "array",
"items": {
"$ref": "#/definitions/value"
}
},
"last_value": {
"$ref": "#/definitions/value"
}
},
"required": ["values", "last_value"],
"additionalProperties": false,
"definitions": {
"value": {
"title": "nl_infectionradar_symptoms_trend_per_age_group_weekly_value",
"type": "object",
"properties": {
"date_of_insertion_unix": {
"type": "integer"
},
"date_of_report_unix": {
"type": "integer"
},
"date_start_unix": {
"type": "integer"
},
"date_end_unix": {
"type": "integer"
},
"percentage_0_24": {
"type": "number"
},
"percentage_25_39": {
"type": "number"
},
"percentage_40_49": {
"type": "number"
},
"percentage_50_59": {
"type": "number"
},
"percentage_60_69": {
"type": "number"
},
"percentage_70_plus": {
"type": "number"
},
"percentage_average": {
"type": "number"
}
},
"required": [
"date_of_insertion_unix",
"date_of_report_unix",
"date_start_unix",
"date_end_unix",
"percentage_0_24",
"percentage_25_39",
"percentage_40_49",
"percentage_50_59",
"percentage_60_69",
"percentage_70_plus",
"percentage_average"
],
"additionalProperties": false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { NlInfectionradarSymptomsTrendPerAgeGroupWeeklyValue, TimeframeOption } from '@corona-dashboard/common';
import { Spacer } from '~/components/base';
import { ErrorBoundary } from '~/components/error-boundary';
import { InteractiveLegend, SelectOption } from '~/components/interactive-legend';
import { TimeSeriesChart } from '~/components/time-series-chart';
import { TimelineEventConfig } from '~/components/time-series-chart/components/timeline';
import { TooltipSeriesList } from '~/components/time-series-chart/components/tooltip/tooltip-series-list';
import { LineSeriesDefinition } from '~/components/time-series-chart/logic';
import { useIntl } from '~/intl';
import { SiteText } from '~/locale';
import { space } from '~/style/theme';
import { getBoundaryDateStartUnix } from '~/utils/get-boundary-date-start-unix';
import { replaceVariablesInText } from '~/utils/replace-variables-in-text';
import { AccessibilityDefinition } from '~/utils/use-accessibility-annotations';
import { useBreakpoints } from '~/utils/use-breakpoints';
import { useList } from '~/utils/use-list';
import { BASE_SERIES_CONFIG } from './series-config';

interface InfectionRadarSymptomsPerAgeGroup {
/**
* The mandatory AccessibilityDefinition provides a reference to annotate the
* graph with a label and description.
*/
accessibility: AccessibilityDefinition;
values: NlInfectionradarSymptomsTrendPerAgeGroupWeeklyValue[];
timeframe: TimeframeOption;
timelineEvents?: TimelineEventConfig[];
text: SiteText['pages']['infection_radar_page']['nl'];
}

export function InfectionRadarSymptomsPerAgeGroup({ values, timeframe, accessibility, timelineEvents, text }: InfectionRadarSymptomsPerAgeGroup) {
const { commonTexts } = useIntl();
const { list, toggle, clear } = useList<string>();
const breakpoints = useBreakpoints(true);

const underReportedDateStart = getBoundaryDateStartUnix(values, 7);

/* Enrich config with dynamic data / locale */
const seriesConfig: LineSeriesDefinition<NlInfectionradarSymptomsTrendPerAgeGroupWeeklyValue>[] = BASE_SERIES_CONFIG.map((baseAgeGroup) => {
const label = baseAgeGroup.metricProperty in text.infected_per_age_group.legend ? text.infected_per_age_group.legend[baseAgeGroup.metricProperty] : baseAgeGroup.metricProperty;

const ariaLabel = replaceVariablesInText(commonTexts.aria_labels.age_old, {
age: label,
});

return {
...baseAgeGroup,
hideInLegend: true,
type: 'line',
shape: 'style' in baseAgeGroup ? baseAgeGroup.style : 'line',
label,
ariaLabel,
legendAriaLabel: ariaLabel,
};
});

/**
* Chart:
* - when nothing selected: all items
* - otherwise: selected items + always enabled items
*/
const chartConfig = seriesConfig.filter((item) => list.includes(item.metricProperty) || list.length === 0);

const interactiveLegendOptions: SelectOption[] = seriesConfig;

/* Conditionally let tooltip span over multiple columns */
const hasTwoColumns = list.length === 0 || list.length > 4;

return (
<ErrorBoundary>
<InteractiveLegend helpText={text.infected_per_age_group.legend_help_text} selectOptions={interactiveLegendOptions} selection={list} onToggleItem={toggle} onReset={clear} />
<Spacer marginBottom={space[2]} />
<TimeSeriesChart
forceLegend
accessibility={accessibility}
values={values}
timeframe={timeframe}
seriesConfig={chartConfig}
minHeight={breakpoints.md ? 300 : 250}
formatTooltip={(data) => <TooltipSeriesList data={data} hasTwoColumns={hasTwoColumns} />}
dataOptions={{
isPercentage: true,
valueAnnotation: text.infected_per_age_group.value_annotation,
timespanAnnotations: [
{
start: underReportedDateStart,
end: Infinity,
label: text.infected_per_age_group.line_chart_legend_inaccurate_label,
shortLabel: text.infected_per_age_group.tooltip_labels.inaccurate,
},
],
timelineEvents,
}}
/>
</ErrorBoundary>
);
}
33 changes: 33 additions & 0 deletions packages/app/src/domain/infection_radar/series-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { colors } from '@corona-dashboard/common';

export const BASE_SERIES_CONFIG = [
{
metricProperty: 'percentage_0_24',
color: colors.magenta1,
},
{
metricProperty: 'percentage_25_39',
color: colors.green3,
},
{
metricProperty: 'percentage_40_49',
color: colors.yellow3,
},
{
metricProperty: 'percentage_50_59',
color: colors.blue6,
},
{
metricProperty: 'percentage_60_69',
color: colors.orange1,
},
{
metricProperty: 'percentage_70_plus',
color: colors.magenta5,
},
{
metricProperty: 'percentage_average',
color: colors.gray5,
style: 'dashed',
},
] as const;
2 changes: 1 addition & 1 deletion packages/app/src/domain/layout/logic/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export type NlItemKeys =
| 'positive_tests'
| 'reproduction_number'
| 'sewage_measurement'
| 'tests'
| 'infection_radar'
| 'vaccinations'
| 'variants';

Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/domain/layout/logic/use-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const mapKeysToReverseRouter = {
positive_tests: 'positieveTesten',
reproduction_number: 'reproductiegetal',
sewage_measurement: 'rioolwater',
tests: 'testen',
infection_radar: 'infectieradar',
vaccinations: 'vaccinaties',
variants: 'varianten',
} as const;
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/domain/layout/nl-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function NlLayout(props: NlLayoutProps) {
const items = useSidebar({
layout: 'nl',
map: [
['development_of_the_virus', ['sewage_measurement', 'tests', 'variants', 'mortality']],
['development_of_the_virus', ['sewage_measurement', 'infection_radar', 'variants', 'mortality']],
['consequences_for_healthcare', ['hospitals_and_care', 'patients']],
['actions_to_take', ['vaccinations']],
[
Expand Down
6 changes: 6 additions & 0 deletions packages/app/src/next-config/redirects/redirects.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ async function redirects() {
destination: '/gemeente/:code/positieve-testen',
permanent: true,
},
// Redirect for COR-1705
{
source: '/landelijk/testen',
destination: '/landelijk/infectieradar',
permanent: true,
},
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { TileList } from '~/components/tile-list';
import { TimeSeriesChart } from '~/components/time-series-chart';
import { Layout } from '~/domain/layout/layout';
import { NlLayout } from '~/domain/layout/nl-layout';
import { InfectionRadarSymptomsPerAgeGroup } from '~/domain/infection_radar/infection-radar-per-age-group';
import { useIntl } from '~/intl';
import { Languages, SiteText } from '~/locale';
import { ElementsQueryResult, getElementsQuery, getTimelineEvents } from '~/queries/get-elements-query';
Expand All @@ -22,46 +23,48 @@ import { useDynamicLokalizeTexts } from '~/utils/cms/use-dynamic-lokalize-texts'
import { getLastInsertionDateOfPage } from '~/utils/get-last-insertion-date-of-page';
import { getPageInformationHeaderContent } from '~/utils/get-page-information-header-content';

const pageMetrics = ['self_test_overall'];
const pageMetrics = ['self_test_overall', 'infection_radar_symptoms_per_age_group'];

const selectLokalizeTexts = (siteText: SiteText) => ({
metadataTexts: siteText.pages.topical_page.nl.nationaal_metadata,
textNl: siteText.pages.tests_page.nl,
textNl: siteText.pages.infection_radar_page.nl,
});

type LokalizeTexts = ReturnType<typeof selectLokalizeTexts>;

export const getStaticProps = createGetStaticProps(
({ locale }: { locale: keyof Languages }) => getLokalizeTexts(selectLokalizeTexts, locale),
getLastGeneratedDate,
selectNlData('self_test_overall'),
selectNlData('self_test_overall', 'infectionradar_symptoms_trend_per_age_group_weekly'),
async (context: GetStaticPropsContext) => {
const { content } = await createGetContent<{
parts: PagePartQueryResult<ArticleParts>;
elements: ElementsQueryResult;
}>((context) => {
const { locale } = context;
return `{
"parts": ${getPagePartsQuery('tests_page')},
"elements": ${getElementsQuery('nl', ['self_test_overall'], locale)}
"parts": ${getPagePartsQuery('infection_radar_page')},
"elements": ${getElementsQuery('nl', ['self_test_overall', 'infectionradar_symptoms_trend_per_age_group_weekly'], locale)}
}`;
})(context);
return {
content: {
articles: getArticleParts(content.parts.pageParts, 'testsPageArticles'),
faqs: getFaqParts(content.parts.pageParts, 'testsPageFAQs'),
dataExplained: getDataExplainedParts(content.parts.pageParts, 'testsPageDataExplained'),
articles: getArticleParts(content.parts.pageParts, 'infectionRadarPageArticles'),
faqs: getFaqParts(content.parts.pageParts, 'infectionRadarPageFAQs'),
dataExplained: getDataExplainedParts(content.parts.pageParts, 'infectionRadarPageDataExplained'),
elements: content.elements,
},
};
}
);

const Tests = (props: StaticProps<typeof getStaticProps>) => {
const InfectionRadar = (props: StaticProps<typeof getStaticProps>) => {
const { pageText, selectedNlData: data, content, lastGenerated } = props;

const [confirmedCasesSelfTestedTimeframe, setConfirmedCasesSelfTestedTimeframe] = useState<TimeframeOption>(TimeframeOption.SIX_MONTHS);

const [confirmedCasesCovidSymptomsPerAgeTimeFrame, setConfirmedCasesCovidSymptomsPerAgeTimeFrame] = useState<TimeframeOption>(TimeframeOption.THREE_MONTHS);

const { commonTexts } = useIntl();

const { metadataTexts, textNl } = useDynamicLokalizeTexts<LokalizeTexts>(pageText, selectLokalizeTexts);
Expand All @@ -80,7 +83,7 @@ const Tests = (props: StaticProps<typeof getStaticProps>) => {
<TileList>
<PageInformationBlock
category={commonTexts.sidebar.categories.development_of_the_virus.title}
screenReaderCategory={commonTexts.sidebar.metrics.positive_tests.title}
screenReaderCategory={commonTexts.sidebar.metrics.infection_radar.title}
title={textNl.title}
icon={<GgdTesten aria-hidden="true" />}
description={textNl.description}
Expand Down Expand Up @@ -131,6 +134,27 @@ const Tests = (props: StaticProps<typeof getStaticProps>) => {
/>
</ChartTile>

<ChartTile
title={textNl.chart_infection_radar_age_groups.title}
description={textNl.chart_infection_radar_age_groups.description}
timeframeOptions={TimeframeOptionsList}
timeframeInitialValue={confirmedCasesCovidSymptomsPerAgeTimeFrame}
metadata={{
source: textNl.chart_infection_radar_age_groups.source.rivm,
}}
onSelectTimeframe={setConfirmedCasesCovidSymptomsPerAgeTimeFrame}
>
<InfectionRadarSymptomsPerAgeGroup
accessibility={{
key: 'reported_cases_covid_19_like_symptoms_time_chart',
}}
values={data.infectionradar_symptoms_trend_per_age_group_weekly.values}
timeframe={confirmedCasesCovidSymptomsPerAgeTimeFrame}
timelineEvents={getTimelineEvents(content.elements.timeSeries, 'infectionradar_symptoms_trend_per_age_group_weekly')}
text={textNl}
/>
</ChartTile>

{content.faqs && content.faqs.questions?.length > 0 && <PageFaqTile questions={content.faqs.questions} title={content.faqs.sectionTitle} />}

{content.articles && content.articles.articles?.length > 0 && (
Expand All @@ -144,4 +168,4 @@ const Tests = (props: StaticProps<typeof getStaticProps>) => {
);
};

export default Tests;
export default InfectionRadar;
2 changes: 1 addition & 1 deletion packages/app/src/types/cms.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export type PageIdentifier =
| 'reproduction_page'
| 'sewer_page'
| 'situations_page'
| 'tests_page'
| 'infection_radar_page'
| 'topical_page'
| 'vaccinations_page'
| 'variants_page'
Expand Down
Loading

0 comments on commit 089307c

Please sign in to comment.