@genspectrum/dashboard-components 1.2.0 → 1.3.0

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.
Files changed (27) hide show
  1. package/custom-elements.json +20 -1
  2. package/dist/assets/mutationOverTimeWorker-C7saVShx.js.map +1 -0
  3. package/dist/components.d.ts +20 -14
  4. package/dist/components.js +46 -8
  5. package/dist/components.js.map +1 -1
  6. package/dist/util.d.ts +14 -14
  7. package/package.json +1 -1
  8. package/src/lapisApi/lapisApi.ts +29 -0
  9. package/src/lapisApi/lapisTypes.ts +35 -1
  10. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay.ts +1 -0
  11. package/src/preact/mutationsOverTime/__mockData__/byWeek.ts +1 -0
  12. package/src/preact/mutationsOverTime/__mockData__/defaultMockData.ts +1 -0
  13. package/src/preact/mutationsOverTime/__mockData__/noDataWhenNoMutationsAreInFilter.ts +1 -0
  14. package/src/preact/mutationsOverTime/__mockData__/noDataWhenThereAreNoDatesInFilter.ts +1 -0
  15. package/src/preact/mutationsOverTime/__mockData__/showsMessageWhenTooManyMutations.ts +1 -0
  16. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +2 -0
  17. package/src/preact/mutationsOverTime/mutations-over-time.tsx +9 -3
  18. package/src/query/queryMutationsOverTime.ts +101 -23
  19. package/src/query/queryMutationsOverTimeNewEndpoint.spec.ts +935 -0
  20. package/src/web-components/visualization/gs-mutations-over-time.spec-d.ts +3 -0
  21. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +3 -0
  22. package/src/web-components/visualization/gs-mutations-over-time.tsx +9 -0
  23. package/standalone-bundle/assets/mutationOverTimeWorker-DRRi3aMG.js.map +1 -0
  24. package/standalone-bundle/dashboard-components.js +3619 -3583
  25. package/standalone-bundle/dashboard-components.js.map +1 -1
  26. package/dist/assets/mutationOverTimeWorker-DpW4YOGl.js.map +0 -1
  27. package/standalone-bundle/assets/mutationOverTimeWorker-CZVvQBze.js.map +0 -1
package/dist/util.d.ts CHANGED
@@ -917,7 +917,7 @@ declare global {
917
917
 
918
918
  declare global {
919
919
  interface HTMLElementTagNameMap {
920
- 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
920
+ 'gs-genome-data-viewer': GenomeDataViewerComponent;
921
921
  }
922
922
  }
923
923
 
@@ -925,7 +925,7 @@ declare global {
925
925
  declare global {
926
926
  namespace JSX {
927
927
  interface IntrinsicElements {
928
- 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
928
+ 'gs-genome-data-viewer': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
929
929
  }
930
930
  }
931
931
  }
@@ -933,7 +933,7 @@ declare global {
933
933
 
934
934
  declare global {
935
935
  interface HTMLElementTagNameMap {
936
- 'gs-genome-data-viewer': GenomeDataViewerComponent;
936
+ 'gs-mutation-comparison-component': MutationComparisonComponent;
937
937
  }
938
938
  }
939
939
 
@@ -941,7 +941,7 @@ declare global {
941
941
  declare global {
942
942
  namespace JSX {
943
943
  interface IntrinsicElements {
944
- 'gs-genome-data-viewer': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
944
+ 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
945
945
  }
946
946
  }
947
947
  }
@@ -981,7 +981,7 @@ declare global {
981
981
 
982
982
  declare global {
983
983
  interface HTMLElementTagNameMap {
984
- 'gs-mutation-comparison-component': MutationComparisonComponent;
984
+ 'gs-aggregate': AggregateComponent;
985
985
  }
986
986
  }
987
987
 
@@ -989,7 +989,7 @@ declare global {
989
989
  declare global {
990
990
  namespace JSX {
991
991
  interface IntrinsicElements {
992
- 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
992
+ 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
993
993
  }
994
994
  }
995
995
  }
@@ -997,7 +997,7 @@ declare global {
997
997
 
998
998
  declare global {
999
999
  interface HTMLElementTagNameMap {
1000
- 'gs-aggregate': AggregateComponent;
1000
+ 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
1001
1001
  }
1002
1002
  }
1003
1003
 
@@ -1005,7 +1005,7 @@ declare global {
1005
1005
  declare global {
1006
1006
  namespace JSX {
1007
1007
  interface IntrinsicElements {
1008
- 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1008
+ 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1009
1009
  }
1010
1010
  }
1011
1011
  }
@@ -1013,7 +1013,7 @@ declare global {
1013
1013
 
1014
1014
  declare global {
1015
1015
  interface HTMLElementTagNameMap {
1016
- 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
1016
+ 'gs-mutations-over-time': MutationsOverTimeComponent;
1017
1017
  }
1018
1018
  }
1019
1019
 
@@ -1021,7 +1021,7 @@ declare global {
1021
1021
  declare global {
1022
1022
  namespace JSX {
1023
1023
  interface IntrinsicElements {
1024
- 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1024
+ 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1025
1025
  }
1026
1026
  }
1027
1027
  }
@@ -1045,7 +1045,7 @@ declare global {
1045
1045
 
1046
1046
  declare global {
1047
1047
  interface HTMLElementTagNameMap {
1048
- 'gs-mutations-over-time': MutationsOverTimeComponent;
1048
+ 'gs-sequences-by-location': SequencesByLocationComponent;
1049
1049
  }
1050
1050
  }
1051
1051
 
@@ -1053,7 +1053,7 @@ declare global {
1053
1053
  declare global {
1054
1054
  namespace JSX {
1055
1055
  interface IntrinsicElements {
1056
- 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1056
+ 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1057
1057
  }
1058
1058
  }
1059
1059
  }
@@ -1061,7 +1061,7 @@ declare global {
1061
1061
 
1062
1062
  declare global {
1063
1063
  interface HTMLElementTagNameMap {
1064
- 'gs-sequences-by-location': SequencesByLocationComponent;
1064
+ 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
1065
1065
  }
1066
1066
  }
1067
1067
 
@@ -1069,7 +1069,7 @@ declare global {
1069
1069
  declare global {
1070
1070
  namespace JSX {
1071
1071
  interface IntrinsicElements {
1072
- 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1072
+ 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1073
1073
  }
1074
1074
  }
1075
1075
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -9,6 +9,8 @@ import {
9
9
  mutationsResponse,
10
10
  problemDetail,
11
11
  type ProblemDetail,
12
+ type MutationsOverTimeRequest,
13
+ mutationsOverTimeResponse,
12
14
  } from './lapisTypes';
13
15
  import { type SequenceType } from '../types';
14
16
  import { lineageDefinitionResponseSchema } from './LineageDefinition';
@@ -112,6 +114,28 @@ export async function fetchSubstitutionsOrDeletions(
112
114
  return mutationsResponse.parse(await response.json());
113
115
  }
114
116
 
117
+ export async function fetchMutationsOverTime(
118
+ lapisUrl: string,
119
+ body: MutationsOverTimeRequest,
120
+ sequenceType: SequenceType,
121
+ signal?: AbortSignal,
122
+ ) {
123
+ const response = await callLapis(
124
+ mutationsOverTimeEndpoint(lapisUrl, sequenceType),
125
+ {
126
+ method: 'POST',
127
+ headers: {
128
+ 'Content-Type': 'application/json',
129
+ },
130
+ body: JSON.stringify(body),
131
+ signal,
132
+ },
133
+ `${sequenceType} mutations over time`,
134
+ );
135
+
136
+ return mutationsOverTimeResponse.parse(await response.json());
137
+ }
138
+
115
139
  export async function fetchReferenceGenome(lapisUrl: string, signal?: AbortSignal) {
116
140
  const response = await callLapis(
117
141
  referenceGenomeEndpoint(lapisUrl),
@@ -215,6 +239,11 @@ export const substitutionsOrDeletionsEndpoint = (lapisUrl: string, sequenceType:
215
239
  ? `${lapisUrl}/sample/aminoAcidMutations`
216
240
  : `${lapisUrl}/sample/nucleotideMutations`;
217
241
  };
242
+ export const mutationsOverTimeEndpoint = (lapisUrl: string, sequenceType: SequenceType) => {
243
+ return sequenceType === 'amino acid'
244
+ ? `${lapisUrl}/component/aminoAcidMutationsOverTime`
245
+ : `${lapisUrl}/component/nucleotideMutationsOverTime`;
246
+ };
218
247
  export const referenceGenomeEndpoint = (lapisUrl: string) => `${lapisUrl}/sample/referenceGenome`;
219
248
  export const lineageDefinitionEndpoint = (lapisUrl: string, lapisField: string) =>
220
249
  `${lapisUrl}/sample/lineageDefinition/${lapisField}`;
@@ -7,6 +7,13 @@ export const orderBy = z.object({
7
7
  type: orderByType,
8
8
  });
9
9
 
10
+ const filterValue = z.union([z.string(), z.number(), z.boolean(), z.null(), z.undefined(), z.array(z.string())]);
11
+
12
+ const dateRange = z.object({
13
+ dateFrom: z.string(),
14
+ dateTo: z.string(),
15
+ });
16
+
10
17
  export const lapisBaseRequest = z
11
18
  .object({
12
19
  limit: z.number().optional(),
@@ -14,7 +21,7 @@ export const lapisBaseRequest = z
14
21
  fields: z.array(z.string()).optional(),
15
22
  orderBy: z.array(orderBy).optional(),
16
23
  })
17
- .catchall(z.union([z.boolean(), z.undefined(), z.string(), z.number(), z.null(), z.array(z.string())]));
24
+ .catchall(filterValue);
18
25
  export type LapisBaseRequest = z.infer<typeof lapisBaseRequest>;
19
26
 
20
27
  export const mutationsRequest = lapisBaseRequest.extend({ minProportion: z.number().optional() });
@@ -32,6 +39,33 @@ const mutationProportionCount = z.object({
32
39
  export const mutationsResponse = makeLapisResponse(z.array(mutationProportionCount));
33
40
  export type MutationsResponse = z.infer<typeof mutationsResponse>;
34
41
 
42
+ export const mutationsOverTimeRequest = z.object({
43
+ filters: z.record(filterValue),
44
+ downloadAsFile: z.boolean().optional(),
45
+ downloadFileBasename: z.string().optional(),
46
+ compression: z.enum(['gzip', 'none']).optional(),
47
+ includeMutations: z.array(z.string()).optional(),
48
+ dateRanges: z.array(dateRange).optional(),
49
+ dateField: z.string().optional(),
50
+ });
51
+ export type MutationsOverTimeRequest = z.infer<typeof mutationsOverTimeRequest>;
52
+
53
+ export const mutationsOverTimeResponse = makeLapisResponse(
54
+ z.object({
55
+ mutations: z.array(z.string()),
56
+ dateRanges: z.array(dateRange),
57
+ data: z.array(
58
+ z.array(
59
+ z.object({
60
+ count: z.number(),
61
+ coverage: z.number(),
62
+ }),
63
+ ),
64
+ ),
65
+ }),
66
+ );
67
+ export type MutationsOverTimeResponse = z.infer<typeof mutationsOverTimeResponse>;
68
+
35
69
  const insertionCount = z.object({
36
70
  insertion: z.string(),
37
71
  count: z.number(),
@@ -11,6 +11,7 @@ export const aminoAcidMutationsByDay: MutationOverTimeMockData = {
11
11
  granularity: 'day',
12
12
  lapisDateField: 'date',
13
13
  lapis: 'https://lapis.cov-spectrum.org/open/v2',
14
+ useNewEndpoint: false,
14
15
  },
15
16
  response: {
16
17
  overallMutationData: [
@@ -11,6 +11,7 @@ export const byWeek: MutationOverTimeMockData = {
11
11
  granularity: 'week',
12
12
  lapisDateField: 'date',
13
13
  lapis: 'https://lapis.cov-spectrum.org/open/v2',
14
+ useNewEndpoint: false,
14
15
  },
15
16
  response: {
16
17
  overallMutationData: [
@@ -11,6 +11,7 @@ export const defaultMockData: MutationOverTimeMockData = {
11
11
  granularity: 'month',
12
12
  lapisDateField: 'date',
13
13
  lapis: 'https://lapis.cov-spectrum.org/open/v2',
14
+ useNewEndpoint: false,
14
15
  },
15
16
  response: {
16
17
  overallMutationData: [
@@ -10,6 +10,7 @@ export const noDataWhenNoMutationsAreInFilter: MutationOverTimeMockData = {
10
10
  granularity: 'year',
11
11
  lapisDateField: 'date',
12
12
  lapis: 'https://lapis.cov-spectrum.org/open/v2',
13
+ useNewEndpoint: false,
13
14
  },
14
15
  response: {
15
16
  overallMutationData: [],
@@ -10,6 +10,7 @@ export const noDataWhenNoMutationsAreInFilter: MutationOverTimeMockData = {
10
10
  granularity: 'year',
11
11
  lapisDateField: 'date',
12
12
  lapis: 'https://lapis.cov-spectrum.org/open/v2',
13
+ useNewEndpoint: false,
13
14
  },
14
15
  response: {
15
16
  overallMutationData: [],
@@ -10,6 +10,7 @@ export const showsMessageWhenTooManyMutations: MutationOverTimeMockData = {
10
10
  granularity: 'year',
11
11
  lapisDateField: 'date',
12
12
  lapis: 'https://lapis.cov-spectrum.org/open/v2',
13
+ useNewEndpoint: false,
13
14
  },
14
15
  response: {
15
16
  overallMutationData: [
@@ -36,6 +36,7 @@ const meta: Meta<MutationsOverTimeProps> = {
36
36
  displayMutations: { control: 'object' },
37
37
  initialMeanProportionInterval: { control: 'object' },
38
38
  pageSizes: { control: 'object' },
39
+ useNewEndpoint: { control: 'boolean' },
39
40
  },
40
41
  parameters: {
41
42
  fetchMock: {},
@@ -80,6 +81,7 @@ export const Default: StoryObj<MutationsOverTimeProps> = {
80
81
  granularity: 'month',
81
82
  lapisDateField: 'date',
82
83
  initialMeanProportionInterval: { min: 0.05, max: 0.9 },
84
+ useNewEndpoint: false,
83
85
  pageSizes: [10, 20, 30, 40, 50],
84
86
  },
85
87
  };
@@ -12,6 +12,7 @@ import {
12
12
  } from './getFilteredMutationsOverTimeData';
13
13
  import { type MutationOverTimeWorkerResponse } from './mutationOverTimeWorker';
14
14
  import MutationsOverTimeGrid from './mutations-over-time-grid';
15
+ import { type MutationOverTimeQuery } from '../../query/queryMutationsOverTime';
15
16
  import {
16
17
  lapisFilterSchema,
17
18
  sequenceTypeSchema,
@@ -52,6 +53,7 @@ const mutationOverTimeSchema = z.object({
52
53
  views: z.array(mutationsOverTimeViewSchema),
53
54
  granularity: temporalGranularitySchema,
54
55
  lapisDateField: z.string().min(1),
56
+ useNewEndpoint: z.boolean().optional(),
55
57
  displayMutations: displayMutationsSchema.optional(),
56
58
  initialMeanProportionInterval: z.object({
57
59
  min: z.number().min(0).max(1),
@@ -76,19 +78,23 @@ export const MutationsOverTime: FunctionComponent<MutationsOverTimeProps> = (com
76
78
  );
77
79
  };
78
80
 
79
- export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeProps> = (componentProps) => {
81
+ export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeProps> = ({
82
+ useNewEndpoint = false,
83
+ ...componentProps
84
+ }) => {
80
85
  const lapis = useLapisUrl();
81
86
  const { lapisFilter, sequenceType, granularity, lapisDateField } = componentProps;
82
87
 
83
- const messageToWorker = useMemo(() => {
88
+ const messageToWorker: MutationOverTimeQuery = useMemo(() => {
84
89
  return {
85
90
  lapisFilter,
86
91
  sequenceType,
87
92
  granularity,
88
93
  lapisDateField,
89
94
  lapis,
95
+ useNewEndpoint,
90
96
  };
91
- }, [granularity, lapis, lapisDateField, lapisFilter, sequenceType]);
97
+ }, [granularity, lapis, lapisDateField, lapisFilter, sequenceType, useNewEndpoint]);
92
98
 
93
99
  const { data, error, isLoading } = useWebWorker<MutationOverTimeWorkerResponse>(
94
100
  messageToWorker,
@@ -1,4 +1,5 @@
1
1
  import { mapDateToGranularityRange } from './queryAggregatedDataOverTime';
2
+ import { fetchMutationsOverTime } from '../lapisApi/lapisApi';
2
3
  import { FetchAggregatedOperator } from '../operator/FetchAggregatedOperator';
3
4
  import { FetchSubstitutionsOrDeletionsOperator } from '../operator/FetchSubstitutionsOrDeletionsOperator';
4
5
  import { GroupByAndSumOperator } from '../operator/GroupByAndSumOperator';
@@ -16,6 +17,7 @@ import {
16
17
  type SubstitutionOrDeletionEntry,
17
18
  type TemporalGranularity,
18
19
  } from '../types';
20
+ import { type Map2DContents } from '../utils/map2d';
19
21
  import { type Deletion, type Substitution, toSubstitutionOrDeletion } from '../utils/mutations';
20
22
  import {
21
23
  compareTemporal,
@@ -92,17 +94,13 @@ export type MutationOverTimeQuery = {
92
94
  lapis: string;
93
95
  lapisDateField: string;
94
96
  granularity: TemporalGranularity;
97
+ useNewEndpoint?: boolean;
95
98
  signal?: AbortSignal;
96
99
  };
97
100
 
98
- export async function queryMutationsOverTimeData({
99
- lapisFilter,
100
- sequenceType,
101
- lapis,
102
- lapisDateField,
103
- granularity,
104
- signal,
105
- }: MutationOverTimeQuery) {
101
+ export async function queryMutationsOverTimeData(query: MutationOverTimeQuery) {
102
+ const { lapisFilter, sequenceType, lapis, lapisDateField, granularity, useNewEndpoint, signal } = query;
103
+
106
104
  const requestedDateRanges = await getDatesInDataset(lapisFilter, lapis, granularity, lapisDateField, signal);
107
105
 
108
106
  if (requestedDateRanges.length > MAX_NUMBER_OF_GRID_COLUMNS) {
@@ -114,7 +112,25 @@ export async function queryMutationsOverTimeData({
114
112
  );
115
113
  }
116
114
 
117
- const subQueries = requestedDateRanges.map(async (date) => {
115
+ const overallMutationData = queryOverallMutationData({
116
+ lapisFilter,
117
+ sequenceType,
118
+ lapis,
119
+ lapisDateField,
120
+ granularity,
121
+ }).then((r) => r.content);
122
+
123
+ return useNewEndpoint === true
124
+ ? queryMutationsOverTimeDataDirectEndpoint(requestedDateRanges, overallMutationData, query)
125
+ : queryMutationsOverTimeDataMultiQuery(requestedDateRanges, overallMutationData, query);
126
+ }
127
+
128
+ async function queryMutationsOverTimeDataMultiQuery(
129
+ allDates: TemporalClass[],
130
+ overallMutationDataPromise: Promise<SubstitutionOrDeletionEntry[]>,
131
+ { lapisFilter, sequenceType, lapis, lapisDateField, signal }: MutationOverTimeQuery,
132
+ ) {
133
+ const subQueries = allDates.map(async (date) => {
118
134
  const dateFrom = date.firstDay.toString();
119
135
  const dateTo = date.lastDay.toString();
120
136
 
@@ -124,8 +140,10 @@ export async function queryMutationsOverTimeData({
124
140
  [`${lapisDateField}To`]: dateTo,
125
141
  };
126
142
 
127
- const data = await fetchAndPrepareSubstitutionsOrDeletions(filter, sequenceType).evaluate(lapis, signal);
128
- const totalCountQuery = await getTotalNumberOfSequencesInDateRange(filter).evaluate(lapis, signal);
143
+ const [data, totalCountQuery] = await Promise.all([
144
+ fetchAndPrepareSubstitutionsOrDeletions(filter, sequenceType).evaluate(lapis, signal),
145
+ getTotalNumberOfSequencesInDateRange(filter).evaluate(lapis, signal),
146
+ ]);
129
147
 
130
148
  return {
131
149
  date,
@@ -135,20 +153,80 @@ export async function queryMutationsOverTimeData({
135
153
  });
136
154
 
137
155
  const data = await Promise.all(subQueries);
156
+ const overallMutationData = await overallMutationDataPromise;
138
157
 
139
- const overallMutationsData = (
140
- await queryOverallMutationData({
141
- lapisFilter,
142
- sequenceType,
143
- lapis,
144
- lapisDateField,
145
- granularity,
146
- })
147
- ).content;
158
+ return {
159
+ mutationOverTimeData: groupByMutation(data, overallMutationData),
160
+ overallMutationData,
161
+ };
162
+ }
163
+
164
+ async function queryMutationsOverTimeDataDirectEndpoint(
165
+ allDates: TemporalClass[],
166
+ overallMutationDataPromise: Promise<SubstitutionOrDeletionEntry[]>,
167
+ { lapisFilter, sequenceType, lapis, lapisDateField, signal }: MutationOverTimeQuery,
168
+ ): Promise<{
169
+ mutationOverTimeData: BaseMutationOverTimeDataMap;
170
+ overallMutationData: SubstitutionOrDeletionEntry[];
171
+ }> {
172
+ const overallMutationData = await overallMutationDataPromise;
173
+ overallMutationData.sort((a, b) => sortSubstitutionsAndDeletions(a.mutation, b.mutation));
174
+ const totalCounts = await Promise.all(
175
+ allDates.map(async (date) => {
176
+ const filter = {
177
+ ...lapisFilter,
178
+ [`${lapisDateField}From`]: date.firstDay.toString(),
179
+ [`${lapisDateField}To`]: date.lastDay.toString(),
180
+ };
181
+
182
+ const totalCountQuery = await getTotalNumberOfSequencesInDateRange(filter).evaluate(lapis, signal);
183
+
184
+ return totalCountQuery.content[0].count;
185
+ }),
186
+ );
187
+
188
+ const includeMutations = overallMutationData.map((value) => value.mutation.code);
189
+ const apiResult = await fetchMutationsOverTime(
190
+ lapis,
191
+ {
192
+ filters: lapisFilter,
193
+ dateRanges: allDates.map((date) => ({
194
+ dateFrom: date.firstDay.toString(),
195
+ dateTo: date.lastDay.toString(),
196
+ })),
197
+ includeMutations,
198
+ dateField: lapisDateField,
199
+ },
200
+ sequenceType,
201
+ signal,
202
+ );
203
+
204
+ const mutationOverTimeData: Map2DContents<Substitution | Deletion, Temporal, MutationOverTimeMutationValue> = {
205
+ keysFirstAxis: new Map(overallMutationData.map((value) => [value.mutation.code, value.mutation])),
206
+ keysSecondAxis: new Map(allDates.map((date) => [date.dateString, date])),
207
+ data: new Map(
208
+ overallMutationData.map((mutation, i) => [
209
+ mutation.mutation.code,
210
+ new Map(
211
+ allDates.map((date, j): [string, MutationOverTimeMutationValue] => [
212
+ date.dateString,
213
+ {
214
+ type: 'value',
215
+ // 'coverage' in the API resp. is the number of seqs. that have a non-ambiguous symbol at position
216
+ // 'count' in the API resp. is the number of seqs with the mutation
217
+ proportion: apiResult.data.data[i][j].count / apiResult.data.data[i][j].coverage,
218
+ count: apiResult.data.data[i][j].count,
219
+ totalCount: totalCounts[j],
220
+ },
221
+ ]),
222
+ ),
223
+ ]),
224
+ ),
225
+ };
148
226
 
149
227
  return {
150
- mutationOverTimeData: groupByMutation(data, overallMutationsData),
151
- overallMutationData: overallMutationsData,
228
+ mutationOverTimeData: new BaseMutationOverTimeDataMap(mutationOverTimeData),
229
+ overallMutationData,
152
230
  };
153
231
  }
154
232
 
@@ -236,7 +314,7 @@ export function serializeTemporal(date: Temporal) {
236
314
  export function groupByMutation(
237
315
  data: MutationOverTimeData[],
238
316
  overallMutationData: (SubstitutionEntry | DeletionEntry)[],
239
- ) {
317
+ ): BaseMutationOverTimeDataMap {
240
318
  const dataArray = new BaseMutationOverTimeDataMap();
241
319
 
242
320
  const allDates = data.map((mutationData) => mutationData.date);