From b547c11a86b6ac69b02d4999862fdecf6624bbe3 Mon Sep 17 00:00:00 2001 From: Fabian Engelniederhammer Date: Thu, 7 Mar 2024 15:51:45 +0100 Subject: [PATCH] feat: show count in "prevalence over time" table #18 --- .../prevalence-over-time-bubble-chart.ts | 8 ++--- .../prevalence-over-time-line-bar-chart.ts | 7 ++-- .../components/prevalence-over-time-table.ts | 30 ++++++++++------- .../src/components/prevalence-over-time.ts | 13 ++------ .../src/query/queryPrevalenceOverTime.ts | 33 ++++++++++++------- 5 files changed, 48 insertions(+), 43 deletions(-) diff --git a/components/src/components/prevalence-over-time-bubble-chart.ts b/components/src/components/prevalence-over-time-bubble-chart.ts index ee76492b..d92728f9 100644 --- a/components/src/components/prevalence-over-time-bubble-chart.ts +++ b/components/src/components/prevalence-over-time-bubble-chart.ts @@ -2,18 +2,16 @@ import { customElement, property } from 'lit/decorators.js'; import { html, LitElement } from 'lit'; import { Chart, registerables } from 'chart.js'; import { TemporalGranularity } from '../types'; -import { addUnit, minusTemporal, Temporal } from '../temporal'; +import { addUnit, minusTemporal } from '../temporal'; import { getMinMaxNumber } from '../utils'; import { getYAxisScale, ScaleType } from './container/component-scaling-selector'; import { LogitScale } from './charts/LogitScale'; +import { PrevalenceOverTimeData } from '../query/queryPrevalenceOverTime'; @customElement('gs-prevalence-over-time-bubble-chart') export class PrevalenceOverTimeBubbleChart extends LitElement { @property({ type: Array }) - data: { - displayName: string; - content: { dateRange: Temporal | null; prevalence: number; count: number; total: number }[]; - }[] = []; + data: PrevalenceOverTimeData = []; @property({ type: String }) granularity: TemporalGranularity = 'day'; diff --git a/components/src/components/prevalence-over-time-line-bar-chart.ts b/components/src/components/prevalence-over-time-line-bar-chart.ts index 18ef5e0c..f94f02ba 100644 --- a/components/src/components/prevalence-over-time-line-bar-chart.ts +++ b/components/src/components/prevalence-over-time-line-bar-chart.ts @@ -1,17 +1,14 @@ import { customElement, property } from 'lit/decorators.js'; import { html, LitElement } from 'lit'; import { Chart, ChartConfiguration, registerables } from 'chart.js'; -import { Temporal } from '../temporal'; import { getYAxisScale, ScaleType } from './container/component-scaling-selector'; import { LogitScale } from './charts/LogitScale'; +import { PrevalenceOverTimeData } from '../query/queryPrevalenceOverTime'; @customElement('gs-prevalence-over-time-line-bar-chart') export class PrevalenceOverTimeLineBarChart extends LitElement { @property({ type: Array }) - data: { - displayName: string; - content: { dateRange: Temporal | null; prevalence: number }[]; - }[] = []; + data: PrevalenceOverTimeData = []; @property() type: 'line' | 'bar' = 'bar'; diff --git a/components/src/components/prevalence-over-time-table.ts b/components/src/components/prevalence-over-time-table.ts index b7c231e9..a5e462e2 100644 --- a/components/src/components/prevalence-over-time-table.ts +++ b/components/src/components/prevalence-over-time-table.ts @@ -1,15 +1,13 @@ import { customElement, property } from 'lit/decorators.js'; -import { html, LitElement } from 'lit'; +import { html } from 'lit'; import { TemporalGranularity } from '../types'; -import { Temporal } from '../temporal'; +import { PrevalenceOverTimeData } from '../query/queryPrevalenceOverTime'; +import { TailwindElement } from '../tailwind-element'; @customElement('gs-prevalence-over-time-table') -export class PrevalenceOverTimeTable extends LitElement { +export class PrevalenceOverTimeTable extends TailwindElement() { @property({ type: Array }) - data: { - displayName: string; - content: { dateRange: Temporal | null; prevalence: number }[]; - }[] = []; + data: PrevalenceOverTimeData = []; @property({ type: String }) granularity: TemporalGranularity = 'day'; @@ -20,15 +18,25 @@ export class PrevalenceOverTimeTable extends LitElement { ${this.granularity} - ${this.data.map((d) => html`${d.displayName}`)} + ${this.data.map( + (dataset) => html` + ${dataset.displayName} prevalence + ${dataset.displayName} count + `, + )} ${this.data[0].content.map( - (d, i) => html` + (row, rowNumber) => html` - ${d.dateRange ?? 'Unknown'} - ${this.data.map((d2) => html`${d2.content[i].prevalence.toFixed(4)}`)} + ${row.dateRange ?? 'Unknown'} + ${this.data.map( + (dataset) => html` + ${dataset.content[rowNumber].prevalence.toFixed(4)} + ${dataset.content[rowNumber].count} + `, + )} `, )} diff --git a/components/src/components/prevalence-over-time.ts b/components/src/components/prevalence-over-time.ts index 80079188..e7499946 100644 --- a/components/src/components/prevalence-over-time.ts +++ b/components/src/components/prevalence-over-time.ts @@ -13,8 +13,7 @@ import './container/component-scaling-selector'; import { type NamedLapisFilter, TemporalGranularity } from '../types'; import { lapisContext } from '../lapis-context'; import { consume } from '@lit/context'; -import { queryPrevalenceOverTime } from '../query/queryPrevalenceOverTime'; -import { Temporal } from '../temporal'; +import { PrevalenceOverTimeData, queryPrevalenceOverTime } from '../query/queryPrevalenceOverTime'; import { ScaleType } from './container/component-scaling-selector'; type View = 'bar' | 'line' | 'bubble' | 'table'; @@ -74,15 +73,7 @@ export class PrevalenceOverTime extends LitElement {

Prevalence over time

Loading... `, - complete: ( - // https://github.com/runem/lit-analyzer/issues/154 - // lit-analyzer complains that the generic types don't match, although they do. - // it works, if we resolve the generic type here (TS will complain if the type here is wrong). - data: { - displayName: string; - content: { count: number; prevalence: number; total: number; dateRange: Temporal | null }[]; - }[], - ) => html` + complete: (data: PrevalenceOverTimeData) => html`

Prevalence over time

diff --git a/components/src/query/queryPrevalenceOverTime.ts b/components/src/query/queryPrevalenceOverTime.ts index da18408d..3b0662a8 100644 --- a/components/src/query/queryPrevalenceOverTime.ts +++ b/components/src/query/queryPrevalenceOverTime.ts @@ -9,23 +9,27 @@ import { SlidingOperator } from '../operator/SlidingOperator'; import { DivisionOperator } from '../operator/DivisionOperator'; import { compareTemporal, generateAllInRange, getMinMaxTemporal, Temporal, TemporalCache } from '../temporal'; +// https://github.com/runem/lit-analyzer/issues/154 +// lit-analyzer complains that the generic types don't match in component props when using this type when inferred by +// Typescript, although they do. +// It works, if we resolve the generic type here manually. +export type PrevalenceOverTimeData = { + displayName: string; + content: { count: number; prevalence: number; total: number; dateRange: Temporal | null }[]; +}[]; + export function queryPrevalenceOverTime( - numerator: NamedLapisFilter | NamedLapisFilter[], - denominator: NamedLapisFilter, + numeratorFilter: NamedLapisFilter | NamedLapisFilter[], + denominatorFilter: NamedLapisFilter, granularity: TemporalGranularity, smoothingWindow: number, lapis: string, signal?: AbortSignal, -) { - const numerators = []; - if (Array.isArray(numerator)) { - numerators.push(...numerator); - } else { - numerators.push(numerator); - } +): Promise { + const numeratorFilters = makeArray(numeratorFilter); - const denominatorData = fetchAndPrepare(denominator, granularity, smoothingWindow); - const subQueries = numerators.map(async (namedLapisFilter) => { + const denominatorData = fetchAndPrepare(denominatorFilter, granularity, smoothingWindow); + const subQueries = numeratorFilters.map(async (namedLapisFilter) => { const numeratorData = fetchAndPrepare(namedLapisFilter, granularity, smoothingWindow); const divide = new DivisionOperator( numeratorData, @@ -45,6 +49,13 @@ export function queryPrevalenceOverTime( return Promise.all(subQueries); } +function makeArray(arrayOrSingleItem: T | T[]) { + if (Array.isArray(arrayOrSingleItem)) { + return arrayOrSingleItem; + } + return [arrayOrSingleItem]; +} + function fetchAndPrepare(filter: LapisFilter, granularity: TemporalGranularity, smoothingWindow: number) { const fetchData = new FetchAggregatedOperator<{ date: string | null;