@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/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 {SARViewer, SARViewerBase, SARViewerVertical} from './viewers/sar-viewer';
13
- import {PeptideSpaceViewer} from './viewers/peptide-space-viewer';
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
- _sarGridSubject = new rxjs.Subject<DG.Grid>();
23
- _sarVGridSubject = new rxjs.Subject<DG.Grid>();
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
- _sarGrid!: DG.Grid;
30
- _sarVGrid!: DG.Grid;
31
- _sourceGrid!: DG.Grid;
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
- _currentSelection!: type.SelectionObject;
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
- sarViewer!: SARViewer;
45
- sarViewerVertical!: SARViewerVertical;
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 onSARGridChanged(): rxjs.Observable<DG.Grid> {return this._sarGridSubject.asObservable();}
68
+ get onMutationCliffsGridChanged(): rxjs.Observable<DG.Grid> {
69
+ return this.mutationCliffsGridSubject.asObservable();
70
+ }
65
71
 
66
- get onSARVGridChanged(): rxjs.Observable<DG.Grid> {return this._sarVGridSubject.asObservable();}
72
+ get onMostPotentResiduesGridChanged(): rxjs.Observable<DG.Grid> {
73
+ return this.mostPotentResiduesGridSubject.asObservable();
74
+ }
67
75
 
68
- get currentSelection(): type.SelectionObject {
69
- this._currentSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
70
- return this._currentSelection;
76
+ get onLogoSummaryGridChanged(): rxjs.Observable<DG.Grid> {
77
+ return this.logoSummaryGridSubject.asObservable();
71
78
  }
72
- set currentSelection(selection: type.SelectionObject) {
73
- this._currentSelection = selection;
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.invalidateSelection();
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
- invalidateSelection(): void {
107
- this.fireBitsetChanged();
108
- this.invalidateGrids();
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('Substitutions', () => mutationCliffsWidget(this.df, this).root, true);
116
- acc.addPane('Distribtution', () => getDistributionWidget(this.df, this).root, true);
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.sarViewer ?? this.sarViewerVertical;
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 viewer = this.getViewer();
149
- const proprtyChanged = this.isPropertyChanged(this.sarViewer) || this.isPropertyChanged(this.sarViewerVertical);
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
- const [viewerGrid, viewerVGrid] = this.initializeViewersComponents();
178
+ this.initializeViewersComponents();
154
179
  //FIXME: modify during the initializeViewersComponents stages
155
- this._sarGridSubject.next(viewerGrid);
156
- this._sarVGridSubject.next(viewerVGrid);
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.invalidateSelection();
185
+ this.fireBitsetChanged();
186
+ this.invalidateGrids();
159
187
  this._isUpdating = false;
160
188
  }
161
189
  }
162
190
 
163
- initializeViewersComponents(): [DG.Grid, DG.Grid] {
164
- if (this._sourceGrid === null)
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._sourceGrid);
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
- const [sarGrid, sarVGrid] = this.createGrids(matrixDf, positionColumns, sequenceDf, alphabet);
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
- this._sarGrid = sarGrid;
220
- this._sarVGrid = sarVGrid;
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, sarGrid, sarVGrid);
257
+ this.setCellRenderers(positionColumns);
227
258
 
228
259
  // show all the statistics in a tooltip over cell
229
- this.setTooltips(positionColumns, sarGrid, sarVGrid);
260
+ this.setTooltips(positionColumns);
230
261
 
231
262
  this.setInteractionCallback();
232
263
 
233
264
  this.setBitsetCallback();
234
265
 
235
- this.postProcessGrids(sarGrid, sarVGrid);
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._sourceGrid.columns.length; i++)
341
- colNames.push(this._sourceGrid.columns.byIndex(i)!);
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._sourceGrid.columns.setOrder(colNames.map((v) => v.name));
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._sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED);
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._sourceGrid.columns.setOrder([newColName]);
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
- matrixDf: DG.DataFrame, positionColumns: string[], sequenceDf: DG.DataFrame, alphabet: string): DG.Grid[] {
456
- const sarGrid = matrixDf.plot.grid();
457
- sarGrid.sort([C.COLUMNS_NAMES.MONOMER]);
458
- sarGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER].concat(positionColumns as C.COLUMNS_NAMES[]));
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
- const sarVGrid = sequenceDf.plot.grid();
461
- sarVGrid.sort([C.COLUMNS_NAMES.POSITION]);
462
- const pValGridCol = sarVGrid.col(C.COLUMNS_NAMES.P_VALUE)!;
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
- let tempCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
467
- if (tempCol)
468
- setAARRenderer(tempCol, alphabet, sarGrid);
469
-
470
- tempCol = sequenceDf.getCol(C.COLUMNS_NAMES.MONOMER);
471
- if (tempCol)
472
- setAARRenderer(tempCol, alphabet, sarGrid);
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 [sarGrid, sarVGrid];
544
+ return grid;
475
545
  }
476
546
 
477
547
  setBarChartInteraction(): void {
478
548
  const eventAction = (ev: MouseEvent): void => {
479
- const cell = this._sourceGrid.hitTest(ev.offsetX, ev.offsetY);
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._sourceGrid.overlay, 'mousemove')
557
+ rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'mousemove')
488
558
  .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
489
- rxjs.fromEvent<MouseEvent>(this._sourceGrid.overlay, 'click')
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[], sarGrid: DG.Grid, sarVGrid: DG.Grid): void {
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
- renderSARCell(canvasContext, currentAAR, currentPosition, this.statsDf, viewer.bidirectionalAnalysis, mdCol,
555
- bound, cellValue, this.currentSelection, this.substitutionsInfo);
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
- sarGrid.onCellRender.subscribe(renderCell);
562
- sarVGrid.onCellRender.subscribe(renderCell);
639
+ this.mutationCliffsGrid.onCellRender.subscribe(renderCell);
640
+ this.mostPotentResiduesGrid.onCellRender.subscribe(renderCell);
563
641
 
564
- this._sourceGrid.setOptions({'colHeaderHeight': 130});
565
- this._sourceGrid.onCellRender.subscribe((gcArgs) => {
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[], sarGrid: DG.Grid, sarVGrid: DG.Grid): void {
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
- sarGrid.onCellTooltip(showTooltip);
608
- sarVGrid.onCellTooltip(showTooltip);
609
- this._sourceGrid.onCellTooltip((cell, x, y) => {
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 sarDf = this._sarGrid.dataFrame;
659
- const sarVDf = this._sarVGrid.dataFrame;
734
+ const mutationCliffsDf = this.mutationCliffsGrid.dataFrame;
735
+ const mostPotentResiduesDf = this.mostPotentResiduesGrid.dataFrame;
736
+ // const invariantMapDf = this.invariantMapGrid.dataFrame;
660
737
 
661
- const chooseAction = (aar: string, position: string, isShiftPressed: boolean): void =>
662
- isShiftPressed ? this.modifyCurrentSelection(aar, position) : this.initCurrentSelection(aar, position);
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._sarGrid.root.addEventListener('click', (ev) => {
665
- const gridCell = this._sarGrid.hitTest(ev.offsetX, ev.offsetY);
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 = sarDf.get(C.COLUMNS_NAMES.MONOMER, gridCell!.tableRowIndex!);
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._sarVGrid.root.addEventListener('click', (ev) => {
675
- const gridCell = this._sarVGrid.hitTest(ev.offsetX, ev.offsetY);
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 = sarVDf.get(C.COLUMNS_NAMES.POSITION, tableRowIdx);
681
- const aar = sarVDf.get(C.COLUMNS_NAMES.MONOMER, tableRowIdx);
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._sarGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(sarDf));
693
- this._sarVGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(sarVDf));
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.currentSelection;
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
- this.currentSelection = tempSelection;
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.SelectionObject = {};
793
+ initCurrentSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
794
+ const tempSelection: type.PositionToAARList = {};
713
795
  tempSelection[position] = [aar];
714
- this.currentSelection = tempSelection;
796
+
797
+ if (isInvariantMapSelection)
798
+ this.invariantMapSelection = tempSelection;
799
+ else
800
+ this.mutationCliffsSelection = tempSelection;
715
801
  }
716
802
 
717
803
  invalidateGrids(): void {
718
- // this.stackedBarchart?.computeData();
719
- this._sarGrid.invalidate();
720
- this._sarVGrid.invalidate();
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 changeBitset = (currentBitset: DG.BitSet): void => {
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.currentSelection);
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._currentSelection[position].includes(positionCol.get(i)!))
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(() => changeBitset(selection));
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(sarGrid: DG.Grid, sarVGrid: DG.Grid): void {
780
- const mdCol: DG.GridColumn = sarVGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
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 [sarGrid, sarVGrid]) {
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 == sarVGrid && colName !== 'Diff' && colName !== C.COLUMNS_NAMES.MONOMER)
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.allowEdit = false;
800
- grid.props.allowRowSelection = false;
801
- grid.props.allowBlockSelection = false;
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(sarGrid);
805
- setViewerGridProps(sarVGrid);
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.sarViewer && this.sarViewerVertical) {
823
- const [sourceViewer, targetViewer] = isSourceSAR ? [this.sarViewer, this.sarViewerVertical] :
824
- [this.sarViewerVertical, this.sarViewer];
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._sourceGrid = this.currentView.grid;
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._sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!.name = this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED];
854
- this._sourceGrid.columns.setOrder([this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED]]);
855
- this._sourceGrid.props.allowColSelection = false;
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._sourceGrid.columns.length; i++) {
868
- const aarCol = this._sourceGrid.columns.byIndex(i);
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.sarViewer = await this.df.plot.fromType('peptide-sar-viewer', options) as SARViewer;
979
+ this.mutationCliffsViewer = await this.df.plot.fromType('peptide-sar-viewer', options) as MutationCliffsViewer;
879
980
 
880
- this.sarViewerVertical =
881
- await this.df.plot.fromType('peptide-sar-viewer-vertical', options) as SARViewerVertical;
981
+ this.mostPotentResiduesViewer =
982
+ await this.df.plot.fromType('peptide-sar-viewer-vertical', options) as MostPotentResiduesViewer;
882
983
 
883
- const sarViewersGroup: viewerTypes[] = [this.sarViewer, this.sarViewerVertical];
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
- this.currentView.filters({filters: [{type: 'Peptides:invariantMapFilter'}]});
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._sourceGrid.props.allowEdit = false;
900
- adjustCellSize(this._sourceGrid);
1006
+ this.sourceGrid.props.allowEdit = false;
1007
+ adjustCellSize(this.sourceGrid);
901
1008
 
902
1009
  this.invalidateGrids();
903
1010
  }
904
1011
 
905
- invalidateSourceGrid(): void {this._sourceGrid.invalidate();}
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
  }