@genspectrum/dashboard-components 1.11.1 → 1.13.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 (58) hide show
  1. package/README.md +0 -7
  2. package/custom-elements.json +6 -25
  3. package/dist/components.d.ts +24 -30
  4. package/dist/components.js +929 -742
  5. package/dist/components.js.map +1 -1
  6. package/dist/util.d.ts +40 -24
  7. package/package.json +1 -5
  8. package/src/lapisApi/lapisApi.ts +21 -1
  9. package/src/lapisApi/lapisTypes.ts +37 -0
  10. package/src/preact/components/annotated-mutation.tsx +2 -2
  11. package/src/preact/{mutationsOverTime/mutations-over-time-grid.tsx → components/features-over-time-grid.tsx} +45 -52
  12. package/src/preact/genomeViewer/genome-data-viewer.tsx +2 -2
  13. package/src/preact/mutationsOverTime/MutationOverTimeData.ts +6 -4
  14. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutations.json +5482 -0
  15. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutationsOverTime.json +5496 -0
  16. package/src/preact/mutationsOverTime/__mockData__/byWeek/mutationsOverTime.json +7100 -0
  17. package/src/preact/mutationsOverTime/__mockData__/byWeek/nucleotideMutations.json +10122 -0
  18. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTime.json +12646 -0
  19. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/nucleotideMutations.json +12632 -0
  20. package/src/preact/mutationsOverTime/__mockData__/request1800s/mutationsOverTime.json +16 -0
  21. package/src/preact/mutationsOverTime/__mockData__/request1800s/nucleotideMutations.json +11 -0
  22. package/src/preact/mutationsOverTime/__mockData__/withDisplayMutations/mutationsOverTime.json +52 -0
  23. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +3 -3
  24. package/src/preact/mutationsOverTime/mutations-over-time-grid-tooltip.tsx +3 -6
  25. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +199 -12
  26. package/src/preact/mutationsOverTime/mutations-over-time.tsx +30 -35
  27. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +30 -3
  28. package/src/query/queryDatesInDataset.ts +89 -0
  29. package/src/query/queryMutationsOverTime.spec.ts +526 -548
  30. package/src/query/queryMutationsOverTime.ts +22 -245
  31. package/src/query/queryQueriesOverTime.spec.ts +432 -0
  32. package/src/query/queryQueriesOverTime.ts +125 -0
  33. package/src/utilEntrypoint.ts +3 -1
  34. package/src/utils/mutations.spec.ts +6 -0
  35. package/src/utils/mutations.ts +1 -1
  36. package/src/utils/temporalClass.ts +4 -0
  37. package/src/web-components/visualization/gs-mutation-comparison.tsx +2 -2
  38. package/src/web-components/visualization/gs-mutations-over-time.spec-d.ts +0 -3
  39. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +283 -17
  40. package/src/web-components/visualization/gs-mutations-over-time.tsx +0 -9
  41. package/standalone-bundle/dashboard-components.js +8935 -8780
  42. package/standalone-bundle/dashboard-components.js.map +1 -1
  43. package/dist/assets/mutationOverTimeWorker-CQQFRoK4.js.map +0 -1
  44. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay.ts +0 -47170
  45. package/src/preact/mutationsOverTime/__mockData__/byWeek.ts +0 -54026
  46. package/src/preact/mutationsOverTime/__mockData__/defaultMockData.ts +0 -108385
  47. package/src/preact/mutationsOverTime/__mockData__/mockConversion.ts +0 -54
  48. package/src/preact/mutationsOverTime/__mockData__/noDataWhenNoMutationsAreInFilter.ts +0 -23
  49. package/src/preact/mutationsOverTime/__mockData__/noDataWhenThereAreNoDatesInFilter.ts +0 -23
  50. package/src/preact/mutationsOverTime/__mockData__/showsMessageWhenTooManyMutations.ts +0 -65527
  51. package/src/preact/mutationsOverTime/__mockData__/withDisplayMutations.ts +0 -352
  52. package/src/preact/mutationsOverTime/__mockData__/withGaps.ts +0 -298
  53. package/src/preact/mutationsOverTime/mutationOverTimeWorker.mock.ts +0 -33
  54. package/src/preact/mutationsOverTime/mutationOverTimeWorker.ts +0 -29
  55. package/src/preact/webWorkers/useWebWorker.ts +0 -74
  56. package/src/preact/webWorkers/workerFunction.ts +0 -30
  57. package/src/query/queryMutationsOverTimeNewEndpoint.spec.ts +0 -1179
  58. package/standalone-bundle/assets/mutationOverTimeWorker-DIpJukJC.js.map +0 -1
@@ -1,48 +1,20 @@
1
- import { mapDateToGranularityRange } from './queryAggregatedDataOverTime';
1
+ import { queryDatesInDataset } from './queryDatesInDataset';
2
2
  import { fetchMutationsOverTime } from '../lapisApi/lapisApi';
3
- import { FetchAggregatedOperator } from '../operator/FetchAggregatedOperator';
4
3
  import { FetchSubstitutionsOrDeletionsOperator } from '../operator/FetchSubstitutionsOrDeletionsOperator';
5
- import { GroupByAndSumOperator } from '../operator/GroupByAndSumOperator';
6
- import { MapOperator } from '../operator/MapOperator';
7
- import { RenameFieldOperator } from '../operator/RenameFieldOperator';
8
- import { SortOperator } from '../operator/SortOperator';
9
4
  import { UserFacingError } from '../preact/components/error-display';
10
5
  import { BaseMutationOverTimeDataMap } from '../preact/mutationsOverTime/MutationOverTimeData';
11
6
  import { sortSubstitutionsAndDeletions } from '../preact/shared/sort/sortSubstitutionsAndDeletions';
12
7
  import {
13
- type DeletionEntry,
14
8
  type LapisFilter,
15
9
  type SequenceType,
16
- type SubstitutionEntry,
17
10
  type SubstitutionOrDeletionEntry,
18
11
  type TemporalGranularity,
19
12
  } from '../types';
20
13
  import { type Map2DContents } from '../utils/map2d';
21
- import {
22
- type Deletion,
23
- type Substitution,
24
- toSubstitutionOrDeletion,
25
- DeletionClass,
26
- SubstitutionClass,
27
- } from '../utils/mutations';
28
- import {
29
- compareTemporal,
30
- dateRangeCompare,
31
- generateAllInRange,
32
- getMinMaxTemporal,
33
- parseDateStringToTemporal,
34
- type Temporal,
35
- type TemporalClass,
36
- toTemporal,
37
- } from '../utils/temporalClass';
38
-
39
- export type MutationOverTimeData = {
40
- date: TemporalClass;
41
- mutations: SubstitutionOrDeletionEntry[];
42
- totalCount: number;
43
- };
14
+ import { type Deletion, type Substitution, DeletionClass, SubstitutionClass } from '../utils/mutations';
15
+ import { type Temporal } from '../utils/temporalClass';
44
16
 
45
- export type MutationOverTimeMutationValue =
17
+ export type ProportionValue =
46
18
  | {
47
19
  type: 'value';
48
20
  proportion: number;
@@ -65,7 +37,7 @@ export type MutationOverTimeMutationValue =
65
37
  }
66
38
  | null;
67
39
 
68
- export function getProportion(value: MutationOverTimeMutationValue) {
40
+ export function getProportion(value: ProportionValue) {
69
41
  switch (value?.type) {
70
42
  case 'value':
71
43
  case 'wastewaterValue':
@@ -130,7 +102,7 @@ async function queryOverallMutationData({
130
102
  includeMutations?: string[];
131
103
  signal?: AbortSignal;
132
104
  }) {
133
- const requestedDateRanges = await getDatesInDataset(lapisFilter, lapis, granularity, lapisDateField, signal);
105
+ const requestedDateRanges = await queryDatesInDataset(lapisFilter, lapis, granularity, lapisDateField, signal);
134
106
 
135
107
  if (requestedDateRanges.length === 0) {
136
108
  if (includeMutations) {
@@ -170,22 +142,16 @@ async function queryOverallMutationData({
170
142
  return dataPromise;
171
143
  }
172
144
 
173
- export type MutationOverTimeQuery = {
174
- lapisFilter: LapisFilter;
175
- displayMutations?: string[];
176
- sequenceType: SequenceType;
177
- lapis: string;
178
- lapisDateField: string;
179
- granularity: TemporalGranularity;
180
- useNewEndpoint?: boolean;
181
- signal?: AbortSignal;
182
- };
183
-
184
- export async function queryMutationsOverTimeData(query: MutationOverTimeQuery) {
185
- const { lapisFilter, displayMutations, sequenceType, lapis, lapisDateField, granularity, useNewEndpoint, signal } =
186
- query;
187
-
188
- const requestedDateRanges = await getDatesInDataset(lapisFilter, lapis, granularity, lapisDateField, signal);
145
+ export async function queryMutationsOverTimeData(
146
+ lapisFilter: LapisFilter,
147
+ sequenceType: SequenceType,
148
+ lapis: string,
149
+ lapisDateField: string,
150
+ granularity: TemporalGranularity,
151
+ displayMutations?: string[],
152
+ signal?: AbortSignal,
153
+ ) {
154
+ const requestedDateRanges = await queryDatesInDataset(lapisFilter, lapis, granularity, lapisDateField, signal);
189
155
 
190
156
  if (requestedDateRanges.length > MAX_NUMBER_OF_GRID_COLUMNS) {
191
157
  throw new UserFacingError(
@@ -196,7 +162,7 @@ export async function queryMutationsOverTimeData(query: MutationOverTimeQuery) {
196
162
  );
197
163
  }
198
164
 
199
- const overallMutationData = queryOverallMutationData({
165
+ const overallMutationData = await queryOverallMutationData({
200
166
  lapisFilter,
201
167
  sequenceType,
202
168
  lapis,
@@ -205,77 +171,14 @@ export async function queryMutationsOverTimeData(query: MutationOverTimeQuery) {
205
171
  granularity,
206
172
  }).then((r) => r.content);
207
173
 
208
- return useNewEndpoint === true
209
- ? queryMutationsOverTimeDataDirectEndpoint(requestedDateRanges, overallMutationData, query)
210
- : queryMutationsOverTimeDataMultiQuery(requestedDateRanges, overallMutationData, query);
211
- }
212
-
213
- async function queryMutationsOverTimeDataMultiQuery(
214
- allDates: TemporalClass[],
215
- overallMutationDataPromise: Promise<SubstitutionOrDeletionEntry[]>,
216
- { lapisFilter, sequenceType, lapis, lapisDateField, signal }: MutationOverTimeQuery,
217
- ) {
218
- const subQueries = allDates.map(async (date) => {
219
- const dateFrom = date.firstDay.toString();
220
- const dateTo = date.lastDay.toString();
221
-
222
- const filter = {
223
- ...lapisFilter,
224
- [`${lapisDateField}From`]: dateFrom,
225
- [`${lapisDateField}To`]: dateTo,
226
- };
227
-
228
- const [data, totalCountQuery] = await Promise.all([
229
- fetchAndPrepareSubstitutionsOrDeletions(filter, sequenceType).evaluate(lapis, signal),
230
- getTotalNumberOfSequencesInDateRange(filter).evaluate(lapis, signal),
231
- ]);
232
-
233
- return {
234
- date,
235
- mutations: data.content,
236
- totalCount: totalCountQuery.content[0].count,
237
- };
238
- });
239
-
240
- const data = await Promise.all(subQueries);
241
- const overallMutationData = await overallMutationDataPromise;
242
-
243
- return {
244
- mutationOverTimeData: groupByMutation(data, overallMutationData),
245
- overallMutationData,
246
- };
247
- }
248
-
249
- async function queryMutationsOverTimeDataDirectEndpoint(
250
- allDates: TemporalClass[],
251
- overallMutationDataPromise: Promise<SubstitutionOrDeletionEntry[]>,
252
- { lapisFilter, sequenceType, lapis, lapisDateField, signal }: MutationOverTimeQuery,
253
- ): Promise<{
254
- mutationOverTimeData: BaseMutationOverTimeDataMap;
255
- overallMutationData: SubstitutionOrDeletionEntry[];
256
- }> {
257
- const overallMutationData = await overallMutationDataPromise;
258
174
  overallMutationData.sort((a, b) => sortSubstitutionsAndDeletions(a.mutation, b.mutation));
259
- const totalCounts = await Promise.all(
260
- allDates.map(async (date) => {
261
- const filter = {
262
- ...lapisFilter,
263
- [`${lapisDateField}From`]: date.firstDay.toString(),
264
- [`${lapisDateField}To`]: date.lastDay.toString(),
265
- };
266
-
267
- const totalCountQuery = await getTotalNumberOfSequencesInDateRange(filter).evaluate(lapis, signal);
268
-
269
- return totalCountQuery.content[0].count;
270
- }),
271
- );
272
175
 
273
176
  const includeMutations = overallMutationData.map((value) => value.mutation.code);
274
177
  const apiResult = await fetchMutationsOverTime(
275
178
  lapis,
276
179
  {
277
180
  filters: lapisFilter,
278
- dateRanges: allDates.map((date) => ({
181
+ dateRanges: requestedDateRanges.map((date) => ({
279
182
  dateFrom: date.firstDay.toString(),
280
183
  dateTo: date.lastDay.toString(),
281
184
  })),
@@ -286,6 +189,7 @@ async function queryMutationsOverTimeDataDirectEndpoint(
286
189
  signal,
287
190
  );
288
191
 
192
+ const totalCounts = apiResult.data.totalCountsByDateRange;
289
193
  const responseMutations = apiResult.data.mutations.map(parseMutationCode);
290
194
  const mutationEntries: SubstitutionOrDeletionEntry[] = responseMutations.map((mutation, i) => {
291
195
  const numbers = {
@@ -307,14 +211,14 @@ async function queryMutationsOverTimeDataDirectEndpoint(
307
211
  }
308
212
  });
309
213
 
310
- const mutationOverTimeData: Map2DContents<Substitution | Deletion, Temporal, MutationOverTimeMutationValue> = {
214
+ const mutationOverTimeData: Map2DContents<Substitution | Deletion, Temporal, ProportionValue> = {
311
215
  keysFirstAxis: new Map(responseMutations.map((mutation) => [mutation.code, mutation])),
312
- keysSecondAxis: new Map(allDates.map((date) => [date.dateString, date])),
216
+ keysSecondAxis: new Map(requestedDateRanges.map((date) => [date.dateString, date])),
313
217
  data: new Map(
314
218
  responseMutations.map((mutation, i) => [
315
219
  mutation.code,
316
220
  new Map(
317
- allDates.map((date, j): [string, MutationOverTimeMutationValue] => {
221
+ requestedDateRanges.map((date, j): [string, ProportionValue] => {
318
222
  if (totalCounts[j] === 0) {
319
223
  return [date.dateString, null];
320
224
  }
@@ -365,75 +269,6 @@ function parseMutationCode(code: string): SubstitutionClass | DeletionClass {
365
269
  throw Error(`Given code is not valid: ${code}`);
366
270
  }
367
271
 
368
- /**
369
- * Returns a list of date ranges as TemporalClass.
370
- * Respects date range filters given in the lapisFilter as <lapisDateField>From and <lapisDateField>To.
371
- * If either side (or both sides) of the range are not given, the min and max are determined from
372
- * the available data.
373
- */
374
- async function getDatesInDataset(
375
- lapisFilter: LapisFilter,
376
- lapis: string,
377
- granularity: TemporalGranularity,
378
- lapisDateField: string,
379
- signal: AbortSignal | undefined,
380
- ) {
381
- const { dateFrom, dateTo } = getDateRangeFromFilter(lapisFilter, lapisDateField, granularity);
382
- if (dateFrom !== null && dateTo !== null) {
383
- return generateAllInRange(dateFrom, dateTo);
384
- }
385
-
386
- const { content: availableDates } = await queryAvailableDates(
387
- lapisFilter,
388
- lapis,
389
- granularity,
390
- lapisDateField,
391
- signal,
392
- );
393
-
394
- const { min, max } = getMinMaxTemporal(availableDates);
395
-
396
- return generateAllInRange(dateFrom ?? min, dateTo ?? max);
397
- }
398
-
399
- function getDateRangeFromFilter(lapisFilter: LapisFilter, lapisDateField: string, granularity: TemporalGranularity) {
400
- const valueFromFilter = lapisFilter[lapisDateField] as string | null;
401
-
402
- if (valueFromFilter) {
403
- return {
404
- dateFrom: parseDateStringToTemporal(valueFromFilter, granularity),
405
- dateTo: parseDateStringToTemporal(valueFromFilter, granularity),
406
- };
407
- }
408
-
409
- const minFromFilter = lapisFilter[`${lapisDateField}From`] as string | null;
410
- const maxFromFilter = lapisFilter[`${lapisDateField}To`] as string | null;
411
-
412
- return {
413
- dateFrom: minFromFilter ? parseDateStringToTemporal(minFromFilter, granularity) : null,
414
- dateTo: maxFromFilter ? parseDateStringToTemporal(maxFromFilter, granularity) : null,
415
- };
416
- }
417
-
418
- function queryAvailableDates(
419
- lapisFilter: LapisFilter,
420
- lapis: string,
421
- granularity: TemporalGranularity,
422
- lapisDateField: string,
423
- signal?: AbortSignal,
424
- ) {
425
- return fetchAndPrepareDates(lapisFilter, granularity, lapisDateField).evaluate(lapis, signal);
426
- }
427
-
428
- function fetchAndPrepareDates(filter: LapisFilter, granularity: TemporalGranularity, lapisDateField: string) {
429
- const fetchData = new FetchAggregatedOperator<Record<string, string | null>>(filter, [lapisDateField]);
430
- const dataWithFixedDateKey = new RenameFieldOperator(fetchData, lapisDateField, 'date');
431
- const mapData = new MapOperator(dataWithFixedDateKey, (data) => mapDateToGranularityRange(data, granularity));
432
- const groupByData = new GroupByAndSumOperator(mapData, 'dateRange', 'count');
433
- const sortData = new SortOperator(groupByData, dateRangeCompare);
434
- return new MapOperator(sortData, (data) => data.dateRange);
435
- }
436
-
437
272
  function fetchAndPrepareSubstitutionsOrDeletions(filter: LapisFilter, sequenceType: SequenceType) {
438
273
  return new FetchSubstitutionsOrDeletionsOperator(filter, sequenceType, MUTATIONS_OVER_TIME_MIN_PROPORTION);
439
274
  }
@@ -445,61 +280,3 @@ export function serializeSubstitutionOrDeletion(mutation: Substitution | Deletio
445
280
  export function serializeTemporal(date: Temporal) {
446
281
  return date.dateString;
447
282
  }
448
-
449
- export function groupByMutation(
450
- data: MutationOverTimeData[],
451
- overallMutationData: (SubstitutionEntry | DeletionEntry)[],
452
- ): BaseMutationOverTimeDataMap {
453
- const dataArray = new BaseMutationOverTimeDataMap();
454
-
455
- const allDates = data.map((mutationData) => mutationData.date);
456
-
457
- const sortedOverallMutationData = overallMutationData
458
- .sort((a, b) => sortSubstitutionsAndDeletions(a.mutation, b.mutation))
459
- .map((entry) => {
460
- return toSubstitutionOrDeletion(entry.mutation);
461
- });
462
- const sortedDates = allDates.sort((a, b) => compareTemporal(a, b)).map((date) => toTemporal(date));
463
-
464
- sortedOverallMutationData.forEach((mutationData) => {
465
- sortedDates.forEach((date) => {
466
- dataArray.set(mutationData, date, null);
467
- });
468
- });
469
-
470
- data.forEach((mutationData) => {
471
- if (mutationData.totalCount == 0) {
472
- return;
473
- }
474
-
475
- const date = toTemporal(mutationData.date);
476
-
477
- mutationData.mutations.forEach((mutationEntry) => {
478
- const mutation = toSubstitutionOrDeletion(mutationEntry.mutation);
479
-
480
- if (dataArray.get(mutation, date) !== undefined) {
481
- dataArray.set(mutation, date, {
482
- type: 'value',
483
- count: mutationEntry.count,
484
- proportion: mutationEntry.proportion,
485
- totalCount: mutationData.totalCount,
486
- });
487
- }
488
- });
489
-
490
- for (const firstAxisKey of dataArray.getFirstAxisKeys()) {
491
- if (dataArray.get(firstAxisKey, date) === null) {
492
- dataArray.set(firstAxisKey, date, {
493
- type: 'belowThreshold',
494
- totalCount: mutationData.totalCount,
495
- });
496
- }
497
- }
498
- });
499
-
500
- return dataArray;
501
- }
502
-
503
- function getTotalNumberOfSequencesInDateRange(filter: LapisFilter) {
504
- return new FetchAggregatedOperator<{ count: number }>(filter);
505
- }