@genspectrum/dashboard-components 1.16.0 → 1.17.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 (36) hide show
  1. package/custom-elements.json +3 -3
  2. package/dist/components.d.ts +4 -4
  3. package/dist/components.js +449 -246
  4. package/dist/components.js.map +1 -1
  5. package/dist/util.d.ts +6 -6
  6. package/package.json +1 -1
  7. package/src/preact/MutationAnnotationsContext.tsx +1 -1
  8. package/src/preact/components/csv-download-button.tsx +22 -14
  9. package/src/preact/components/features-over-time-grid.tsx +189 -43
  10. package/src/preact/components/mutations-over-time-mutations-filter.stories.tsx +1 -1
  11. package/src/preact/components/mutations-over-time-mutations-filter.tsx +1 -1
  12. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutationsOverTimePage1.json +52 -0
  13. package/src/preact/mutationsOverTime/__mockData__/byWeek/mutationsOverTimePage1.json +76 -0
  14. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mockDefaultMutationsOverTimeWithFilter.json +43 -0
  15. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTimePage1.json +126 -0
  16. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTimePage2.json +116 -0
  17. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTimePageSize20.json +216 -0
  18. package/src/preact/mutationsOverTime/getFilteredMutationCodes.spec.ts +236 -0
  19. package/src/preact/mutationsOverTime/{getFilteredMutationsOverTimeData.ts → getFilteredMutationCodes.ts} +29 -44
  20. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +128 -23
  21. package/src/preact/mutationsOverTime/mutations-over-time.tsx +139 -74
  22. package/src/preact/mutationsOverTime/useMutationsOverTimePageData.ts +111 -0
  23. package/src/preact/shared/tanstackTable/pagination-context.tsx +5 -2
  24. package/src/preact/shared/tanstackTable/pagination.tsx +11 -9
  25. package/src/preact/shared/tanstackTable/tanstackTable.tsx +7 -4
  26. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +1 -1
  27. package/src/query/queryMutationsOverTime.spec.ts +187 -662
  28. package/src/query/queryMutationsOverTime.ts +46 -33
  29. package/src/utils/useControlledState.ts +15 -0
  30. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +78 -22
  31. package/standalone-bundle/dashboard-components.js +6872 -6690
  32. package/standalone-bundle/dashboard-components.js.map +1 -1
  33. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutationsOverTime.json +0 -5496
  34. package/src/preact/mutationsOverTime/__mockData__/byWeek/mutationsOverTime.json +0 -7100
  35. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTime.json +0 -12646
  36. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +0 -417
package/dist/util.d.ts CHANGED
@@ -848,11 +848,11 @@ declare const queriesOverTimeSchema: default_2.ZodObject<{
848
848
  max: number;
849
849
  };
850
850
  height?: string | undefined;
851
- hideGaps?: boolean | undefined;
852
851
  customColumns?: {
853
852
  values: Record<string, string | number>;
854
853
  header: string;
855
854
  }[] | undefined;
855
+ hideGaps?: boolean | undefined;
856
856
  }, {
857
857
  lapisFilter: Record<string, string | number | boolean | string[] | null | undefined> & {
858
858
  nucleotideMutations?: string[] | undefined;
@@ -876,11 +876,11 @@ declare const queriesOverTimeSchema: default_2.ZodObject<{
876
876
  max: number;
877
877
  };
878
878
  height?: string | undefined;
879
- hideGaps?: boolean | undefined;
880
879
  customColumns?: {
881
880
  values: Record<string, string | number>;
882
881
  header: string;
883
882
  }[] | undefined;
883
+ hideGaps?: boolean | undefined;
884
884
  }>;
885
885
 
886
886
  export declare type QueriesOverTimeView = default_2.infer<typeof queriesOverTimeViewSchema>;
@@ -1188,7 +1188,7 @@ declare global {
1188
1188
 
1189
1189
  declare global {
1190
1190
  interface HTMLElementTagNameMap {
1191
- 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1191
+ 'gs-aggregate': AggregateComponent;
1192
1192
  }
1193
1193
  }
1194
1194
 
@@ -1196,7 +1196,7 @@ declare global {
1196
1196
  declare global {
1197
1197
  namespace React.JSX {
1198
1198
  interface IntrinsicElements {
1199
- 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1199
+ 'gs-aggregate': AggregateComponent;
1200
1200
  }
1201
1201
  }
1202
1202
  }
@@ -1204,7 +1204,7 @@ declare global {
1204
1204
 
1205
1205
  declare global {
1206
1206
  interface HTMLElementTagNameMap {
1207
- 'gs-aggregate': AggregateComponent;
1207
+ 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1208
1208
  }
1209
1209
  }
1210
1210
 
@@ -1212,7 +1212,7 @@ declare global {
1212
1212
  declare global {
1213
1213
  namespace React.JSX {
1214
1214
  interface IntrinsicElements {
1215
- 'gs-aggregate': AggregateComponent;
1215
+ 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1216
1216
  }
1217
1217
  }
1218
1218
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -98,7 +98,7 @@ export function useRawMutationAnnotations() {
98
98
  export function useMutationAnnotationsProvider() {
99
99
  const mutationAnnotations = useContext(MutationAnnotationsContext);
100
100
 
101
- return getMutationAnnotationsProvider(mutationAnnotations);
101
+ return useMemo(() => getMutationAnnotationsProvider(mutationAnnotations), [mutationAnnotations]);
102
102
  }
103
103
 
104
104
  export function getMutationAnnotationsProvider(mutationAnnotations: MutationAnnotationsContextValue) {
@@ -1,4 +1,5 @@
1
1
  import { type FunctionComponent } from 'preact';
2
+ import { useState } from 'preact/hooks';
2
3
 
3
4
  type ToStringable = {
4
5
  toString: () => string;
@@ -9,7 +10,7 @@ export type DataValue = string | number | boolean | null | undefined | ToStringa
9
10
  export interface CsvDownloadButtonProps {
10
11
  label?: string;
11
12
  filename?: string;
12
- getData: () => Record<string, DataValue>[];
13
+ getData: () => Record<string, DataValue>[] | Promise<Record<string, DataValue>[]>;
13
14
  className?: string;
14
15
  }
15
16
 
@@ -19,19 +20,26 @@ export const CsvDownloadButton: FunctionComponent<CsvDownloadButtonProps> = ({
19
20
  getData,
20
21
  className,
21
22
  }) => {
22
- const download = () => {
23
- const content = getDownloadContent();
24
- const blob = new Blob([content], { type: 'text/csv' });
25
- const url = URL.createObjectURL(blob);
26
- const a = document.createElement('a');
27
- a.href = url;
28
- a.download = filename;
29
- a.click();
30
- URL.revokeObjectURL(url);
23
+ const [isDownloading, setIsDownloading] = useState(false);
24
+
25
+ const download = async () => {
26
+ setIsDownloading(true);
27
+ try {
28
+ const content = await getDownloadContent();
29
+ const blob = new Blob([content], { type: 'text/csv' });
30
+ const url = URL.createObjectURL(blob);
31
+ const a = document.createElement('a');
32
+ a.href = url;
33
+ a.download = filename;
34
+ a.click();
35
+ URL.revokeObjectURL(url);
36
+ } finally {
37
+ setIsDownloading(false);
38
+ }
31
39
  };
32
40
 
33
- const getDownloadContent = () => {
34
- const data = getData();
41
+ const getDownloadContent = async () => {
42
+ const data = await getData();
35
43
  const keys = getDataKeys(data);
36
44
  const header = keys.join(',');
37
45
  const rows = data.map((row) => keys.map((key) => row[key]).join(',')).join('\n');
@@ -50,8 +58,8 @@ export const CsvDownloadButton: FunctionComponent<CsvDownloadButtonProps> = ({
50
58
  };
51
59
 
52
60
  return (
53
- <button className={className} onClick={download}>
54
- {label}
61
+ <button className={className} onClick={() => void download()} disabled={isDownloading}>
62
+ {isDownloading ? 'Downloading...' : label}
55
63
  </button>
56
64
  );
57
65
  };
@@ -1,5 +1,7 @@
1
+ import { createColumnHelper, getCoreRowModel, getPaginationRowModel } from '@tanstack/table-core';
2
+ import type { Table } from '@tanstack/table-core';
1
3
  import { type FunctionComponent, type JSX } from 'preact';
2
- import { useMemo } from 'preact/hooks';
4
+ import { type Dispatch, type StateUpdater, useMemo } from 'preact/hooks';
3
5
  import z from 'zod';
4
6
 
5
7
  import { type ColorScale, getColorWithinScale, getTextColorForScale } from './color-scale-selector';
@@ -11,13 +13,7 @@ import { type TemporalDataMap } from '../mutationsOverTime/MutationOverTimeData'
11
13
  import { formatProportion } from '../shared/table/formatProportion';
12
14
  import { type PageSizes, Pagination } from '../shared/tanstackTable/pagination';
13
15
  import { usePageSizeContext } from '../shared/tanstackTable/pagination-context';
14
- import {
15
- createColumnHelper,
16
- flexRender,
17
- getCoreRowModel,
18
- getPaginationRowModel,
19
- usePreactTable,
20
- } from '../shared/tanstackTable/tanstackTable';
16
+ import { flexRender, usePreactTable } from '../shared/tanstackTable/tanstackTable';
21
17
 
22
18
  const NON_BREAKING_SPACE = '\u00A0';
23
19
 
@@ -60,18 +56,118 @@ function FeaturesOverTimeGrid<F>({
60
56
  featureRenderer,
61
57
  tooltipPortalTarget,
62
58
  }: FeaturesOverTimeGridProps<F>) {
63
- const tableData = useMemo(() => {
64
- const firstAxisKeys = data.getFirstAxisKeys();
65
- return data.getAsArray().map((row, index): RowType<F> => {
66
- const firstAxisKey = firstAxisKeys[index];
67
- const customValues = customColumns.map((col) => col.values[featureRenderer.asString(firstAxisKey)]);
68
- return { feature: firstAxisKey, values: [...row], customValues };
69
- });
70
- }, [data, customColumns, featureRenderer]);
59
+ const tableData = useGridTableData(data, customColumns, featureRenderer);
60
+ const columns = useGridColumns(
61
+ data.getSecondAxisKeys(),
62
+ rowLabelHeader,
63
+ customColumns,
64
+ colorScale,
65
+ tooltipPortalTarget,
66
+ featureRenderer,
67
+ );
68
+ const { pageSize } = usePageSizeContext();
69
+
70
+ const table = usePreactTable({
71
+ data: tableData,
72
+ columns,
73
+ getCoreRowModel: getCoreRowModel(),
74
+ getPaginationRowModel: getPaginationRowModel(),
75
+ initialState: {
76
+ pagination: { pageIndex: 0, pageSize },
77
+ },
78
+ });
79
+
80
+ return <FeaturesOverTimeGridDisplay table={table} pageSizes={pageSizes} />;
81
+ }
82
+
83
+ export interface FeaturesOverTimeGridServerPaginatedProps<F> {
84
+ rowLabelHeader: string;
85
+ data: TemporalDataMap<F> | null;
86
+ isLoading: boolean;
87
+ /** Labels to show in the row label column while the page data is loading. */
88
+ loadingRowLabels: string[];
89
+ /** Date columns to show in the header while loading */
90
+ requestedDateRanges: Temporal[];
91
+ colorScale: ColorScale;
92
+ pageSizes: PageSizes;
93
+ /** Controlled page index (0-based). */
94
+ pageIndex: number;
95
+ /** Total number of rows across all pages. */
96
+ totalRows: number;
97
+ onPageChange: Dispatch<StateUpdater<number>>;
98
+ customColumns?: CustomColumn[];
99
+ featureRenderer: FeatureRenderer<F>;
100
+ tooltipPortalTarget: HTMLElement | null;
101
+ }
102
+
103
+ export function FeaturesOverTimeGridServerPaginated<F>({
104
+ rowLabelHeader,
105
+ data,
106
+ isLoading,
107
+ loadingRowLabels,
108
+ requestedDateRanges,
109
+ colorScale,
110
+ pageSizes,
111
+ pageIndex,
112
+ totalRows,
113
+ onPageChange,
114
+ customColumns = EMPTY_COLUMNS,
115
+ featureRenderer,
116
+ tooltipPortalTarget,
117
+ }: FeaturesOverTimeGridServerPaginatedProps<F>) {
118
+ const tableData = useGridTableData(data, customColumns, featureRenderer);
119
+ const columns = useGridColumns(
120
+ data?.getSecondAxisKeys() ?? requestedDateRanges,
121
+ rowLabelHeader,
122
+ customColumns,
123
+ colorScale,
124
+ tooltipPortalTarget,
125
+ featureRenderer,
126
+ );
127
+ const { pageSize, setPageSize } = usePageSizeContext();
128
+
129
+ const table = usePreactTable({
130
+ data: tableData,
131
+ columns,
132
+ getCoreRowModel: getCoreRowModel(),
133
+ // getPaginationRowModel not needed with manualPagination: true
134
+ manualPagination: true,
135
+ pageCount: Math.ceil(totalRows / pageSize),
136
+ state: {
137
+ pagination: { pageIndex, pageSize },
138
+ },
139
+ onPaginationChange: (updater) => {
140
+ const current = { pageIndex, pageSize };
141
+ const next = typeof updater === 'function' ? updater(current) : updater;
142
+ if (next.pageIndex !== current.pageIndex) {
143
+ onPageChange(next.pageIndex);
144
+ }
145
+ if (next.pageSize !== current.pageSize) {
146
+ setPageSize(next.pageSize);
147
+ }
148
+ },
149
+ });
71
150
 
72
- const columns = useMemo(() => {
151
+ return (
152
+ <FeaturesOverTimeGridDisplay
153
+ table={table}
154
+ pageSizes={pageSizes}
155
+ loadingState={{ isLoading, loadingRowLabels }}
156
+ totalRows={totalRows}
157
+ />
158
+ );
159
+ }
160
+
161
+ function useGridColumns<F>(
162
+ dates: Temporal[],
163
+ rowLabelHeader: string,
164
+ customColumns: CustomColumn[],
165
+ colorScale: ColorScale,
166
+ tooltipPortalTarget: HTMLElement | null,
167
+ featureRenderer: FeatureRenderer<F>,
168
+ ) {
169
+ return useMemo(() => {
73
170
  const columnHelper = createColumnHelper<RowType<F>>();
74
- const dates = data.getSecondAxisKeys();
75
171
 
76
172
  const featureHeader = columnHelper.accessor((row) => row.feature, {
77
173
  id: 'feature',
@@ -137,21 +233,48 @@ function FeaturesOverTimeGrid<F>({
137
233
  });
138
234
 
139
235
  return [featureHeader, ...customColumnHeaders, ...dateHeaders];
140
- }, [colorScale, data, customColumns, tooltipPortalTarget, featureRenderer, rowLabelHeader]);
236
+ }, [colorScale, dates, customColumns, tooltipPortalTarget, featureRenderer, rowLabelHeader]);
237
+ }
141
238
 
142
- const { pageSize } = usePageSizeContext();
143
- const table = usePreactTable({
144
- data: tableData,
145
- columns,
146
- getCoreRowModel: getCoreRowModel(),
147
- getPaginationRowModel: getPaginationRowModel(),
148
- initialState: {
149
- pagination: {
150
- pageIndex: 0,
151
- pageSize,
152
- },
153
- },
154
- });
239
+ function useGridTableData<F>(
240
+ data: TemporalDataMap<F> | null | undefined,
241
+ customColumns: CustomColumn[],
242
+ featureRenderer: FeatureRenderer<F>,
243
+ ) {
244
+ return useMemo(() => {
245
+ if (!data) {
246
+ return [];
247
+ }
248
+ const firstAxisKeys = data.getFirstAxisKeys();
249
+ return data.getAsArray().map((row, index): RowType<F> => {
250
+ const firstAxisKey = firstAxisKeys[index];
251
+ const customValues = customColumns.map((col) => col.values[featureRenderer.asString(firstAxisKey)]);
252
+ return { feature: firstAxisKey, values: [...row], customValues };
253
+ });
254
+ }, [data, customColumns, featureRenderer]);
255
+ }
256
+
257
+ type FeaturesOverTimeGridDisplayProps<F> = {
258
+ table: Table<RowType<F>>;
259
+ pageSizes: PageSizes;
260
+ /** Override for the pagination row count (server-driven pagination). */
261
+ totalRows?: number;
262
+ loadingState?:
263
+ | {
264
+ isLoading: boolean;
265
+ /** Labels to render in the row label column while loading. One skeleton row is shown per label. */
266
+ loadingRowLabels: string[];
267
+ }
268
+ | { isLoading: false; loadingRowLabels?: never };
269
+ };
270
+
271
+ function FeaturesOverTimeGridDisplay<F>({
272
+ table,
273
+ pageSizes,
274
+ loadingState,
275
+ totalRows,
276
+ }: FeaturesOverTimeGridDisplayProps<F>) {
277
+ const displayedTotalRows = totalRows ?? table.getCoreRowModel().rows.length;
155
278
 
156
279
  return (
157
280
  <div className='w-full'>
@@ -170,22 +293,45 @@ function FeaturesOverTimeGrid<F>({
170
293
  ))}
171
294
  </thead>
172
295
  <tbody>
173
- {table.getRowModel().rows.map((row) => (
174
- <tr key={row.id}>
175
- {row.getVisibleCells().map((cell) => (
176
- <td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
296
+ {loadingState?.isLoading ? (
297
+ loadingState.loadingRowLabels.map((label, rowIndex) => (
298
+ <tr key={label}>
299
+ <td className='text-center'>{label}</td>
300
+ {rowIndex === 0 && (
301
+ <td
302
+ rowSpan={loadingState.loadingRowLabels.length}
303
+ colSpan={table.getFlatHeaders().length - 1}
304
+ className='text-center'
305
+ >
306
+ <span className='loading loading-spinner loading-sm' />
307
+ </td>
308
+ )}
309
+ </tr>
310
+ ))
311
+ ) : (
312
+ <>
313
+ {table.getRowModel().rows.map((row) => (
314
+ <tr key={row.id}>
315
+ {row.getVisibleCells().map((cell) => (
316
+ <td key={cell.id}>
317
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
318
+ </td>
319
+ ))}
320
+ </tr>
177
321
  ))}
178
- </tr>
179
- ))}
180
- {table.getRowModel().rows.length === 0 && (
181
- <td colSpan={table.getFlatHeaders().length}>
182
- <div className={'text-center'}>No data available for your filters.</div>
183
- </td>
322
+ {table.getRowModel().rows.length === 0 && (
323
+ <tr>
324
+ <td colSpan={table.getFlatHeaders().length}>
325
+ <div className={'text-center'}>No data available for your filters.</div>
326
+ </td>
327
+ </tr>
328
+ )}
329
+ </>
184
330
  )}
185
331
  </tbody>
186
332
  </table>
187
333
  <div className={'mt-2'}>
188
- <Pagination table={table} pageSizes={pageSizes} />
334
+ <Pagination table={table} pageSizes={pageSizes} totalRows={displayedTotalRows} />
189
335
  </div>
190
336
  </div>
191
337
  );
@@ -9,7 +9,7 @@ import {
9
9
  } from './mutations-over-time-mutations-filter';
10
10
  import { type MutationAnnotations } from '../../web-components/mutation-annotations-context';
11
11
  import { MutationAnnotationsContextProvider } from '../MutationAnnotationsContext';
12
- import { type MutationFilter } from '../mutationsOverTime/getFilteredMutationsOverTimeData';
12
+ import { type MutationFilter } from '../mutationsOverTime/getFilteredMutationCodes';
13
13
 
14
14
  const meta: Meta = {
15
15
  title: 'Component/Mutations over time mutations filter',
@@ -3,7 +3,7 @@ import { type Dispatch, type StateUpdater, useCallback, useEffect, useState } fr
3
3
 
4
4
  import { Dropdown } from './dropdown';
5
5
  import { useRawMutationAnnotations } from '../MutationAnnotationsContext';
6
- import { type MutationFilter } from '../mutationsOverTime/getFilteredMutationsOverTimeData';
6
+ import { type MutationFilter } from '../mutationsOverTime/getFilteredMutationCodes';
7
7
  import { DeleteIcon } from '../shared/icons/DeleteIcon';
8
8
 
9
9
  export type MutationsOverTimeMutationsFilterProps = {
@@ -0,0 +1,52 @@
1
+ {
2
+ "data": {
3
+ "mutations": ["ORF1a:T170I", "ORF1a:F499L", "S:T572I"],
4
+ "dateRanges": [
5
+ { "dateFrom": "2024-01-20", "dateTo": "2024-01-20" },
6
+ { "dateFrom": "2024-01-21", "dateTo": "2024-01-21" },
7
+ { "dateFrom": "2024-01-22", "dateTo": "2024-01-22" },
8
+ { "dateFrom": "2024-01-23", "dateTo": "2024-01-23" },
9
+ { "dateFrom": "2024-01-24", "dateTo": "2024-01-24" },
10
+ { "dateFrom": "2024-01-25", "dateTo": "2024-01-25" },
11
+ { "dateFrom": "2024-01-26", "dateTo": "2024-01-26" }
12
+ ],
13
+ "data": [
14
+ [
15
+ { "count": 121, "coverage": 455 },
16
+ { "count": 155, "coverage": 535 },
17
+ { "count": 314, "coverage": 1100 },
18
+ { "count": 245, "coverage": 1032 },
19
+ { "count": 225, "coverage": 968 },
20
+ { "count": 197, "coverage": 839 },
21
+ { "count": 171, "coverage": 635 }
22
+ ],
23
+ [
24
+ { "count": 20, "coverage": 459 },
25
+ { "count": 33, "coverage": 536 },
26
+ { "count": 79, "coverage": 1149 },
27
+ { "count": 71, "coverage": 1061 },
28
+ { "count": 66, "coverage": 995 },
29
+ { "count": 49, "coverage": 857 },
30
+ { "count": 42, "coverage": 668 }
31
+ ],
32
+ [
33
+ { "count": 26, "coverage": 456 },
34
+ { "count": 37, "coverage": 541 },
35
+ { "count": 83, "coverage": 1141 },
36
+ { "count": 81, "coverage": 1051 },
37
+ { "count": 70, "coverage": 986 },
38
+ { "count": 64, "coverage": 848 },
39
+ { "count": 48, "coverage": 664 }
40
+ ]
41
+ ],
42
+ "totalCountsByDateRange": [466, 548, 1155, 1067, 1007, 865, 671]
43
+ },
44
+ "info": {
45
+ "dataVersion": "1774200829",
46
+ "requestId": "74b55554-9ee9-4776-ba5f-48b93782ed51",
47
+ "requestInfo": "sars_cov-2_nextstrain_open on lapis.cov-spectrum.org at 2026-03-24T08:59:15.215671655",
48
+ "reportTo": "Please report to https://github.com/GenSpectrum/LAPIS/issues in case you encounter any unexpected issues. Please include the request ID and the requestInfo in your report.",
49
+ "lapisVersion": "0.7.0",
50
+ "siloVersion": "0.10.0"
51
+ }
52
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "data": {
3
+ "mutations": ["C44T", "C774T", "C1762A", "C11747T", "G17562T", "T18453C", "G21641T", "C23277T", "C29870A"],
4
+ "dateRanges": [
5
+ { "dateFrom": "2024-01-15", "dateTo": "2024-01-21" },
6
+ { "dateFrom": "2024-01-22", "dateTo": "2024-01-28" },
7
+ { "dateFrom": "2024-01-29", "dateTo": "2024-02-04" },
8
+ { "dateFrom": "2024-02-05", "dateTo": "2024-02-11" }
9
+ ],
10
+ "data": [
11
+ [
12
+ { "count": 548, "coverage": 923 },
13
+ { "count": 605, "coverage": 1040 },
14
+ { "count": 571, "coverage": 916 },
15
+ { "count": 481, "coverage": 858 }
16
+ ],
17
+ [
18
+ { "count": 1404, "coverage": 5260 },
19
+ { "count": 1358, "coverage": 5415 },
20
+ { "count": 1473, "coverage": 5404 },
21
+ { "count": 1370, "coverage": 5120 }
22
+ ],
23
+ [
24
+ { "count": 348, "coverage": 5345 },
25
+ { "count": 360, "coverage": 5592 },
26
+ { "count": 273, "coverage": 5470 },
27
+ { "count": 214, "coverage": 5158 }
28
+ ],
29
+ [
30
+ { "count": 322, "coverage": 5006 },
31
+ { "count": 323, "coverage": 5215 },
32
+ { "count": 259, "coverage": 5214 },
33
+ { "count": 191, "coverage": 4831 }
34
+ ],
35
+ [
36
+ { "count": 381, "coverage": 5383 },
37
+ { "count": 402, "coverage": 5618 },
38
+ { "count": 353, "coverage": 5500 },
39
+ { "count": 303, "coverage": 5194 }
40
+ ],
41
+ [
42
+ { "count": 1175, "coverage": 5348 },
43
+ { "count": 1104, "coverage": 5557 },
44
+ { "count": 1156, "coverage": 5474 },
45
+ { "count": 1100, "coverage": 5176 }
46
+ ],
47
+ [
48
+ { "count": 300, "coverage": 4800 },
49
+ { "count": 512, "coverage": 4860 },
50
+ { "count": 311, "coverage": 4833 },
51
+ { "count": 124, "coverage": 4390 }
52
+ ],
53
+ [
54
+ { "count": 337, "coverage": 5391 },
55
+ { "count": 431, "coverage": 5615 },
56
+ { "count": 565, "coverage": 5497 },
57
+ { "count": 662, "coverage": 5193 }
58
+ ],
59
+ [
60
+ { "count": 17, "coverage": 251 },
61
+ { "count": 7, "coverage": 260 },
62
+ { "count": 17, "coverage": 237 },
63
+ { "count": 13, "coverage": 185 }
64
+ ]
65
+ ],
66
+ "totalCountsByDateRange": [5395, 5624, 5506, 5200]
67
+ },
68
+ "info": {
69
+ "dataVersion": "1774200829",
70
+ "requestId": "84c09426-1724-49bb-8e8e-fcc0d98d2c89",
71
+ "requestInfo": "sars_cov-2_nextstrain_open on lapis.cov-spectrum.org at 2026-03-24T08:56:49.830150373",
72
+ "reportTo": "Please report to https://github.com/GenSpectrum/LAPIS/issues in case you encounter any unexpected issues. Please include the request ID and the requestInfo in your report.",
73
+ "lapisVersion": "0.7.0",
74
+ "siloVersion": "0.10.0"
75
+ }
76
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "data": {
3
+ "mutations": ["T21653-", "T21655-"],
4
+ "dateRanges": [
5
+ { "dateFrom": "2024-01-01", "dateTo": "2024-01-31" },
6
+ { "dateFrom": "2024-02-01", "dateTo": "2024-02-29" },
7
+ { "dateFrom": "2024-03-01", "dateTo": "2024-03-31" },
8
+ { "dateFrom": "2024-04-01", "dateTo": "2024-04-30" },
9
+ { "dateFrom": "2024-05-01", "dateTo": "2024-05-31" },
10
+ { "dateFrom": "2024-06-01", "dateTo": "2024-06-30" },
11
+ { "dateFrom": "2024-07-01", "dateTo": "2024-07-31" }
12
+ ],
13
+ "data": [
14
+ [
15
+ { "count": 13, "coverage": 13948 },
16
+ { "count": 15, "coverage": 17359 },
17
+ { "count": 41, "coverage": 8319 },
18
+ { "count": 237, "coverage": 6564 },
19
+ { "count": 1370, "coverage": 9054 },
20
+ { "count": 5079, "coverage": 16077 },
21
+ { "count": 3245, "coverage": 7342 }
22
+ ],
23
+ [
24
+ { "count": 12, "coverage": 13941 },
25
+ { "count": 14, "coverage": 17360 },
26
+ { "count": 41, "coverage": 8327 },
27
+ { "count": 235, "coverage": 6596 },
28
+ { "count": 1376, "coverage": 9143 },
29
+ { "count": 5089, "coverage": 16368 },
30
+ { "count": 3256, "coverage": 7477 }
31
+ ]
32
+ ],
33
+ "totalCountsByDateRange": [14065, 17583, 8432, 6680, 9252, 16512, 7508]
34
+ },
35
+ "info": {
36
+ "dataVersion": "1774200829",
37
+ "requestId": "dcc96846-d1d8-4ae2-afbe-60fcda8e64f0",
38
+ "requestInfo": "sars_cov-2_nextstrain_open on lapis.cov-spectrum.org at 2026-03-24T09:25:28.706437897",
39
+ "reportTo": "Please report to https://github.com/GenSpectrum/LAPIS/issues in case you encounter any unexpected issues. Please include the request ID and the requestInfo in your report.",
40
+ "lapisVersion": "0.7.0",
41
+ "siloVersion": "0.10.0"
42
+ }
43
+ }