From 3b2a3d781ff59bc400ee8ac7d2887e5d52376373 Mon Sep 17 00:00:00 2001 From: AP <116002914+VWSCoronaDashboard28@users.noreply.github.com> Date: Fri, 23 Dec 2022 12:49:49 +0100 Subject: [PATCH] Feature/COR-1262-infection-radar-main (#4556) * feat: added schema for selftest_overall * fix: changed the naming * feat(infection-radar-data): Update the schema based on requirement for date ranges. * feat(self-test-schema): Adjust the schema property based on discussion on COR-1262 ticket. * feature(infection-radar-schema): Update schema to so validate script does not fail. * feat(infection-radar): added the infection radar without the timeline * feat(infection-radar-timeline): Implement a timeline for the new graph. * feat(infection-radar): Fix casing typo. Co-authored-by: VWSCoronaDashboard21 Co-authored-by: VWSCoronaDashboard28 --- docs/timeline-events.md | 11 +++-- packages/app/schema/nl/__index.json | 4 ++ packages/app/schema/nl/self_test_overall.json | 40 ++++++++++++++++ .../time-series-chart/logic/legend.tsx | 46 ++++--------------- .../landelijk/positief-geteste-mensen.tsx | 44 ++++++++++++++++-- packages/cms/src/data/data-structure.ts | 5 +- .../cms/src/elements/schemas/shared/index.ts | 1 + packages/cms/src/lokalize/key-mutations.csv | 5 ++ packages/common/src/types/data.ts | 12 +++++ 9 files changed, 121 insertions(+), 47 deletions(-) create mode 100644 packages/app/schema/nl/self_test_overall.json diff --git a/docs/timeline-events.md b/docs/timeline-events.md index 539af29980..6fb81bb608 100644 --- a/docs/timeline-events.md +++ b/docs/timeline-events.md @@ -1,4 +1,4 @@ -# Timeline Events +# Timelines && Timeline Events All time-series charts can optionally render events on a timeline to illustrate and describe certain parts of the data. @@ -6,13 +6,13 @@ and describe certain parts of the data. These events are managed via the CMS. To enable and configure events for a chart, two things need to -happen. First we need to add the chart instance as an "element" to the CMS +happen. First we need to add the chart instance as an `element` to the CMS dataset, and secondly we need to query the element data in the page where the chart is used, and pass the results to the component. ## CMS Data Elements -The "elements" section in the CMS provides a flexible way to connect any sort of +The `elements` section in the CMS provides a flexible way to connect any sort of configuration to a data element on one of the pages. Unlike the various pages defined in the CMS, the elements works as a flat collection of objects that each point to a unique data element in the app via a combination of `scope`, `element type`, `metric name` and optionally a `metric property`. @@ -27,13 +27,16 @@ we have an element with id `nl__tested_overall__time_series`. ## Adding An Element -Adding a new element is only possible via a command-line interface. +Adding a new element is **only** possible via a command-line interface. This script can be run using `packages/cms/yarn elements:add`. The script is self-explanatory as it allows the user to select the data item using a series of selection lists that are generated from the schema's. +NOTE: When you use this script, the element will be created in both development +and production datasets. + ## Query Elements Data Data for all elements on the pages is fetched together as part of the getStaticProps diff --git a/packages/app/schema/nl/__index.json b/packages/app/schema/nl/__index.json index b955224bdd..1aaad8a88f 100644 --- a/packages/app/schema/nl/__index.json +++ b/packages/app/schema/nl/__index.json @@ -32,6 +32,7 @@ "nursing_home", "proto_name", "reproduction", + "self_test_overall", "sewer", "tested_ggd", "tested_ggd_archived", @@ -241,6 +242,9 @@ }, "variants": { "$ref": "variants.json" + }, + "self_test_overall": { + "$ref": "self_test_overall.json" } }, "$defs": { diff --git a/packages/app/schema/nl/self_test_overall.json b/packages/app/schema/nl/self_test_overall.json new file mode 100644 index 0000000000..6cdc0b9110 --- /dev/null +++ b/packages/app/schema/nl/self_test_overall.json @@ -0,0 +1,40 @@ +{ + "definitions": { + "value": { + "title": "nl_self_test_overall_value", + "type": "object", + "required": ["infected_percentage", "date_start_unix", "date_end_unix", "date_of_insertion_unix"], + "additionalProperties": false, + "properties": { + "infected_percentage": { + "type": ["number", "null"] + }, + "date_start_unix": { + "type": "integer" + }, + "date_end_unix": { + "type": "integer" + }, + "date_of_insertion_unix": { + "type": "integer" + } + } + } + }, + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "nl_self_test_overall", + "type": "object", + "required": ["values", "last_value"], + "additionalProperties": false, + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/definitions/value" + } + }, + "last_value": { + "$ref": "#/definitions/value" + } + } +} diff --git a/packages/app/src/components/time-series-chart/logic/legend.tsx b/packages/app/src/components/time-series-chart/logic/legend.tsx index 48355ff672..f05ef2267c 100644 --- a/packages/app/src/components/time-series-chart/logic/legend.tsx +++ b/packages/app/src/components/time-series-chart/logic/legend.tsx @@ -4,12 +4,7 @@ import { useMemo } from 'react'; import { isDefined } from 'ts-is-present'; import { LegendItem } from '~/components/legend'; import { useIntl } from '~/intl'; -import { - HatchedTimespanAnnotationIcon, - OutOfBoundsIcon, - SeriesIcon, - SolidTimespanAnnotationIcon, -} from '../components'; +import { HatchedTimespanAnnotationIcon, OutOfBoundsIcon, SeriesIcon, SolidTimespanAnnotationIcon } from '../components'; import { TimelineMarker } from '../components/timeline'; import { isVisibleEvent } from '../components/timeline/logic'; import { DataOptions } from './common'; @@ -21,11 +16,10 @@ export function useLegendItems( domain: number[], config: SeriesConfig, dataOptions?: DataOptions, - hasOutofBoudsValues = false, + hasOutOfBoundsValues = false, forceLegend = false ) { - const { timelineEvents, timespanAnnotations, outOfBoundsConfig } = - dataOptions || {}; + const { timelineEvents, timespanAnnotations, outOfBoundsConfig } = dataOptions || {}; const { commonTexts } = useIntl(); return useMemo(() => { @@ -53,7 +47,7 @@ export function useLegendItems( }) .filter(isDefined); - if (hasOutofBoudsValues) { + if (hasOutOfBoundsValues) { legendItems.push({ label: outOfBoundsConfig?.label, shape: 'custom', @@ -66,20 +60,13 @@ export function useLegendItems( */ if (timespanAnnotations) { for (const annotation of timespanAnnotations) { - const isAnnotationVisible = - (first(domain) as number) <= annotation.end && - annotation.start <= (last(domain) as number); + const isAnnotationVisible = (first(domain) as number) <= annotation.end && annotation.start <= (last(domain) as number); if (isAnnotationVisible) { legendItems.push({ label: annotation.label, shape: 'custom', - shapeComponent: - annotation.fill === 'solid' || !isDefined(annotation.fill) ? ( - - ) : ( - - ), + shapeComponent: annotation.fill === 'solid' || !isDefined(annotation.fill) ? : , } as LegendItem); } } @@ -89,9 +76,7 @@ export function useLegendItems( * Add timeline events to the legend */ if (timelineEvents) { - const hasVisibleEvents = timelineEvents.some((x) => - isVisibleEvent(x, domain) - ); + const hasVisibleEvents = timelineEvents.some((x) => isVisibleEvent(x, domain)); if (hasVisibleEvents) { legendItems.push({ @@ -125,22 +110,11 @@ export function useLegendItems( * one) to determine if a legend is required. We only have to render a * legend when there's at least two items. */ - const isLegendRequired = - forceLegend || legendItems.length + splitLegendGroups.length > 1; + const isLegendRequired = forceLegend || legendItems.length + splitLegendGroups.length > 1; return { legendItems: isLegendRequired ? legendItems : undefined, - splitLegendGroups: - splitLegendGroups.length > 0 ? splitLegendGroups : undefined, + splitLegendGroups: splitLegendGroups.length > 0 ? splitLegendGroups : undefined, }; - }, [ - config, - domain, - commonTexts, - timelineEvents, - timespanAnnotations, - outOfBoundsConfig, - hasOutofBoudsValues, - forceLegend, - ]); + }, [config, domain, commonTexts, timelineEvents, timespanAnnotations, outOfBoundsConfig, hasOutOfBoundsValues, forceLegend]); } diff --git a/packages/app/src/pages/landelijk/positief-geteste-mensen.tsx b/packages/app/src/pages/landelijk/positief-geteste-mensen.tsx index 7c7898938b..01da6e2d13 100644 --- a/packages/app/src/pages/landelijk/positief-geteste-mensen.tsx +++ b/packages/app/src/pages/landelijk/positief-geteste-mensen.tsx @@ -41,6 +41,7 @@ export const getStaticProps = createGetStaticProps( 'difference.tested_overall__infected_moving_average', 'difference.tested_overall__infected_per_100k_moving_average', 'g_number', + 'self_test_overall', 'tested_ggd', 'tested_overall', 'tested_per_age_group' @@ -56,8 +57,8 @@ export const getStaticProps = createGetStaticProps( }>((context) => { const { locale } = context; return `{ - "parts": ${getPagePartsQuery('positive_tests_page')}, - "elements": ${getElementsQuery('nl', ['tested_overall', 'tested_ggd', 'tested_per_age_group'], locale)} + "parts": ${getPagePartsQuery('positive_tests_page')}, + "elements": ${getElementsQuery('nl', ['tested_overall', 'tested_ggd', 'tested_per_age_group', 'self_test_overall'], locale)} }`; })(context); return { @@ -94,7 +95,9 @@ const GgdGraphToggle = ({ selectedGgdGraph, onChange }: { selectedGgdGraph: stri function PositivelyTestedPeople(props: StaticProps) { const { pageText, selectedNlData: data, choropleth, content, lastGenerated } = props; - const [confirmedCasesInfectedTimeframe, setConfirmedCasesInfectedTimeframe] = useState(TimeframeOption.ALL); + const [confirmedCasesSelfTestedTimeframe, setConfirmedCasesSelfTestedTimeframe] = useState(TimeframeOption.SIX_MONTHS); + + const [confirmedCasesInfectedTimeframe, setConfirmedCasesInfectedTimeframe] = useState(TimeframeOption.SIX_MONTHS); const [confirmedCasesInfectedPercentageTimeframe, setConfirmedCasesInfectedPercentageTimeframe] = useState(TimeframeOption.ALL); @@ -142,6 +145,40 @@ function PositivelyTestedPeople(props: StaticProps) { articles={content.articles} /> + {/* Infection radar */} + + + + {/* Infection radar end */} + ) { source: textNl.bronnen.rivm, }} timeframeOptions={TimeframeOptionsList} + timeframeInitialValue={TimeframeOption.SIX_MONTHS} onSelectTimeframe={setConfirmedCasesInfectedTimeframe} > > = { hospital_nice_per_age_group: 'Ziekenhuisopnames (per leeftijd)', tested_per_age_group: 'Positief getest (per leeftijd)', elderly_at_home: '70-plussers', + self_test_overall: 'Zelfgerapporteerde positieve coronatestuitslagen', }; function getTitleForMetricName(metricName: MetricName) { diff --git a/packages/cms/src/lokalize/key-mutations.csv b/packages/cms/src/lokalize/key-mutations.csv index 87e2da4f50..58b6fa597e 100644 --- a/packages/cms/src/lokalize/key-mutations.csv +++ b/packages/cms/src/lokalize/key-mutations.csv @@ -14,3 +14,8 @@ timestamp,action,key,document_id,move_to 2022-12-19T14:40:04.536Z,delete,pages.disability_care_page.nl.besmette_locaties.linechart_metric_label,jF33EuwumlGuwav2FD3vZU,__ 2022-12-19T14:40:04.543Z,delete,pages.disability_care_page.nl.besmette_locaties.linechart_titel,jF33EuwumlGuwav2FD3vAK,__ 2022-12-19T14:40:04.552Z,delete,pages.reproduction_page.nl.lineLegendLabel,jF33EuwumlGuwav2FD3aUm,__ +2022-12-21T14:44:25.746Z,add,common.accessibility.charts.confirmed_cases_self_tested_over_time_chart.description,KoLyCpXIGU95m7jfEHTDR2,__ +2022-12-21T14:44:26.728Z,add,common.accessibility.charts.confirmed_cases_self_tested_over_time_chart.label,PKXdxxxKAnTg0F1ZCgYHiV,__ +2022-12-21T16:15:20.048Z,add,pages.positive_tests_page.nl.linechart_self_test_titel,PKXdxxxKAnTg0F1ZCghMkN,__ +2022-12-21T16:15:20.949Z,add,pages.positive_tests_page.nl.linechart_self_test_toelichting,PKXdxxxKAnTg0F1ZCghMnz,__ +2022-12-21T16:15:22.001Z,add,pages.positive_tests_page.nl.linechart_self_test_tooltip_label,KoLyCpXIGU95m7jfEHZKIf,__ diff --git a/packages/common/src/types/data.ts b/packages/common/src/types/data.ts index 020fa4d12a..a713330b7f 100644 --- a/packages/common/src/types/data.ts +++ b/packages/common/src/types/data.ts @@ -280,6 +280,7 @@ export interface Nl { vaccine_coverage_per_age_group_estimated_archived_20220908: NlVaccineCoveragePerAgeGroupEstimatedArchived_20220908Value; vaccine_stock: NlVaccineStock; variants?: NlVariants; + self_test_overall: NlSelfTestOverall; } export interface NlDifference { tested_overall__infected_per_100k_moving_average: DifferenceDecimal; @@ -1134,12 +1135,23 @@ export interface NlVariantsVariantValue { date_of_insertion_unix: number; date_of_report_unix: number; } +export interface NlSelfTestOverall { + values: NlSelfTestOverallValue[]; + last_value: NlSelfTestOverallValue; +} +export interface NlSelfTestOverallValue { + infected_percentage: number | null; + date_start_unix: number; + date_end_unix: number; + date_of_insertion_unix: number; +} export type TopicalIcon = | 'AfstandSporten' | 'AlcoholVerkoop' | 'Archive' | 'Arrow' + | 'ArrowWithIntensity' | 'Arts' | 'Avondklok' | 'BarChart'