@datagrok/peptides 1.3.9 → 1.5.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/src/model.ts CHANGED
@@ -9,8 +9,8 @@ import * as rxjs from 'rxjs';
9
9
 
10
10
  import * as C from './utils/constants';
11
11
  import * as type from './utils/types';
12
- import {isGridCellInvalid, scaleActivity} from './utils/misc';
13
- import {MutationCliffsViewer, MostPotentResiduesViewer} from './viewers/sar-viewer';
12
+ import {calculateSelected, extractMonomerInfo, scaleActivity} from './utils/misc';
13
+ import {MonomerPosition, MostPotentResiduesViewer} from './viewers/sar-viewer';
14
14
  import * as CR from './utils/cell-renderer';
15
15
  import {mutationCliffsWidget} from './widgets/mutation-cliffs';
16
16
  import {getDistributionAndStats, getDistributionWidget} from './widgets/distribution';
@@ -24,29 +24,24 @@ import {findMutations} from './utils/algorithms';
24
24
  export class PeptidesModel {
25
25
  static modelName = 'peptidesModel';
26
26
 
27
- mutationCliffsGridSubject = new rxjs.Subject<DG.Grid>();
28
- mostPotentResiduesGridSubject = new rxjs.Subject<DG.Grid>();
29
- logoSummaryGridSubject = new rxjs.Subject<DG.Grid>();
27
+ settingsSubject: rxjs.Subject<type.PeptidesSettings> = new rxjs.Subject();
28
+ _mutatinCliffsSelectionSubject: rxjs.Subject<undefined> = new rxjs.Subject();
30
29
 
31
30
  _isUpdating: boolean = false;
32
31
  isBitsetChangedInitialized = false;
33
32
  isCellChanging = false;
34
33
 
35
- mutationCliffsGrid!: DG.Grid;
36
- mostPotentResiduesGrid!: DG.Grid;
37
- logoSummaryGrid!: DG.Grid;
38
- sourceGrid!: DG.Grid;
39
34
  df: DG.DataFrame;
40
35
  splitCol!: DG.Column<boolean>;
41
36
  edf: DG.DataFrame | null = null;
42
- monomerPositionStatsDf!: DG.DataFrame;
43
- clusterStatsDf!: DG.DataFrame;
37
+ _monomerPositionStatsDf?: DG.DataFrame;
38
+ _clusterStatsDf?: DG.DataFrame;
44
39
  _mutationCliffsSelection!: type.PositionToAARList;
45
40
  _invariantMapSelection!: type.PositionToAARList;
46
41
  _logoSummarySelection!: number[];
47
- substitutionsInfo: type.SubstitutionsInfo = new Map();
42
+ _substitutionsInfo?: type.SubstitutionsInfo;
48
43
  isInitialized = false;
49
- currentView!: DG.TableView;
44
+ _analysisView?: DG.TableView;
50
45
 
51
46
  isPeptideSpaceChangingBitset = false;
52
47
  isChangingEdfBitset = false;
@@ -58,33 +53,123 @@ export class PeptidesModel {
58
53
  _settings!: type.PeptidesSettings;
59
54
  isRibbonSet = false;
60
55
 
61
- cp: bio.SeqPalette;
62
- xorBitset?: DG.BitSet;
56
+ _cp?: bio.SeqPalette;
63
57
  initBitset: DG.BitSet;
64
- isInvariantMapTrigger: boolean = false;;
58
+ isInvariantMapTrigger: boolean = false;
59
+ headerSelectedMonomers: type.MonomerSelectionStats = {};
60
+ webLogoBounds: {[positon: string]: {[monomer: string]: DG.Rect}} = {};
61
+ cachedWebLogoTooltip: {bar: string; tooltip: HTMLDivElement | null;} = {bar: '', tooltip: null};
62
+ _monomerPositionDf?: DG.DataFrame;
63
+ _alphabet?: string;
64
+ _mostPotentResiduesDf?: DG.DataFrame;
65
+ _matrixDf?: DG.DataFrame;
66
+ _splitSeqDf?: DG.DataFrame;
65
67
 
66
68
  private constructor(dataFrame: DG.DataFrame) {
67
69
  this.df = dataFrame;
68
70
  this.initBitset = this.df.filter.clone();
69
- this.cp = bio.pickUpPalette(this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE));
70
71
  }
71
72
 
72
- static async getInstance(dataFrame: DG.DataFrame): Promise<PeptidesModel> {
73
+ static getInstance(dataFrame: DG.DataFrame): PeptidesModel {
73
74
  dataFrame.temp[PeptidesModel.modelName] ??= new PeptidesModel(dataFrame);
74
- await (dataFrame.temp[PeptidesModel.modelName] as PeptidesModel).init();
75
+ (dataFrame.temp[PeptidesModel.modelName] as PeptidesModel).init();
75
76
  return dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
76
77
  }
77
78
 
78
- get onMutationCliffsGridChanged(): rxjs.Observable<DG.Grid> {
79
- return this.mutationCliffsGridSubject.asObservable();
79
+ get monomerPositionDf(): DG.DataFrame {
80
+ this._monomerPositionDf ??= this.createMonomerPositionDf();
81
+ return this._monomerPositionDf;
82
+ }
83
+ set monomerPositionDf(df: DG.DataFrame) {
84
+ this._monomerPositionDf = df;
85
+ }
86
+
87
+ get monomerPositionStatsDf(): DG.DataFrame {
88
+ this._monomerPositionStatsDf ??= this.calculateMonomerPositionStatistics();
89
+ return this._monomerPositionStatsDf;
90
+ }
91
+ set monomerPositionStatsDf(df: DG.DataFrame) {
92
+ this._monomerPositionStatsDf = df;
93
+ }
94
+
95
+ get matrixDf(): DG.DataFrame {
96
+ this._matrixDf ??= this.buildMatrixDf();
97
+ return this._matrixDf;
98
+ }
99
+ set matrixDf(df: DG.DataFrame) {
100
+ this._matrixDf = df;
101
+ }
102
+
103
+ get splitSeqDf(): DG.DataFrame {
104
+ this._splitSeqDf ??= this.buildSplitSeqDf();
105
+ return this._splitSeqDf;
106
+ }
107
+ set splitSeqDf(df: DG.DataFrame) {
108
+ this._splitSeqDf = df;
109
+ }
110
+
111
+ get mostPotentResiduesDf(): DG.DataFrame {
112
+ this._mostPotentResiduesDf ??= this.createMostPotentResiduesDf();
113
+ return this._mostPotentResiduesDf;
114
+ }
115
+ set mostPotentResiduesDf(df: DG.DataFrame) {
116
+ this._mostPotentResiduesDf = df;
117
+ }
118
+
119
+ get alphabet(): string {
120
+ const col = this.settings.sequenceColumnName ? this.df.getCol(this.settings.sequenceColumnName) :
121
+ this.df.columns.bySemType(DG.SEMTYPE.MACROMOLECULE)!;
122
+ return col.getTag(bio.TAGS.alphabet);
123
+ }
124
+
125
+ get substitutionsInfo(): type.SubstitutionsInfo {
126
+ if (this._substitutionsInfo)
127
+ return this._substitutionsInfo;
128
+
129
+ const scaledActivityCol: DG.Column<number> = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
130
+ //TODO: set categories ordering the same to share compare indexes instead of strings
131
+ const monomerColumns: type.RawColumn[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER).map(extractMonomerInfo);
132
+ this._substitutionsInfo = findMutations(scaledActivityCol.getRawData(), monomerColumns, this.settings);
133
+ return this._substitutionsInfo;
134
+ }
135
+ set substitutionsInfo(si: type.SubstitutionsInfo) {
136
+ this._substitutionsInfo = si;
80
137
  }
81
138
 
82
- get onMostPotentResiduesGridChanged(): rxjs.Observable<DG.Grid> {
83
- return this.mostPotentResiduesGridSubject.asObservable();
139
+ get clusterStatsDf(): DG.DataFrame {
140
+ this._clusterStatsDf ??= this.calculateClusterStatistics();
141
+ return this._clusterStatsDf;
142
+ }
143
+ set clusterStatsDf(df: DG.DataFrame) {
144
+ this._clusterStatsDf = df;
84
145
  }
85
146
 
86
- get onLogoSummaryGridChanged(): rxjs.Observable<DG.Grid> {
87
- return this.logoSummaryGridSubject.asObservable();
147
+ get cp(): bio.SeqPalette {
148
+ this._cp ??= bio.pickUpPalette(this.df.getCol(this.settings.sequenceColumnName!));
149
+ return this._cp;
150
+ }
151
+ set cp(_cp: bio.SeqPalette) {
152
+ this._cp = _cp;
153
+ }
154
+
155
+ get analysisView(): DG.TableView {
156
+ const shell = grok.shell;
157
+ if (this.df.getTag('newAnalysis') !== '1') {
158
+ this._analysisView = wu(shell.tableViews).find(({dataFrame}) => dataFrame.tags[C.PEPTIDES_ANALYSIS] === '1')!;
159
+ grok.shell.v = this._analysisView;
160
+ }
161
+
162
+ this._analysisView ??= shell.addTableView(this.df);
163
+ this.df.setTag('newAnalysis', '');
164
+ return this._analysisView;
165
+ }
166
+
167
+ get onMutationCliffsSelectionChanged(): rxjs.Observable<undefined> {
168
+ return this._mutatinCliffsSelectionSubject.asObservable();
169
+ }
170
+
171
+ get onSettingsChanged(): rxjs.Observable<type.PeptidesSettings> {
172
+ return this.settingsSubject.asObservable();
88
173
  }
89
174
 
90
175
  get mutationCliffsSelection(): type.PositionToAARList {
@@ -96,7 +181,7 @@ export class PeptidesModel {
96
181
  this._mutationCliffsSelection = selection;
97
182
  this.df.tags[C.TAGS.SELECTION] = JSON.stringify(selection);
98
183
  this.fireBitsetChanged();
99
- this.invalidateGrids();
184
+ this._mutatinCliffsSelectionSubject.next();
100
185
  }
101
186
 
102
187
  get invariantMapSelection(): type.PositionToAARList {
@@ -110,7 +195,7 @@ export class PeptidesModel {
110
195
  this.isInvariantMapTrigger = true;
111
196
  this.df.filter.fireChanged();
112
197
  this.isInvariantMapTrigger = false;
113
- this.invalidateGrids();
198
+ this.analysisView.grid.invalidate();
114
199
  }
115
200
 
116
201
  get logoSummarySelection(): number[] {
@@ -122,7 +207,7 @@ export class PeptidesModel {
122
207
  this._logoSummarySelection = selection;
123
208
  this.df.tags[C.TAGS.CLUSTER_SELECTION] = JSON.stringify(selection);
124
209
  this.fireBitsetChanged();
125
- this.invalidateGrids();
210
+ this.analysisView.grid.invalidate();
126
211
  }
127
212
 
128
213
  get splitByPos(): boolean {
@@ -170,150 +255,165 @@ export class PeptidesModel {
170
255
  return this._settings;
171
256
  }
172
257
  set settings(s: type.PeptidesSettings) {
173
- for (const [key, value] of Object.entries(s))
258
+ const newSettingsEntries = Object.entries(s);
259
+ const updateVars: Set<string> = new Set();
260
+ for (const [key, value] of newSettingsEntries) {
174
261
  this._settings[key as keyof type.PeptidesSettings] = value as any;
262
+ switch (key) {
263
+ case 'scaling':
264
+ updateVars.add('activity');
265
+ updateVars.add('mutationCliffs');
266
+ updateVars.add('stats');
267
+ break;
268
+ // case 'columns':
269
+ // updateVars.add('grid');
270
+ // break;
271
+ case 'maxMutations':
272
+ case 'minActivityDelta':
273
+ updateVars.add('mutationCliffs');
274
+ break;
275
+ }
276
+ }
175
277
  this.df.setTag('settings', JSON.stringify(this._settings));
176
- //TODO: update only needed components
177
- this.updateDefault();
278
+ // this.updateDefault();
279
+ for (const variable of updateVars) {
280
+ switch (variable) {
281
+ case 'activity':
282
+ this.createScaledCol();
283
+ break;
284
+ case 'mutationCliffs':
285
+ const scaledActivityCol: DG.Column<number> = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
286
+ //TODO: set categories ordering the same to share compare indexes instead of strings
287
+ const monomerColumns: type.RawColumn[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER).map(extractMonomerInfo);
288
+ this.substitutionsInfo = findMutations(scaledActivityCol.getRawData(), monomerColumns, this.settings);
289
+ break;
290
+ case 'stats':
291
+ this.monomerPositionStatsDf = this.calculateMonomerPositionStatistics();
292
+ this.monomerPositionDf = this.createMonomerPositionDf();
293
+ this.mostPotentResiduesDf = this.createMostPotentResiduesDf();
294
+ this.clusterStatsDf = this.calculateClusterStatistics();
295
+ break;
296
+ case 'grid':
297
+ this.updateGrid();
298
+ break;
299
+ }
300
+ }
301
+
302
+ //TODO: handle settings change
303
+ this.settingsSubject.next(this.settings);
304
+ }
305
+
306
+ createMonomerPositionDf(): DG.DataFrame {
307
+ const matrixDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER])
308
+ .pivot(C.COLUMNS_NAMES.POSITION)
309
+ .add('first', C.COLUMNS_NAMES.MEAN_DIFFERENCE, '')
310
+ .aggregate();
311
+ const monomerCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
312
+ for (let i = 0; i < monomerCol.length; ++i) {
313
+ if (monomerCol.get(i) == '') {
314
+ matrixDf.rows.removeAt(i);
315
+ break;
316
+ }
317
+ }
318
+ matrixDf.name = 'SAR';
319
+
320
+ return matrixDf;
321
+ }
322
+
323
+ buildMatrixDf(): DG.DataFrame {
324
+ const splitSeqDfColumns = this.splitSeqDf.columns;
325
+ const positionColumns = splitSeqDfColumns.names();
326
+ return this.splitSeqDf
327
+ .groupBy(positionColumns)
328
+ .aggregate()
329
+ .unpivot([], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER);
330
+ }
331
+
332
+ buildSplitSeqDf(): DG.DataFrame {
333
+ const sequenceCol = this.df.getCol(this.settings.sequenceColumnName!);
334
+ const splitSeqDf = splitAlignedSequences(sequenceCol);
335
+
336
+ return splitSeqDf;
178
337
  }
179
338
 
180
339
  createAccordion(): DG.Accordion {
181
340
  const acc = ui.accordion();
182
341
  acc.root.style.width = '100%';
183
342
  acc.addTitle(ui.h1(`${this.df.selection.trueCount} selected rows`));
184
- acc.addPane('Mutation Cliff pairs', () => mutationCliffsWidget(this.df, this).root, true);
185
- acc.addPane('Distribution', () => getDistributionWidget(this.df, this).root, true);
343
+ acc.addPane('Mutation Cliff pairs', () => mutationCliffsWidget(this.df, this).root);
344
+ acc.addPane('Distribution', () => getDistributionWidget(this.df, this).root);
186
345
 
187
346
  return acc;
188
347
  }
189
348
 
190
349
  updateDefault(): void {
191
- if ((this.sourceGrid && !this._isUpdating) || !this.isInitialized) {
192
- // this.isInitialized = true;
350
+ if (!this._isUpdating || !this.isInitialized) {
193
351
  this._isUpdating = true;
194
- this.initializeViewersComponents();
195
- //FIXME: modify during the initializeViewersComponents stages
196
- this.mutationCliffsGridSubject.next(this.mutationCliffsGrid);
197
- this.mostPotentResiduesGridSubject.next(this.mostPotentResiduesGrid);
198
- if (this.df.getTag(C.TAGS.CLUSTERS))
199
- this.logoSummaryGridSubject.next(this.logoSummaryGrid);
200
-
201
- this.fireBitsetChanged();
202
- this.invalidateGrids();
352
+ this.updateGrid();
353
+
354
+ this.analysisView.grid.invalidate();
203
355
  this._isUpdating = false;
204
356
  }
205
357
  }
206
358
 
207
- initializeViewersComponents(): void {
208
- if (this.sourceGrid === null)
209
- throw new Error(`Source grid is not initialized`);
210
-
211
- //Split the aligned sequence into separate AARs
212
- const col = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
213
- const alphabet = col.tags['alphabet'];
214
- const splitSeqDf = splitAlignedSequences(col);
215
-
216
- // this.barData = calculateBarsData(splitSeqDf.columns.toList(), this.df.selection);
217
-
218
- const positionColumns = splitSeqDf.columns.names();
219
-
220
- const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY)!;
221
- splitSeqDf.columns.add(activityCol);
222
-
223
- this.joinDataFrames(positionColumns, splitSeqDf, alphabet);
359
+ updateGrid(): void {
360
+ this.joinDataFrames();
224
361
 
225
362
  this.sortSourceGrid();
226
363
 
227
- this.createScaledCol(splitSeqDf);
228
-
229
- //unpivot a table and handle duplicates
230
- let matrixDf = splitSeqDf.groupBy(positionColumns).aggregate();
231
-
232
- matrixDf = matrixDf.unpivot([], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER);
233
-
234
- //statistics for specific AAR at a specific position
235
- this.monomerPositionStatsDf = this.calculateMonomerPositionStatistics(matrixDf);
236
-
237
- // SAR matrix table
238
- //pivot a table to make it matrix-like
239
- matrixDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER])
240
- .pivot(C.COLUMNS_NAMES.POSITION)
241
- .add('first', C.COLUMNS_NAMES.MEAN_DIFFERENCE, '')
242
- .aggregate();
243
- matrixDf.name = 'SAR';
244
-
245
- // Setting category order
246
- this.setCategoryOrder(matrixDf);
247
-
248
- // SAR vertical table (naive, choose best Mean difference from pVals <= 0.01)
249
- const sequenceDf = this.createVerticalTable();
250
-
251
- const scaledActivityCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
252
- const monomerColumns = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER);
253
- this.substitutionsInfo = findMutations(scaledActivityCol, monomerColumns, this.settings);
254
-
255
- [this.mutationCliffsGrid, this.mostPotentResiduesGrid] =
256
- this.createGrids(matrixDf, sequenceDf, positionColumns, alphabet);
364
+ this.createScaledCol();
257
365
 
258
- if (this.df.getTag(C.TAGS.CLUSTERS)) {
259
- this.clusterStatsDf = this.calculateClusterStatistics();
260
- this.logoSummaryGrid = this.createLogoSummaryGrid();
261
- }
262
-
263
- // init invariant map & mutation cliffs selections
264
- this.initSelections(positionColumns);
265
-
266
- positionColumns.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
366
+ this.initSelections();
267
367
 
268
- // this.setBarChartInteraction();
368
+ this.setWebLogoInteraction();
369
+ this.webLogoBounds = {};
269
370
 
270
- this.setCellRenderers(positionColumns);
371
+ this.setCellRenderers();
271
372
 
272
- // show all the statistics in a tooltip over cell
273
- this.setTooltips(positionColumns);
274
-
275
- this.setInteractionCallback();
373
+ this.setTooltips();
276
374
 
277
375
  this.setBitsetCallback();
278
376
 
279
377
  this.postProcessGrids();
280
378
  }
281
379
 
282
- initSelections(positionColumns: string[]): void {
380
+ initSelections(): void {
283
381
  const tempInvariantMapSelection: type.PositionToAARList = this.invariantMapSelection;
284
382
  const mutationCliffsSelection: type.PositionToAARList = this.mutationCliffsSelection;
383
+ const positionColumns = this.splitSeqDf.columns.names();
285
384
  for (const pos of positionColumns) {
286
385
  tempInvariantMapSelection[pos] ??= [];
287
386
  mutationCliffsSelection[pos] ??= [];
288
387
  }
289
388
  this.invariantMapSelection = tempInvariantMapSelection;
290
389
  this.mutationCliffsSelection = mutationCliffsSelection;
291
- // this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
292
390
  }
293
391
 
294
- joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame, alphabet: string): void {
392
+ joinDataFrames(): void {
295
393
  // append splitSeqDf columns to source table and make sure columns are not added more than once
296
394
  const name = this.df.name;
297
395
  const cols = this.df.columns;
396
+ const positionColumns = this.splitSeqDf.columns.names();
298
397
  for (const colName of positionColumns) {
299
398
  const col = this.df.col(colName);
300
- const newCol = splitSeqDf.getCol(colName);
399
+ const newCol = this.splitSeqDf.getCol(colName);
301
400
  if (col === null)
302
401
  cols.add(newCol);
303
402
  else {
304
403
  cols.remove(colName);
305
404
  cols.add(newCol);
306
405
  }
307
- CR.setAARRenderer(newCol, alphabet, this.sourceGrid);
406
+ CR.setAARRenderer(newCol, this.alphabet);
308
407
  }
309
408
  this.df.name = name;
310
- this.currentView.name = name;
311
409
  }
312
410
 
313
411
  sortSourceGrid(): void {
314
412
  const colNames: DG.GridColumn[] = [];
315
- for (let i = 1; i < this.sourceGrid.columns.length; i++)
316
- colNames.push(this.sourceGrid.columns.byIndex(i)!);
413
+ const sourceGridCols = this.analysisView.grid.columns;
414
+ const sourceGridColsCount = sourceGridCols.length;
415
+ for (let i = 1; i < sourceGridColsCount; i++)
416
+ colNames.push(sourceGridCols.byIndex(i)!);
317
417
 
318
418
  colNames.sort((a, b) => {
319
419
  if (a.column!.semType == C.SEM_TYPES.MONOMER) {
@@ -325,99 +425,133 @@ export class PeptidesModel {
325
425
  return 1;
326
426
  return 0;
327
427
  });
328
- this.sourceGrid.columns.setOrder(colNames.map((v) => v.name));
428
+ sourceGridCols.setOrder(colNames.map((v) => v.name));
329
429
  }
330
430
 
331
- createScaledCol(splitSeqDf: DG.DataFrame): void {
332
- const scaledCol = scaleActivity(this.df.getCol(C.COLUMNS_NAMES.ACTIVITY), this.settings.scaling);
431
+ createScaledCol(): void {
432
+ const sourceGrid = this.analysisView.grid;
433
+ const scaledCol = scaleActivity(this.df.getCol(this.settings.activityColumnName!), this.settings.scaling);
333
434
  //TODO: make another func
334
- splitSeqDf.columns.add(scaledCol);
335
435
  this.df.columns.replace(C.COLUMNS_NAMES.ACTIVITY_SCALED, scaledCol);
336
- const gridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED);
337
- if (gridCol)
338
- gridCol.name = scaledCol.getTag('gridName');
436
+ // const gridCol = sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED);
437
+ // if (gridCol)
438
+ // gridCol.name = scaledCol.getTag('gridName');
339
439
 
340
- this.sourceGrid.columns.setOrder([scaledCol.getTag('gridName')]);
440
+ sourceGrid.columns.setOrder([scaledCol.name]);
341
441
  }
342
442
 
343
- calculateMonomerPositionStatistics(matrixDf: DG.DataFrame): DG.DataFrame {
344
- matrixDf = matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER]).aggregate();
443
+ calculateMonomerPositionStatistics(): DG.DataFrame {
444
+ const matrixDf = this.matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER]).aggregate();
445
+ const matrixLen = matrixDf.rowCount;
446
+
447
+ const posRawColumns: type.RawColumn[] = [];
448
+
449
+ const posCol: DG.Column<string> = matrixDf.getCol(C.COLUMNS_NAMES.POSITION);
450
+ const posColData = posCol.getRawData();
451
+ const posColCategories = posCol.categories;
452
+ for (const position of posColCategories) {
453
+ const currentCol = this.df.getCol(position);
454
+ posRawColumns.push({
455
+ name: position,
456
+ rawData: currentCol.getRawData(),
457
+ cat: currentCol.categories,
458
+ });
459
+ }
460
+
461
+ const monomerCol: DG.Column<string> = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
462
+ const monomerColData = monomerCol.getRawData();
463
+ const monomerColCategories = monomerCol.categories;
345
464
 
346
465
  //calculate p-values based on t-test
347
466
  const matrixCols = matrixDf.columns;
348
- const mdCol = matrixCols.addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
349
- const pValCol = matrixCols.addNewFloat(C.COLUMNS_NAMES.P_VALUE);
350
- const countCol = matrixCols.addNewInt(C.COLUMNS_NAMES.COUNT);
351
- const ratioCol = matrixCols.addNewFloat(C.COLUMNS_NAMES.RATIO);
352
- const aarCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
353
- const posCol = matrixDf.getCol(C.COLUMNS_NAMES.POSITION);
354
- const activityCol: number[] = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).toList();
355
- const sourceDfLen = activityCol.length;
356
-
357
- for (let i = 0; i < matrixDf.rowCount; i++) {
358
- const position: string = posCol.get(i);
359
- const aar: string = aarCol.get(i);
360
- const mask = DG.BitSet.create(sourceDfLen, (j) => this.df.get(position, j) === aar);
361
- const stats = getStats(activityCol, mask);
362
-
363
- mdCol.set(i, stats.meanDifference);
364
- pValCol.set(i, stats.pValue);
365
- countCol.set(i, stats.count);
366
- ratioCol.set(i, stats.ratio);
467
+ const mdColData = matrixCols.addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE).init(0).getRawData();
468
+ const pValColData = matrixCols.addNewFloat(C.COLUMNS_NAMES.P_VALUE).init(0).getRawData();
469
+ const countColData = matrixCols.addNewInt(C.COLUMNS_NAMES.COUNT).init(0).getRawData();
470
+ const ratioColData = matrixCols.addNewFloat(C.COLUMNS_NAMES.RATIO).init(0).getRawData();
471
+ const activityColData = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData();
472
+ const sourceDfLen = activityColData.length;
473
+
474
+ for (let i = 0; i < matrixLen; i++) {
475
+ const positionRawIdx = posColData[i];
476
+ const currentPosRawCol = posRawColumns[positionRawIdx];
477
+ const monomerRawIdx = monomerColData[i];
478
+ const mask: boolean[] = new Array(sourceDfLen);
479
+ const monomer = monomerColCategories[monomerRawIdx];
480
+ if (monomer == '')
481
+ continue;
482
+
483
+ let trueCount = 0;
484
+ for (let j = 0; j < sourceDfLen; ++j) {
485
+ mask[j] = currentPosRawCol.cat![currentPosRawCol.rawData[j]] == monomer;
486
+
487
+ if (mask[j])
488
+ ++trueCount;
489
+ }
490
+
491
+ const maskInfo = {
492
+ trueCount: trueCount,
493
+ falseCount: sourceDfLen - trueCount,
494
+ mask: mask,
495
+ };
496
+
497
+ const stats = getStats(activityColData, maskInfo);
498
+
499
+ mdColData[i] = stats.meanDifference;
500
+ pValColData[i] = stats.pValue;
501
+ countColData[i] = stats.count;
502
+ ratioColData[i] = stats.ratio;
367
503
  }
504
+ matrixDf.fireValuesChanged();
368
505
 
369
- const monomerCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
370
- matrixDf = matrixDf.clone(DG.BitSet.create(matrixDf.rowCount, (i) => monomerCol.get(i) ? true : false));
371
506
  return matrixDf as DG.DataFrame;
372
507
  }
373
508
 
374
509
  calculateClusterStatistics(): DG.DataFrame {
375
- const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
376
- const statsDf = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
377
- const clustersCol = statsDf.getCol(C.COLUMNS_NAMES.CLUSTERS);
510
+ const originalClustersCol = this.df.getCol(this.settings.clustersColumnName!);
511
+ const originalClustersColData = originalClustersCol.getRawData();
512
+ const originalClustersColCategories = originalClustersCol.categories;
513
+
514
+ const statsDf = this.df.groupBy([this.settings.clustersColumnName!]).aggregate();
515
+ const clustersCol = statsDf.getCol(this.settings.clustersColumnName!);
516
+ clustersCol.setCategoryOrder(originalClustersColCategories);
517
+ const clustersColData = clustersCol.getRawData();
518
+
378
519
  const statsDfCols = statsDf.columns;
379
- const mdCol = statsDfCols.addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
380
- const pValCol = statsDfCols.addNewFloat(C.COLUMNS_NAMES.P_VALUE);
381
- const countCol = statsDfCols.addNewInt(C.COLUMNS_NAMES.COUNT);
382
- const ratioCol = statsDfCols.addNewFloat(C.COLUMNS_NAMES.RATIO);
383
- const activityList: number[] = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).toList();
384
-
385
- for (let rowIdx = 0; rowIdx < clustersCol.length; ++rowIdx) {
386
- const cluster = clustersCol.get(rowIdx);
387
- const mask = DG.BitSet.create(activityList.length, (bitIdx) => originalClustersCol.get(bitIdx) === cluster);
388
- const stats = getStats(activityList, mask);
389
-
390
- mdCol.set(rowIdx, stats.meanDifference);
391
- pValCol.set(rowIdx, stats.pValue);
392
- countCol.set(rowIdx, stats.count);
393
- ratioCol.set(rowIdx, stats.ratio);
394
- }
395
- return statsDf;
396
- }
520
+ const mdColData = statsDfCols.addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE).getRawData();
521
+ const pValColData = statsDfCols.addNewFloat(C.COLUMNS_NAMES.P_VALUE).getRawData();
522
+ const countColData = statsDfCols.addNewInt(C.COLUMNS_NAMES.COUNT).getRawData();
523
+ const ratioColData = statsDfCols.addNewFloat(C.COLUMNS_NAMES.RATIO).getRawData();
524
+ const activityColData: type.RawData = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData();
525
+ const activityColLen = activityColData.length;
526
+
527
+ for (let rowIdx = 0; rowIdx < clustersColData.length; ++rowIdx) {
528
+ const clusterIdx = clustersColData[rowIdx];
529
+ const mask = new Array(activityColLen);
530
+ let trueCount = 0;
531
+ for (let maskIdx = 0; maskIdx < activityColLen; ++maskIdx) {
532
+ mask[maskIdx] = clusterIdx == originalClustersColData[maskIdx];
533
+
534
+ if (mask[maskIdx])
535
+ ++trueCount;
536
+ }
397
537
 
398
- setCategoryOrder(matrixDf: DG.DataFrame): void {
399
- let sortArgument: string = C.COLUMNS_NAMES.MEAN_DIFFERENCE;
400
- if (this.settings.isBidirectional) {
401
- const mdCol = this.monomerPositionStatsDf.getCol(sortArgument);
402
- sortArgument = 'Absolute Mean difference';
403
- const absMDCol = this.monomerPositionStatsDf.columns.addNewFloat(sortArgument);
404
- absMDCol.init((i) => Math.abs(mdCol.get(i)));
405
- }
538
+ const maskInfo = {
539
+ trueCount: trueCount,
540
+ falseCount: activityColLen - trueCount,
541
+ mask: mask,
542
+ };
406
543
 
407
- const aarWeightsDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER]).sum(sortArgument, 'weight')
408
- .aggregate();
409
- const aarList = aarWeightsDf.getCol(C.COLUMNS_NAMES.MONOMER).toList();
410
- const getWeight = (aar: string): number => aarWeightsDf
411
- .groupBy(['weight'])
412
- .where(`${C.COLUMNS_NAMES.MONOMER} = ${aar}`)
413
- .aggregate()
414
- .get('weight', 0) as number;
415
- aarList.sort((first, second) => getWeight(second) - getWeight(first));
544
+ const stats = getStats(activityColData, maskInfo);
416
545
 
417
- matrixDf.getCol(C.COLUMNS_NAMES.MONOMER).setCategoryOrder(aarList);
546
+ mdColData[rowIdx] = stats.meanDifference;
547
+ pValColData[rowIdx] = stats.pValue;
548
+ countColData[rowIdx] = stats.count;
549
+ ratioColData[rowIdx] = stats.ratio;
550
+ }
551
+ return statsDf;
418
552
  }
419
553
 
420
- createVerticalTable(): DG.DataFrame {
554
+ createMostPotentResiduesDf(): DG.DataFrame {
421
555
  // TODO: aquire ALL of the positions
422
556
  const columns = [C.COLUMNS_NAMES.MEAN_DIFFERENCE, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.POSITION,
423
557
  'Count', 'Ratio', C.COLUMNS_NAMES.P_VALUE];
@@ -430,6 +564,7 @@ export class PeptidesModel {
430
564
  const posColCategories = sequenceDf.getCol(C.COLUMNS_NAMES.POSITION).categories;
431
565
  const mdCol = sequenceDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
432
566
  const posCol = sequenceDf.getCol(C.COLUMNS_NAMES.POSITION);
567
+ const monomerCol = sequenceDf.getCol(C.COLUMNS_NAMES.MONOMER);
433
568
  const rowCount = sequenceDf.rowCount;
434
569
  for (const pos of posColCategories) {
435
570
  tempStats = DG.Stats.fromColumn(mdCol, DG.BitSet.create(rowCount, (i) => posCol.get(i) === pos));
@@ -437,115 +572,12 @@ export class PeptidesModel {
437
572
  (tempStats.max > Math.abs(tempStats.min) ? tempStats.max : tempStats.min) :
438
573
  tempStats.max;
439
574
  }
440
- sequenceDf = sequenceDf.clone(DG.BitSet.create(rowCount, (i) => mdCol.get(i) === maxAtPos[posCol.get(i)]));
575
+ sequenceDf = sequenceDf.clone(DG.BitSet.create(rowCount,
576
+ (i) => monomerCol.get(i) !== '' && maxAtPos[posCol.get(i)] != 0 && mdCol.get(i) === maxAtPos[posCol.get(i)]));
441
577
 
442
578
  return sequenceDf;
443
579
  }
444
580
 
445
- createGrids(mutationCliffsDf: DG.DataFrame, mostPotentResiduesDf: DG.DataFrame, positionColumns: string[],
446
- alphabet: string): [DG.Grid, DG.Grid] {
447
- // Creating Mutation Cliffs grid and sorting columns
448
- const mutationCliffsGrid = mutationCliffsDf.plot.grid();
449
- mutationCliffsGrid.sort([C.COLUMNS_NAMES.MONOMER]);
450
- mutationCliffsGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER].concat(positionColumns as C.COLUMNS_NAMES[]));
451
-
452
- // Creating Monomer-Position grid, sorting and setting column format
453
- const mostPotentResiduesGrid = mostPotentResiduesDf.plot.grid();
454
- mostPotentResiduesGrid.sort([C.COLUMNS_NAMES.POSITION]);
455
- const pValGridCol = mostPotentResiduesGrid.col(C.COLUMNS_NAMES.P_VALUE)!;
456
- pValGridCol.format = '#.000';
457
- pValGridCol.name = 'P-value';
458
-
459
- // Setting Monomer column renderer
460
- CR.setAARRenderer(mutationCliffsDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mutationCliffsGrid);
461
- CR.setAARRenderer(mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mostPotentResiduesGrid);
462
-
463
- return [mutationCliffsGrid, mostPotentResiduesGrid];
464
- }
465
-
466
- createLogoSummaryGrid(): DG.Grid {
467
- const summaryTable = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
468
- const summaryTableLength = summaryTable.rowCount;
469
- const clustersCol: DG.Column<number> = summaryTable.getCol(C.COLUMNS_NAMES.CLUSTERS);
470
- const membersCol: DG.Column<number> = summaryTable.columns.addNewInt('Members');
471
- const webLogoCol: DG.Column<string> = summaryTable.columns.addNew('WebLogo', DG.COLUMN_TYPE.STRING);
472
- const tempDfList: DG.DataFrame[] = new Array(summaryTableLength);
473
- const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
474
- const peptideCol: DG.Column<string> = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
475
-
476
- for (let index = 0; index < summaryTableLength; ++index) {
477
- const indexes: number[] = [];
478
- for (let j = 0; j < originalClustersCol.length; ++j) {
479
- if (originalClustersCol.get(j) === clustersCol.get(index))
480
- indexes.push(j);
481
- }
482
- const tCol = DG.Column.string('peptides', indexes.length);
483
- tCol.init((i) => peptideCol.get(indexes[i]));
484
-
485
- for (const tag of peptideCol.tags)
486
- tCol.setTag(tag[0], tag[1]);
487
-
488
- const dfSlice = DG.DataFrame.fromColumns([tCol]);
489
- tempDfList[index] = dfSlice;
490
- webLogoCol.set(index, index.toString());
491
- membersCol.set(index, dfSlice.rowCount);
492
- //TODO: user should be able to choose threshold
493
- if (dfSlice.rowCount <= Math.ceil(this.clusterStatsDf.getCol(C.COLUMNS_NAMES.COUNT).stats.max * 0.70))
494
- summaryTable.filter.set(index, false, false);
495
- }
496
- webLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
497
-
498
- const grid = summaryTable.plot.grid();
499
- const gridClustersCol = grid.col(C.COLUMNS_NAMES.CLUSTERS)!;
500
- gridClustersCol.name = 'Clusters';
501
- gridClustersCol.visible = true;
502
- grid.columns.rowHeader!.visible = false;
503
- grid.props.rowHeight = 55;
504
- grid.onCellPrepare((cell) => {
505
- if (cell.isTableCell && cell.tableColumn?.name === 'WebLogo') {
506
- tempDfList[parseInt(cell.cell.value)].plot.fromType('WebLogo', {maxHeight: 50})
507
- .then((viewer) => cell.element = viewer.root);
508
- }
509
- });
510
- grid.root.addEventListener('click', (ev) => {
511
- const cell = grid.hitTest(ev.offsetX, ev.offsetY);
512
- if (!cell || !cell.isTableCell)
513
- return;
514
-
515
- const cluster = clustersCol.get(cell.tableRowIndex!)!;
516
- summaryTable.currentRowIdx = -1;
517
- if (ev.shiftKey)
518
- this.modifyClusterSelection(cluster);
519
- else
520
- this.initClusterSelection(cluster);
521
- // this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
522
- });
523
- grid.onCellRender.subscribe((gridCellArgs) => {
524
- const gc = gridCellArgs.cell;
525
- if (gc.tableColumn?.name !== C.COLUMNS_NAMES.CLUSTERS || gc.isColHeader)
526
- return;
527
- const canvasContext = gridCellArgs.g;
528
- const bound = gridCellArgs.bounds;
529
- canvasContext.save();
530
- canvasContext.beginPath();
531
- canvasContext.rect(bound.x, bound.y, bound.width, bound.height);
532
- canvasContext.clip();
533
- CR.renderLogoSummaryCell(canvasContext, gc.cell.value, this.logoSummarySelection, bound);
534
- gridCellArgs.preventDefault();
535
- canvasContext.restore();
536
- });
537
- grid.onCellTooltip((cell, x, y) => {
538
- if (!cell.isColHeader && cell.tableColumn?.name === C.COLUMNS_NAMES.CLUSTERS)
539
- this.showTooltipCluster(cell.cell.value, x, y);
540
- return true;
541
- });
542
- const webLogoGridCol = grid.columns.byName('WebLogo')!;
543
- webLogoGridCol.cellType = 'html';
544
- webLogoGridCol.width = 350;
545
-
546
- return grid;
547
- }
548
-
549
581
  modifyClusterSelection(cluster: number): void {
550
582
  const tempSelection = this.logoSummarySelection;
551
583
  const idx = tempSelection.indexOf(cluster);
@@ -561,167 +593,102 @@ export class PeptidesModel {
561
593
  this.logoSummarySelection = [cluster];
562
594
  }
563
595
 
564
- // setBarChartInteraction(): void {
565
- // const eventAction = (ev: MouseEvent): void => {
566
- // const cell = this.sourceGrid.hitTest(ev.offsetX, ev.offsetY);
567
- // if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.MONOMER) {
568
- // const newBarPart = this.findAARandPosition(cell, ev);
569
- // this.requestBarchartAction(ev, newBarPart);
570
- // }
571
- // };
572
-
573
- // // The following events makes the barchart interactive
574
- // rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'mousemove')
575
- // .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
576
- // rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'click')
577
- // .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
578
- // }
579
-
580
- // findAARandPosition(cell: DG.GridCell, ev: MouseEvent): { monomer: string, position: string } | null {
581
- // const barCoords = this.barsBounds[cell.tableColumn!.name];
582
- // for (const [monomer, coords] of Object.entries(barCoords)) {
583
- // const isIntersectingX = ev.offsetX >= coords.x && ev.offsetX <= coords.x + coords.width;
584
- // const isIntersectingY = ev.offsetY >= coords.y && ev.offsetY <= coords.y + coords.height;
585
- // if (isIntersectingX && isIntersectingY)
586
- // return {monomer: monomer, position: cell.tableColumn!.name};
587
- // }
588
-
589
- // return null;
590
- // }
591
-
592
- // requestBarchartAction(ev: MouseEvent, barPart: { position: string, monomer: string } | null): void {
593
- // if (!barPart)
594
- // return;
595
- // const monomer = barPart.monomer;
596
- // const position = barPart.position;
597
- // if (ev.type === 'click') {
598
- // ev.shiftKey ? this.modifyMonomerPositionSelection(monomer, position, true) :
599
- // this.initMonomerPositionSelection(monomer, position, true);
600
- // this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
601
- // } else {
602
- // const bar = `${monomer}:${position}`;
603
- // if (this.cachedBarchartTooltip.bar == bar)
604
- // ui.tooltip.show(this.cachedBarchartTooltip.tooltip!, ev.clientX, ev.clientY);
605
- // else
606
- // this.cachedBarchartTooltip = {bar: bar, tooltip: this.showTooltipAt(monomer, position, ev.clientX, ev.clientY)};
607
- // }
608
- // }
609
-
610
- setCellRenderers(renderColNames: string[]): void {
611
- const mdCol = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
612
- //decompose into two different renering funcs
613
- const renderCell = (args: DG.GridCellRenderArgs): void => {
614
- const canvasContext = args.g;
615
- const bound = args.bounds;
616
-
617
- canvasContext.save();
618
- canvasContext.beginPath();
619
- canvasContext.rect(bound.x, bound.y, bound.width, bound.height);
620
- canvasContext.clip();
621
-
622
- // Hide row column
623
- const cell = args.cell;
624
- if (cell.isRowHeader && cell.gridColumn.visible) {
625
- cell.gridColumn.visible = false;
626
- args.preventDefault();
627
- return;
628
- }
629
-
630
- const tableColName = cell.tableColumn?.name;
631
- const tableRowIndex = cell.tableRowIndex!;
632
- if (cell.isTableCell && tableColName && tableRowIndex !== null && renderColNames.indexOf(tableColName) !== -1) {
633
- const cellValue: number | null = cell.cell.value;
634
-
635
- if (cellValue && cellValue !== DG.INT_NULL && cellValue !== DG.FLOAT_NULL) {
636
- const gridTable = cell.grid.table;
637
- const currentPosition: string = tableColName !== C.COLUMNS_NAMES.MEAN_DIFFERENCE ?
638
- tableColName : gridTable.get(C.COLUMNS_NAMES.POSITION, tableRowIndex);
639
- const currentAAR: string = gridTable.get(C.COLUMNS_NAMES.MONOMER, tableRowIndex);
640
-
641
- if (this.isInvariantMap) {
642
- const value: number = this.monomerPositionStatsDf
643
- .groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.COUNT])
644
- .where(`${C.COLUMNS_NAMES.POSITION} = ${currentPosition} and ${C.COLUMNS_NAMES.MONOMER} = ${currentAAR}`)
645
- .aggregate().get(C.COLUMNS_NAMES.COUNT, 0);
646
- CR.renderInvaraintMapCell(
647
- canvasContext, currentAAR, currentPosition, this.invariantMapSelection, value, bound);
648
- } else {
649
- CR.renderMutationCliffCell(canvasContext, currentAAR, currentPosition, this.monomerPositionStatsDf,
650
- mdCol, bound, cellValue, this.mutationCliffsSelection, this.substitutionsInfo,
651
- this.settings.isBidirectional);
652
- }
653
- }
654
- args.preventDefault();
596
+ setWebLogoInteraction(): void {
597
+ const sourceView = this.analysisView.grid;
598
+ const eventAction = (ev: MouseEvent): void => {
599
+ const cell = sourceView.hitTest(ev.offsetX, ev.offsetY);
600
+ if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.MONOMER) {
601
+ const newBarPart = this.findAARandPosition(cell, ev);
602
+ this.requestBarchartAction(ev, newBarPart);
655
603
  }
656
- canvasContext.restore();
657
604
  };
658
- this.mutationCliffsGrid.onCellRender.subscribe(renderCell);
659
- this.mostPotentResiduesGrid.onCellRender.subscribe(renderCell);
660
605
 
661
- this.sourceGrid.setOptions({'colHeaderHeight': 130});
662
- this.sourceGrid.onCellRender.subscribe((gcArgs) => {
663
- const ctx = gcArgs.g;
664
- const bounds = gcArgs.bounds;
665
- const col = gcArgs.cell.tableColumn;
606
+ // The following events makes the barchart interactive
607
+ rxjs.fromEvent<MouseEvent>(sourceView.overlay, 'mousemove')
608
+ .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
609
+ rxjs.fromEvent<MouseEvent>(sourceView.overlay, 'click')
610
+ .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
611
+ }
666
612
 
667
- ctx.save();
668
- ctx.beginPath();
669
- ctx.rect(bounds.x, bounds.y, bounds.width, bounds.height);
670
- ctx.clip();
671
-
672
- if (gcArgs.cell.isColHeader && col?.semType == C.SEM_TYPES.MONOMER) {
673
- const countStatsCol: DG.Column<number> = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.COUNT);
674
- const monomerStatsCol: DG.Column<string> = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MONOMER);
675
- const positionStatsCol: DG.Column<string> = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.POSITION);
676
- const rowMask = DG.BitSet.create(this.monomerPositionStatsDf.rowCount, (i) => positionStatsCol.get(i) === col.name);
677
- //TODO: precalc on stats creation
678
- const sortedStatsOrder = this.monomerPositionStatsDf.getSortedOrder([C.COLUMNS_NAMES.COUNT], [false], rowMask)
679
- .sort((a, b) => {
680
- if (monomerStatsCol.get(a) === '-')
681
- return -1;
682
- else if (monomerStatsCol.get(b) === '-')
683
- return +1;
684
- return 0;
685
- });
686
- const statsInfo: type.StatsInfo = {
687
- countCol: countStatsCol,
688
- monomerCol: monomerStatsCol,
689
- orderedIndexes: sortedStatsOrder,
690
- };
691
-
692
- CR.drawLogoInBounds(ctx, bounds, statsInfo, this.df.rowCount, this.cp);
693
- gcArgs.preventDefault();
694
- }
613
+ findAARandPosition(cell: DG.GridCell, ev: MouseEvent): { monomer: string, position: string } | null {
614
+ const barCoords = this.webLogoBounds[cell.tableColumn!.name];
615
+ for (const [monomer, coords] of Object.entries(barCoords)) {
616
+ const isIntersectingX = ev.offsetX >= coords.x && ev.offsetX <= coords.x + coords.width;
617
+ const isIntersectingY = ev.offsetY >= coords.y && ev.offsetY <= coords.y + coords.height;
618
+ if (isIntersectingX && isIntersectingY)
619
+ return {monomer: monomer, position: cell.tableColumn!.name};
620
+ }
695
621
 
696
- ctx.restore();
697
- });
622
+ return null;
698
623
  }
699
624
 
700
- setTooltips(renderColNames: string[]): void {
701
- const showTooltip = (cell: DG.GridCell, x: number, y: number): boolean => {
702
- const tableCol = cell.tableColumn;
703
- const tableColName = tableCol?.name;
704
- const tableRowIndex = cell.tableRowIndex;
625
+ requestBarchartAction(ev: MouseEvent, barPart: { position: string, monomer: string } | null): void {
626
+ if (!barPart)
627
+ return;
628
+ const monomer = barPart.monomer;
629
+ const position = barPart.position;
630
+ if (ev.type === 'click') {
631
+ ev.shiftKey ? this.modifyMonomerPositionSelection(monomer, position, false) :
632
+ this.initMonomerPositionSelection(monomer, position, false);
633
+ } else {
634
+ const bar = `${position} = ${monomer}`;
635
+ if (this.cachedWebLogoTooltip.bar == bar)
636
+ ui.tooltip.show(this.cachedWebLogoTooltip.tooltip!, ev.clientX, ev.clientY);
637
+ else
638
+ this.cachedWebLogoTooltip = {bar: bar, tooltip: this.showTooltipAt(monomer, position, ev.clientX, ev.clientY)};
705
639
 
706
- if (!cell.isRowHeader && !cell.isColHeader && tableCol && tableRowIndex != null) {
707
- const table = cell.grid.table;
708
- const currentAAR = table.get(C.COLUMNS_NAMES.MONOMER, tableRowIndex);
640
+ //TODO: how to unghighlight?
641
+ // this.df.rows.match(bar).highlight();
642
+ }
643
+ }
709
644
 
710
- if (tableCol.semType == C.SEM_TYPES.MONOMER)
711
- this.showMonomerTooltip(currentAAR, x, y);
712
- else if (cell.cell.value && renderColNames.includes(tableColName!)) {
713
- const currentPosition = tableColName !== C.COLUMNS_NAMES.MEAN_DIFFERENCE ? tableColName :
714
- table.get(C.COLUMNS_NAMES.POSITION, tableRowIndex);
645
+ setCellRenderers(): void {
646
+ const sourceGrid = this.analysisView.grid;
647
+ sourceGrid.setOptions({'colHeaderHeight': 130});
648
+ sourceGrid.onCellRender.subscribe((gcArgs) => {
649
+ const ctx = gcArgs.g;
650
+ const bounds = gcArgs.bounds;
651
+ const col = gcArgs.cell.tableColumn;
715
652
 
716
- this.showTooltipAt(currentAAR, currentPosition, x, y);
653
+ ctx.save();
654
+ try {
655
+ // ctx.beginPath();
656
+ // ctx.rect(bounds.x, bounds.y, bounds.width, bounds.height);
657
+ // ctx.clip();
658
+
659
+ //TODO: optimize
660
+ if (gcArgs.cell.isColHeader && col?.semType == C.SEM_TYPES.MONOMER) {
661
+ const monomerStatsCol: DG.Column<string> = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MONOMER);
662
+ const positionStatsCol: DG.Column<string> = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.POSITION);
663
+ const rowMask = DG.BitSet.create(this.monomerPositionStatsDf.rowCount,
664
+ (i) => positionStatsCol.get(i) === col.name);
665
+ //TODO: precalc on stats creation
666
+ const sortedStatsOrder = this.monomerPositionStatsDf.getSortedOrder([C.COLUMNS_NAMES.COUNT], [false], rowMask)
667
+ .sort((a, b) => {
668
+ if (monomerStatsCol.get(a) === '-' || monomerStatsCol.get(a) === '')
669
+ return -1;
670
+ else if (monomerStatsCol.get(b) === '-' || monomerStatsCol.get(b) === '')
671
+ return +1;
672
+ return 0;
673
+ });
674
+ const statsInfo: type.StatsInfo = {
675
+ countCol: this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.COUNT),
676
+ monomerCol: monomerStatsCol,
677
+ orderedIndexes: sortedStatsOrder,
678
+ };
679
+
680
+ this.webLogoBounds[col.name] =
681
+ CR.drawLogoInBounds(ctx, bounds, statsInfo, this.df.rowCount, this.cp, this.headerSelectedMonomers[col.name]);
682
+ gcArgs.preventDefault();
717
683
  }
684
+ } finally {
685
+ ctx.restore();
718
686
  }
719
- return true;
720
- };
687
+ });
688
+ }
721
689
 
722
- this.mutationCliffsGrid.onCellTooltip(showTooltip);
723
- this.mostPotentResiduesGrid.onCellTooltip(showTooltip);
724
- this.sourceGrid.onCellTooltip((cell, x, y) => {
690
+ setTooltips(): void {
691
+ this.analysisView.grid.onCellTooltip((cell, x, y) => {
725
692
  const col = cell.tableColumn;
726
693
  const cellValue = cell.cell.value;
727
694
  if (cellValue && col && col.semType === C.SEM_TYPES.MONOMER)
@@ -734,8 +701,8 @@ export class PeptidesModel {
734
701
  const tooltipElements: HTMLDivElement[] = [];
735
702
  const monomerName = aar.toLowerCase();
736
703
 
737
- let mw = getMonomerWorks();
738
- let mol = mw?.getCappedRotatedMonomer('PEPTIDE', aar);
704
+ const mw = getMonomerWorks();
705
+ const mol = mw?.getCappedRotatedMonomer('PEPTIDE', aar);
739
706
 
740
707
  if (mol) {
741
708
  tooltipElements.push(ui.div(monomerName));
@@ -747,13 +714,30 @@ export class PeptidesModel {
747
714
  ui.tooltip.show(ui.divV(tooltipElements), x, y);
748
715
  }
749
716
 
717
+ //TODO: move out to viewer code
750
718
  showTooltipAt(aar: string, position: string, x: number, y: number): HTMLDivElement | null {
751
719
  const currentStatsDf = this.monomerPositionStatsDf.rows.match({Pos: position, AAR: aar}).toDataFrame();
752
- const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
720
+ const activityCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
753
721
  //TODO: use bitset instead of splitCol
754
722
  const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
755
723
  const currentPosCol = this.df.getCol(position);
756
- splitCol.init((i) => currentPosCol.get(i) == aar);
724
+ const indexes: number[] = [];
725
+ splitCol.init((i) => {
726
+ const sameMonomer = currentPosCol.get(i) == aar;
727
+ if (sameMonomer)
728
+ indexes.push(i);
729
+
730
+ return sameMonomer;
731
+ });
732
+ const colResults: {[colName: string]: number} = {};
733
+ for (const [col, agg] of Object.entries(this.settings.columns ?? {})) {
734
+ const currentCol = this.df.getCol(col);
735
+ const currentColData = currentCol.getRawData();
736
+ const tempCol = DG.Column.float('', indexes.length);
737
+ tempCol.init((i) => currentColData[indexes[i]]);
738
+ colResults[`${agg}(${col})`] = tempCol.stats[agg as keyof DG.Stats] as number;
739
+ }
740
+
757
741
  const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
758
742
  const stats: Stats = {
759
743
  count: currentStatsDf.get(C.COLUMNS_NAMES.COUNT, 0),
@@ -764,19 +748,21 @@ export class PeptidesModel {
764
748
  if (!stats.count)
765
749
  return null;
766
750
 
767
- const tooltip = getDistributionAndStats(distributionTable, stats, `${position} : ${aar}`, 'Other', true);
751
+ const distroStatsElem = getDistributionAndStats(distributionTable, stats, `${position} : ${aar}`, 'Other', true);
768
752
 
769
- ui.tooltip.show(tooltip, x, y);
753
+ ui.tooltip.show(ui.divV([distroStatsElem, ui.tableFromMap(colResults)]), x, y);
770
754
 
771
- return tooltip;
755
+ return distroStatsElem;
772
756
  }
773
757
 
774
758
  showTooltipCluster(cluster: number, x: number, y: number): HTMLDivElement | null {
775
- const currentStatsDf = this.clusterStatsDf.rows.match({clusters: cluster}).toDataFrame();
776
- const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
759
+ const matcher: {[key: string]: number} = {};
760
+ matcher[this.settings.clustersColumnName!] = cluster;
761
+ const currentStatsDf = this.clusterStatsDf.rows.match(matcher).toDataFrame();
762
+ const activityCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
777
763
  //TODO: use bitset instead of splitCol
778
764
  const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
779
- const currentClusterCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
765
+ const currentClusterCol = this.df.getCol(this.settings.clustersColumnName!);
780
766
  splitCol.init((i) => currentClusterCol.get(i) == cluster);
781
767
  const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
782
768
  const stats: Stats = {
@@ -795,49 +781,6 @@ export class PeptidesModel {
795
781
  return tooltip;
796
782
  }
797
783
 
798
- setInteractionCallback(): void {
799
- const mutationCliffsDf = this.mutationCliffsGrid.dataFrame;
800
- const mostPotentResiduesDf = this.mostPotentResiduesGrid.dataFrame;
801
-
802
- const chooseAction =
803
- (aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean = true): void => {
804
- isShiftPressed ? this.modifyMonomerPositionSelection(aar, position, isInvariantMapSelection) :
805
- this.initMonomerPositionSelection(aar, position, isInvariantMapSelection);
806
- // this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
807
- };
808
-
809
- this.mutationCliffsGrid.root.addEventListener('click', (ev) => {
810
- const gridCell = this.mutationCliffsGrid.hitTest(ev.offsetX, ev.offsetY);
811
- if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name == C.COLUMNS_NAMES.MONOMER)
812
- return;
813
-
814
- const position = gridCell!.tableColumn!.name;
815
- const aar = mutationCliffsDf.get(C.COLUMNS_NAMES.MONOMER, gridCell!.tableRowIndex!);
816
- chooseAction(aar, position, ev.shiftKey, this.isInvariantMap);
817
- });
818
-
819
- this.mostPotentResiduesGrid.root.addEventListener('click', (ev) => {
820
- const gridCell = this.mostPotentResiduesGrid.hitTest(ev.offsetX, ev.offsetY);
821
- if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name != C.COLUMNS_NAMES.MEAN_DIFFERENCE)
822
- return;
823
-
824
- const tableRowIdx = gridCell!.tableRowIndex!;
825
- const position = mostPotentResiduesDf.get(C.COLUMNS_NAMES.POSITION, tableRowIdx);
826
- const aar = mostPotentResiduesDf.get(C.COLUMNS_NAMES.MONOMER, tableRowIdx);
827
- chooseAction(aar, position, ev.shiftKey, false);
828
- });
829
-
830
- const cellChanged = (table: DG.DataFrame): void => {
831
- if (this.isCellChanging)
832
- return;
833
- this.isCellChanging = true;
834
- table.currentRowIdx = -1;
835
- this.isCellChanging = false;
836
- };
837
- this.mutationCliffsGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mutationCliffsDf));
838
- this.mostPotentResiduesGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mostPotentResiduesDf));
839
- }
840
-
841
784
  modifyMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
842
785
  const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
843
786
  const tempSelectionAt = tempSelection[position];
@@ -865,19 +808,13 @@ export class PeptidesModel {
865
808
  this.mutationCliffsSelection = tempSelection;
866
809
  }
867
810
 
868
- invalidateGrids(): void {
869
- this.mutationCliffsGrid.invalidate();
870
- this.mostPotentResiduesGrid.invalidate();
871
- this.logoSummaryGrid?.invalidate();
872
- this.sourceGrid?.invalidate();
873
- }
874
-
875
811
  setBitsetCallback(): void {
876
812
  if (this.isBitsetChangedInitialized)
877
813
  return;
878
814
  const selection = this.df.selection;
879
815
  const filter = this.df.filter;
880
- const clusterCol = this.df.col(C.COLUMNS_NAMES.CLUSTERS);
816
+ const clusterCol = this.df.col(this.settings.clustersColumnName!);
817
+ const clusterColData = clusterCol?.getRawData();
881
818
 
882
819
  const changeSelectionBitset = (currentBitset: DG.BitSet): void => {
883
820
  const edfSelection = this.edf?.selection;
@@ -904,7 +841,7 @@ export class PeptidesModel {
904
841
  if (this.mutationCliffsSelection[position].includes(positionCol.get(i)!))
905
842
  return true;
906
843
  }
907
- if (this.logoSummarySelection.includes(clusterCol?.get(i)!))
844
+ if (clusterColData && this.logoSummarySelection.includes(clusterColData[i]))
908
845
  return true;
909
846
  return false;
910
847
  };
@@ -914,7 +851,7 @@ export class PeptidesModel {
914
851
  };
915
852
 
916
853
  selection.onChanged.subscribe(() => changeSelectionBitset(selection));
917
-
854
+
918
855
  filter.onChanged.subscribe(() => {
919
856
  const positionList = Object.keys(this.invariantMapSelection);
920
857
  const invariantMapBitset = DG.BitSet.create(filter.length, (index) => {
@@ -931,7 +868,6 @@ export class PeptidesModel {
931
868
  if (!this.isInvariantMapTrigger)
932
869
  this.initBitset = filter.clone();
933
870
 
934
- // filter.copyFrom(invariantMapBitset.and(this.initBitset), false);
935
871
  const temp = invariantMapBitset.and(this.initBitset);
936
872
  filter.init((i) => temp.get(i), false);
937
873
  });
@@ -942,48 +878,36 @@ export class PeptidesModel {
942
878
  this.isPeptideSpaceChangingBitset = isPeptideSpaceSource;
943
879
  this.df.selection.fireChanged();
944
880
  this.modifyOrCreateSplitCol();
945
- grok.shell.o = this.createAccordion().root;
881
+ this.headerSelectedMonomers = calculateSelected(this.df);
882
+ const acc = this.createAccordion();
883
+ grok.shell.o = acc.root;
884
+ for (const pane of acc.panes)
885
+ pane.expanded = true;
946
886
  this.isPeptideSpaceChangingBitset = false;
947
887
  }
948
888
 
949
889
  postProcessGrids(): void {
950
- const mdCol: DG.GridColumn = this.mostPotentResiduesGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
951
- mdCol.name = 'Diff';
952
-
953
- for (const grid of [this.mutationCliffsGrid, this.mostPotentResiduesGrid]) {
954
- const gridProps = grid.props;
955
- gridProps.rowHeight = 20;
956
- const girdCols = grid.columns;
957
- const colNum = girdCols.length;
958
- for (let i = 0; i < colNum; ++i) {
959
- const col = girdCols.byIndex(i)!;
960
- const colName = col.name;
961
- col.width =
962
- grid == this.mostPotentResiduesGrid && colName !== 'Diff' && colName !== C.COLUMNS_NAMES.MONOMER ? 50 :
963
- gridProps.rowHeight + 10;
964
- }
965
- }
966
-
967
- const setViewerGridProps = (grid: DG.Grid): void => {
968
- const gridProps = grid.props;
969
- gridProps.allowEdit = false;
970
- gridProps.allowRowSelection = false;
971
- gridProps.allowBlockSelection = false;
972
- gridProps.allowColSelection = false;
973
- };
974
-
975
- setViewerGridProps(this.mutationCliffsGrid);
976
- setViewerGridProps(this.mostPotentResiduesGrid);
977
- if (this.df.getTag(C.TAGS.CLUSTERS))
978
- setViewerGridProps(this.logoSummaryGrid);
979
-
980
- for (let gcIndex = 0; gcIndex < this.sourceGrid.columns.length; ++gcIndex) {
981
- const col = this.sourceGrid.columns.byIndex(gcIndex)!;
982
- col.visible =
983
- col.column?.semType === C.SEM_TYPES.MONOMER ||
984
- col.column?.name === C.COLUMNS_NAMES.ACTIVITY_SCALED ||
985
- Object.keys(this.settings.columns ?? {}).includes(col.column?.name ?? '');
890
+ const posCols = this.splitSeqDf.columns.names();
891
+ const sourceGrid = this.analysisView.grid;
892
+ const sourceGridCols = sourceGrid.columns;
893
+ const sourceGridColsLen = sourceGridCols.length;
894
+ const visibleColumns = Object.keys(this.settings.columns ?? {});
895
+ const sourceGridProps = sourceGrid.props;
896
+ sourceGridProps.allowColSelection = false;
897
+ sourceGridProps.allowEdit = false;
898
+ sourceGridProps.allowRowResizing = false;
899
+ sourceGridProps.showCurrentRowIndicator = false;
900
+ this.df.temp[C.EMBEDDING_STATUS] = false;
901
+ for (let colIdx = 1; colIdx < sourceGridColsLen; ++colIdx) {
902
+ const gridCol = sourceGridCols.byIndex(colIdx)!;
903
+ const tableColName = gridCol.column!.name;
904
+ gridCol.visible = posCols.includes(tableColName) || (tableColName === C.COLUMNS_NAMES.ACTIVITY_SCALED) ||
905
+ visibleColumns.includes(tableColName);
906
+ gridCol.width = 60;
986
907
  }
908
+ setTimeout(() => {
909
+ sourceGridProps.rowHeight = 20;
910
+ }, 500);
987
911
  }
988
912
 
989
913
  getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
@@ -1000,76 +924,41 @@ export class PeptidesModel {
1000
924
  }
1001
925
 
1002
926
  /** Class initializer */
1003
- async init(): Promise<void> {
927
+ init(): void {
1004
928
  if (this.isInitialized)
1005
929
  return;
1006
930
  this.isInitialized = true;
1007
931
 
1008
- // Don't find the dataset if the analysis started from button
1009
- if (this.df.getTag('newAnalysis') !== '1')
1010
- this.currentView = wu(grok.shell.tableViews).find(({dataFrame}) => dataFrame.tags[C.PEPTIDES_ANALYSIS] === '1')!;
1011
-
1012
- this.currentView ??= grok.shell.addTableView(this.df);
1013
-
1014
- this.df.setTag('newAnalysis', '');
1015
932
  if (!this.isRibbonSet) {
933
+ //TODO: don't pass model, pass parameters instead
1016
934
  const settingsButton = ui.bigButton('Settings', () => getSettingsDialog(this), 'Peptides analysis settings');
1017
- this.currentView.setRibbonPanels([[settingsButton]], false);
935
+ this.analysisView.setRibbonPanels([[settingsButton]], false);
1018
936
  this.isRibbonSet = true;
1019
937
  }
1020
- grok.shell.v = this.currentView;
1021
- this.sourceGrid = this.currentView.grid;
1022
- if (this.df.tags[C.PEPTIDES_ANALYSIS] === '1')
1023
- return;
1024
938
 
1025
939
  this.df.tags[C.PEPTIDES_ANALYSIS] = '1';
1026
- const scaledGridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!;
1027
- scaledGridCol.name = scaledGridCol.column!.getTag('gridName');
1028
- scaledGridCol.format = '#.000';
1029
- this.sourceGrid.columns.setOrder([scaledGridCol.name]);
1030
- this.sourceGrid.props.allowColSelection = false;
1031
-
1032
- this.df.temp[C.EMBEDDING_STATUS] = false;
1033
- const adjustCellSize = (grid: DG.Grid): void => {
1034
- const colNum = grid.columns.length;
1035
- for (let i = 0; i < colNum; ++i) {
1036
- const iCol = grid.columns.byIndex(i)!;
1037
- iCol.width = isNaN(parseInt(iCol.name)) ? 50 : 40;
1038
- }
1039
- grid.props.rowHeight = 20;
1040
- };
1041
940
 
1042
- for (let i = 0; i < this.sourceGrid.columns.length; i++) {
1043
- const currentCol = this.sourceGrid.columns.byIndex(i);
1044
- if (currentCol?.column?.getTag(C.TAGS.VISIBLE) === '0')
1045
- currentCol.visible = false;
1046
- }
1047
-
1048
- const options = {scaling: this.df.tags['scaling']};
941
+ this.updateDefault();
1049
942
 
1050
- const dockManager = this.currentView.dockManager;
943
+ this.analysisView.grid.invalidate();
944
+ }
1051
945
 
1052
- const mutationCliffsViewer = await this.df.plot.fromType('peptide-sar-viewer', options) as MutationCliffsViewer;
946
+ async addViewers(): Promise<void> {
947
+ const dockManager = this.analysisView.dockManager;
948
+ const dfPlt = this.df.plot;
1053
949
 
1054
- const mostPotentResiduesViewer =
1055
- await this.df.plot.fromType('peptide-sar-viewer-vertical', options) as MostPotentResiduesViewer;
950
+ const mutationCliffsViewer = await dfPlt.fromType('peptide-sar-viewer') as MonomerPosition;
951
+ const mostPotentResiduesViewer = await dfPlt.fromType('peptide-sar-viewer-vertical') as MostPotentResiduesViewer;
952
+ if (this.settings.clustersColumnName)
953
+ await this.addLogoSummaryTableViewer();
1056
954
 
1057
- if (this.df.getTag(C.TAGS.CLUSTERS)) {
1058
- const logoSummary = await this.df.plot.fromType('logo-summary-viewer') as LogoSummary;
1059
- dockManager.dock(logoSummary, DG.DOCK_TYPE.RIGHT, null, 'Logo Summary Table');
1060
- }
1061
-
1062
- this.updateDefault();
1063
-
1064
- const mcNode =
1065
- dockManager.dock(mutationCliffsViewer, DG.DOCK_TYPE.DOWN, null, mutationCliffsViewer.name);
955
+ const mcNode = dockManager.dock(mutationCliffsViewer, DG.DOCK_TYPE.DOWN, null, mutationCliffsViewer.name);
1066
956
 
1067
957
  dockManager.dock(mostPotentResiduesViewer, DG.DOCK_TYPE.RIGHT, mcNode, mostPotentResiduesViewer.name, 0.3);
958
+ }
1068
959
 
1069
-
1070
- this.sourceGrid.props.allowEdit = false;
1071
- adjustCellSize(this.sourceGrid);
1072
-
1073
- this.invalidateGrids();
960
+ async addLogoSummaryTableViewer(): Promise<void> {
961
+ const logoSummary = await this.df.plot.fromType('logo-summary-viewer') as LogoSummary;
962
+ this.analysisView.dockManager.dock(logoSummary, DG.DOCK_TYPE.RIGHT, null, 'Logo Summary Table');
1074
963
  }
1075
964
  }