@datagrok/peptides 1.2.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/dist/package-test.js +309 -393
- package/dist/package.js +363 -393
- package/package.json +1 -1
- package/src/model.ts +246 -159
- package/src/package.ts +13 -13
- package/src/styles.css +1 -0
- package/src/tests/core.ts +5 -5
- package/src/tests/peptide-space-test.ts +32 -32
- package/src/utils/cell-renderer.ts +27 -59
- package/src/utils/constants.ts +3 -0
- package/src/utils/types.ts +1 -1
- package/src/viewers/logo-summary.ts +42 -0
- package/src/viewers/sar-viewer.ts +31 -13
- package/src/widgets/analyze-peptides.ts +15 -6
- package/src/widgets/distribution.ts +1 -1
- package/src/widgets/mutation-cliffs.ts +1 -1
- package/test-Peptides-7770371320b2-8cac318e.html +246 -0
- package/src/utils/invariant-map.ts +0 -163
package/src/model.ts
CHANGED
|
@@ -5,35 +5,39 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
import {splitAlignedSequences} from '@datagrok-libraries/bio/src/utils/splitter';
|
|
6
6
|
|
|
7
7
|
import * as rxjs from 'rxjs';
|
|
8
|
+
import $ from 'cash-dom';
|
|
8
9
|
|
|
9
10
|
import * as C from './utils/constants';
|
|
10
11
|
import * as type from './utils/types';
|
|
11
12
|
import {calculateBarsData, getTypedArrayConstructor, isGridCellInvalid, scaleActivity} from './utils/misc';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {renderBarchart, renderSARCell, setAARRenderer} from './utils/cell-renderer';
|
|
13
|
+
import {MutationCliffsViewer, SARViewerBase, MostPotentResiduesViewer} from './viewers/sar-viewer';
|
|
14
|
+
import {renderBarchart, renderMutationCliffCell, setAARRenderer, renderInvaraintMapCell} from './utils/cell-renderer';
|
|
15
15
|
import {mutationCliffsWidget} from './widgets/mutation-cliffs';
|
|
16
16
|
import {getDistributionAndStats, getDistributionWidget} from './widgets/distribution';
|
|
17
17
|
import {getStats, Stats} from './utils/statistics';
|
|
18
|
+
import {LogoSummary} from './viewers/logo-summary';
|
|
18
19
|
|
|
19
20
|
export class PeptidesModel {
|
|
20
21
|
static modelName = 'peptidesModel';
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
mutationCliffsGridSubject = new rxjs.Subject<DG.Grid>();
|
|
24
|
+
mostPotentResiduesGridSubject = new rxjs.Subject<DG.Grid>();
|
|
25
|
+
logoSummaryGridSubject = new rxjs.Subject<DG.Grid>();
|
|
24
26
|
|
|
25
27
|
_isUpdating: boolean = false;
|
|
26
28
|
isBitsetChangedInitialized = false;
|
|
27
29
|
isCellChanging = false;
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
mutationCliffsGrid!: DG.Grid;
|
|
32
|
+
mostPotentResiduesGrid!: DG.Grid;
|
|
33
|
+
logoSummaryGrid!: DG.Grid;
|
|
34
|
+
sourceGrid!: DG.Grid;
|
|
32
35
|
df: DG.DataFrame;
|
|
33
36
|
splitCol!: DG.Column<boolean>;
|
|
34
37
|
edf: DG.DataFrame | null = null;
|
|
35
38
|
statsDf!: DG.DataFrame;
|
|
36
|
-
|
|
39
|
+
_mutationCliffsSelection: type.PositionToAARList = {};
|
|
40
|
+
_invariantMapSelection: type.PositionToAARList = {};
|
|
37
41
|
substitutionsInfo: type.SubstitutionsInfo = new Map();
|
|
38
42
|
isInitialized = false;
|
|
39
43
|
currentView!: DG.TableView;
|
|
@@ -41,8 +45,8 @@ export class PeptidesModel {
|
|
|
41
45
|
isPeptideSpaceChangingBitset = false;
|
|
42
46
|
isChangingEdfBitset = false;
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
mutationCliffsViewer!: MutationCliffsViewer;
|
|
49
|
+
mostPotentResiduesViewer!: MostPotentResiduesViewer;
|
|
46
50
|
|
|
47
51
|
_usedProperties: {[propName: string]: string | number | boolean} = {};
|
|
48
52
|
monomerMap: {[key: string]: {molfile: string, fullName: string}} = {};
|
|
@@ -61,21 +65,41 @@ export class PeptidesModel {
|
|
|
61
65
|
return dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
get
|
|
68
|
+
get onMutationCliffsGridChanged(): rxjs.Observable<DG.Grid> {
|
|
69
|
+
return this.mutationCliffsGridSubject.asObservable();
|
|
70
|
+
}
|
|
65
71
|
|
|
66
|
-
get
|
|
72
|
+
get onMostPotentResiduesGridChanged(): rxjs.Observable<DG.Grid> {
|
|
73
|
+
return this.mostPotentResiduesGridSubject.asObservable();
|
|
74
|
+
}
|
|
67
75
|
|
|
68
|
-
get
|
|
69
|
-
this.
|
|
70
|
-
return this._currentSelection;
|
|
76
|
+
get onLogoSummaryGridChanged(): rxjs.Observable<DG.Grid> {
|
|
77
|
+
return this.logoSummaryGridSubject.asObservable();
|
|
71
78
|
}
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
|
|
80
|
+
get mutationCliffsSelection(): type.PositionToAARList {
|
|
81
|
+
this._mutationCliffsSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
|
|
82
|
+
return this._mutationCliffsSelection;
|
|
83
|
+
}
|
|
84
|
+
set mutationCliffsSelection(selection: type.PositionToAARList) {
|
|
85
|
+
this._mutationCliffsSelection = selection;
|
|
74
86
|
this.df.tags[C.TAGS.SELECTION] = JSON.stringify(selection);
|
|
75
|
-
this.
|
|
87
|
+
this.fireBitsetChanged();
|
|
88
|
+
this.invalidateGrids();
|
|
76
89
|
this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
|
|
77
90
|
}
|
|
78
91
|
|
|
92
|
+
get invariantMapSelection(): type.PositionToAARList {
|
|
93
|
+
this._invariantMapSelection ??= JSON.parse(this.df.tags[C.TAGS.FILTER] || '{}');
|
|
94
|
+
return this._invariantMapSelection;
|
|
95
|
+
}
|
|
96
|
+
set invariantMapSelection(selection: type.PositionToAARList) {
|
|
97
|
+
this._invariantMapSelection = selection;
|
|
98
|
+
this.df.tags[C.TAGS.FILTER] = JSON.stringify(selection);
|
|
99
|
+
this.df.filter.fireChanged();
|
|
100
|
+
this.invalidateGrids();
|
|
101
|
+
}
|
|
102
|
+
|
|
79
103
|
get usedProperties(): {[propName: string]: string | number | boolean} {
|
|
80
104
|
this._usedProperties = JSON.parse(this.df.tags['sarProperties'] ?? '{}');
|
|
81
105
|
return this._usedProperties;
|
|
@@ -103,23 +127,25 @@ export class PeptidesModel {
|
|
|
103
127
|
this.df.tags['distributionSplit'] = `${splitByAARFlag}${flag ? 1 : 0}`;
|
|
104
128
|
}
|
|
105
129
|
|
|
106
|
-
|
|
107
|
-
this.
|
|
108
|
-
|
|
130
|
+
get isInvariantMap(): boolean {
|
|
131
|
+
return this.df.getTag('isInvariantMap') === '1';
|
|
132
|
+
}
|
|
133
|
+
set isInvariantMap(x: boolean) {
|
|
134
|
+
this.df.setTag('isInvariantMap', x ? '1' : '0');
|
|
109
135
|
}
|
|
110
136
|
|
|
111
137
|
createAccordion(): DG.Accordion {
|
|
112
138
|
const acc = ui.accordion();
|
|
113
139
|
acc.root.style.width = '100%';
|
|
114
140
|
acc.addTitle(ui.h1(`${this.df.selection.trueCount} selected rows`));
|
|
115
|
-
acc.addPane('
|
|
116
|
-
acc.addPane('
|
|
141
|
+
acc.addPane('Mutation Cliff pairs', () => mutationCliffsWidget(this.df, this).root, true);
|
|
142
|
+
acc.addPane('Distribution', () => getDistributionWidget(this.df, this).root, true);
|
|
117
143
|
|
|
118
144
|
return acc;
|
|
119
145
|
}
|
|
120
146
|
|
|
121
147
|
getViewer(): SARViewerBase {
|
|
122
|
-
const viewer = this.
|
|
148
|
+
const viewer = this.mutationCliffsViewer ?? this.mostPotentResiduesViewer;
|
|
123
149
|
if (!viewer)
|
|
124
150
|
throw new Error('ViewerError: none of the SAR viewers is initialized');
|
|
125
151
|
return viewer;
|
|
@@ -145,28 +171,29 @@ export class PeptidesModel {
|
|
|
145
171
|
}
|
|
146
172
|
|
|
147
173
|
updateDefault(): void {
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
if ((this._sourceGrid && !this._isUpdating && proprtyChanged) || !this.isInitialized) {
|
|
174
|
+
const proprtyChanged = this.isPropertyChanged(this.mutationCliffsViewer) || this.isPropertyChanged(this.mostPotentResiduesViewer);
|
|
175
|
+
if ((this.sourceGrid && !this._isUpdating && proprtyChanged) || !this.isInitialized) {
|
|
151
176
|
this.isInitialized = true;
|
|
152
177
|
this._isUpdating = true;
|
|
153
|
-
|
|
178
|
+
this.initializeViewersComponents();
|
|
154
179
|
//FIXME: modify during the initializeViewersComponents stages
|
|
155
|
-
this.
|
|
156
|
-
this.
|
|
180
|
+
this.mutationCliffsGridSubject.next(this.mutationCliffsGrid);
|
|
181
|
+
this.mostPotentResiduesGridSubject.next(this.mostPotentResiduesGrid);
|
|
182
|
+
if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
183
|
+
this.logoSummaryGridSubject.next(this.logoSummaryGrid);
|
|
157
184
|
|
|
158
|
-
this.
|
|
185
|
+
this.fireBitsetChanged();
|
|
186
|
+
this.invalidateGrids();
|
|
159
187
|
this._isUpdating = false;
|
|
160
188
|
}
|
|
161
189
|
}
|
|
162
190
|
|
|
163
|
-
initializeViewersComponents():
|
|
164
|
-
if (this.
|
|
191
|
+
initializeViewersComponents(): void {
|
|
192
|
+
if (this.sourceGrid === null)
|
|
165
193
|
throw new Error(`Source grid is not initialized`);
|
|
166
194
|
|
|
167
195
|
//Split the aligned sequence into separate AARs
|
|
168
196
|
const col: DG.Column<string> = this.df.columns.bySemType(C.SEM_TYPES.MACROMOLECULE)!;
|
|
169
|
-
// const alphabet = col.tags[DG.TAGS.UNITS].split(':')[2];
|
|
170
197
|
const alphabet = col.tags['alphabet'];
|
|
171
198
|
const splitSeqDf = splitAlignedSequences(col);
|
|
172
199
|
|
|
@@ -181,7 +208,7 @@ export class PeptidesModel {
|
|
|
181
208
|
|
|
182
209
|
for (const dfCol of this.df.columns) {
|
|
183
210
|
if (positionColumns.includes(dfCol.name))
|
|
184
|
-
setAARRenderer(dfCol, alphabet, this.
|
|
211
|
+
setAARRenderer(dfCol, alphabet, this.sourceGrid);
|
|
185
212
|
}
|
|
186
213
|
|
|
187
214
|
this.sortSourceGrid();
|
|
@@ -214,28 +241,29 @@ export class PeptidesModel {
|
|
|
214
241
|
|
|
215
242
|
this.calcSubstitutions();
|
|
216
243
|
|
|
217
|
-
|
|
244
|
+
[this.mutationCliffsGrid, this.mostPotentResiduesGrid] =
|
|
245
|
+
this.createGrids(matrixDf, sequenceDf, positionColumns, alphabet);
|
|
246
|
+
|
|
247
|
+
if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
248
|
+
this.logoSummaryGrid = this.createLogoSummaryGrid();
|
|
218
249
|
|
|
219
|
-
|
|
220
|
-
this.
|
|
250
|
+
// init invariant map & mutation cliffs selections
|
|
251
|
+
this.initSelections(positionColumns);
|
|
221
252
|
|
|
222
253
|
positionColumns.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
223
254
|
|
|
224
255
|
this.setBarChartInteraction();
|
|
225
256
|
|
|
226
|
-
this.setCellRenderers(positionColumns
|
|
257
|
+
this.setCellRenderers(positionColumns);
|
|
227
258
|
|
|
228
259
|
// show all the statistics in a tooltip over cell
|
|
229
|
-
this.setTooltips(positionColumns
|
|
260
|
+
this.setTooltips(positionColumns);
|
|
230
261
|
|
|
231
262
|
this.setInteractionCallback();
|
|
232
263
|
|
|
233
264
|
this.setBitsetCallback();
|
|
234
265
|
|
|
235
|
-
this.postProcessGrids(
|
|
236
|
-
|
|
237
|
-
//TODO: return class instead
|
|
238
|
-
return [sarGrid, sarVGrid];
|
|
266
|
+
this.postProcessGrids();
|
|
239
267
|
}
|
|
240
268
|
|
|
241
269
|
calcSubstitutions(): void {
|
|
@@ -323,6 +351,17 @@ export class PeptidesModel {
|
|
|
323
351
|
}
|
|
324
352
|
}
|
|
325
353
|
|
|
354
|
+
initSelections(positionColumns: string[]): void {
|
|
355
|
+
const tempInvariantMapSelection: type.PositionToAARList = this.invariantMapSelection;
|
|
356
|
+
const mutationCliffsSelection: type.PositionToAARList = this.mutationCliffsSelection;
|
|
357
|
+
for (const pos of positionColumns) {
|
|
358
|
+
tempInvariantMapSelection[pos] ??= [];
|
|
359
|
+
mutationCliffsSelection[pos] ??= [];
|
|
360
|
+
}
|
|
361
|
+
this.invariantMapSelection = tempInvariantMapSelection;
|
|
362
|
+
this.mutationCliffsSelection = mutationCliffsSelection;
|
|
363
|
+
}
|
|
364
|
+
|
|
326
365
|
joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame): void {
|
|
327
366
|
// append splitSeqDf columns to source table and make sure columns are not added more than once
|
|
328
367
|
const name = this.df.name;
|
|
@@ -337,8 +376,8 @@ export class PeptidesModel {
|
|
|
337
376
|
|
|
338
377
|
sortSourceGrid(): void {
|
|
339
378
|
const colNames: DG.GridColumn[] = [];
|
|
340
|
-
for (let i = 1; i < this.
|
|
341
|
-
colNames.push(this.
|
|
379
|
+
for (let i = 1; i < this.sourceGrid.columns.length; i++)
|
|
380
|
+
colNames.push(this.sourceGrid.columns.byIndex(i)!);
|
|
342
381
|
|
|
343
382
|
colNames.sort((a, b)=>{
|
|
344
383
|
if (a.column!.semType == C.SEM_TYPES.MONOMER) {
|
|
@@ -350,7 +389,7 @@ export class PeptidesModel {
|
|
|
350
389
|
return 1;
|
|
351
390
|
return 0;
|
|
352
391
|
});
|
|
353
|
-
this.
|
|
392
|
+
this.sourceGrid.columns.setOrder(colNames.map((v) => v.name));
|
|
354
393
|
}
|
|
355
394
|
|
|
356
395
|
createScaledCol(activityScaling: string, splitSeqDf: DG.DataFrame): void {
|
|
@@ -362,13 +401,13 @@ export class PeptidesModel {
|
|
|
362
401
|
splitSeqDf.columns.add(scaledCol);
|
|
363
402
|
const oldScaledCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
364
403
|
this.df.columns.replace(oldScaledCol, scaledCol);
|
|
365
|
-
const gridCol = this.
|
|
404
|
+
const gridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
366
405
|
if (gridCol !== null) {
|
|
367
406
|
gridCol.name = newColName;
|
|
368
407
|
this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED] = newColName;
|
|
369
408
|
}
|
|
370
409
|
|
|
371
|
-
this.
|
|
410
|
+
this.sourceGrid.columns.setOrder([newColName]);
|
|
372
411
|
}
|
|
373
412
|
|
|
374
413
|
calculateStatistics(matrixDf: DG.DataFrame): DG.DataFrame {
|
|
@@ -451,32 +490,63 @@ export class PeptidesModel {
|
|
|
451
490
|
return sequenceDf;
|
|
452
491
|
}
|
|
453
492
|
|
|
454
|
-
createGrids(
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
493
|
+
createGrids(mutationCliffsDf: DG.DataFrame, mostPotentResiduesDf: DG.DataFrame, positionColumns: string[],
|
|
494
|
+
alphabet: string): [DG.Grid, DG.Grid] {
|
|
495
|
+
// Creating Mutation Cliffs grid and sorting columns
|
|
496
|
+
const mutationCliffsGrid = mutationCliffsDf.plot.grid();
|
|
497
|
+
mutationCliffsGrid.sort([C.COLUMNS_NAMES.MONOMER]);
|
|
498
|
+
mutationCliffsGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER].concat(positionColumns as C.COLUMNS_NAMES[]));
|
|
459
499
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
500
|
+
// Creating Monomer-Position grid, sorting and setting column format
|
|
501
|
+
const mostPotentResiduesGrid = mostPotentResiduesDf.plot.grid();
|
|
502
|
+
mostPotentResiduesGrid.sort([C.COLUMNS_NAMES.POSITION]);
|
|
503
|
+
const pValGridCol = mostPotentResiduesGrid.col(C.COLUMNS_NAMES.P_VALUE)!;
|
|
463
504
|
pValGridCol.format = '#.000';
|
|
464
505
|
pValGridCol.name = 'P-value';
|
|
465
506
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
507
|
+
// Setting Monomer column renderer
|
|
508
|
+
setAARRenderer(mutationCliffsDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mutationCliffsGrid);
|
|
509
|
+
setAARRenderer(mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mostPotentResiduesGrid);
|
|
510
|
+
|
|
511
|
+
return [mutationCliffsGrid, mostPotentResiduesGrid];
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
createLogoSummaryGrid(): DG.Grid {
|
|
515
|
+
const summaryTable = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
|
|
516
|
+
const summaryTableLength = summaryTable.rowCount;
|
|
517
|
+
const webLogoCol: DG.Column<string> = summaryTable.columns.addNew('WebLogo', DG.COLUMN_TYPE.STRING);
|
|
518
|
+
const clustersCol: DG.Column<number> = summaryTable.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
519
|
+
clustersCol.name = 'Clusters';
|
|
520
|
+
const tempDfList: DG.DataFrame[] = new Array(summaryTableLength);
|
|
521
|
+
const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
|
|
522
|
+
|
|
523
|
+
for (let index = 0; index < summaryTableLength; ++index) {
|
|
524
|
+
// const tempDf: DG.DataFrame = this.df.rows.match(`${C.COLUMNS_NAMES.CLUSTERS} = ${clustersCol.get(index)}`).toDataFrame();
|
|
525
|
+
const bs = DG.BitSet.create(this.df.rowCount);
|
|
526
|
+
bs.init((i) => clustersCol.get(index) === originalClustersCol.get(i));
|
|
527
|
+
const tempDf = this.df.clone(bs, [C.COLUMNS_NAMES.ALIGNED_SEQUENCE]);
|
|
528
|
+
tempDfList[index] = tempDf;
|
|
529
|
+
webLogoCol.set(index, index.toString());
|
|
530
|
+
}
|
|
531
|
+
webLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
532
|
+
|
|
533
|
+
const grid = summaryTable.plot.grid();
|
|
534
|
+
grid.columns.rowHeader!.visible = false;
|
|
535
|
+
grid.props.rowHeight = 55;
|
|
536
|
+
grid.onCellPrepare((cell) => {
|
|
537
|
+
if (cell.isTableCell && cell.tableColumn?.name === 'WebLogo')
|
|
538
|
+
tempDfList[parseInt(cell.cell.value)].plot.fromType('WebLogo').then((viewer) => cell.element = viewer.root);
|
|
539
|
+
});
|
|
540
|
+
const webLogoGridCol = grid.columns.byName('WebLogo')!;
|
|
541
|
+
webLogoGridCol.cellType = 'html';
|
|
542
|
+
webLogoGridCol.width = 350;
|
|
473
543
|
|
|
474
|
-
return
|
|
544
|
+
return grid;
|
|
475
545
|
}
|
|
476
546
|
|
|
477
547
|
setBarChartInteraction(): void {
|
|
478
548
|
const eventAction = (ev: MouseEvent): void => {
|
|
479
|
-
const cell = this.
|
|
549
|
+
const cell = this.sourceGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
480
550
|
if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.MONOMER) {
|
|
481
551
|
const newBarPart = this.findAARandPosition(cell, ev);
|
|
482
552
|
this.requestBarchartAction(ev, newBarPart);
|
|
@@ -484,9 +554,9 @@ export class PeptidesModel {
|
|
|
484
554
|
};
|
|
485
555
|
|
|
486
556
|
// The following events makes the barchart interactive
|
|
487
|
-
rxjs.fromEvent<MouseEvent>(this.
|
|
557
|
+
rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'mousemove')
|
|
488
558
|
.subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
489
|
-
rxjs.fromEvent<MouseEvent>(this.
|
|
559
|
+
rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'click')
|
|
490
560
|
.subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
491
561
|
}
|
|
492
562
|
|
|
@@ -508,8 +578,8 @@ export class PeptidesModel {
|
|
|
508
578
|
const monomer = barPart.monomer;
|
|
509
579
|
const position = barPart.position;
|
|
510
580
|
if (ev.type === 'click') {
|
|
511
|
-
ev.shiftKey ? this.modifyCurrentSelection(monomer, position) :
|
|
512
|
-
this.initCurrentSelection(monomer, position);
|
|
581
|
+
ev.shiftKey ? this.modifyCurrentSelection(monomer, position, true) :
|
|
582
|
+
this.initCurrentSelection(monomer, position, true);
|
|
513
583
|
} else {
|
|
514
584
|
const bar = `${monomer}:${position}`;
|
|
515
585
|
if (this.cachedBarchartTooltip.bar == bar)
|
|
@@ -519,7 +589,7 @@ export class PeptidesModel {
|
|
|
519
589
|
}
|
|
520
590
|
}
|
|
521
591
|
|
|
522
|
-
setCellRenderers(renderColNames: string[]
|
|
592
|
+
setCellRenderers(renderColNames: string[]): void {
|
|
523
593
|
const mdCol = this.statsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
524
594
|
//decompose into two different renering funcs
|
|
525
595
|
const renderCell = (args: DG.GridCellRenderArgs): void => {
|
|
@@ -551,18 +621,26 @@ export class PeptidesModel {
|
|
|
551
621
|
const currentAAR: string = gridTable.get(C.COLUMNS_NAMES.MONOMER, tableRowIndex);
|
|
552
622
|
|
|
553
623
|
const viewer = this.getViewer();
|
|
554
|
-
|
|
555
|
-
|
|
624
|
+
if (this.isInvariantMap) {
|
|
625
|
+
const value: number = this.statsDf
|
|
626
|
+
.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.COUNT])
|
|
627
|
+
.where(`${C.COLUMNS_NAMES.POSITION} = ${currentPosition} and ${C.COLUMNS_NAMES.MONOMER} = ${currentAAR}`)
|
|
628
|
+
.aggregate().get(C.COLUMNS_NAMES.COUNT, 0);
|
|
629
|
+
renderInvaraintMapCell(canvasContext, currentAAR, currentPosition, this.invariantMapSelection, value, bound);
|
|
630
|
+
} else {
|
|
631
|
+
renderMutationCliffCell(canvasContext, currentAAR, currentPosition, this.statsDf,
|
|
632
|
+
viewer.bidirectionalAnalysis, mdCol, bound, cellValue, this.mutationCliffsSelection, this.substitutionsInfo);
|
|
633
|
+
}
|
|
556
634
|
}
|
|
557
635
|
args.preventDefault();
|
|
558
636
|
}
|
|
559
637
|
canvasContext.restore();
|
|
560
638
|
};
|
|
561
|
-
|
|
562
|
-
|
|
639
|
+
this.mutationCliffsGrid.onCellRender.subscribe(renderCell);
|
|
640
|
+
this.mostPotentResiduesGrid.onCellRender.subscribe(renderCell);
|
|
563
641
|
|
|
564
|
-
this.
|
|
565
|
-
this.
|
|
642
|
+
this.sourceGrid.setOptions({'colHeaderHeight': 130});
|
|
643
|
+
this.sourceGrid.onCellRender.subscribe((gcArgs) => {
|
|
566
644
|
const context = gcArgs.g;
|
|
567
645
|
const bounds = gcArgs.bounds;
|
|
568
646
|
const col = gcArgs.cell.tableColumn;
|
|
@@ -582,7 +660,7 @@ export class PeptidesModel {
|
|
|
582
660
|
});
|
|
583
661
|
}
|
|
584
662
|
|
|
585
|
-
setTooltips(renderColNames: string[]
|
|
663
|
+
setTooltips(renderColNames: string[]): void {
|
|
586
664
|
const showTooltip = (cell: DG.GridCell, x: number, y: number): boolean => {
|
|
587
665
|
const tableCol = cell.tableColumn;
|
|
588
666
|
const tableColName = tableCol?.name;
|
|
@@ -604,9 +682,9 @@ export class PeptidesModel {
|
|
|
604
682
|
return true;
|
|
605
683
|
};
|
|
606
684
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
this.
|
|
685
|
+
this.mutationCliffsGrid.onCellTooltip(showTooltip);
|
|
686
|
+
this.mostPotentResiduesGrid.onCellTooltip(showTooltip);
|
|
687
|
+
this.sourceGrid.onCellTooltip((cell, x, y) => {
|
|
610
688
|
const col = cell.tableColumn;
|
|
611
689
|
const cellValue = cell.cell.value;
|
|
612
690
|
if (cellValue && col && col.semType === C.SEM_TYPES.MONOMER)
|
|
@@ -617,8 +695,6 @@ export class PeptidesModel {
|
|
|
617
695
|
|
|
618
696
|
showMonomerTooltip(aar: string, x: number, y: number): void {
|
|
619
697
|
const tooltipElements: HTMLDivElement[] = [];
|
|
620
|
-
//@ts-ignore: no types for org
|
|
621
|
-
// const monomer: type.HELMMonomer = org.helm.webeditor.monomers.getMonomer('HELM_AA', aar);
|
|
622
698
|
const monomer: type.HELMMonomer = this.monomerLib[aar.toLowerCase()];
|
|
623
699
|
|
|
624
700
|
if (monomer) {
|
|
@@ -655,30 +731,33 @@ export class PeptidesModel {
|
|
|
655
731
|
}
|
|
656
732
|
|
|
657
733
|
setInteractionCallback(): void {
|
|
658
|
-
const
|
|
659
|
-
const
|
|
734
|
+
const mutationCliffsDf = this.mutationCliffsGrid.dataFrame;
|
|
735
|
+
const mostPotentResiduesDf = this.mostPotentResiduesGrid.dataFrame;
|
|
736
|
+
// const invariantMapDf = this.invariantMapGrid.dataFrame;
|
|
660
737
|
|
|
661
|
-
const chooseAction =
|
|
662
|
-
|
|
738
|
+
const chooseAction =
|
|
739
|
+
(aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean = true): void =>
|
|
740
|
+
isShiftPressed ? this.modifyCurrentSelection(aar, position, isInvariantMapSelection) :
|
|
741
|
+
this.initCurrentSelection(aar, position, isInvariantMapSelection);
|
|
663
742
|
|
|
664
|
-
this.
|
|
665
|
-
const gridCell = this.
|
|
743
|
+
this.mutationCliffsGrid.root.addEventListener('click', (ev) => {
|
|
744
|
+
const gridCell = this.mutationCliffsGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
666
745
|
if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name == C.COLUMNS_NAMES.MONOMER)
|
|
667
746
|
return;
|
|
668
747
|
|
|
669
748
|
const position = gridCell!.tableColumn!.name;
|
|
670
|
-
const aar =
|
|
671
|
-
chooseAction(aar, position, ev.shiftKey);
|
|
749
|
+
const aar = mutationCliffsDf.get(C.COLUMNS_NAMES.MONOMER, gridCell!.tableRowIndex!);
|
|
750
|
+
chooseAction(aar, position, ev.shiftKey, this.isInvariantMap);
|
|
672
751
|
});
|
|
673
752
|
|
|
674
|
-
this.
|
|
675
|
-
const gridCell = this.
|
|
753
|
+
this.mostPotentResiduesGrid.root.addEventListener('click', (ev) => {
|
|
754
|
+
const gridCell = this.mostPotentResiduesGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
676
755
|
if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name != C.COLUMNS_NAMES.MEAN_DIFFERENCE)
|
|
677
756
|
return;
|
|
678
757
|
|
|
679
758
|
const tableRowIdx = gridCell!.tableRowIndex!;
|
|
680
|
-
const position =
|
|
681
|
-
const aar =
|
|
759
|
+
const position = mostPotentResiduesDf.get(C.COLUMNS_NAMES.POSITION, tableRowIdx);
|
|
760
|
+
const aar = mostPotentResiduesDf.get(C.COLUMNS_NAMES.MONOMER, tableRowIdx);
|
|
682
761
|
chooseAction(aar, position, ev.shiftKey);
|
|
683
762
|
});
|
|
684
763
|
|
|
@@ -689,12 +768,12 @@ export class PeptidesModel {
|
|
|
689
768
|
table.currentRowIdx = -1;
|
|
690
769
|
this.isCellChanging = false;
|
|
691
770
|
};
|
|
692
|
-
this.
|
|
693
|
-
this.
|
|
771
|
+
this.mutationCliffsGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mutationCliffsDf));
|
|
772
|
+
this.mostPotentResiduesGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mostPotentResiduesDf));
|
|
694
773
|
}
|
|
695
774
|
|
|
696
|
-
modifyCurrentSelection(aar: string, position: string): void {
|
|
697
|
-
const tempSelection = this.
|
|
775
|
+
modifyCurrentSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
|
|
776
|
+
const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
|
|
698
777
|
if (!tempSelection.hasOwnProperty(position))
|
|
699
778
|
tempSelection[position] = [aar];
|
|
700
779
|
else {
|
|
@@ -705,20 +784,26 @@ export class PeptidesModel {
|
|
|
705
784
|
tempSelectionAt.splice(aarIndex, 1);
|
|
706
785
|
}
|
|
707
786
|
|
|
708
|
-
|
|
787
|
+
if (isInvariantMapSelection)
|
|
788
|
+
this.invariantMapSelection = tempSelection;
|
|
789
|
+
else
|
|
790
|
+
this.mutationCliffsSelection = tempSelection;
|
|
709
791
|
}
|
|
710
792
|
|
|
711
|
-
initCurrentSelection(aar: string, position: string): void {
|
|
712
|
-
const tempSelection: type.
|
|
793
|
+
initCurrentSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
|
|
794
|
+
const tempSelection: type.PositionToAARList = {};
|
|
713
795
|
tempSelection[position] = [aar];
|
|
714
|
-
|
|
796
|
+
|
|
797
|
+
if (isInvariantMapSelection)
|
|
798
|
+
this.invariantMapSelection = tempSelection;
|
|
799
|
+
else
|
|
800
|
+
this.mutationCliffsSelection = tempSelection;
|
|
715
801
|
}
|
|
716
802
|
|
|
717
803
|
invalidateGrids(): void {
|
|
718
|
-
|
|
719
|
-
this.
|
|
720
|
-
this.
|
|
721
|
-
this._sourceGrid?.invalidate();
|
|
804
|
+
this.mutationCliffsGrid.invalidate();
|
|
805
|
+
this.mostPotentResiduesGrid.invalidate();
|
|
806
|
+
this.sourceGrid?.invalidate();
|
|
722
807
|
//TODO: this.peptideSpaceGrid.invalidate();
|
|
723
808
|
}
|
|
724
809
|
|
|
@@ -726,8 +811,9 @@ export class PeptidesModel {
|
|
|
726
811
|
if (this.isBitsetChangedInitialized)
|
|
727
812
|
return;
|
|
728
813
|
const selection = this.df.selection;
|
|
814
|
+
const filter = this.df.filter;
|
|
729
815
|
|
|
730
|
-
const
|
|
816
|
+
const changeSelectionBitset = (currentBitset: DG.BitSet): void => {
|
|
731
817
|
const edfSelection = this.edf?.selection;
|
|
732
818
|
if (this.isPeptideSpaceChangingBitset) {
|
|
733
819
|
if (edfSelection == null)
|
|
@@ -743,7 +829,7 @@ export class PeptidesModel {
|
|
|
743
829
|
this.isChangingEdfBitset = false;
|
|
744
830
|
};
|
|
745
831
|
|
|
746
|
-
const positionList = Object.keys(this.
|
|
832
|
+
const positionList = Object.keys(this.mutationCliffsSelection);
|
|
747
833
|
if (positionList.length == 0) {
|
|
748
834
|
currentBitset.init(() => false, false);
|
|
749
835
|
updateEdfSelection();
|
|
@@ -754,7 +840,7 @@ export class PeptidesModel {
|
|
|
754
840
|
const getBitAt = (i: number): boolean => {
|
|
755
841
|
for (const position of positionList) {
|
|
756
842
|
const positionCol: DG.Column<string> = this.df.getCol(position);
|
|
757
|
-
if (this.
|
|
843
|
+
if (this._mutationCliffsSelection[position].includes(positionCol.get(i)!))
|
|
758
844
|
return true;
|
|
759
845
|
}
|
|
760
846
|
return false;
|
|
@@ -764,7 +850,18 @@ export class PeptidesModel {
|
|
|
764
850
|
updateEdfSelection();
|
|
765
851
|
};
|
|
766
852
|
|
|
767
|
-
selection.onChanged.subscribe(() =>
|
|
853
|
+
selection.onChanged.subscribe(() => changeSelectionBitset(selection));
|
|
854
|
+
filter.onChanged.subscribe(() => {
|
|
855
|
+
const positionList = Object.keys(this.invariantMapSelection);
|
|
856
|
+
filter.init((i) => {
|
|
857
|
+
let result = true;
|
|
858
|
+
for (const position of positionList) {
|
|
859
|
+
const aarList = this._invariantMapSelection[position];
|
|
860
|
+
result &&= aarList.length == 0 || aarList.includes(this.df.get(position, i));
|
|
861
|
+
}
|
|
862
|
+
return result;
|
|
863
|
+
}, false);
|
|
864
|
+
});
|
|
768
865
|
this.isBitsetChangedInitialized = true;
|
|
769
866
|
}
|
|
770
867
|
|
|
@@ -776,11 +873,11 @@ export class PeptidesModel {
|
|
|
776
873
|
this.isPeptideSpaceChangingBitset = false;
|
|
777
874
|
}
|
|
778
875
|
|
|
779
|
-
postProcessGrids(
|
|
780
|
-
const mdCol: DG.GridColumn =
|
|
876
|
+
postProcessGrids(): void {
|
|
877
|
+
const mdCol: DG.GridColumn = this.mostPotentResiduesGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
|
|
781
878
|
mdCol.name = 'Diff';
|
|
782
879
|
|
|
783
|
-
for (const grid of [
|
|
880
|
+
for (const grid of [this.mutationCliffsGrid, this.mostPotentResiduesGrid]) {
|
|
784
881
|
const gridProps = grid.props;
|
|
785
882
|
gridProps.rowHeight = 20;
|
|
786
883
|
const girdCols = grid.columns;
|
|
@@ -788,7 +885,7 @@ export class PeptidesModel {
|
|
|
788
885
|
for (let i = 0; i < colNum; ++i) {
|
|
789
886
|
const col = girdCols.byIndex(i)!;
|
|
790
887
|
const colName = col.name;
|
|
791
|
-
if (grid ==
|
|
888
|
+
if (grid == this.mostPotentResiduesGrid && colName !== 'Diff' && colName !== C.COLUMNS_NAMES.MONOMER)
|
|
792
889
|
col.width = 50;
|
|
793
890
|
else
|
|
794
891
|
col.width = gridProps.rowHeight + 10;
|
|
@@ -796,13 +893,17 @@ export class PeptidesModel {
|
|
|
796
893
|
}
|
|
797
894
|
|
|
798
895
|
const setViewerGridProps = (grid: DG.Grid): void => {
|
|
799
|
-
grid.props
|
|
800
|
-
|
|
801
|
-
|
|
896
|
+
const gridProps = grid.props;
|
|
897
|
+
gridProps.allowEdit = false;
|
|
898
|
+
gridProps.allowRowSelection = false;
|
|
899
|
+
gridProps.allowBlockSelection = false;
|
|
900
|
+
gridProps.allowColSelection = false;
|
|
802
901
|
};
|
|
803
902
|
|
|
804
|
-
setViewerGridProps(
|
|
805
|
-
setViewerGridProps(
|
|
903
|
+
setViewerGridProps(this.mutationCliffsGrid);
|
|
904
|
+
setViewerGridProps(this.mostPotentResiduesGrid);
|
|
905
|
+
if (this.df.getTag(C.TAGS.CLUSTERS))
|
|
906
|
+
setViewerGridProps(this.logoSummaryGrid);
|
|
806
907
|
}
|
|
807
908
|
|
|
808
909
|
getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
|
|
@@ -819,9 +920,9 @@ export class PeptidesModel {
|
|
|
819
920
|
}
|
|
820
921
|
|
|
821
922
|
syncProperties(isSourceSAR = true): void {
|
|
822
|
-
if (this.
|
|
823
|
-
const [sourceViewer, targetViewer] = isSourceSAR ? [this.
|
|
824
|
-
[this.
|
|
923
|
+
if (this.mutationCliffsViewer && this.mostPotentResiduesViewer) {
|
|
924
|
+
const [sourceViewer, targetViewer] = isSourceSAR ? [this.mutationCliffsViewer, this.mostPotentResiduesViewer] :
|
|
925
|
+
[this.mostPotentResiduesViewer, this.mutationCliffsViewer];
|
|
825
926
|
const properties = sourceViewer.props.getProperties();
|
|
826
927
|
const newProps: {[propName: string]: string | number | boolean} = {};
|
|
827
928
|
for (const property of properties) {
|
|
@@ -845,14 +946,14 @@ export class PeptidesModel {
|
|
|
845
946
|
|
|
846
947
|
this.currentView = this.df.tags[C.PEPTIDES_ANALYSIS] == 'true' ? grok.shell.v as DG.TableView :
|
|
847
948
|
grok.shell.addTableView(this.df);
|
|
848
|
-
this.
|
|
949
|
+
this.sourceGrid = this.currentView.grid;
|
|
849
950
|
if (this.df.tags[C.PEPTIDES_ANALYSIS] == 'true')
|
|
850
951
|
return;
|
|
851
952
|
|
|
852
953
|
this.df.tags[C.PEPTIDES_ANALYSIS] = 'true';
|
|
853
|
-
this.
|
|
854
|
-
this.
|
|
855
|
-
this.
|
|
954
|
+
this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!.name = this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED];
|
|
955
|
+
this.sourceGrid.columns.setOrder([this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED]]);
|
|
956
|
+
this.sourceGrid.props.allowColSelection = false;
|
|
856
957
|
|
|
857
958
|
this.df.temp[C.EMBEDDING_STATUS] = false;
|
|
858
959
|
const adjustCellSize = (grid: DG.Grid): void => {
|
|
@@ -864,8 +965,8 @@ export class PeptidesModel {
|
|
|
864
965
|
grid.props.rowHeight = 20;
|
|
865
966
|
};
|
|
866
967
|
|
|
867
|
-
for (let i = 0; i < this.
|
|
868
|
-
const aarCol = this.
|
|
968
|
+
for (let i = 0; i < this.sourceGrid.columns.length; i++) {
|
|
969
|
+
const aarCol = this.sourceGrid.columns.byIndex(i);
|
|
869
970
|
if (aarCol && aarCol.name && aarCol.column?.semType !== C.SEM_TYPES.MONOMER &&
|
|
870
971
|
aarCol.name !== this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED])
|
|
871
972
|
aarCol.visible = false;
|
|
@@ -875,12 +976,15 @@ export class PeptidesModel {
|
|
|
875
976
|
|
|
876
977
|
const dockManager = this.currentView.dockManager;
|
|
877
978
|
|
|
878
|
-
this.
|
|
979
|
+
this.mutationCliffsViewer = await this.df.plot.fromType('peptide-sar-viewer', options) as MutationCliffsViewer;
|
|
879
980
|
|
|
880
|
-
this.
|
|
881
|
-
await this.df.plot.fromType('peptide-sar-viewer-vertical', options) as
|
|
981
|
+
this.mostPotentResiduesViewer =
|
|
982
|
+
await this.df.plot.fromType('peptide-sar-viewer-vertical', options) as MostPotentResiduesViewer;
|
|
882
983
|
|
|
883
|
-
|
|
984
|
+
if (this.df.getTag(C.TAGS.CLUSTERS)) {
|
|
985
|
+
const logoSummary = await this.df.plot.fromType('logo-summary-viewer') as LogoSummary;
|
|
986
|
+
dockManager.dock(logoSummary, DG.DOCK_TYPE.RIGHT, null, 'Logo Summary Table');
|
|
987
|
+
}
|
|
884
988
|
|
|
885
989
|
// TODO: completely remove this viewer?
|
|
886
990
|
// if (this.df.rowCount <= 10000) {
|
|
@@ -892,35 +996,18 @@ export class PeptidesModel {
|
|
|
892
996
|
|
|
893
997
|
this.updateDefault();
|
|
894
998
|
|
|
895
|
-
|
|
999
|
+
const mcNode =
|
|
1000
|
+
dockManager.dock(this.mutationCliffsViewer, DG.DOCK_TYPE.DOWN, null, this.mutationCliffsViewer.name);
|
|
1001
|
+
|
|
1002
|
+
dockManager.dock(
|
|
1003
|
+
this.mostPotentResiduesViewer, DG.DOCK_TYPE.RIGHT, mcNode, this.mostPotentResiduesViewer.name, 0.3);
|
|
896
1004
|
|
|
897
|
-
dockViewers(sarViewersGroup, DG.DOCK_TYPE.RIGHT, dockManager, DG.DOCK_TYPE.DOWN);
|
|
898
1005
|
|
|
899
|
-
this.
|
|
900
|
-
adjustCellSize(this.
|
|
1006
|
+
this.sourceGrid.props.allowEdit = false;
|
|
1007
|
+
adjustCellSize(this.sourceGrid);
|
|
901
1008
|
|
|
902
1009
|
this.invalidateGrids();
|
|
903
1010
|
}
|
|
904
1011
|
|
|
905
|
-
invalidateSourceGrid(): void {this.
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
type viewerTypes = SARViewer | SARViewerVertical;
|
|
909
|
-
|
|
910
|
-
function dockViewers(
|
|
911
|
-
viewerList: viewerTypes[], attachDirection: DG.DockType, dockManager: DG.DockManager,
|
|
912
|
-
initialAttachDirection?: DG.DockType): DG.DockNode[] | null {
|
|
913
|
-
const viewerListLength = viewerList.length;
|
|
914
|
-
if (viewerListLength === 0)
|
|
915
|
-
return null;
|
|
916
|
-
|
|
917
|
-
let currentViewer = viewerList[0];
|
|
918
|
-
const nodeList = [dockManager.dock(currentViewer, initialAttachDirection, null, currentViewer.name ?? '')];
|
|
919
|
-
const ratio = 1 / viewerListLength;
|
|
920
|
-
|
|
921
|
-
for (let i = 1; i < viewerListLength; i++) {
|
|
922
|
-
currentViewer = viewerList[i];
|
|
923
|
-
nodeList.push(dockManager.dock(currentViewer, attachDirection, nodeList[i - 1], currentViewer.name ?? '', ratio));
|
|
924
|
-
}
|
|
925
|
-
return nodeList;
|
|
1012
|
+
invalidateSourceGrid(): void {this.sourceGrid.invalidate();}
|
|
926
1013
|
}
|