@genspectrum/dashboard-components 1.1.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.
- package/custom-elements.json +22 -3
- package/dist/{NumberRangeFilterChangedEvent-B64OQZjX.js → NumberRangeFilterChangedEvent-CQ32Qy8D.js} +2 -2
- package/dist/NumberRangeFilterChangedEvent-CQ32Qy8D.js.map +1 -0
- package/dist/assets/mutationOverTimeWorker-C7saVShx.js.map +1 -0
- package/dist/components.d.ts +33 -27
- package/dist/components.js +119 -127
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +27 -27
- package/dist/util.js +1 -1
- package/package.json +7 -3
- package/src/lapisApi/lapisApi.ts +31 -2
- package/src/lapisApi/lapisTypes.ts +35 -1
- package/src/operator/DivisionOperator.ts +4 -2
- package/src/operator/FetchDetailsOperator.ts +1 -1
- package/src/operator/RenameFieldOperator.ts +3 -3
- package/src/preact/aggregatedData/aggregate.tsx +0 -5
- package/src/preact/components/annotated-mutation.tsx +0 -1
- package/src/preact/components/clearable-select.stories.tsx +1 -1
- package/src/preact/components/confidence-interval-selector.tsx +1 -1
- package/src/preact/components/error-boundary.tsx +1 -5
- package/src/preact/components/error-display.tsx +1 -1
- package/src/preact/components/fullscreen.tsx +2 -5
- package/src/preact/components/info.stories.tsx +1 -1
- package/src/preact/components/min-max-range-slider.tsx +1 -1
- package/src/preact/components/proportion-selector.tsx +4 -4
- package/src/preact/components/select.tsx +1 -1
- package/src/preact/components/table.tsx +1 -1
- package/src/preact/components/tabs.tsx +1 -1
- package/src/preact/components/tooltip.stories.tsx +1 -1
- package/src/preact/components/tooltip.tsx +1 -1
- package/src/preact/genomeViewer/CDSPlot.tsx +3 -3
- package/src/preact/genomeViewer/loadGff3.ts +5 -8
- package/src/preact/lineageFilter/lineage-filter.tsx +1 -1
- package/src/preact/locationFilter/location-filter.tsx +4 -4
- package/src/preact/mutationComparison/getMutationComparisonTableData.ts +1 -3
- package/src/preact/mutationComparison/mutation-comparison-venn.tsx +1 -1
- package/src/preact/mutationComparison/mutation-comparison.tsx +0 -5
- package/src/preact/mutationFilter/mutation-filter-info.tsx +2 -2
- package/src/preact/mutationFilter/mutation-filter.tsx +1 -1
- package/src/preact/mutations/getMutationsGridData.ts +2 -6
- package/src/preact/mutations/getMutationsTableData.ts +1 -1
- package/src/preact/mutations/mutations-grid.tsx +1 -1
- package/src/preact/mutations/mutations.tsx +0 -5
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay.ts +1 -0
- package/src/preact/mutationsOverTime/__mockData__/byWeek.ts +1 -0
- package/src/preact/mutationsOverTime/__mockData__/defaultMockData.ts +1 -0
- package/src/preact/mutationsOverTime/__mockData__/noDataWhenNoMutationsAreInFilter.ts +1 -0
- package/src/preact/mutationsOverTime/__mockData__/noDataWhenThereAreNoDatesInFilter.ts +1 -0
- package/src/preact/mutationsOverTime/__mockData__/showsMessageWhenTooManyMutations.ts +1 -0
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +2 -0
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +11 -11
- package/src/preact/numberRangeFilter/number-range-filter.tsx +4 -4
- package/src/preact/numberSequencesOverTime/getNumberOfSequencesOverTimeTableData.ts +1 -4
- package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +0 -5
- package/src/preact/prevalenceOverTime/prevalence-over-time-bar-chart.tsx +1 -1
- package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +1 -1
- package/src/preact/prevalenceOverTime/prevalence-over-time-line-chart.tsx +1 -1
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +1 -1
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage-chart.tsx +5 -5
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +1 -4
- package/src/preact/sequencesByLocation/loadMapSource.tsx +5 -2
- package/src/preact/shared/aspectRatio/AspectRatio.tsx +1 -1
- package/src/preact/shared/floating-ui/hooks.ts +2 -2
- package/src/preact/shared/sort/sortMutationPositions.ts +2 -2
- package/src/preact/shared/tanstackTable/pagination.tsx +2 -2
- package/src/preact/shared/tanstackTable/tanstackTable.tsx +1 -1
- package/src/preact/statistic/statistics.tsx +0 -5
- package/src/preact/textFilter/fetchStringAutocompleteList.ts +1 -10
- package/src/preact/textFilter/text-filter.tsx +1 -6
- package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +1 -1
- package/src/preact/webWorkers/useWebWorker.ts +2 -1
- package/src/preact/webWorkers/workerFunction.ts +2 -2
- package/src/query/computeMapLocationData.ts +1 -1
- package/src/query/queryAggregatedDataOverTime.ts +3 -3
- package/src/query/queryMutationsOverTime.spec.ts +9 -9
- package/src/query/queryMutationsOverTime.ts +121 -37
- package/src/query/queryMutationsOverTimeNewEndpoint.spec.ts +935 -0
- package/src/query/queryRelativeGrowthAdvantage.ts +5 -9
- package/src/query/queryWastewaterMutationsOverTime.ts +1 -1
- package/src/types.ts +1 -1
- package/src/utils/mutations.ts +10 -10
- package/src/utils/type-utils.ts +1 -1
- package/src/utils/typeAssertions.spec.ts +1 -1
- package/src/web-components/gs-app.spec-d.ts +1 -1
- package/src/web-components/gs-app.stories.ts +1 -1
- package/src/web-components/input/gs-date-range-filter.tsx +2 -2
- package/src/web-components/input/gs-lineage-filter.tsx +2 -2
- package/src/web-components/input/gs-location-filter.tsx +3 -3
- package/src/web-components/input/gs-mutation-filter.tsx +2 -2
- package/src/web-components/input/gs-number-range-filter.spec.ts +1 -1
- package/src/web-components/input/gs-text-filter.tsx +2 -2
- package/src/web-components/visualization/gs-aggregate.tsx +2 -2
- package/src/web-components/visualization/gs-genome-data-viewer.spec-d.ts +1 -1
- package/src/web-components/visualization/gs-mutation-comparison.tsx +2 -2
- package/src/web-components/visualization/gs-mutations-over-time.spec-d.ts +3 -0
- package/src/web-components/visualization/gs-mutations-over-time.stories.ts +3 -0
- package/src/web-components/visualization/gs-mutations-over-time.tsx +9 -0
- package/src/web-components/visualization/gs-mutations.tsx +2 -2
- package/src/web-components/visualization/gs-number-sequences-over-time.tsx +2 -2
- package/src/web-components/visualization/gs-prevalence-over-time.tsx +2 -2
- package/src/web-components/visualization/gs-relative-growth-advantage.tsx +2 -2
- package/src/web-components/visualization/gs-sequences-by-location.tsx +2 -2
- package/src/web-components/visualization/gs-statistics.tsx +2 -2
- package/standalone-bundle/assets/mutationOverTimeWorker-DRRi3aMG.js.map +1 -0
- package/standalone-bundle/dashboard-components.js +4454 -4430
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/dist/NumberRangeFilterChangedEvent-B64OQZjX.js.map +0 -1
- package/dist/assets/mutationOverTimeWorker-DjH04AQB.js.map +0 -1
- package/standalone-bundle/assets/mutationOverTimeWorker-B6bf3R3j.js.map +0 -1
|
@@ -8,7 +8,6 @@ import { lapisFilterSchema } from '../../types';
|
|
|
8
8
|
import { DownshiftCombobox } from '../components/downshift-combobox';
|
|
9
9
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
10
10
|
import { LoadingDisplay } from '../components/loading-display';
|
|
11
|
-
import { NoDataDisplay } from '../components/no-data-display';
|
|
12
11
|
import { ResizeContainer } from '../components/resize-container';
|
|
13
12
|
import { useQuery } from '../useQuery';
|
|
14
13
|
|
|
@@ -60,10 +59,6 @@ const TextFilterInner: FunctionComponent<TextFilterInnerProps> = ({
|
|
|
60
59
|
throw error;
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
if (data === null) {
|
|
64
|
-
return <NoDataDisplay />;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
62
|
return <TextSelector lapisField={lapisField} value={value} placeholderText={placeholderText} data={data} />;
|
|
68
63
|
};
|
|
69
64
|
|
|
@@ -106,5 +101,5 @@ function filterByInputValue(item: SelectItem, inputValue: string | null) {
|
|
|
106
101
|
if (inputValue === null || inputValue === '') {
|
|
107
102
|
return true;
|
|
108
103
|
}
|
|
109
|
-
return item.value
|
|
104
|
+
return item.value.toLowerCase().includes(inputValue.toLowerCase());
|
|
110
105
|
}
|
|
@@ -100,7 +100,7 @@ function useDisplayedSegments(mutations: MutationOverTimeDataPerLocation) {
|
|
|
100
100
|
const displayedSegments = useMemo(() => {
|
|
101
101
|
const unique = [
|
|
102
102
|
...new Set(
|
|
103
|
-
mutations.flatMap(({ data }) => data.getFirstAxisKeys().map((mutation) => mutation.segment
|
|
103
|
+
mutations.flatMap(({ data }) => data.getFirstAxisKeys().map((mutation) => mutation.segment ?? '')),
|
|
104
104
|
),
|
|
105
105
|
];
|
|
106
106
|
|
|
@@ -23,7 +23,8 @@ export type ErrorWorkerStatus =
|
|
|
23
23
|
};
|
|
24
24
|
export type WorkerStatus<Response> = LoadingWorkerStatus | SuccessWorkerStatus<Response> | ErrorWorkerStatus;
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- We need the return type
|
|
27
|
+
export function useWebWorker<Response>(messageToWorker: unknown, WorkerConstructor: new () => Worker) {
|
|
27
28
|
const [data, setData] = useState<Response | undefined>(undefined);
|
|
28
29
|
const [error, setError] = useState<Error | undefined>(undefined);
|
|
29
30
|
const [isLoading, setIsLoading] = useState(true);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ErrorWorkerStatus, type LoadingWorkerStatus, type SuccessWorkerStatus } from './useWebWorker';
|
|
2
2
|
import { UserFacingError } from '../components/error-display';
|
|
3
3
|
|
|
4
|
-
export async function workerFunction
|
|
4
|
+
export async function workerFunction(queryFunction: () => unknown) {
|
|
5
5
|
try {
|
|
6
6
|
postMessage({ status: 'loading' } satisfies LoadingWorkerStatus);
|
|
7
7
|
|
|
@@ -10,7 +10,7 @@ export async function workerFunction<R>(queryFunction: () => R) {
|
|
|
10
10
|
postMessage({
|
|
11
11
|
status: 'success',
|
|
12
12
|
data: workerResponse,
|
|
13
|
-
} satisfies SuccessWorkerStatus<
|
|
13
|
+
} satisfies SuccessWorkerStatus<unknown>);
|
|
14
14
|
} catch (error) {
|
|
15
15
|
postMessage(
|
|
16
16
|
(error instanceof UserFacingError
|
|
@@ -83,7 +83,7 @@ function matchLocationDataAndGeoJsonFeatures(
|
|
|
83
83
|
|
|
84
84
|
const locations: Feature<GeometryObject, EnhancedGeoJsonFeatureProperties>[] = geojsonData.features.map(
|
|
85
85
|
(feature) => {
|
|
86
|
-
const name = feature
|
|
86
|
+
const name = feature.properties.name;
|
|
87
87
|
if (typeof name !== 'string') {
|
|
88
88
|
throw new Error(
|
|
89
89
|
`GeoJSON feature with id '${feature.id}' does not have 'properties.name' of type string, was: '${name}'`,
|
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
type TemporalClass,
|
|
15
15
|
} from '../utils/temporalClass';
|
|
16
16
|
|
|
17
|
-
export function queryAggregatedDataOverTime
|
|
17
|
+
export function queryAggregatedDataOverTime(
|
|
18
18
|
filter: LapisFilter,
|
|
19
19
|
granularity: TemporalGranularity,
|
|
20
20
|
smoothingWindow: number,
|
|
21
|
-
lapisDateField:
|
|
21
|
+
lapisDateField: string,
|
|
22
22
|
) {
|
|
23
|
-
const fetchData = new FetchAggregatedOperator<
|
|
23
|
+
const fetchData = new FetchAggregatedOperator<Record<string, string | null>>(filter, [lapisDateField]);
|
|
24
24
|
const dataWithFixedDateKey = new RenameFieldOperator(fetchData, lapisDateField, 'date');
|
|
25
25
|
const mapData = new MapOperator(dataWithFixedDateKey, (d) => mapDateToGranularityRange(d, granularity));
|
|
26
26
|
const groupByData = new GroupByAndSumOperator(mapData, 'dateRange', 'count');
|
|
@@ -231,18 +231,14 @@ describe('queryMutationsOverTime', () => {
|
|
|
231
231
|
minProportion: 0.001,
|
|
232
232
|
},
|
|
233
233
|
response: {
|
|
234
|
-
data: [
|
|
235
|
-
getSomeTestMutation(0.1, 1),
|
|
236
|
-
getSomeTestMutation(0.3, 3),
|
|
237
|
-
getSomeOtherTestMutation(0.4, 4),
|
|
238
|
-
],
|
|
234
|
+
data: [getSomeTestMutation(0.2, 4), getSomeOtherTestMutation(0.4, 4)],
|
|
239
235
|
},
|
|
240
236
|
},
|
|
241
237
|
],
|
|
242
238
|
'nucleotide',
|
|
243
239
|
);
|
|
244
240
|
|
|
245
|
-
const { mutationOverTimeData } = await queryMutationsOverTimeData({
|
|
241
|
+
const { mutationOverTimeData, overallMutationData } = await queryMutationsOverTimeData({
|
|
246
242
|
lapisFilter,
|
|
247
243
|
sequenceType: 'nucleotide',
|
|
248
244
|
lapis: DUMMY_LAPIS_URL,
|
|
@@ -263,6 +259,8 @@ describe('queryMutationsOverTime', () => {
|
|
|
263
259
|
],
|
|
264
260
|
]);
|
|
265
261
|
|
|
262
|
+
expect(overallMutationData.length).toBe(2);
|
|
263
|
+
|
|
266
264
|
const sequences = mutationOverTimeData.getFirstAxisKeys();
|
|
267
265
|
expect(sequences[0].code).toBe('otherSequenceName:G234C');
|
|
268
266
|
expect(sequences[1].code).toBe('sequenceName:A123T');
|
|
@@ -435,14 +433,14 @@ describe('queryMutationsOverTime', () => {
|
|
|
435
433
|
minProportion: 0.001,
|
|
436
434
|
},
|
|
437
435
|
response: {
|
|
438
|
-
data: [getSomeTestMutation(0.
|
|
436
|
+
data: [getSomeTestMutation(0.25, 5)],
|
|
439
437
|
},
|
|
440
438
|
},
|
|
441
439
|
],
|
|
442
440
|
'nucleotide',
|
|
443
441
|
);
|
|
444
442
|
|
|
445
|
-
const { mutationOverTimeData } = await queryMutationsOverTimeData({
|
|
443
|
+
const { mutationOverTimeData, overallMutationData } = await queryMutationsOverTimeData({
|
|
446
444
|
lapisFilter,
|
|
447
445
|
sequenceType: 'nucleotide',
|
|
448
446
|
lapis: DUMMY_LAPIS_URL,
|
|
@@ -450,6 +448,8 @@ describe('queryMutationsOverTime', () => {
|
|
|
450
448
|
granularity: 'day',
|
|
451
449
|
});
|
|
452
450
|
|
|
451
|
+
expect(overallMutationData.length).toBe(1);
|
|
452
|
+
|
|
453
453
|
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
454
454
|
[
|
|
455
455
|
{ type: 'value', proportion: 0.2, count: 2, totalCount: 11 },
|
|
@@ -527,7 +527,7 @@ describe('queryMutationsOverTime', () => {
|
|
|
527
527
|
minProportion: 0.001,
|
|
528
528
|
},
|
|
529
529
|
response: {
|
|
530
|
-
data: [getSomeTestMutation(0.
|
|
530
|
+
data: [getSomeTestMutation(0.15, 3)],
|
|
531
531
|
},
|
|
532
532
|
},
|
|
533
533
|
],
|
|
@@ -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,
|
|
@@ -54,7 +56,7 @@ export type MutationOverTimeMutationValue =
|
|
|
54
56
|
const MAX_NUMBER_OF_GRID_COLUMNS = 200;
|
|
55
57
|
export const MUTATIONS_OVER_TIME_MIN_PROPORTION = 0.001;
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
async function queryOverallMutationData({
|
|
58
60
|
lapisFilter,
|
|
59
61
|
sequenceType,
|
|
60
62
|
lapis,
|
|
@@ -69,9 +71,9 @@ export async function queryOverallMutationData({
|
|
|
69
71
|
lapisDateField: string;
|
|
70
72
|
signal?: AbortSignal;
|
|
71
73
|
}) {
|
|
72
|
-
const
|
|
74
|
+
const requestedDateRanges = await getDatesInDataset(lapisFilter, lapis, granularity, lapisDateField, signal);
|
|
73
75
|
|
|
74
|
-
if (
|
|
76
|
+
if (requestedDateRanges.length === 0) {
|
|
75
77
|
return {
|
|
76
78
|
content: [],
|
|
77
79
|
};
|
|
@@ -79,8 +81,8 @@ export async function queryOverallMutationData({
|
|
|
79
81
|
|
|
80
82
|
const filter = {
|
|
81
83
|
...lapisFilter,
|
|
82
|
-
[`${lapisDateField}From`]:
|
|
83
|
-
[`${lapisDateField}To`]:
|
|
84
|
+
[`${lapisDateField}From`]: requestedDateRanges[0].firstDay.toString(),
|
|
85
|
+
[`${lapisDateField}To`]: requestedDateRanges[requestedDateRanges.length - 1].lastDay.toString(),
|
|
84
86
|
};
|
|
85
87
|
|
|
86
88
|
return fetchAndPrepareSubstitutionsOrDeletions(filter, sequenceType).evaluate(lapis, signal);
|
|
@@ -92,28 +94,42 @@ 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
|
-
|
|
101
|
-
lapis,
|
|
102
|
-
lapisDateField,
|
|
103
|
-
granularity,
|
|
104
|
-
signal,
|
|
105
|
-
}: MutationOverTimeQuery) {
|
|
106
|
-
const allDates = await getDatesInDataset(lapisFilter, lapis, granularity, lapisDateField, signal);
|
|
101
|
+
export async function queryMutationsOverTimeData(query: MutationOverTimeQuery) {
|
|
102
|
+
const { lapisFilter, sequenceType, lapis, lapisDateField, granularity, useNewEndpoint, signal } = query;
|
|
103
|
+
|
|
104
|
+
const requestedDateRanges = await getDatesInDataset(lapisFilter, lapis, granularity, lapisDateField, signal);
|
|
107
105
|
|
|
108
|
-
if (
|
|
106
|
+
if (requestedDateRanges.length > MAX_NUMBER_OF_GRID_COLUMNS) {
|
|
109
107
|
throw new UserFacingError(
|
|
110
108
|
'Too many dates',
|
|
111
|
-
`The dataset would contain ${
|
|
109
|
+
`The dataset would contain ${requestedDateRanges.length} date intervals. ` +
|
|
112
110
|
`Please reduce the number to below ${MAX_NUMBER_OF_GRID_COLUMNS} to display the data. ` +
|
|
113
111
|
'You can achieve this by either narrowing the date range in the provided LAPIS filter or by selecting a larger granularity.',
|
|
114
112
|
);
|
|
115
113
|
}
|
|
116
114
|
|
|
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
|
+
) {
|
|
117
133
|
const subQueries = allDates.map(async (date) => {
|
|
118
134
|
const dateFrom = date.firstDay.toString();
|
|
119
135
|
const dateTo = date.lastDay.toString();
|
|
@@ -124,8 +140,10 @@ export async function queryMutationsOverTimeData({
|
|
|
124
140
|
[`${lapisDateField}To`]: dateTo,
|
|
125
141
|
};
|
|
126
142
|
|
|
127
|
-
const data = await
|
|
128
|
-
|
|
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,23 +153,89 @@ export async function queryMutationsOverTimeData({
|
|
|
135
153
|
});
|
|
136
154
|
|
|
137
155
|
const data = await Promise.all(subQueries);
|
|
156
|
+
const overallMutationData = await overallMutationDataPromise;
|
|
138
157
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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:
|
|
151
|
-
overallMutationData
|
|
228
|
+
mutationOverTimeData: new BaseMutationOverTimeDataMap(mutationOverTimeData),
|
|
229
|
+
overallMutationData,
|
|
152
230
|
};
|
|
153
231
|
}
|
|
154
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Returns a list of date ranges as TemporalClass.
|
|
235
|
+
* Respects date range filters given in the lapisFilter as <lapisDateField>From and <lapisDateField>To.
|
|
236
|
+
* If either side (or both sides) of the range are not given, the min and max are determined from
|
|
237
|
+
* the available data.
|
|
238
|
+
*/
|
|
155
239
|
async function getDatesInDataset(
|
|
156
240
|
lapisFilter: LapisFilter,
|
|
157
241
|
lapis: string,
|
|
@@ -159,6 +243,11 @@ async function getDatesInDataset(
|
|
|
159
243
|
lapisDateField: string,
|
|
160
244
|
signal: AbortSignal | undefined,
|
|
161
245
|
) {
|
|
246
|
+
const { dateFrom, dateTo } = getDateRangeFromFilter(lapisFilter, lapisDateField, granularity);
|
|
247
|
+
if (dateFrom !== null && dateTo !== null) {
|
|
248
|
+
return generateAllInRange(dateFrom, dateTo);
|
|
249
|
+
}
|
|
250
|
+
|
|
162
251
|
const { content: availableDates } = await queryAvailableDates(
|
|
163
252
|
lapisFilter,
|
|
164
253
|
lapis,
|
|
@@ -167,7 +256,6 @@ async function getDatesInDataset(
|
|
|
167
256
|
signal,
|
|
168
257
|
);
|
|
169
258
|
|
|
170
|
-
const { dateFrom, dateTo } = getDateRangeFromFilter(lapisFilter, lapisDateField, granularity);
|
|
171
259
|
const { min, max } = getMinMaxTemporal(availableDates);
|
|
172
260
|
|
|
173
261
|
return generateAllInRange(dateFrom ?? min, dateTo ?? max);
|
|
@@ -202,12 +290,8 @@ function queryAvailableDates(
|
|
|
202
290
|
return fetchAndPrepareDates(lapisFilter, granularity, lapisDateField).evaluate(lapis, signal);
|
|
203
291
|
}
|
|
204
292
|
|
|
205
|
-
function fetchAndPrepareDates
|
|
206
|
-
filter
|
|
207
|
-
granularity: TemporalGranularity,
|
|
208
|
-
lapisDateField: LapisDateField,
|
|
209
|
-
) {
|
|
210
|
-
const fetchData = new FetchAggregatedOperator<{ [key in LapisDateField]: string | null }>(filter, [lapisDateField]);
|
|
293
|
+
function fetchAndPrepareDates(filter: LapisFilter, granularity: TemporalGranularity, lapisDateField: string) {
|
|
294
|
+
const fetchData = new FetchAggregatedOperator<Record<string, string | null>>(filter, [lapisDateField]);
|
|
211
295
|
const dataWithFixedDateKey = new RenameFieldOperator(fetchData, lapisDateField, 'date');
|
|
212
296
|
const mapData = new MapOperator(dataWithFixedDateKey, (data) => mapDateToGranularityRange(data, granularity));
|
|
213
297
|
const groupByData = new GroupByAndSumOperator(mapData, 'dateRange', 'count');
|
|
@@ -230,7 +314,7 @@ export function serializeTemporal(date: Temporal) {
|
|
|
230
314
|
export function groupByMutation(
|
|
231
315
|
data: MutationOverTimeData[],
|
|
232
316
|
overallMutationData: (SubstitutionEntry | DeletionEntry)[],
|
|
233
|
-
) {
|
|
317
|
+
): BaseMutationOverTimeDataMap {
|
|
234
318
|
const dataArray = new BaseMutationOverTimeDataMap();
|
|
235
319
|
|
|
236
320
|
const allDates = data.map((mutationData) => mutationData.date);
|