@genspectrum/dashboard-components 0.6.9 → 0.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +3 -3
- package/dist/dashboard-components.js +366 -75
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +2 -2
- package/dist/style.css +65 -1
- package/package.json +4 -1
- package/src/preact/aggregatedData/aggregate.tsx +14 -4
- package/src/preact/components/chart.tsx +14 -1
- package/src/preact/components/fullscreen.tsx +57 -0
- package/src/preact/components/info.tsx +88 -1
- package/src/preact/components/resize-container.tsx +5 -1
- package/src/preact/mutationComparison/mutation-comparison.tsx +2 -0
- package/src/preact/mutations/mutations.tsx +34 -2
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_byDayOverall.json +4726 -0
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byMonthOverall.json +11143 -0
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byWeekOverall.json +9154 -0
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +66 -22
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +16 -7
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +17 -0
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +28 -4
- package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +42 -6
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +2 -1
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +46 -53
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +2 -0
- package/src/query/queryMutationsOverTime.ts +10 -1
- package/src/web-components/visualization/gs-mutations-over-time.stories.ts +51 -1
- package/src/web-components/visualization/gs-prevalence-over-time.tsx +2 -4
|
@@ -61,38 +61,82 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
describe('filterProportion', () => {
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
const belowFilter = 0.1;
|
|
65
|
+
const atFilterMin = 0.2;
|
|
66
|
+
const inFilter = 0.5;
|
|
67
|
+
const atFilterMax = 0.9;
|
|
68
|
+
const aboveFilter = 0.99;
|
|
69
|
+
const proportionInterval = { min: atFilterMin, max: atFilterMax };
|
|
66
70
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const proportionInterval = { min: 0.2, max: 0.9 };
|
|
71
|
+
const someSubstitution = new Substitution('someSegment', 'A', 'T', 123);
|
|
72
|
+
const someOtherMutation = new Substitution('someOtherSegment', 'A', 'G', 9);
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
data
|
|
73
|
-
data.set(someSubstitution, yearMonthDay('2021-02-02'), aboveFilter);
|
|
74
|
+
it('should remove mutations where overall proportion is below filter', () => {
|
|
75
|
+
const data = getMutationOverTimeData();
|
|
74
76
|
|
|
75
|
-
filterProportion(data, proportionInterval);
|
|
77
|
+
filterProportion(data, getOverallMutationData(belowFilter), proportionInterval);
|
|
76
78
|
|
|
77
|
-
expect(data.getAsArray({ count: 0, proportion: 0 })
|
|
79
|
+
expect(data.getAsArray({ count: 0, proportion: 0 })).to.toHaveLength(0);
|
|
78
80
|
});
|
|
79
81
|
|
|
80
|
-
it('should
|
|
81
|
-
const data =
|
|
82
|
+
it('should remove mutations where overall proportion is above filter', () => {
|
|
83
|
+
const data = getMutationOverTimeData();
|
|
84
|
+
|
|
85
|
+
filterProportion(data, getOverallMutationData(aboveFilter), proportionInterval);
|
|
86
|
+
|
|
87
|
+
expect(data.getAsArray({ count: 0, proportion: 0 })).to.toHaveLength(0);
|
|
88
|
+
});
|
|
82
89
|
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
const inFilter = { count: 5, proportion: 0.5 };
|
|
86
|
-
const proportionInterval = { min: 0.2, max: 0.9 };
|
|
90
|
+
it('should remove mutations where overall proportion is missing', () => {
|
|
91
|
+
const data = getMutationOverTimeData();
|
|
87
92
|
|
|
88
|
-
|
|
89
|
-
data.set(someSubstitution, yearMonthDay('2021-01-01'), belowFilter);
|
|
90
|
-
data.set(someSubstitution, yearMonthDay('2021-02-02'), aboveFilter);
|
|
91
|
-
data.set(someSubstitution, yearMonthDay('2021-03-03'), inFilter);
|
|
93
|
+
filterProportion(data, getOverallMutationData(aboveFilter, someOtherMutation), proportionInterval);
|
|
92
94
|
|
|
93
|
-
|
|
95
|
+
expect(data.getAsArray({ count: 0, proportion: 0 })).to.toHaveLength(0);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should not remove mutation where overall proportion is at lower border of filter', () => {
|
|
99
|
+
const data = getMutationOverTimeData();
|
|
100
|
+
|
|
101
|
+
filterProportion(data, getOverallMutationData(inFilter), proportionInterval);
|
|
94
102
|
|
|
95
|
-
expect(data.getRow(someSubstitution, { count: 0, proportion: 0 })
|
|
103
|
+
expect(data.getRow(someSubstitution, { count: 0, proportion: 0 })).to.toHaveLength(2);
|
|
96
104
|
});
|
|
105
|
+
|
|
106
|
+
it('should not remove mutation where overall proportion is within filter', () => {
|
|
107
|
+
const data = getMutationOverTimeData();
|
|
108
|
+
|
|
109
|
+
filterProportion(data, getOverallMutationData(inFilter), proportionInterval);
|
|
110
|
+
|
|
111
|
+
expect(data.getRow(someSubstitution, { count: 0, proportion: 0 })).to.toHaveLength(2);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should not remove mutation where overall proportion is at upper border of filter', () => {
|
|
115
|
+
const data = getMutationOverTimeData();
|
|
116
|
+
|
|
117
|
+
filterProportion(data, getOverallMutationData(inFilter), proportionInterval);
|
|
118
|
+
|
|
119
|
+
expect(data.getRow(someSubstitution, { count: 0, proportion: 0 })).to.toHaveLength(2);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
function getMutationOverTimeData() {
|
|
123
|
+
const data = new Map2d<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>();
|
|
124
|
+
data.set(someSubstitution, yearMonthDay('2021-01-01'), { count: 1, proportion: 0.1 });
|
|
125
|
+
data.set(someSubstitution, yearMonthDay('2021-02-02'), { count: 99, proportion: 0.99 });
|
|
126
|
+
return data;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function getOverallMutationData(proportion: number = 0.1, mutation: Substitution = someSubstitution) {
|
|
130
|
+
return {
|
|
131
|
+
content: [
|
|
132
|
+
{
|
|
133
|
+
type: 'substitution' as const,
|
|
134
|
+
count: -1,
|
|
135
|
+
mutation,
|
|
136
|
+
proportion,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
97
141
|
});
|
|
98
142
|
});
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { type Dataset } from '../../operator/Dataset';
|
|
1
2
|
import { type MutationOverTimeDataGroupedByMutation } from '../../query/queryMutationsOverTime';
|
|
3
|
+
import { type DeletionEntry, type SubstitutionEntry } from '../../types';
|
|
2
4
|
import type { DisplayedSegment } from '../components/SegmentSelector';
|
|
3
5
|
import type { DisplayedMutationType } from '../components/mutation-type-selector';
|
|
4
6
|
|
|
5
7
|
export function getFilteredMutationOverTimeData(
|
|
6
8
|
data: MutationOverTimeDataGroupedByMutation,
|
|
9
|
+
overallMutationData: Dataset<SubstitutionEntry | DeletionEntry>,
|
|
7
10
|
displayedSegments: DisplayedSegment[],
|
|
8
11
|
displayedMutationTypes: DisplayedMutationType[],
|
|
9
12
|
proportionInterval: { min: number; max: number },
|
|
@@ -11,7 +14,7 @@ export function getFilteredMutationOverTimeData(
|
|
|
11
14
|
const filteredData = data.copy();
|
|
12
15
|
filterDisplayedSegments(displayedSegments, filteredData);
|
|
13
16
|
filterMutationTypes(displayedMutationTypes, filteredData);
|
|
14
|
-
filterProportion(filteredData, proportionInterval);
|
|
17
|
+
filterProportion(filteredData, overallMutationData, proportionInterval);
|
|
15
18
|
|
|
16
19
|
return filteredData;
|
|
17
20
|
}
|
|
@@ -48,18 +51,24 @@ export function filterMutationTypes(
|
|
|
48
51
|
|
|
49
52
|
export function filterProportion(
|
|
50
53
|
data: MutationOverTimeDataGroupedByMutation,
|
|
54
|
+
overallMutationData: Dataset<SubstitutionEntry | DeletionEntry>,
|
|
51
55
|
proportionInterval: {
|
|
52
56
|
min: number;
|
|
53
57
|
max: number;
|
|
54
58
|
},
|
|
55
59
|
) {
|
|
60
|
+
const overallProportionsByMutation = overallMutationData.content.reduce(
|
|
61
|
+
(acc, { mutation, proportion }) => ({
|
|
62
|
+
...acc,
|
|
63
|
+
[mutation.toString()]: proportion,
|
|
64
|
+
}),
|
|
65
|
+
{} as Record<string, number>,
|
|
66
|
+
);
|
|
67
|
+
|
|
56
68
|
data.getFirstAxisKeys().forEach((mutation) => {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
(value) => value.proportion >= proportionInterval.min && value.proportion <= proportionInterval.max,
|
|
61
|
-
)
|
|
62
|
-
) {
|
|
69
|
+
const overallProportion = overallProportionsByMutation[mutation.toString()] || -1;
|
|
70
|
+
|
|
71
|
+
if (overallProportion < proportionInterval.min || overallProportion > proportionInterval.max) {
|
|
63
72
|
data.deleteRow(mutation);
|
|
64
73
|
}
|
|
65
74
|
});
|
|
@@ -14,6 +14,7 @@ import nucleotideMutation_tooManyMutations from './__mockData__/nucleotideMutati
|
|
|
14
14
|
import { MutationsOverTime, type MutationsOverTimeProps } from './mutations-over-time';
|
|
15
15
|
import { AGGREGATED_ENDPOINT, LAPIS_URL, NUCLEOTIDE_MUTATIONS_ENDPOINT } from '../../constants';
|
|
16
16
|
import referenceGenome from '../../lapisApi/__mockData__/referenceGenome.json';
|
|
17
|
+
import nucleotideMutations_byMonthOverall from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_byMonthOverall.json';
|
|
17
18
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
18
19
|
import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
|
|
19
20
|
|
|
@@ -90,6 +91,22 @@ export const Default: StoryObj<MutationsOverTimeProps> = {
|
|
|
90
91
|
body: aggregated_date,
|
|
91
92
|
},
|
|
92
93
|
},
|
|
94
|
+
{
|
|
95
|
+
matcher: {
|
|
96
|
+
name: 'nucleotideMutations_overall',
|
|
97
|
+
url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
|
|
98
|
+
body: {
|
|
99
|
+
pangoLineage: 'JN.1*',
|
|
100
|
+
dateFrom: '2024-01-15',
|
|
101
|
+
dateTo: '2024-07-10',
|
|
102
|
+
minProportion: 0.001,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
response: {
|
|
106
|
+
status: 200,
|
|
107
|
+
body: nucleotideMutations_byMonthOverall,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
93
110
|
{
|
|
94
111
|
matcher: {
|
|
95
112
|
name: 'nucleotideMutations_01',
|
|
@@ -3,11 +3,19 @@ import { type Dispatch, type StateUpdater, useContext, useMemo, useState } from
|
|
|
3
3
|
|
|
4
4
|
import { getFilteredMutationOverTimeData } from './getFilteredMutationsOverTimeData';
|
|
5
5
|
import MutationsOverTimeGrid from './mutations-over-time-grid';
|
|
6
|
+
import { type Dataset } from '../../operator/Dataset';
|
|
6
7
|
import {
|
|
7
8
|
type MutationOverTimeDataGroupedByMutation,
|
|
8
9
|
queryMutationsOverTimeData,
|
|
10
|
+
queryOverallMutationData,
|
|
9
11
|
} from '../../query/queryMutationsOverTime';
|
|
10
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
type DeletionEntry,
|
|
14
|
+
type LapisFilter,
|
|
15
|
+
type SequenceType,
|
|
16
|
+
type SubstitutionEntry,
|
|
17
|
+
type TemporalGranularity,
|
|
18
|
+
} from '../../types';
|
|
11
19
|
import { compareTemporal } from '../../utils/temporal';
|
|
12
20
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
13
21
|
import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '../components/SegmentSelector';
|
|
@@ -16,6 +24,7 @@ import { ColorScaleSelectorDropdown } from '../components/color-scale-selector-d
|
|
|
16
24
|
import { CsvDownloadButton } from '../components/csv-download-button';
|
|
17
25
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
18
26
|
import { ErrorDisplay } from '../components/error-display';
|
|
27
|
+
import { Fullscreen } from '../components/fullscreen';
|
|
19
28
|
import Info from '../components/info';
|
|
20
29
|
import { LoadingDisplay } from '../components/loading-display';
|
|
21
30
|
import { type DisplayedMutationType, MutationTypeSelector } from '../components/mutation-type-selector';
|
|
@@ -63,7 +72,11 @@ export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeInnerPro
|
|
|
63
72
|
}) => {
|
|
64
73
|
const lapis = useContext(LapisUrlContext);
|
|
65
74
|
const { data, error, isLoading } = useQuery(async () => {
|
|
66
|
-
|
|
75
|
+
const [mutationOverTimeData, overallMutationData] = await Promise.all([
|
|
76
|
+
queryMutationsOverTimeData(lapisFilter, sequenceType, lapis, lapisDateField, granularity),
|
|
77
|
+
queryOverallMutationData(lapisFilter, sequenceType, lapis),
|
|
78
|
+
]);
|
|
79
|
+
return { mutationOverTimeData, overallMutationData };
|
|
67
80
|
}, [lapisFilter, sequenceType, lapis, granularity, lapisDateField]);
|
|
68
81
|
|
|
69
82
|
if (isLoading) {
|
|
@@ -78,19 +91,28 @@ export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeInnerPro
|
|
|
78
91
|
return <NoDataDisplay />;
|
|
79
92
|
}
|
|
80
93
|
|
|
81
|
-
return
|
|
94
|
+
return (
|
|
95
|
+
<MutationsOverTimeTabs
|
|
96
|
+
overallMutationData={data.overallMutationData}
|
|
97
|
+
mutationOverTimeData={data.mutationOverTimeData}
|
|
98
|
+
sequenceType={sequenceType}
|
|
99
|
+
views={views}
|
|
100
|
+
/>
|
|
101
|
+
);
|
|
82
102
|
};
|
|
83
103
|
|
|
84
104
|
type MutationOverTimeTabsProps = {
|
|
85
105
|
mutationOverTimeData: MutationOverTimeDataGroupedByMutation;
|
|
86
106
|
sequenceType: SequenceType;
|
|
87
107
|
views: View[];
|
|
108
|
+
overallMutationData: Dataset<SubstitutionEntry | DeletionEntry>;
|
|
88
109
|
};
|
|
89
110
|
|
|
90
111
|
const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
91
112
|
mutationOverTimeData,
|
|
92
113
|
sequenceType,
|
|
93
114
|
views,
|
|
115
|
+
overallMutationData,
|
|
94
116
|
}) => {
|
|
95
117
|
const [proportionInterval, setProportionInterval] = useState({ min: 0.05, max: 0.9 });
|
|
96
118
|
const [colorScale, setColorScale] = useState<ColorScale>({ min: 0, max: 1, color: 'indigo' });
|
|
@@ -105,11 +127,12 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
|
105
127
|
() =>
|
|
106
128
|
getFilteredMutationOverTimeData(
|
|
107
129
|
mutationOverTimeData,
|
|
130
|
+
overallMutationData,
|
|
108
131
|
displayedSegments,
|
|
109
132
|
displayedMutationTypes,
|
|
110
133
|
proportionInterval,
|
|
111
134
|
),
|
|
112
|
-
[mutationOverTimeData, displayedSegments, displayedMutationTypes, proportionInterval],
|
|
135
|
+
[mutationOverTimeData, overallMutationData, displayedSegments, displayedMutationTypes, proportionInterval],
|
|
113
136
|
);
|
|
114
137
|
|
|
115
138
|
const getTab = (view: View) => {
|
|
@@ -188,6 +211,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
188
211
|
filename='mutations_over_time.csv'
|
|
189
212
|
/>
|
|
190
213
|
<Info>Info for mutations over time</Info>
|
|
214
|
+
<Fullscreen />
|
|
191
215
|
</>
|
|
192
216
|
);
|
|
193
217
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FunctionComponent } from 'preact';
|
|
1
2
|
import { useContext, useState } from 'preact/hooks';
|
|
2
3
|
|
|
3
4
|
import { getNumberOfSequencesOverTimeTableData } from './getNumberOfSequencesOverTimeTableData';
|
|
@@ -13,6 +14,7 @@ import { LapisUrlContext } from '../LapisUrlContext';
|
|
|
13
14
|
import { CsvDownloadButton } from '../components/csv-download-button';
|
|
14
15
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
15
16
|
import { ErrorDisplay } from '../components/error-display';
|
|
17
|
+
import { Fullscreen } from '../components/fullscreen';
|
|
16
18
|
import Info, { InfoHeadline1, InfoParagraph } from '../components/info';
|
|
17
19
|
import { LoadingDisplay } from '../components/loading-display';
|
|
18
20
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
@@ -76,17 +78,32 @@ const NumberSequencesOverTimeInner = ({
|
|
|
76
78
|
return <NoDataDisplay />;
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
return
|
|
81
|
+
return (
|
|
82
|
+
<NumberSequencesOverTimeTabs
|
|
83
|
+
views={views}
|
|
84
|
+
data={data}
|
|
85
|
+
granularity={granularity}
|
|
86
|
+
smoothingWindow={smoothingWindow}
|
|
87
|
+
pageSize={pageSize}
|
|
88
|
+
/>
|
|
89
|
+
);
|
|
80
90
|
};
|
|
81
91
|
|
|
82
92
|
interface NumberSequencesOverTimeTabsProps {
|
|
83
93
|
views: NumberSequencesOverTimeView[];
|
|
84
94
|
data: NumberOfSequencesDatasets;
|
|
85
95
|
granularity: TemporalGranularity;
|
|
96
|
+
smoothingWindow: number;
|
|
86
97
|
pageSize: boolean | number;
|
|
87
98
|
}
|
|
88
99
|
|
|
89
|
-
const NumberSequencesOverTimeTabs = ({
|
|
100
|
+
const NumberSequencesOverTimeTabs = ({
|
|
101
|
+
views,
|
|
102
|
+
data,
|
|
103
|
+
granularity,
|
|
104
|
+
smoothingWindow,
|
|
105
|
+
pageSize,
|
|
106
|
+
}: NumberSequencesOverTimeTabsProps) => {
|
|
90
107
|
const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
|
|
91
108
|
|
|
92
109
|
const getTab = (view: NumberSequencesOverTimeView) => {
|
|
@@ -119,6 +136,7 @@ const NumberSequencesOverTimeTabs = ({ views, data, granularity, pageSize }: Num
|
|
|
119
136
|
activeTab={activeTab}
|
|
120
137
|
data={data}
|
|
121
138
|
granularity={granularity}
|
|
139
|
+
smoothingWindow={smoothingWindow}
|
|
122
140
|
yAxisScaleType={yAxisScaleType}
|
|
123
141
|
setYAxisScaleType={setYAxisScaleType}
|
|
124
142
|
/>
|
|
@@ -133,9 +151,17 @@ interface ToolbarProps {
|
|
|
133
151
|
granularity: TemporalGranularity;
|
|
134
152
|
yAxisScaleType: ScaleType;
|
|
135
153
|
setYAxisScaleType: (scaleType: ScaleType) => void;
|
|
154
|
+
smoothingWindow: number;
|
|
136
155
|
}
|
|
137
156
|
|
|
138
|
-
const Toolbar = ({
|
|
157
|
+
const Toolbar = ({
|
|
158
|
+
activeTab,
|
|
159
|
+
data,
|
|
160
|
+
granularity,
|
|
161
|
+
yAxisScaleType,
|
|
162
|
+
setYAxisScaleType,
|
|
163
|
+
smoothingWindow,
|
|
164
|
+
}: ToolbarProps) => {
|
|
139
165
|
return (
|
|
140
166
|
<>
|
|
141
167
|
{activeTab !== 'Table' && (
|
|
@@ -150,16 +176,26 @@ const Toolbar = ({ activeTab, data, granularity, yAxisScaleType, setYAxisScaleTy
|
|
|
150
176
|
getData={() => getNumberOfSequencesOverTimeTableData(data, granularity)}
|
|
151
177
|
filename='number_of_sequences_over_time.csv'
|
|
152
178
|
/>
|
|
153
|
-
<NumberSequencesOverTimeInfo />
|
|
179
|
+
<NumberSequencesOverTimeInfo granularity={granularity} smoothingWindow={smoothingWindow} />
|
|
180
|
+
<Fullscreen />
|
|
154
181
|
</>
|
|
155
182
|
);
|
|
156
183
|
};
|
|
157
184
|
|
|
158
|
-
|
|
185
|
+
type NumberSequencesOverTimeInfoProps = {
|
|
186
|
+
granularity: TemporalGranularity;
|
|
187
|
+
smoothingWindow: number;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const NumberSequencesOverTimeInfo: FunctionComponent<NumberSequencesOverTimeInfoProps> = ({
|
|
191
|
+
granularity,
|
|
192
|
+
smoothingWindow,
|
|
193
|
+
}) => (
|
|
159
194
|
<Info>
|
|
160
195
|
<InfoHeadline1>Number of sequences over time</InfoHeadline1>
|
|
161
196
|
<InfoParagraph>
|
|
162
|
-
|
|
197
|
+
This presents the number of available sequences of a variant per <b>{granularity}</b>
|
|
198
|
+
{smoothingWindow > 0 && `, smoothed using a ${smoothingWindow}-${granularity} sliding window`}.
|
|
163
199
|
</InfoParagraph>
|
|
164
200
|
</Info>
|
|
165
201
|
);
|
|
@@ -51,7 +51,8 @@ const Template = {
|
|
|
51
51
|
height={args.height}
|
|
52
52
|
lapisDateField={args.lapisDateField}
|
|
53
53
|
pageSize={args.pageSize}
|
|
54
|
-
|
|
54
|
+
yAxisMaxLinear={args.yAxisMaxLinear}
|
|
55
|
+
yAxisMaxLogarithmic={args.yAxisMaxLogarithmic}
|
|
55
56
|
/>
|
|
56
57
|
</LapisUrlContext.Provider>
|
|
57
58
|
),
|
|
@@ -13,25 +13,23 @@ import { ConfidenceIntervalSelector } from '../components/confidence-interval-se
|
|
|
13
13
|
import { CsvDownloadButton } from '../components/csv-download-button';
|
|
14
14
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
15
15
|
import { ErrorDisplay } from '../components/error-display';
|
|
16
|
-
import
|
|
16
|
+
import { Fullscreen } from '../components/fullscreen';
|
|
17
|
+
import Info, { InfoComponentCode, InfoHeadline1, InfoHeadline2, InfoParagraph } from '../components/info';
|
|
17
18
|
import { LoadingDisplay } from '../components/loading-display';
|
|
18
19
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
19
20
|
import { ResizeContainer } from '../components/resize-container';
|
|
20
21
|
import { ScalingSelector } from '../components/scaling-selector';
|
|
21
22
|
import Tabs from '../components/tabs';
|
|
22
23
|
import { type ConfidenceIntervalMethod } from '../shared/charts/confideceInterval';
|
|
23
|
-
import type
|
|
24
|
+
import { type AxisMax } from '../shared/charts/getYAxisMax';
|
|
24
25
|
import { type ScaleType } from '../shared/charts/getYAxisScale';
|
|
25
26
|
import { useQuery } from '../useQuery';
|
|
26
27
|
|
|
27
28
|
export type View = 'bar' | 'line' | 'bubble' | 'table';
|
|
28
29
|
|
|
29
|
-
export interface PrevalenceOverTimeProps
|
|
30
|
+
export interface PrevalenceOverTimeProps {
|
|
30
31
|
width: string;
|
|
31
32
|
height: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface PrevalenceOverTimeInnerProps {
|
|
35
33
|
numeratorFilter: NamedLapisFilter | NamedLapisFilter[];
|
|
36
34
|
denominatorFilter: LapisFilter;
|
|
37
35
|
granularity: TemporalGranularity;
|
|
@@ -40,32 +38,25 @@ export interface PrevalenceOverTimeInnerProps {
|
|
|
40
38
|
confidenceIntervalMethods: ConfidenceIntervalMethod[];
|
|
41
39
|
lapisDateField: string;
|
|
42
40
|
pageSize: boolean | number;
|
|
43
|
-
|
|
41
|
+
yAxisMaxLinear?: AxisMax;
|
|
42
|
+
yAxisMaxLogarithmic?: AxisMax;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = (
|
|
45
|
+
export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
|
|
46
|
+
const { width, height } = componentProps;
|
|
47
47
|
const size = { height, width };
|
|
48
48
|
|
|
49
49
|
return (
|
|
50
50
|
<ErrorBoundary size={size}>
|
|
51
51
|
<ResizeContainer size={size}>
|
|
52
|
-
<PrevalenceOverTimeInner {...
|
|
52
|
+
<PrevalenceOverTimeInner {...componentProps} />
|
|
53
53
|
</ResizeContainer>
|
|
54
54
|
</ErrorBoundary>
|
|
55
55
|
);
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
-
export const PrevalenceOverTimeInner: FunctionComponent<
|
|
59
|
-
numeratorFilter,
|
|
60
|
-
denominatorFilter,
|
|
61
|
-
granularity,
|
|
62
|
-
smoothingWindow,
|
|
63
|
-
views,
|
|
64
|
-
confidenceIntervalMethods,
|
|
65
|
-
lapisDateField,
|
|
66
|
-
pageSize,
|
|
67
|
-
yAxisMaxConfig,
|
|
68
|
-
}) => {
|
|
58
|
+
export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
|
|
59
|
+
const { numeratorFilter, denominatorFilter, granularity, smoothingWindow, lapisDateField } = componentProps;
|
|
69
60
|
const lapis = useContext(LapisUrlContext);
|
|
70
61
|
|
|
71
62
|
const { data, error, isLoading } = useQuery(
|
|
@@ -93,39 +84,21 @@ export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeInnerP
|
|
|
93
84
|
return <NoDataDisplay />;
|
|
94
85
|
}
|
|
95
86
|
|
|
96
|
-
return
|
|
97
|
-
<PrevalenceOverTimeTabs
|
|
98
|
-
views={views}
|
|
99
|
-
data={data}
|
|
100
|
-
granularity={granularity}
|
|
101
|
-
confidenceIntervalMethods={confidenceIntervalMethods}
|
|
102
|
-
pageSize={pageSize}
|
|
103
|
-
yAxisMaxConfig={yAxisMaxConfig}
|
|
104
|
-
/>
|
|
105
|
-
);
|
|
87
|
+
return <PrevalenceOverTimeTabs data={data} {...componentProps} />;
|
|
106
88
|
};
|
|
107
89
|
|
|
108
|
-
type PrevalenceOverTimeTabsProps = {
|
|
109
|
-
views: View[];
|
|
90
|
+
type PrevalenceOverTimeTabsProps = PrevalenceOverTimeProps & {
|
|
110
91
|
data: PrevalenceOverTimeData;
|
|
111
|
-
granularity: TemporalGranularity;
|
|
112
|
-
confidenceIntervalMethods: ConfidenceIntervalMethod[];
|
|
113
|
-
pageSize: boolean | number;
|
|
114
|
-
yAxisMaxConfig: YAxisMaxConfig;
|
|
115
92
|
};
|
|
116
93
|
|
|
117
|
-
const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = ({
|
|
118
|
-
views,
|
|
119
|
-
|
|
120
|
-
granularity,
|
|
121
|
-
confidenceIntervalMethods,
|
|
122
|
-
pageSize,
|
|
123
|
-
yAxisMaxConfig,
|
|
124
|
-
}) => {
|
|
94
|
+
const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = ({ data, ...componentProps }) => {
|
|
95
|
+
const { views, granularity, confidenceIntervalMethods, pageSize, yAxisMaxLinear, yAxisMaxLogarithmic } =
|
|
96
|
+
componentProps;
|
|
125
97
|
const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
|
|
126
98
|
const [confidenceIntervalMethod, setConfidenceIntervalMethod] = useState<ConfidenceIntervalMethod>(
|
|
127
99
|
confidenceIntervalMethods.length > 0 ? confidenceIntervalMethods[0] : 'none',
|
|
128
100
|
);
|
|
101
|
+
const yAxisMaxConfig = { linear: yAxisMaxLinear, logarithmic: yAxisMaxLogarithmic };
|
|
129
102
|
|
|
130
103
|
const getTab = (view: View) => {
|
|
131
104
|
switch (view) {
|
|
@@ -180,20 +153,18 @@ const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = (
|
|
|
180
153
|
yAxisScaleType={yAxisScaleType}
|
|
181
154
|
setYAxisScaleType={setYAxisScaleType}
|
|
182
155
|
data={data}
|
|
183
|
-
granularity={granularity}
|
|
184
|
-
confidenceIntervalMethods={confidenceIntervalMethods}
|
|
185
156
|
confidenceIntervalMethod={confidenceIntervalMethod}
|
|
186
157
|
setConfidenceIntervalMethod={setConfidenceIntervalMethod}
|
|
158
|
+
{...componentProps}
|
|
187
159
|
/>
|
|
188
160
|
);
|
|
189
161
|
|
|
190
162
|
return <Tabs tabs={tabs} toolbar={toolbar} />;
|
|
191
163
|
};
|
|
192
164
|
|
|
193
|
-
type ToolbarProps = {
|
|
165
|
+
type ToolbarProps = PrevalenceOverTimeProps & {
|
|
194
166
|
activeTab: string;
|
|
195
167
|
data: PrevalenceOverTimeData;
|
|
196
|
-
granularity: TemporalGranularity;
|
|
197
168
|
yAxisScaleType: ScaleType;
|
|
198
169
|
setYAxisScaleType: (scaleType: ScaleType) => void;
|
|
199
170
|
confidenceIntervalMethods: ConfidenceIntervalMethod[];
|
|
@@ -205,12 +176,12 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
205
176
|
activeTab,
|
|
206
177
|
yAxisScaleType,
|
|
207
178
|
setYAxisScaleType,
|
|
208
|
-
confidenceIntervalMethods,
|
|
209
179
|
confidenceIntervalMethod,
|
|
210
180
|
setConfidenceIntervalMethod,
|
|
211
181
|
data,
|
|
212
|
-
|
|
182
|
+
...componentProps
|
|
213
183
|
}) => {
|
|
184
|
+
const { confidenceIntervalMethods, granularity } = componentProps;
|
|
214
185
|
return (
|
|
215
186
|
<>
|
|
216
187
|
{activeTab !== 'Table' && (
|
|
@@ -229,16 +200,38 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
229
200
|
filename='prevalence_over_time.csv'
|
|
230
201
|
/>
|
|
231
202
|
|
|
232
|
-
<PrevalenceOverTimeInfo />
|
|
203
|
+
<PrevalenceOverTimeInfo {...componentProps} />
|
|
204
|
+
<Fullscreen />
|
|
233
205
|
</>
|
|
234
206
|
);
|
|
235
207
|
};
|
|
236
208
|
|
|
237
|
-
const PrevalenceOverTimeInfo: FunctionComponent = () => {
|
|
209
|
+
const PrevalenceOverTimeInfo: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
|
|
210
|
+
const { granularity, smoothingWindow, views } = componentProps;
|
|
211
|
+
const lapis = useContext(LapisUrlContext);
|
|
238
212
|
return (
|
|
239
213
|
<Info>
|
|
240
214
|
<InfoHeadline1>Prevalence over time</InfoHeadline1>
|
|
241
|
-
<InfoParagraph>
|
|
215
|
+
<InfoParagraph>
|
|
216
|
+
This presents the proportion of one or more variants per <b>{granularity}</b>
|
|
217
|
+
{smoothingWindow > 0 && `, smoothed using a ${smoothingWindow}-${granularity} sliding window`}. The
|
|
218
|
+
proportion is calculated as the number of sequences of the variant(s) divided by the number of sequences
|
|
219
|
+
that match the <code>denominatorFilter</code> (see below).
|
|
220
|
+
</InfoParagraph>
|
|
221
|
+
<InfoParagraph>
|
|
222
|
+
Sequences that have no assigned date will not be shown in the line and bubble chart. They will show up
|
|
223
|
+
in the bar and table view with date "unknown".
|
|
224
|
+
</InfoParagraph>
|
|
225
|
+
{views.includes('bubble') && (
|
|
226
|
+
<>
|
|
227
|
+
<InfoHeadline2>Bubble chart</InfoHeadline2>
|
|
228
|
+
<InfoParagraph>
|
|
229
|
+
The size of the bubble scales with the total number of available sequences from the{' '}
|
|
230
|
+
{granularity}.
|
|
231
|
+
</InfoParagraph>
|
|
232
|
+
</>
|
|
233
|
+
)}
|
|
234
|
+
<InfoComponentCode componentName='prevalence-over-time' params={componentProps} lapisUrl={lapis} />
|
|
242
235
|
</Info>
|
|
243
236
|
);
|
|
244
237
|
};
|
|
@@ -10,6 +10,7 @@ import { type LapisFilter } from '../../types';
|
|
|
10
10
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
11
11
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
12
12
|
import { ErrorDisplay } from '../components/error-display';
|
|
13
|
+
import { Fullscreen } from '../components/fullscreen';
|
|
13
14
|
import Info, { InfoHeadline1, InfoHeadline2, InfoLink, InfoParagraph } from '../components/info';
|
|
14
15
|
import { LoadingDisplay } from '../components/loading-display';
|
|
15
16
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
@@ -156,6 +157,7 @@ const RelativeGrowthAdvantageToolbar: FunctionComponent<RelativeGrowthAdvantageT
|
|
|
156
157
|
<>
|
|
157
158
|
<ScalingSelector yAxisScaleType={yAxisScaleType} setYAxisScaleType={setYAxisScaleType} />
|
|
158
159
|
<RelativeGrowthAdvantageInfo generationTime={generationTime} />
|
|
160
|
+
<Fullscreen />
|
|
159
161
|
</>
|
|
160
162
|
);
|
|
161
163
|
};
|
|
@@ -36,9 +36,18 @@ export type MutationOverTimeDataGroupedByMutation = Map2d<
|
|
|
36
36
|
|
|
37
37
|
const MAX_NUMBER_OF_GRID_COLUMNS = 200;
|
|
38
38
|
|
|
39
|
+
export async function queryOverallMutationData(
|
|
40
|
+
lapisFilter: LapisFilter,
|
|
41
|
+
sequenceType: SequenceType,
|
|
42
|
+
lapis: string,
|
|
43
|
+
signal?: AbortSignal,
|
|
44
|
+
) {
|
|
45
|
+
return fetchAndPrepareSubstitutionsOrDeletions(lapisFilter, sequenceType).evaluate(lapis, signal);
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
export async function queryMutationsOverTimeData(
|
|
40
49
|
lapisFilter: LapisFilter,
|
|
41
|
-
sequenceType:
|
|
50
|
+
sequenceType: SequenceType,
|
|
42
51
|
lapis: string,
|
|
43
52
|
lapisDateField: string,
|
|
44
53
|
granularity: TemporalGranularity,
|