@genspectrum/dashboard-components 1.14.1 → 1.14.2
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/dist/components.d.ts +55 -55
- package/dist/components.js +27 -1
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +55 -55
- package/package.json +1 -2
- package/src/preact/components/features-over-time-grid.tsx +10 -2
- package/src/preact/wastewater/mutationsOverTime/computeWastewaterMutationsOverTimeDataPerLocation.spec.ts +55 -0
- package/src/preact/wastewater/mutationsOverTime/computeWastewaterMutationsOverTimeDataPerLocation.ts +26 -0
- package/standalone-bundle/dashboard-components.js +3664 -3652
- package/standalone-bundle/dashboard-components.js.map +1 -1
package/dist/util.d.ts
CHANGED
|
@@ -1106,11 +1106,7 @@ declare global {
|
|
|
1106
1106
|
|
|
1107
1107
|
declare global {
|
|
1108
1108
|
interface HTMLElementTagNameMap {
|
|
1109
|
-
'gs-
|
|
1110
|
-
}
|
|
1111
|
-
interface HTMLElementEventMap {
|
|
1112
|
-
[gsEventNames.dateRangeFilterChanged]: CustomEvent<Record<string, string>>;
|
|
1113
|
-
[gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
|
|
1109
|
+
'gs-genome-data-viewer': GenomeDataViewerComponent;
|
|
1114
1110
|
}
|
|
1115
1111
|
}
|
|
1116
1112
|
|
|
@@ -1118,7 +1114,7 @@ declare global {
|
|
|
1118
1114
|
declare global {
|
|
1119
1115
|
namespace JSX {
|
|
1120
1116
|
interface IntrinsicElements {
|
|
1121
|
-
'gs-
|
|
1117
|
+
'gs-genome-data-viewer': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1122
1118
|
}
|
|
1123
1119
|
}
|
|
1124
1120
|
}
|
|
@@ -1126,10 +1122,7 @@ declare global {
|
|
|
1126
1122
|
|
|
1127
1123
|
declare global {
|
|
1128
1124
|
interface HTMLElementTagNameMap {
|
|
1129
|
-
'gs-
|
|
1130
|
-
}
|
|
1131
|
-
interface HTMLElementEventMap {
|
|
1132
|
-
[gsEventNames.locationChanged]: LocationChangedEvent;
|
|
1125
|
+
'gs-mutation-comparison': MutationComparisonComponent;
|
|
1133
1126
|
}
|
|
1134
1127
|
}
|
|
1135
1128
|
|
|
@@ -1137,7 +1130,7 @@ declare global {
|
|
|
1137
1130
|
declare global {
|
|
1138
1131
|
namespace JSX {
|
|
1139
1132
|
interface IntrinsicElements {
|
|
1140
|
-
'gs-
|
|
1133
|
+
'gs-mutation-comparison': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1141
1134
|
}
|
|
1142
1135
|
}
|
|
1143
1136
|
}
|
|
@@ -1145,10 +1138,7 @@ declare global {
|
|
|
1145
1138
|
|
|
1146
1139
|
declare global {
|
|
1147
1140
|
interface HTMLElementTagNameMap {
|
|
1148
|
-
'gs-
|
|
1149
|
-
}
|
|
1150
|
-
interface HTMLElementEventMap {
|
|
1151
|
-
[gsEventNames.textFilterChanged]: TextFilterChangedEvent;
|
|
1141
|
+
'gs-mutations': MutationsComponent;
|
|
1152
1142
|
}
|
|
1153
1143
|
}
|
|
1154
1144
|
|
|
@@ -1156,7 +1146,7 @@ declare global {
|
|
|
1156
1146
|
declare global {
|
|
1157
1147
|
namespace JSX {
|
|
1158
1148
|
interface IntrinsicElements {
|
|
1159
|
-
'gs-
|
|
1149
|
+
'gs-mutations': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1160
1150
|
}
|
|
1161
1151
|
}
|
|
1162
1152
|
}
|
|
@@ -1164,10 +1154,7 @@ declare global {
|
|
|
1164
1154
|
|
|
1165
1155
|
declare global {
|
|
1166
1156
|
interface HTMLElementTagNameMap {
|
|
1167
|
-
'gs-
|
|
1168
|
-
}
|
|
1169
|
-
interface HTMLElementEventMap {
|
|
1170
|
-
[gsEventNames.mutationFilterChanged]: CustomEvent<MutationsFilter>;
|
|
1157
|
+
'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
|
|
1171
1158
|
}
|
|
1172
1159
|
}
|
|
1173
1160
|
|
|
@@ -1175,7 +1162,7 @@ declare global {
|
|
|
1175
1162
|
declare global {
|
|
1176
1163
|
namespace JSX {
|
|
1177
1164
|
interface IntrinsicElements {
|
|
1178
|
-
'gs-
|
|
1165
|
+
'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1179
1166
|
}
|
|
1180
1167
|
}
|
|
1181
1168
|
}
|
|
@@ -1183,11 +1170,7 @@ declare global {
|
|
|
1183
1170
|
|
|
1184
1171
|
declare global {
|
|
1185
1172
|
interface HTMLElementTagNameMap {
|
|
1186
|
-
'gs-
|
|
1187
|
-
}
|
|
1188
|
-
interface HTMLElementEventMap {
|
|
1189
|
-
[gsEventNames.lineageFilterChanged]: LineageFilterChangedEvent;
|
|
1190
|
-
[gsEventNames.lineageFilterMultiChanged]: LineageMultiFilterChangedEvent;
|
|
1173
|
+
'gs-prevalence-over-time': PrevalenceOverTimeComponent;
|
|
1191
1174
|
}
|
|
1192
1175
|
}
|
|
1193
1176
|
|
|
@@ -1195,7 +1178,7 @@ declare global {
|
|
|
1195
1178
|
declare global {
|
|
1196
1179
|
namespace JSX {
|
|
1197
1180
|
interface IntrinsicElements {
|
|
1198
|
-
'gs-
|
|
1181
|
+
'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1199
1182
|
}
|
|
1200
1183
|
}
|
|
1201
1184
|
}
|
|
@@ -1203,11 +1186,7 @@ declare global {
|
|
|
1203
1186
|
|
|
1204
1187
|
declare global {
|
|
1205
1188
|
interface HTMLElementTagNameMap {
|
|
1206
|
-
'gs-
|
|
1207
|
-
}
|
|
1208
|
-
interface HTMLElementEventMap {
|
|
1209
|
-
[gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
|
|
1210
|
-
[gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
|
|
1189
|
+
'gs-aggregate': AggregateComponent;
|
|
1211
1190
|
}
|
|
1212
1191
|
}
|
|
1213
1192
|
|
|
@@ -1215,7 +1194,7 @@ declare global {
|
|
|
1215
1194
|
declare global {
|
|
1216
1195
|
namespace JSX {
|
|
1217
1196
|
interface IntrinsicElements {
|
|
1218
|
-
'gs-
|
|
1197
|
+
'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1219
1198
|
}
|
|
1220
1199
|
}
|
|
1221
1200
|
}
|
|
@@ -1223,7 +1202,7 @@ declare global {
|
|
|
1223
1202
|
|
|
1224
1203
|
declare global {
|
|
1225
1204
|
interface HTMLElementTagNameMap {
|
|
1226
|
-
'gs-
|
|
1205
|
+
'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
|
|
1227
1206
|
}
|
|
1228
1207
|
}
|
|
1229
1208
|
|
|
@@ -1231,7 +1210,7 @@ declare global {
|
|
|
1231
1210
|
declare global {
|
|
1232
1211
|
namespace JSX {
|
|
1233
1212
|
interface IntrinsicElements {
|
|
1234
|
-
'gs-
|
|
1213
|
+
'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1235
1214
|
}
|
|
1236
1215
|
}
|
|
1237
1216
|
}
|
|
@@ -1239,7 +1218,7 @@ declare global {
|
|
|
1239
1218
|
|
|
1240
1219
|
declare global {
|
|
1241
1220
|
interface HTMLElementTagNameMap {
|
|
1242
|
-
'gs-
|
|
1221
|
+
'gs-mutations-over-time': MutationsOverTimeComponent;
|
|
1243
1222
|
}
|
|
1244
1223
|
}
|
|
1245
1224
|
|
|
@@ -1247,7 +1226,7 @@ declare global {
|
|
|
1247
1226
|
declare global {
|
|
1248
1227
|
namespace JSX {
|
|
1249
1228
|
interface IntrinsicElements {
|
|
1250
|
-
'gs-
|
|
1229
|
+
'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1251
1230
|
}
|
|
1252
1231
|
}
|
|
1253
1232
|
}
|
|
@@ -1255,7 +1234,7 @@ declare global {
|
|
|
1255
1234
|
|
|
1256
1235
|
declare global {
|
|
1257
1236
|
interface HTMLElementTagNameMap {
|
|
1258
|
-
'gs-
|
|
1237
|
+
'gs-queries-over-time': QueriesOverTimeComponent;
|
|
1259
1238
|
}
|
|
1260
1239
|
}
|
|
1261
1240
|
|
|
@@ -1263,7 +1242,7 @@ declare global {
|
|
|
1263
1242
|
declare global {
|
|
1264
1243
|
namespace JSX {
|
|
1265
1244
|
interface IntrinsicElements {
|
|
1266
|
-
'gs-
|
|
1245
|
+
'gs-queries-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1267
1246
|
}
|
|
1268
1247
|
}
|
|
1269
1248
|
}
|
|
@@ -1271,7 +1250,7 @@ declare global {
|
|
|
1271
1250
|
|
|
1272
1251
|
declare global {
|
|
1273
1252
|
interface HTMLElementTagNameMap {
|
|
1274
|
-
'gs-
|
|
1253
|
+
'gs-sequences-by-location': SequencesByLocationComponent;
|
|
1275
1254
|
}
|
|
1276
1255
|
}
|
|
1277
1256
|
|
|
@@ -1279,7 +1258,7 @@ declare global {
|
|
|
1279
1258
|
declare global {
|
|
1280
1259
|
namespace JSX {
|
|
1281
1260
|
interface IntrinsicElements {
|
|
1282
|
-
'gs-
|
|
1261
|
+
'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1283
1262
|
}
|
|
1284
1263
|
}
|
|
1285
1264
|
}
|
|
@@ -1287,7 +1266,7 @@ declare global {
|
|
|
1287
1266
|
|
|
1288
1267
|
declare global {
|
|
1289
1268
|
interface HTMLElementTagNameMap {
|
|
1290
|
-
'gs-
|
|
1269
|
+
'gs-statistics': StatisticsComponent;
|
|
1291
1270
|
}
|
|
1292
1271
|
}
|
|
1293
1272
|
|
|
@@ -1295,7 +1274,7 @@ declare global {
|
|
|
1295
1274
|
declare global {
|
|
1296
1275
|
namespace JSX {
|
|
1297
1276
|
interface IntrinsicElements {
|
|
1298
|
-
'gs-
|
|
1277
|
+
'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1299
1278
|
}
|
|
1300
1279
|
}
|
|
1301
1280
|
}
|
|
@@ -1303,7 +1282,11 @@ declare global {
|
|
|
1303
1282
|
|
|
1304
1283
|
declare global {
|
|
1305
1284
|
interface HTMLElementTagNameMap {
|
|
1306
|
-
'gs-
|
|
1285
|
+
'gs-date-range-filter': DateRangeFilterComponent;
|
|
1286
|
+
}
|
|
1287
|
+
interface HTMLElementEventMap {
|
|
1288
|
+
[gsEventNames.dateRangeFilterChanged]: CustomEvent<Record<string, string>>;
|
|
1289
|
+
[gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
|
|
1307
1290
|
}
|
|
1308
1291
|
}
|
|
1309
1292
|
|
|
@@ -1311,7 +1294,7 @@ declare global {
|
|
|
1311
1294
|
declare global {
|
|
1312
1295
|
namespace JSX {
|
|
1313
1296
|
interface IntrinsicElements {
|
|
1314
|
-
'gs-
|
|
1297
|
+
'gs-date-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1315
1298
|
}
|
|
1316
1299
|
}
|
|
1317
1300
|
}
|
|
@@ -1319,7 +1302,10 @@ declare global {
|
|
|
1319
1302
|
|
|
1320
1303
|
declare global {
|
|
1321
1304
|
interface HTMLElementTagNameMap {
|
|
1322
|
-
'gs-
|
|
1305
|
+
'gs-location-filter': LocationFilterComponent;
|
|
1306
|
+
}
|
|
1307
|
+
interface HTMLElementEventMap {
|
|
1308
|
+
[gsEventNames.locationChanged]: LocationChangedEvent;
|
|
1323
1309
|
}
|
|
1324
1310
|
}
|
|
1325
1311
|
|
|
@@ -1327,7 +1313,7 @@ declare global {
|
|
|
1327
1313
|
declare global {
|
|
1328
1314
|
namespace JSX {
|
|
1329
1315
|
interface IntrinsicElements {
|
|
1330
|
-
'gs-
|
|
1316
|
+
'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1331
1317
|
}
|
|
1332
1318
|
}
|
|
1333
1319
|
}
|
|
@@ -1335,7 +1321,10 @@ declare global {
|
|
|
1335
1321
|
|
|
1336
1322
|
declare global {
|
|
1337
1323
|
interface HTMLElementTagNameMap {
|
|
1338
|
-
'gs-
|
|
1324
|
+
'gs-text-filter': TextFilterComponent;
|
|
1325
|
+
}
|
|
1326
|
+
interface HTMLElementEventMap {
|
|
1327
|
+
[gsEventNames.textFilterChanged]: TextFilterChangedEvent;
|
|
1339
1328
|
}
|
|
1340
1329
|
}
|
|
1341
1330
|
|
|
@@ -1343,7 +1332,7 @@ declare global {
|
|
|
1343
1332
|
declare global {
|
|
1344
1333
|
namespace JSX {
|
|
1345
1334
|
interface IntrinsicElements {
|
|
1346
|
-
'gs-
|
|
1335
|
+
'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1347
1336
|
}
|
|
1348
1337
|
}
|
|
1349
1338
|
}
|
|
@@ -1351,7 +1340,10 @@ declare global {
|
|
|
1351
1340
|
|
|
1352
1341
|
declare global {
|
|
1353
1342
|
interface HTMLElementTagNameMap {
|
|
1354
|
-
'gs-
|
|
1343
|
+
'gs-mutation-filter': MutationFilterComponent;
|
|
1344
|
+
}
|
|
1345
|
+
interface HTMLElementEventMap {
|
|
1346
|
+
[gsEventNames.mutationFilterChanged]: CustomEvent<MutationsFilter>;
|
|
1355
1347
|
}
|
|
1356
1348
|
}
|
|
1357
1349
|
|
|
@@ -1359,7 +1351,7 @@ declare global {
|
|
|
1359
1351
|
declare global {
|
|
1360
1352
|
namespace JSX {
|
|
1361
1353
|
interface IntrinsicElements {
|
|
1362
|
-
'gs-
|
|
1354
|
+
'gs-mutation-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1363
1355
|
}
|
|
1364
1356
|
}
|
|
1365
1357
|
}
|
|
@@ -1367,7 +1359,11 @@ declare global {
|
|
|
1367
1359
|
|
|
1368
1360
|
declare global {
|
|
1369
1361
|
interface HTMLElementTagNameMap {
|
|
1370
|
-
'gs-
|
|
1362
|
+
'gs-lineage-filter': LineageFilterComponent;
|
|
1363
|
+
}
|
|
1364
|
+
interface HTMLElementEventMap {
|
|
1365
|
+
[gsEventNames.lineageFilterChanged]: LineageFilterChangedEvent;
|
|
1366
|
+
[gsEventNames.lineageFilterMultiChanged]: LineageMultiFilterChangedEvent;
|
|
1371
1367
|
}
|
|
1372
1368
|
}
|
|
1373
1369
|
|
|
@@ -1375,7 +1371,7 @@ declare global {
|
|
|
1375
1371
|
declare global {
|
|
1376
1372
|
namespace JSX {
|
|
1377
1373
|
interface IntrinsicElements {
|
|
1378
|
-
'gs-
|
|
1374
|
+
'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1379
1375
|
}
|
|
1380
1376
|
}
|
|
1381
1377
|
}
|
|
@@ -1383,7 +1379,11 @@ declare global {
|
|
|
1383
1379
|
|
|
1384
1380
|
declare global {
|
|
1385
1381
|
interface HTMLElementTagNameMap {
|
|
1386
|
-
'gs-
|
|
1382
|
+
'gs-number-range-filter': NumberRangeFilterComponent;
|
|
1383
|
+
}
|
|
1384
|
+
interface HTMLElementEventMap {
|
|
1385
|
+
[gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
|
|
1386
|
+
[gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
|
|
1387
1387
|
}
|
|
1388
1388
|
}
|
|
1389
1389
|
|
|
@@ -1391,7 +1391,7 @@ declare global {
|
|
|
1391
1391
|
declare global {
|
|
1392
1392
|
namespace JSX {
|
|
1393
1393
|
interface IntrinsicElements {
|
|
1394
|
-
'gs-
|
|
1394
|
+
'gs-number-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1395
1395
|
}
|
|
1396
1396
|
}
|
|
1397
1397
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genspectrum/dashboard-components",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.2",
|
|
4
4
|
"description": "GenSpectrum web components for building dashboards",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -126,7 +126,6 @@
|
|
|
126
126
|
"depcheck": "^1.4.7",
|
|
127
127
|
"eslint": "^9.31.0",
|
|
128
128
|
"eslint-plugin-import": "^2.29.1",
|
|
129
|
-
"eslint-plugin-jest": "^28.2.0",
|
|
130
129
|
"eslint-plugin-react": "^7.37.5",
|
|
131
130
|
"eslint-plugin-react-hooks": "^5.2.0",
|
|
132
131
|
"eslint-plugin-storybook": "^0.12.0",
|
|
@@ -30,7 +30,7 @@ export type CustomColumn = z.infer<typeof customColumnSchema>;
|
|
|
30
30
|
export interface FeatureRenderer<D> {
|
|
31
31
|
asString(value: D): string;
|
|
32
32
|
renderRowLabel(value: D): JSX.Element;
|
|
33
|
-
renderTooltip(value: D, temporal: Temporal, proportionValue: ProportionValue
|
|
33
|
+
renderTooltip(value: D, temporal: Temporal, proportionValue: ProportionValue): JSX.Element;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export interface FeaturesOverTimeGridProps<F> {
|
|
@@ -99,12 +99,20 @@ function FeaturesOverTimeGrid<F>({
|
|
|
99
99
|
</div>
|
|
100
100
|
),
|
|
101
101
|
cell: ({ getValue, row, column, table }) => {
|
|
102
|
-
const
|
|
102
|
+
const valueRaw = getValue();
|
|
103
|
+
const value = valueRaw ?? null;
|
|
103
104
|
const rowIndex = row.index;
|
|
104
105
|
const columnIndex = column.getIndex();
|
|
105
106
|
const numberOfRows = table.getRowModel().rows.length;
|
|
106
107
|
const numberOfColumns = table.getAllColumns().length;
|
|
107
108
|
|
|
109
|
+
if (valueRaw === undefined) {
|
|
110
|
+
// eslint-disable-next-line no-console -- We want to warn that something might be wrong.
|
|
111
|
+
console.error(
|
|
112
|
+
`Found undefined value for ${row.original.feature} - ${date.dateString}. This shouldn't happen.`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
108
116
|
const tooltip = featureRenderer.renderTooltip(row.original.feature, date, value);
|
|
109
117
|
|
|
110
118
|
return (
|
|
@@ -156,4 +156,59 @@ describe('groupMutationDataByLocation', () => {
|
|
|
156
156
|
|
|
157
157
|
expect(location1Data.getFirstAxisKeys()).to.deep.equal([mutation1, mutation2, mutation3]);
|
|
158
158
|
});
|
|
159
|
+
|
|
160
|
+
test('should backfill missing mutation-date combinations with explicit null', () => {
|
|
161
|
+
const input: WastewaterData = [
|
|
162
|
+
{
|
|
163
|
+
location: location1,
|
|
164
|
+
date: temporalCache.getYearMonthDay('2025-01-01'),
|
|
165
|
+
nucleotideMutationFrequency: [
|
|
166
|
+
{ mutation: mutation1, proportion: 0.1 },
|
|
167
|
+
// mutation2 missing for this date
|
|
168
|
+
],
|
|
169
|
+
aminoAcidMutationFrequency: [],
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
location: location1,
|
|
173
|
+
date: temporalCache.getYearMonthDay('2025-01-02'),
|
|
174
|
+
nucleotideMutationFrequency: [
|
|
175
|
+
// mutation1 missing for this date
|
|
176
|
+
{ mutation: mutation2, proportion: 0.2 },
|
|
177
|
+
],
|
|
178
|
+
aminoAcidMutationFrequency: [],
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
const result = groupMutationDataByLocation(input, 'nucleotide');
|
|
183
|
+
|
|
184
|
+
expect(result).to.have.length(1);
|
|
185
|
+
const location1Data = result[0].data;
|
|
186
|
+
|
|
187
|
+
// Both mutations should appear in all dates
|
|
188
|
+
expect(location1Data.getFirstAxisKeys()).to.deep.equal([mutation1, mutation2]);
|
|
189
|
+
expect(location1Data.getSecondAxisKeys()).to.deep.equal([
|
|
190
|
+
temporalCache.getYearMonthDay('2025-01-01'),
|
|
191
|
+
temporalCache.getYearMonthDay('2025-01-02'),
|
|
192
|
+
]);
|
|
193
|
+
|
|
194
|
+
// Verify backfilled nulls (not undefined)
|
|
195
|
+
expect(location1Data.get(mutation1, temporalCache.getYearMonthDay('2025-01-02'))).to.equal(null);
|
|
196
|
+
expect(location1Data.get(mutation2, temporalCache.getYearMonthDay('2025-01-01'))).to.equal(null);
|
|
197
|
+
|
|
198
|
+
// Verify actual values still present
|
|
199
|
+
expect(location1Data.get(mutation1, temporalCache.getYearMonthDay('2025-01-01'))).to.deep.equal({
|
|
200
|
+
type: 'wastewaterValue',
|
|
201
|
+
proportion: 0.1,
|
|
202
|
+
});
|
|
203
|
+
expect(location1Data.get(mutation2, temporalCache.getYearMonthDay('2025-01-02'))).to.deep.equal({
|
|
204
|
+
type: 'wastewaterValue',
|
|
205
|
+
proportion: 0.2,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Verify the complete grid with getAsArray
|
|
209
|
+
expect(location1Data.getAsArray()).to.deep.equal([
|
|
210
|
+
[{ type: 'wastewaterValue', proportion: 0.1 }, null],
|
|
211
|
+
[null, { type: 'wastewaterValue', proportion: 0.2 }],
|
|
212
|
+
]);
|
|
213
|
+
});
|
|
159
214
|
});
|
package/src/preact/wastewater/mutationsOverTime/computeWastewaterMutationsOverTimeDataPerLocation.ts
CHANGED
|
@@ -21,15 +21,26 @@ export async function computeWastewaterMutationsOverTimeDataPerLocation(
|
|
|
21
21
|
|
|
22
22
|
export function groupMutationDataByLocation(data: WastewaterData, sequenceType: 'nucleotide' | 'amino acid') {
|
|
23
23
|
const locationMap = new Map<string, MutationOverTimeDataMap<TemporalClass>>();
|
|
24
|
+
// Track all unique mutations and dates per location for backfilling
|
|
25
|
+
const locationMutations = new Map<string, Set<string>>();
|
|
26
|
+
const locationDates = new Map<string, Set<string>>();
|
|
27
|
+
|
|
28
|
+
// First pass: populate sparse data and track all keys
|
|
24
29
|
for (const row of data) {
|
|
25
30
|
if (!locationMap.has(row.location)) {
|
|
26
31
|
locationMap.set(row.location, new BaseMutationOverTimeDataMap<TemporalClass>());
|
|
32
|
+
locationMutations.set(row.location, new Set());
|
|
33
|
+
locationDates.set(row.location, new Set());
|
|
27
34
|
}
|
|
28
35
|
const map = locationMap.get(row.location)!;
|
|
36
|
+
const mutations = locationMutations.get(row.location)!;
|
|
37
|
+
const dates = locationDates.get(row.location)!;
|
|
29
38
|
|
|
30
39
|
const mutationFrequencies =
|
|
31
40
|
sequenceType === 'nucleotide' ? row.nucleotideMutationFrequency : row.aminoAcidMutationFrequency;
|
|
32
41
|
for (const mutation of mutationFrequencies) {
|
|
42
|
+
dates.add(row.date.dateString);
|
|
43
|
+
mutations.add(mutation.mutation.code);
|
|
33
44
|
map.set(
|
|
34
45
|
mutation.mutation,
|
|
35
46
|
row.date,
|
|
@@ -38,6 +49,21 @@ export function groupMutationDataByLocation(data: WastewaterData, sequenceType:
|
|
|
38
49
|
}
|
|
39
50
|
}
|
|
40
51
|
|
|
52
|
+
// Second pass: backfill missing cells with explicit null
|
|
53
|
+
for (const [location, map] of locationMap.entries()) {
|
|
54
|
+
const allMutations = Array.from(locationMutations.get(location)!);
|
|
55
|
+
const allDates = Array.from(locationDates.get(location)!).map((dateStr) => map.keysSecondAxis.get(dateStr)!);
|
|
56
|
+
|
|
57
|
+
for (const mutationCode of allMutations) {
|
|
58
|
+
const mutation = map.keysFirstAxis.get(mutationCode)!;
|
|
59
|
+
for (const date of allDates) {
|
|
60
|
+
if (map.get(mutation, date) === undefined) {
|
|
61
|
+
map.set(mutation, date, null);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
41
67
|
return [...locationMap.entries()].map(([location, data]) => ({
|
|
42
68
|
location,
|
|
43
69
|
data: new SortedMap2d(
|