@datagrok/peptides 1.24.0 → 1.25.1
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/CHANGELOG.md +13 -0
- package/dist/216.js +1 -1
- package/dist/216.js.map +1 -1
- package/dist/774.js +2 -0
- package/dist/774.js.map +1 -0
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/package.json +3 -3
- package/src/model.ts +1 -4
- package/src/tests/misc.ts +4 -2
- package/src/utils/algorithms.ts +18 -27
- package/src/utils/cell-renderer.ts +1 -1
- package/src/utils/constants.ts +1 -4
- package/src/utils/misc.ts +2 -1
- package/src/utils/parallel-mutation-cliffs.ts +1 -4
- package/src/utils/tooltips.ts +2 -0
- package/src/viewers/logo-summary.ts +1 -4
- package/src/viewers/sar-viewer.ts +152 -96
- package/src/widgets/selection.ts +1 -1
- package/src/workers/mutation-cliffs-worker.ts +7 -3
- package/test-console-output-1.log +143 -142
- package/test-record-1.mp4 +0 -0
|
@@ -41,6 +41,7 @@ import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/types/monomer-lib
|
|
|
41
41
|
import {PolymerTypes} from '@datagrok-libraries/bio/src/helm/consts';
|
|
42
42
|
import {PeptideUtils} from '../peptideUtils';
|
|
43
43
|
import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
|
|
44
|
+
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
44
45
|
|
|
45
46
|
export enum SELECTION_MODE {
|
|
46
47
|
MUTATION_CLIFFS = 'Mutation Cliffs',
|
|
@@ -60,6 +61,7 @@ export enum SAR_PROPERTIES {
|
|
|
60
61
|
ACTIVITY_TARGET = 'activityTarget',
|
|
61
62
|
VALUE_INVARIANT_MAP = 'value',
|
|
62
63
|
AGGREGATION_INVARIANT_MAP_VALUE = 'valueAggregation',
|
|
64
|
+
DATA_SOURCE = 'dataSource',
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
export enum MONOMER_POSITION_PROPERTIES {
|
|
@@ -73,6 +75,7 @@ export enum MONOMER_POSITION_PROPERTIES {
|
|
|
73
75
|
UPPER_BOUND_COLOR = 'upperBoundColor',
|
|
74
76
|
LOG_SCALE_COLOR = 'logScaleColor',
|
|
75
77
|
SHOW_FILTER_CONTROLS = 'showFilterControls',
|
|
78
|
+
SHOW_TOTAL_COUNT_COLUMN = 'showTotalCountColumn',
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
export enum PROPERTY_CATEGORIES {
|
|
@@ -95,6 +98,7 @@ export interface ISARViewer {
|
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
/** Abstract class for MonomerPosition and MostPotentResidues viewers. */
|
|
101
|
+
|
|
98
102
|
export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
99
103
|
keyPressed: boolean = false;
|
|
100
104
|
sequenceColumnName: string;
|
|
@@ -108,10 +112,9 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
108
112
|
_scaledActivityColumn: DG.Column | null = null;
|
|
109
113
|
doRender: boolean = true;
|
|
110
114
|
activityTarget: C.ACTIVITY_TARGET;
|
|
111
|
-
targetColumnInput?: DG.InputBase<DG.Column | null>;
|
|
112
|
-
targetCategoryInput: DG.ChoiceInput<string | null | undefined>;
|
|
113
115
|
valueColumnName: string;
|
|
114
116
|
valueAggregation: DG.AGG;
|
|
117
|
+
dataSource: 'All' | 'Filtered' = 'Filtered';
|
|
115
118
|
|
|
116
119
|
mutationCliffsDebouncer: (
|
|
117
120
|
activityArray: type.RawData, monomerInfoArray: type.RawColumn[], options?: MutationCliffsOptions
|
|
@@ -123,6 +126,10 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
123
126
|
// General properties
|
|
124
127
|
this.sequenceColumnName = this.column(SAR_PROPERTIES.SEQUENCE,
|
|
125
128
|
{category: PROPERTY_CATEGORIES.GENERAL, semType: DG.SEMTYPE.MACROMOLECULE, nullable: false});
|
|
129
|
+
this.dataSource = this.string(SAR_PROPERTIES.DATA_SOURCE, 'Filtered', {category: PROPERTY_CATEGORIES.GENERAL, choices: ['All', 'Filtered'], nullable: false,
|
|
130
|
+
description: 'Data source for calculations and rendering. Can be set to whole data set or filtered data only.',
|
|
131
|
+
}) as 'All' | 'Filtered';
|
|
132
|
+
|
|
126
133
|
this.activityColumnName = this.column(SAR_PROPERTIES.ACTIVITY,
|
|
127
134
|
{category: PROPERTY_CATEGORIES.GENERAL, nullable: false});
|
|
128
135
|
this.activityScaling = this.string(SAR_PROPERTIES.ACTIVITY_SCALING, C.SCALING_METHODS.NONE,
|
|
@@ -132,9 +139,8 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
132
139
|
{category: PROPERTY_CATEGORIES.GENERAL, choices: Object.values(C.ACTIVITY_TARGET), nullable: false},
|
|
133
140
|
) as C.ACTIVITY_TARGET;
|
|
134
141
|
// Mutation Cliffs/invariant map properties
|
|
135
|
-
// hide it and make it editable through the code
|
|
136
142
|
this.targetColumnName = this.column(SAR_PROPERTIES.TARGET, {
|
|
137
|
-
category: PROPERTY_CATEGORIES.GENERAL, nullable: true, columnTypeFilter: 'categorical', userEditable:
|
|
143
|
+
category: PROPERTY_CATEGORIES.GENERAL, nullable: true, columnTypeFilter: 'categorical', userEditable: false}); // not userEditable to account for reverse compatibility
|
|
138
144
|
this.minActivityDelta = this.float(SAR_PROPERTIES.MIN_ACTIVITY_DELTA, 0,
|
|
139
145
|
{category: PROPERTY_CATEGORIES.MUTATION_CLIFFS, min: 0, max: 100});
|
|
140
146
|
this.maxMutations = this.int(SAR_PROPERTIES.MAX_MUTATIONS, 1,
|
|
@@ -152,25 +158,25 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
152
158
|
return await findMutations(activityArray, monomerInfoArray, options);
|
|
153
159
|
});
|
|
154
160
|
|
|
155
|
-
this.targetCategoryInput = ui.input.choice('Category', {value: null, items: [], nullable: true,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
});
|
|
171
|
-
this.targetCategoryInput.root.style.display = 'none';
|
|
172
|
-
this.targetCategoryInput.root.style.maxWidth = '50%';
|
|
173
|
-
this.targetCategoryInput.root.style.marginLeft = '8px'
|
|
161
|
+
// this.targetCategoryInput = ui.input.choice('Category', {value: null, items: [], nullable: true,
|
|
162
|
+
// onValueChanged: () => {
|
|
163
|
+
// this._mutationCliffs = null;
|
|
164
|
+
// this._mutationCliffStats = null;
|
|
165
|
+
// this._mutationCliffsSelection = null;
|
|
166
|
+
// this._invariantMapSelection = null;
|
|
167
|
+
// this.doRender = false;
|
|
168
|
+
// this._monomerPositionStats = null;
|
|
169
|
+
// this.positionColumns?.forEach((col) => {
|
|
170
|
+
// col.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] = null;
|
|
171
|
+
// });
|
|
172
|
+
// if (this.sequenceColumnName && this.activityColumnName)
|
|
173
|
+
// this.calculateMutationCliffs().then((mc) => {this.mutationCliffs = mc.cliffs; this.cliffStats = mc.cliffStats;});
|
|
174
|
+
// this.viewerGrid.invalidate();
|
|
175
|
+
// },
|
|
176
|
+
// });
|
|
177
|
+
// this.targetCategoryInput.root.style.display = 'none';
|
|
178
|
+
// this.targetCategoryInput.root.style.maxWidth = '50%';
|
|
179
|
+
// this.targetCategoryInput.root.style.marginLeft = '8px'
|
|
174
180
|
}
|
|
175
181
|
|
|
176
182
|
_viewerGrid: DG.Grid | null = null;
|
|
@@ -253,10 +259,7 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
253
259
|
this.sequenceColumnName === other?.sequenceColumnName &&
|
|
254
260
|
this.activityColumnName === other?.activityColumnName &&
|
|
255
261
|
this.activityScaling === other?.activityScaling &&
|
|
256
|
-
((other instanceof SARViewer &&
|
|
257
|
-
this.targetCategoryInput?.value === other?.targetCategoryInput?.value) ||
|
|
258
|
-
(!(other instanceof SARViewer) && (this.targetColumnName == null || this.targetCategoryInput?.value == null))
|
|
259
|
-
) &&
|
|
262
|
+
((other instanceof SARViewer && other.dataSource === this.dataSource)) &&
|
|
260
263
|
((other instanceof SARViewer && this.valueColumnName == other?.valueColumnName && this.valueAggregation == other?.valueAggregation) ||
|
|
261
264
|
(!(other instanceof SARViewer) &&
|
|
262
265
|
(!this.valueColumnName || !this.valueAggregation || this.valueAggregation == DG.AGG.VALUE_COUNT || this.valueAggregation == DG.AGG.TOTAL_COUNT))
|
|
@@ -277,15 +280,12 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
277
280
|
else if (this instanceof MostPotentResidues)
|
|
278
281
|
this._monomerPositionStats = getSharedStats(VIEWER_TYPE.SEQUENCE_VARIABILITY_MAP);
|
|
279
282
|
|
|
280
|
-
|
|
281
|
-
const targetCol = this.targetColumnName ? this.dataFrame.col(this.targetColumnName) : null;
|
|
282
|
-
const targetCategory = this.targetCategoryInput.value;
|
|
283
283
|
const invariantMapValueCol = this.dataFrame.col(this.valueColumnName);
|
|
284
284
|
const invariantMapValueAgg = this.valueAggregation;
|
|
285
285
|
|
|
286
286
|
this._monomerPositionStats ??= calculateMonomerPositionStatistics(this.getScaledActivityColumn(),
|
|
287
287
|
this.dataFrame.filter, this.positionColumns,
|
|
288
|
-
{
|
|
288
|
+
{isFiltered: this.dataSource === 'Filtered' && this.dataFrame.filter.anyFalse,
|
|
289
289
|
aggValue: (invariantMapValueAgg && invariantMapValueCol) ? {col: invariantMapValueCol, type: invariantMapValueAgg} : undefined,
|
|
290
290
|
});
|
|
291
291
|
return this._monomerPositionStats;
|
|
@@ -306,12 +306,11 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
306
306
|
|
|
307
307
|
const isMutationCliffsEqual = (v1: SARViewer, v2: SARViewer | null): boolean =>
|
|
308
308
|
v1.sequenceColumnName === v2?.sequenceColumnName &&
|
|
309
|
-
v1.activityColumnName === v2
|
|
310
|
-
v1.activityScaling === v2
|
|
311
|
-
v1.targetColumnName === v2?.targetColumnName &&
|
|
312
|
-
v1.targetCategoryInput?.value === v2?.targetCategoryInput?.value &&
|
|
309
|
+
v1.activityColumnName === v2?.activityColumnName &&
|
|
310
|
+
v1.activityScaling === v2?.activityScaling &&
|
|
313
311
|
v1.minActivityDelta === v2?.minActivityDelta &&
|
|
314
|
-
v1.maxMutations === v2?.maxMutations
|
|
312
|
+
v1.maxMutations === v2?.maxMutations &&
|
|
313
|
+
v1?.dataSource === v2?.dataSource;
|
|
315
314
|
|
|
316
315
|
const getSharedMutationCliffs = (viewerType: VIEWER_TYPE): type.MutationCliffs | null => {
|
|
317
316
|
const viewer = this.model.findViewer(viewerType) as SARViewer | null;
|
|
@@ -351,7 +350,7 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
351
350
|
}
|
|
352
351
|
set cliffStats(stats: type.MutationCliffStats | null) {
|
|
353
352
|
this._mutationCliffStats = stats;
|
|
354
|
-
this.viewerGrid.invalidate;
|
|
353
|
+
this.viewerGrid.invalidate();
|
|
355
354
|
}
|
|
356
355
|
|
|
357
356
|
_mutationCliffsSelection: type.Selection | null = null;
|
|
@@ -361,10 +360,9 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
361
360
|
* @return - mutation cliffs selection.
|
|
362
361
|
*/
|
|
363
362
|
get mutationCliffsSelection(): type.Selection {
|
|
364
|
-
const tagSuffix = this instanceof MonomerPosition ? C.SUFFIXES.MP : C.SUFFIXES.MPR;
|
|
365
|
-
const tagSelection = this.dataFrame.getTag(`${tagSuffix}${C.TAGS.MUTATION_CLIFFS_SELECTION}`);
|
|
366
|
-
this._mutationCliffsSelection ??=
|
|
367
|
-
JSON.parse(tagSelection);
|
|
363
|
+
// const tagSuffix = this instanceof MonomerPosition ? C.SUFFIXES.MP : C.SUFFIXES.MPR;
|
|
364
|
+
// const tagSelection = this.dataFrame.getTag(`${tagSuffix}${C.TAGS.MUTATION_CLIFFS_SELECTION}`);
|
|
365
|
+
this._mutationCliffsSelection ??= initSelection(this.positionColumns);
|
|
368
366
|
return this._mutationCliffsSelection!;
|
|
369
367
|
}
|
|
370
368
|
|
|
@@ -374,15 +372,13 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
374
372
|
*/
|
|
375
373
|
set mutationCliffsSelection(selection: type.Selection) {
|
|
376
374
|
this._mutationCliffsSelection = selection;
|
|
377
|
-
const tagSuffix = this instanceof MonomerPosition ? C.SUFFIXES.MP : C.SUFFIXES.MPR;
|
|
378
|
-
this.dataFrame.setTag(`${tagSuffix}${C.TAGS.MUTATION_CLIFFS_SELECTION}`, JSON.stringify(selection));
|
|
379
375
|
this.model.fireBitsetChanged(this instanceof MonomerPosition ? VIEWER_TYPE.SEQUENCE_VARIABILITY_MAP :
|
|
380
376
|
VIEWER_TYPE.MOST_POTENT_RESIDUES);
|
|
381
377
|
|
|
382
378
|
const mpViewer = this.model.findViewer(VIEWER_TYPE.SEQUENCE_VARIABILITY_MAP) as MonomerPosition | null;
|
|
383
|
-
mpViewer?.viewerGrid
|
|
379
|
+
mpViewer?.viewerGrid?.invalidate();
|
|
384
380
|
const mprViewer = this.model.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues | null;
|
|
385
|
-
mprViewer?.viewerGrid
|
|
381
|
+
mprViewer?.viewerGrid?.invalidate();
|
|
386
382
|
|
|
387
383
|
this.model.analysisView.grid.invalidate();
|
|
388
384
|
}
|
|
@@ -450,9 +446,7 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
450
446
|
* @return - invariant map selection.
|
|
451
447
|
*/
|
|
452
448
|
get invariantMapSelection(): type.Selection {
|
|
453
|
-
|
|
454
|
-
this._invariantMapSelection ??= tagSelection === null ? initSelection(this.positionColumns) :
|
|
455
|
-
JSON.parse(tagSelection);
|
|
449
|
+
this._invariantMapSelection ??= initSelection(this.positionColumns);
|
|
456
450
|
return this._invariantMapSelection!;
|
|
457
451
|
}
|
|
458
452
|
|
|
@@ -462,22 +456,10 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
462
456
|
*/
|
|
463
457
|
set invariantMapSelection(selection: type.Selection) {
|
|
464
458
|
this._invariantMapSelection = selection;
|
|
465
|
-
this.dataFrame.setTag(`${C.SUFFIXES.MP}${C.TAGS.INVARIANT_MAP_SELECTION}`, JSON.stringify(selection));
|
|
466
459
|
this.model.fireBitsetChanged(VIEWER_TYPE.SEQUENCE_VARIABILITY_MAP);
|
|
467
460
|
this.model.analysisView.grid.invalidate();
|
|
468
461
|
}
|
|
469
462
|
|
|
470
|
-
private resetTargetCategoryValue(): void {
|
|
471
|
-
const colName = this.targetColumnName;
|
|
472
|
-
const col = colName ? this.dataFrame.col(colName) : null;
|
|
473
|
-
this.targetCategoryInput.items = col?.categories ?? [];
|
|
474
|
-
this.targetCategoryInput.value = null;
|
|
475
|
-
if (!colName)
|
|
476
|
-
this.targetCategoryInput.root.style.display = 'none';
|
|
477
|
-
else
|
|
478
|
-
this.targetCategoryInput.root.style.display = 'flex';
|
|
479
|
-
}
|
|
480
|
-
|
|
481
463
|
/**
|
|
482
464
|
* Processes property changes.
|
|
483
465
|
* @param property - changed property.
|
|
@@ -527,15 +509,13 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
527
509
|
if (this instanceof MostPotentResidues || this instanceof MonomerPosition)
|
|
528
510
|
this._viewerGrid = null;
|
|
529
511
|
break;
|
|
512
|
+
case SAR_PROPERTIES.DATA_SOURCE:
|
|
513
|
+
this.onFilterChanged(false, false);
|
|
514
|
+
this.doRender = true;
|
|
515
|
+
break;
|
|
530
516
|
}
|
|
531
517
|
if (this._mutationCliffs === null && this.sequenceColumnName && this.activityColumnName && this.dataFrame)
|
|
532
518
|
this.calculateMutationCliffs().then((mc) => {this.mutationCliffs = mc.cliffs; this.cliffStats = mc.cliffStats;});
|
|
533
|
-
|
|
534
|
-
// do this last to avoid recalculating mutation cliffs
|
|
535
|
-
if (property.name === `${SAR_PROPERTIES.TARGET}${COLUMN_NAME}` && this.targetColumnInput) {
|
|
536
|
-
this.targetColumnInput.value = this.targetColumnName ? this.dataFrame.col(this.targetColumnName) : null;
|
|
537
|
-
this.resetTargetCategoryValue();
|
|
538
|
-
}
|
|
539
519
|
}
|
|
540
520
|
|
|
541
521
|
/**
|
|
@@ -603,6 +583,18 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
603
583
|
}, {isChecked: (meta) => this._monomerMetaColumns.has(meta)});
|
|
604
584
|
});
|
|
605
585
|
}));
|
|
586
|
+
this.subs.push(DG.debounce(this.dataFrame.onFilterChanged, 300).subscribe(() => {
|
|
587
|
+
if (this.dataSource === 'Filtered') {
|
|
588
|
+
// this._monomerPositionStats = null;
|
|
589
|
+
// this._invariantMapSelection = null;
|
|
590
|
+
// this._mutationCliffs = null;
|
|
591
|
+
// this._mutationCliffStats = null;
|
|
592
|
+
// this._mutationCliffsSelection = null;
|
|
593
|
+
// this._viewerGrid = null;
|
|
594
|
+
// this.render();
|
|
595
|
+
this.onFilterChanged(true, false);
|
|
596
|
+
}
|
|
597
|
+
}));
|
|
606
598
|
} else {
|
|
607
599
|
const msg = 'PeptidesError: dataframe is missing Macromolecule or numeric columns';
|
|
608
600
|
grok.log.error(msg);
|
|
@@ -610,6 +602,27 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
610
602
|
}
|
|
611
603
|
}
|
|
612
604
|
|
|
605
|
+
/// Override in inheritors if needed
|
|
606
|
+
onFilterChanged(render?: boolean, onlySetNulls = false): void {
|
|
607
|
+
this._monomerPositionStats = null;
|
|
608
|
+
this._invariantMapSelection = null;
|
|
609
|
+
this._mutationCliffStats = null;
|
|
610
|
+
this._mutationCliffsSelection = null;
|
|
611
|
+
this._mutationCliffs = null;
|
|
612
|
+
if (onlySetNulls)
|
|
613
|
+
return;
|
|
614
|
+
|
|
615
|
+
this._viewerGrid = null;
|
|
616
|
+
const mprViewer = this.model.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues | null;
|
|
617
|
+
if (mprViewer && !(this instanceof MostPotentResidues) && mprViewer.dataSource === 'Filtered')
|
|
618
|
+
mprViewer.onFilterChanged(false, true);
|
|
619
|
+
|
|
620
|
+
if (render)
|
|
621
|
+
this.render();
|
|
622
|
+
if (this.sequenceColumnName && this.activityColumnName)
|
|
623
|
+
this.calculateMutationCliffs().then((mc) => {this.mutationCliffs = mc.cliffs; this.cliffStats = mc.cliffStats;});
|
|
624
|
+
}
|
|
625
|
+
|
|
613
626
|
/**
|
|
614
627
|
* Calculates Mutation Cliffs.
|
|
615
628
|
* @return - mutation cliffs.
|
|
@@ -618,11 +631,11 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
618
631
|
const scaledActivityCol: DG.Column<number> = this.dataFrame.getCol(this.activityColumnName);
|
|
619
632
|
//TODO: set categories ordering the same to share compare indexes instead of strings
|
|
620
633
|
const monomerCols: type.RawColumn[] = this.positionColumns.map(extractColInfo);
|
|
621
|
-
const
|
|
634
|
+
const filter = (this.dataSource === 'Filtered' && this.dataFrame.filter.anyFalse) ?
|
|
635
|
+
this.dataFrame.filter : null;
|
|
622
636
|
|
|
623
637
|
const options: MutationCliffsOptions = {
|
|
624
|
-
maxMutations: this.maxMutations, minActivityDelta: this.minActivityDelta,
|
|
625
|
-
targetCol, currentTarget: this.targetCategoryInput.value,
|
|
638
|
+
maxMutations: this.maxMutations, minActivityDelta: this.minActivityDelta, filter: (filter?.getBuffer() as unknown as Uint32Array) ?? undefined,
|
|
626
639
|
};
|
|
627
640
|
const activityRawData = scaledActivityCol.getRawData();
|
|
628
641
|
|
|
@@ -646,6 +659,7 @@ export class MonomerPosition extends SARViewer {
|
|
|
646
659
|
upperBoundColor: number;
|
|
647
660
|
logScaleColor: boolean = false;
|
|
648
661
|
showFilterControls: boolean = true;
|
|
662
|
+
showTotalCountColumn: boolean = false;
|
|
649
663
|
/** Sets MonomerPosition properties. */
|
|
650
664
|
constructor() {
|
|
651
665
|
super();
|
|
@@ -657,6 +671,7 @@ export class MonomerPosition extends SARViewer {
|
|
|
657
671
|
this.lowerBoundColor = this.int(MONOMER_POSITION_PROPERTIES.LOWER_BOUND_COLOR, 0xFF0000FF, {category: PROPERTY_CATEGORIES.INVARIANT_MAP, editor: 'color'});
|
|
658
672
|
this.middleColor = this.int(MONOMER_POSITION_PROPERTIES.MIDDLE_COLOR, 0xFFFFFFFF, {category: PROPERTY_CATEGORIES.INVARIANT_MAP, editor: 'color'});
|
|
659
673
|
this.upperBoundColor = this.int(MONOMER_POSITION_PROPERTIES.UPPER_BOUND_COLOR, 0xFFFF0000, {category: PROPERTY_CATEGORIES.INVARIANT_MAP, editor: 'color'});
|
|
674
|
+
this.showTotalCountColumn = this.bool(MONOMER_POSITION_PROPERTIES.SHOW_TOTAL_COUNT_COLUMN, false, {category: PROPERTY_CATEGORIES.GENERAL, description: 'Show total monomer count column'});
|
|
660
675
|
|
|
661
676
|
this.logScaleColor = this.bool(MONOMER_POSITION_PROPERTIES.LOG_SCALE_COLOR, false, {category: PROPERTY_CATEGORIES.INVARIANT_MAP});
|
|
662
677
|
this.customColorRange = this.bool(MONOMER_POSITION_PROPERTIES.CUSTOM_COLOR_RANGE, false, {category: PROPERTY_CATEGORIES.INVARIANT_MAP});
|
|
@@ -717,13 +732,6 @@ export class MonomerPosition extends SARViewer {
|
|
|
717
732
|
if (isApplicableDataframe(this.dataFrame)) {
|
|
718
733
|
this.getProperty(`${MONOMER_POSITION_PROPERTIES.COLOR}${COLUMN_NAME}`)
|
|
719
734
|
?.set(this, this.activityColumnName);
|
|
720
|
-
this.targetColumnInput = ui.input.column('Target', {value: undefined, nullable: true, table: this.dataFrame, filter: (col: DG.Column) => col.isCategorical,
|
|
721
|
-
onValueChanged: (value) => {
|
|
722
|
-
const prop = this.getProperty(`${SAR_PROPERTIES.TARGET}${COLUMN_NAME}`);
|
|
723
|
-
if (prop && prop.get(this) != (value?.name ?? null))
|
|
724
|
-
prop?.set(this, value?.name ?? null);
|
|
725
|
-
},
|
|
726
|
-
});
|
|
727
735
|
} else {
|
|
728
736
|
const msg = 'PeptidesError: dataframe is missing Macromolecule or numeric columns';
|
|
729
737
|
grok.log.error(msg);
|
|
@@ -742,6 +750,11 @@ export class MonomerPosition extends SARViewer {
|
|
|
742
750
|
case SAR_PROPERTIES.SEQUENCE:
|
|
743
751
|
this._invariantMapSelection = null;
|
|
744
752
|
break;
|
|
753
|
+
case MONOMER_POSITION_PROPERTIES.SHOW_TOTAL_COUNT_COLUMN:
|
|
754
|
+
if (this._viewerGrid && this._viewerGrid.columns.byName(C.COLUMNS_NAMES.TOTAL_COUNT)) {
|
|
755
|
+
this._viewerGrid.columns.byName(C.COLUMNS_NAMES.TOTAL_COUNT)!.visible = this.showTotalCountColumn;
|
|
756
|
+
this.doRender = false;
|
|
757
|
+
}
|
|
745
758
|
}
|
|
746
759
|
|
|
747
760
|
// this will cause colors to recalculate
|
|
@@ -777,6 +790,25 @@ export class MonomerPosition extends SARViewer {
|
|
|
777
790
|
return monomersArray.map((_m) => ({}));
|
|
778
791
|
return monomersArray.map((m) => lib.getMonomer(PolymerTypes.PEPTIDE, m)?.meta ?? {});
|
|
779
792
|
});
|
|
793
|
+
// add sum column
|
|
794
|
+
const sumCol = monomerPositionDf.columns.addNewInt(C.COLUMNS_NAMES.TOTAL_COUNT);
|
|
795
|
+
const monomerCounts: Record<string, number> = {};
|
|
796
|
+
const stats = this.monomerPositionStats;
|
|
797
|
+
for (const posCol of Object.keys(stats)) {
|
|
798
|
+
if (posCol === 'general')
|
|
799
|
+
continue;
|
|
800
|
+
const posStats = stats[posCol]!;
|
|
801
|
+
for (const monomer of Object.keys(posStats)) {
|
|
802
|
+
if (monomer === 'general')
|
|
803
|
+
continue;
|
|
804
|
+
if (!monomerCounts[monomer])
|
|
805
|
+
monomerCounts[monomer] = 0;
|
|
806
|
+
monomerCounts[monomer] += posStats[monomer]!.count;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
sumCol.init((i) => monomerCounts[monomerCol.get(i)] ?? 0);
|
|
810
|
+
|
|
811
|
+
// add meta columns
|
|
780
812
|
this._monomerMetaColumns.forEach((meta) => {
|
|
781
813
|
const metaCol = monomerPositionDf.columns.addNewString(meta);
|
|
782
814
|
monomersMetaPromise.then((metaInfo) => {
|
|
@@ -797,10 +829,9 @@ export class MonomerPosition extends SARViewer {
|
|
|
797
829
|
const colorColData = colorCol!.getRawData();
|
|
798
830
|
let minColorVal = 9999999;
|
|
799
831
|
let maxColorVal = -9999999;
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
const
|
|
803
|
-
const targetCategoryIndex = targetCategory == null ? null : targetCol?.categories.indexOf(targetCategory);
|
|
832
|
+
const filter = (this.dataSource === 'Filtered' && this.dataFrame.filter.anyFalse) ?
|
|
833
|
+
this.dataFrame.filter : null;
|
|
834
|
+
const isTarget = filter == null ? (index: number) => true : (index: number) => filter.get(index);
|
|
804
835
|
for (const pCol of this.positionColumns) {
|
|
805
836
|
pCol.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] = {};
|
|
806
837
|
const colorCache = pCol.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE];
|
|
@@ -817,8 +848,7 @@ export class MonomerPosition extends SARViewer {
|
|
|
817
848
|
const colorValuesIndexes: number[] = [];
|
|
818
849
|
for (let i = 0; i < pCol.length; ++i) {
|
|
819
850
|
const isCurrentMonomer = positionColCategories[positionColData[i]] === pMonomer;
|
|
820
|
-
|
|
821
|
-
if (isCurrentMonomer && isTarget)
|
|
851
|
+
if (isCurrentMonomer && isTarget(i))
|
|
822
852
|
colorValuesIndexes.push(i);
|
|
823
853
|
}
|
|
824
854
|
const cellColorDataCol = DG.Column.float('color', colorValuesIndexes.length)
|
|
@@ -869,7 +899,13 @@ export class MonomerPosition extends SARViewer {
|
|
|
869
899
|
const grid = monomerPositionDf.plot.grid();
|
|
870
900
|
grid.sort([C.COLUMNS_NAMES.MONOMER]);
|
|
871
901
|
const positionColumns = this.positionColumns.map((col) => col.name);
|
|
872
|
-
grid.columns.setOrder([C.COLUMNS_NAMES.MONOMER, ...this._monomerMetaColumns, ...positionColumns]);
|
|
902
|
+
grid.columns.setOrder([C.COLUMNS_NAMES.TOTAL_COUNT, C.COLUMNS_NAMES.MONOMER, ...this._monomerMetaColumns, ...positionColumns]);
|
|
903
|
+
|
|
904
|
+
const sumGridCol = grid.columns.byName(C.COLUMNS_NAMES.TOTAL_COUNT);
|
|
905
|
+
if (this.showTotalCountColumn && sumGridCol != null)
|
|
906
|
+
sumGridCol.visible = true;
|
|
907
|
+
else if (sumGridCol != null)
|
|
908
|
+
sumGridCol.visible = false;
|
|
873
909
|
const monomerCol = monomerPositionDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
874
910
|
CR.setMonomerRenderer(monomerCol, this.alphabet, true);
|
|
875
911
|
this.cacheInvariantMapColors();
|
|
@@ -879,7 +915,7 @@ export class MonomerPosition extends SARViewer {
|
|
|
879
915
|
this.colorAggregation as DG.AGG));
|
|
880
916
|
|
|
881
917
|
grid.onCellTooltip((gridCell: DG.GridCell, x: number, y: number) => {
|
|
882
|
-
if (!gridCell.isTableCell || !gridCell?.cell.column?.name || this._monomerMetaColumns.has(gridCell.cell.column.name)) {
|
|
918
|
+
if (!gridCell.isTableCell || !gridCell?.cell.column?.name || this._monomerMetaColumns.has(gridCell.cell.column.name) || gridCell.cell.column.name === C.COLUMNS_NAMES.TOTAL_COUNT) {
|
|
883
919
|
this.model.unhighlight();
|
|
884
920
|
return true;
|
|
885
921
|
}
|
|
@@ -916,7 +952,7 @@ export class MonomerPosition extends SARViewer {
|
|
|
916
952
|
grid.root.addEventListener('mouseleave', (_ev) => this.model.unhighlight());
|
|
917
953
|
DG.debounce(grid.onCurrentCellChanged, 500).subscribe((gridCell: DG.GridCell) => {
|
|
918
954
|
try {
|
|
919
|
-
if (!gridCell || !gridCell.dart || !gridCell?.cell?.column?.name || this._monomerMetaColumns.has(gridCell.cell.column.name))
|
|
955
|
+
if (!gridCell || !gridCell.dart || !gridCell?.cell?.column?.name || this._monomerMetaColumns.has(gridCell.cell.column.name) || gridCell.cell.column.name == C.COLUMNS_NAMES.TOTAL_COUNT ||!gridCell.isTableCell)
|
|
920
956
|
return;
|
|
921
957
|
if (gridCell.gridRow === -1) {
|
|
922
958
|
if (this.mode === SELECTION_MODE.INVARIANT_MAP)
|
|
@@ -928,7 +964,7 @@ export class MonomerPosition extends SARViewer {
|
|
|
928
964
|
grid.invalidate();
|
|
929
965
|
}
|
|
930
966
|
|
|
931
|
-
if (!this.keyPressed)
|
|
967
|
+
if (!this.keyPressed || gridCell.cell.column!.name === C.COLUMNS_NAMES.MONOMER)
|
|
932
968
|
return;
|
|
933
969
|
|
|
934
970
|
|
|
@@ -964,9 +1000,12 @@ export class MonomerPosition extends SARViewer {
|
|
|
964
1000
|
|
|
965
1001
|
grid.invalidate();
|
|
966
1002
|
setTimeout(() => grid?.invalidate(), 300);
|
|
1003
|
+
} catch (e) {
|
|
1004
|
+
console.error(e);
|
|
967
1005
|
} finally {
|
|
968
1006
|
this.keyPressed = false;
|
|
969
|
-
if (gridCell.tableColumn?.name && gridCell.grid
|
|
1007
|
+
if (gridCell.tableColumn?.name && gridCell.grid && gridCell.isTableCell && gridCell?.cell?.column.name != C.COLUMNS_NAMES.MONOMER &&
|
|
1008
|
+
!this._monomerMetaColumns.has(gridCell.cell?.column?.name) && gridCell.cell?.column.name !== C.COLUMNS_NAMES.TOTAL_COUNT)
|
|
970
1009
|
this.currentGridCell = DG.GridCell.fromColumnRow(gridCell.grid, gridCell.tableColumn.name, gridCell.gridRow);
|
|
971
1010
|
else
|
|
972
1011
|
this.currentGridCell = null;
|
|
@@ -1010,7 +1049,7 @@ export class MonomerPosition extends SARViewer {
|
|
|
1010
1049
|
grid.root.addEventListener('click', (ev) => {
|
|
1011
1050
|
const gridCell = grid.hitTest(ev.offsetX, ev.offsetY);
|
|
1012
1051
|
if (!gridCell?.isTableCell || gridCell?.tableColumn?.name === C.COLUMNS_NAMES.MONOMER ||
|
|
1013
|
-
(gridCell?.tableColumn?.name && this._monomerMetaColumns.has(gridCell.tableColumn.name))
|
|
1052
|
+
(gridCell?.tableColumn?.name && this._monomerMetaColumns.has(gridCell.tableColumn.name) || gridCell?.tableColumn?.name === C.COLUMNS_NAMES.TOTAL_COUNT)
|
|
1014
1053
|
)
|
|
1015
1054
|
return;
|
|
1016
1055
|
|
|
@@ -1032,6 +1071,7 @@ export class MonomerPosition extends SARViewer {
|
|
|
1032
1071
|
});
|
|
1033
1072
|
|
|
1034
1073
|
setViewerGridProps(grid);
|
|
1074
|
+
grid.props.showRowHeader = true;
|
|
1035
1075
|
|
|
1036
1076
|
// Monomer cell renderer overrides width settings. This way I ensure is "initially" set.
|
|
1037
1077
|
const afterDraw = grid.onAfterDrawContent.subscribe(() => {
|
|
@@ -1117,9 +1157,8 @@ export class MonomerPosition extends SARViewer {
|
|
|
1117
1157
|
this.viewerGrid.invalidate();
|
|
1118
1158
|
}, 'Show Sequence Variability Map Table in full screen');
|
|
1119
1159
|
$(expand).addClass('pep-help-icon');
|
|
1120
|
-
this.targetColumnInput && (this.targetColumnInput.root.style.maxWidth = '50%');
|
|
1121
1160
|
this.monomerSearchInput.root.style.marginRight = '8px';
|
|
1122
|
-
const targetInputsHost = ui.divH([this.monomerSearchInput.input
|
|
1161
|
+
const targetInputsHost = ui.divH([this.monomerSearchInput.input],
|
|
1123
1162
|
{style: {alignSelf: 'center', justifyContent: 'center', width: '100%', flexWrap: 'wrap'}});
|
|
1124
1163
|
targetInputsHost.style.display = this.showFilterControls ? 'flex' : 'none';
|
|
1125
1164
|
const header = ui.divH([expand, switchHost, targetInputsHost], {style: {alignSelf: 'center', lineHeight: 'normal', flexDirection: 'column', width: '100%'}});
|
|
@@ -1162,6 +1201,23 @@ export class MostPotentResidues extends SARViewer {
|
|
|
1162
1201
|
this.render();
|
|
1163
1202
|
}
|
|
1164
1203
|
|
|
1204
|
+
onFilterChanged(render?: boolean, onlySetNulls = false): void {
|
|
1205
|
+
this._monomerPositionStats = null;
|
|
1206
|
+
this._invariantMapSelection = null;
|
|
1207
|
+
this._mutationCliffStats = null;
|
|
1208
|
+
this._mutationCliffsSelection = null;
|
|
1209
|
+
this._mutationCliffs = null;
|
|
1210
|
+
if (onlySetNulls)
|
|
1211
|
+
return;
|
|
1212
|
+
this._viewerGrid = null;
|
|
1213
|
+
const sarViewer = this.model.findViewer(VIEWER_TYPE.SEQUENCE_VARIABILITY_MAP) as MonomerPosition | null;
|
|
1214
|
+
if (sarViewer?.dataSource === 'Filtered')
|
|
1215
|
+
sarViewer.onFilterChanged(false, true);
|
|
1216
|
+
if (render)
|
|
1217
|
+
this.render();
|
|
1218
|
+
// do not recalculate mutation cliffs on filter change for MostPotentResidues viewer
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1165
1221
|
/**
|
|
1166
1222
|
* Creates most potent residues dataframe to be used in MostPotentResidues grid.
|
|
1167
1223
|
* @return - most potent residues dataframe.
|
|
@@ -1442,12 +1498,12 @@ function renderCell(args: DG.GridCellRenderArgs, viewer: SARViewer, isInvariantM
|
|
|
1442
1498
|
|
|
1443
1499
|
// Hide row column
|
|
1444
1500
|
const cell = args.cell;
|
|
1445
|
-
if (cell.isRowHeader && cell.gridColumn.visible) {
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
}
|
|
1501
|
+
// if (cell.isRowHeader && cell.gridColumn.visible) {
|
|
1502
|
+
// cell.gridColumn.visible = false;
|
|
1503
|
+
// args.preventDefault();
|
|
1504
|
+
// canvasContext.restore();
|
|
1505
|
+
// return;
|
|
1506
|
+
// }
|
|
1451
1507
|
|
|
1452
1508
|
const tableColName = cell.tableColumn?.name;
|
|
1453
1509
|
const tableRowIndex = cell.tableRowIndex!;
|
package/src/widgets/selection.ts
CHANGED
|
@@ -104,7 +104,7 @@ export function getSelectionWidget(table: DG.DataFrame, options: SelectionWidget
|
|
|
104
104
|
}, 500);
|
|
105
105
|
|
|
106
106
|
const mpStats = calculateMonomerPositionStatistics(options.activityColumn, newTable.filter,
|
|
107
|
-
options.positionColumns, {isFiltered:
|
|
107
|
+
options.positionColumns, {isFiltered: true});
|
|
108
108
|
|
|
109
109
|
const cachedWebLogoTooltip: () => CachedWebLogoTooltip = () => {
|
|
110
110
|
return {bar: '', tooltip: null};
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
2
|
+
|
|
1
3
|
onmessage = async (event): Promise<void> => {
|
|
2
|
-
const {startIdx, endIdx, activityArray, monomerInfoArray, settings
|
|
4
|
+
const {startIdx, endIdx, activityArray, monomerInfoArray, settings} = event.data;
|
|
5
|
+
const filterArray: Uint32Array = settings.filter;
|
|
6
|
+
const filter = filterArray ? new BitArray(filterArray, filterArray.length * 32) : null;
|
|
7
|
+
|
|
3
8
|
const pos: string[] = [];
|
|
4
9
|
const seq1Idxs: number[] = [];
|
|
5
10
|
const seq2Idxs: number[] = [];
|
|
@@ -12,8 +17,7 @@ onmessage = async (event): Promise<void> => {
|
|
|
12
17
|
let seq2Idx = startCol;
|
|
13
18
|
const tempData = new Array(monomerInfoArray.length);
|
|
14
19
|
while (cnt < chunkSize) {
|
|
15
|
-
if (!
|
|
16
|
-
settings.targetCol?.rawData[seq2Idx] !== currentTargetIdx))) {
|
|
20
|
+
if (!filter || (filter.getBit(seq1Idx) && filter.getBit(seq2Idx))) {
|
|
17
21
|
let substCounter = 0;
|
|
18
22
|
const activityValSeq1 = activityArray[seq1Idx];
|
|
19
23
|
const activityValSeq2 = activityArray[seq2Idx];
|