@datagrok/peptides 1.3.7 → 1.3.9

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
@@ -1,7 +1,6 @@
1
1
  import * as ui from 'datagrok-api/ui';
2
2
  import * as grok from 'datagrok-api/grok';
3
3
  import * as DG from 'datagrok-api/dg';
4
- import * as bio from '@datagrok-libraries/bio';
5
4
 
6
5
  import {splitAlignedSequences} from '@datagrok-libraries/bio/src/utils/splitter';
7
6
 
@@ -10,13 +9,17 @@ import * as rxjs from 'rxjs';
10
9
 
11
10
  import * as C from './utils/constants';
12
11
  import * as type from './utils/types';
13
- import {calculateBarsData, getTypedArrayConstructor, isGridCellInvalid, scaleActivity} from './utils/misc';
14
- import {MutationCliffsViewer, SARViewerBase, MostPotentResiduesViewer} from './viewers/sar-viewer';
12
+ import {isGridCellInvalid, scaleActivity} from './utils/misc';
13
+ import {MutationCliffsViewer, MostPotentResiduesViewer} from './viewers/sar-viewer';
15
14
  import * as CR from './utils/cell-renderer';
16
15
  import {mutationCliffsWidget} from './widgets/mutation-cliffs';
17
16
  import {getDistributionAndStats, getDistributionWidget} from './widgets/distribution';
18
17
  import {getStats, Stats} from './utils/statistics';
19
18
  import {LogoSummary} from './viewers/logo-summary';
19
+ import {getSettingsDialog} from './widgets/settings';
20
+ import {getMonomerWorks} from './package';
21
+ import * as bio from '@datagrok-libraries/bio';
22
+ import {findMutations} from './utils/algorithms';
20
23
 
21
24
  export class PeptidesModel {
22
25
  static modelName = 'peptidesModel';
@@ -48,19 +51,22 @@ export class PeptidesModel {
48
51
  isPeptideSpaceChangingBitset = false;
49
52
  isChangingEdfBitset = false;
50
53
 
51
- mutationCliffsViewer!: MutationCliffsViewer;
52
- mostPotentResiduesViewer!: MostPotentResiduesViewer;
53
-
54
- _usedProperties: { [propName: string]: string | number | boolean } = {};
55
54
  monomerMap: { [key: string]: { molfile: string, fullName: string } } = {};
56
- barData: type.MonomerDfStats = {};
57
- barsBounds: { [position: string]: type.BarCoordinates } = {};
58
- cachedBarchartTooltip: { bar: string, tooltip: null | HTMLDivElement } = {bar: '', tooltip: null};
59
55
  monomerLib: bio.IMonomerLib | null = null; // To get monomers from lib(s)
60
56
  monomerWorks: bio.MonomerWorks | null = null; // To get processed monomers
61
57
 
58
+ _settings!: type.PeptidesSettings;
59
+ isRibbonSet = false;
60
+
61
+ cp: bio.SeqPalette;
62
+ xorBitset?: DG.BitSet;
63
+ initBitset: DG.BitSet;
64
+ isInvariantMapTrigger: boolean = false;;
65
+
62
66
  private constructor(dataFrame: DG.DataFrame) {
63
67
  this.df = dataFrame;
68
+ this.initBitset = this.df.filter.clone();
69
+ this.cp = bio.pickUpPalette(this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE));
64
70
  }
65
71
 
66
72
  static async getInstance(dataFrame: DG.DataFrame): Promise<PeptidesModel> {
@@ -101,7 +107,9 @@ export class PeptidesModel {
101
107
  set invariantMapSelection(selection: type.PositionToAARList) {
102
108
  this._invariantMapSelection = selection;
103
109
  this.df.tags[C.TAGS.FILTER] = JSON.stringify(selection);
110
+ this.isInvariantMapTrigger = true;
104
111
  this.df.filter.fireChanged();
112
+ this.isInvariantMapTrigger = false;
105
113
  this.invalidateGrids();
106
114
  }
107
115
 
@@ -117,16 +125,6 @@ export class PeptidesModel {
117
125
  this.invalidateGrids();
118
126
  }
119
127
 
120
- get usedProperties(): { [propName: string]: string | number | boolean } {
121
- this._usedProperties = JSON.parse(this.df.tags['sarProperties'] ?? '{}');
122
- return this._usedProperties;
123
- }
124
-
125
- set usedProperties(properties: { [propName: string]: string | number | boolean }) {
126
- this.df.tags['sarProperties'] = JSON.stringify(properties);
127
- this._usedProperties = properties;
128
- }
129
-
130
128
  get splitByPos(): boolean {
131
129
  const splitByPosFlag = (this.df.tags['distributionSplit'] ?? '00')[0];
132
130
  return splitByPosFlag == '1' ? true : false;
@@ -156,9 +154,10 @@ export class PeptidesModel {
156
154
  }
157
155
 
158
156
  get isMutationCliffSelectionEmpty(): boolean {
159
- for (const aarList of Object.values(this.mutationCliffsSelection))
157
+ for (const aarList of Object.values(this.mutationCliffsSelection)) {
160
158
  if (aarList.length !== 0)
161
159
  return false;
160
+ }
162
161
  return true;
163
162
  }
164
163
 
@@ -166,6 +165,18 @@ export class PeptidesModel {
166
165
  return this.logoSummarySelection.length === 0;
167
166
  }
168
167
 
168
+ get settings(): type.PeptidesSettings {
169
+ this._settings ??= JSON.parse(this.df.getTag('settings') ?? '{}');
170
+ return this._settings;
171
+ }
172
+ set settings(s: type.PeptidesSettings) {
173
+ for (const [key, value] of Object.entries(s))
174
+ this._settings[key as keyof type.PeptidesSettings] = value as any;
175
+ this.df.setTag('settings', JSON.stringify(this._settings));
176
+ //TODO: update only needed components
177
+ this.updateDefault();
178
+ }
179
+
169
180
  createAccordion(): DG.Accordion {
170
181
  const acc = ui.accordion();
171
182
  acc.root.style.width = '100%';
@@ -176,37 +187,9 @@ export class PeptidesModel {
176
187
  return acc;
177
188
  }
178
189
 
179
- getViewer(): SARViewerBase {
180
- const viewer = this.mutationCliffsViewer ?? this.mostPotentResiduesViewer;
181
- if (!viewer)
182
- throw new Error('ViewerError: none of the SAR viewers is initialized');
183
- return viewer;
184
- }
185
-
186
- isPropertyChanged(viewer: SARViewerBase): boolean {
187
- let result = false;
188
- if (typeof viewer == 'undefined')
189
- return result;
190
-
191
- const viewerProps = viewer.props.getProperties();
192
- const tempProps = this.usedProperties;
193
- for (const property of viewerProps) {
194
- const propName = property.name;
195
- const propVal = property.get(viewer);
196
- if (tempProps[propName] != propVal) {
197
- tempProps[propName] = propVal;
198
- result = true;
199
- }
200
- }
201
- this.usedProperties = tempProps;
202
- return result;
203
- }
204
-
205
190
  updateDefault(): void {
206
- const proprtyChanged =
207
- this.isPropertyChanged(this.mutationCliffsViewer) || this.isPropertyChanged(this.mostPotentResiduesViewer);
208
- if ((this.sourceGrid && !this._isUpdating && proprtyChanged) || !this.isInitialized) {
209
- this.isInitialized = true;
191
+ if ((this.sourceGrid && !this._isUpdating) || !this.isInitialized) {
192
+ // this.isInitialized = true;
210
193
  this._isUpdating = true;
211
194
  this.initializeViewersComponents();
212
195
  //FIXME: modify during the initializeViewersComponents stages
@@ -230,7 +213,7 @@ export class PeptidesModel {
230
213
  const alphabet = col.tags['alphabet'];
231
214
  const splitSeqDf = splitAlignedSequences(col);
232
215
 
233
- this.barData = calculateBarsData(splitSeqDf.columns.toList(), this.df.selection);
216
+ // this.barData = calculateBarsData(splitSeqDf.columns.toList(), this.df.selection);
234
217
 
235
218
  const positionColumns = splitSeqDf.columns.names();
236
219
 
@@ -241,9 +224,7 @@ export class PeptidesModel {
241
224
 
242
225
  this.sortSourceGrid();
243
226
 
244
- const viewer = this.getViewer();
245
-
246
- this.createScaledCol(viewer.scaling, splitSeqDf);
227
+ this.createScaledCol(splitSeqDf);
247
228
 
248
229
  //unpivot a table and handle duplicates
249
230
  let matrixDf = splitSeqDf.groupBy(positionColumns).aggregate();
@@ -267,7 +248,9 @@ export class PeptidesModel {
267
248
  // SAR vertical table (naive, choose best Mean difference from pVals <= 0.01)
268
249
  const sequenceDf = this.createVerticalTable();
269
250
 
270
- this.calcSubstitutions();
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);
271
254
 
272
255
  [this.mutationCliffsGrid, this.mostPotentResiduesGrid] =
273
256
  this.createGrids(matrixDf, sequenceDf, positionColumns, alphabet);
@@ -282,7 +265,7 @@ export class PeptidesModel {
282
265
 
283
266
  positionColumns.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
284
267
 
285
- this.setBarChartInteraction();
268
+ // this.setBarChartInteraction();
286
269
 
287
270
  this.setCellRenderers(positionColumns);
288
271
 
@@ -296,91 +279,6 @@ export class PeptidesModel {
296
279
  this.postProcessGrids();
297
280
  }
298
281
 
299
- calcSubstitutions(): void {
300
- const activityValues: DG.Column<number> = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
301
- const columnList: DG.Column<string>[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER);
302
- const nCols = columnList.length;
303
- if (nCols == 0)
304
- throw new Error(`Couldn't find any column of semType '${C.SEM_TYPES.MONOMER}'`);
305
-
306
- const viewer = this.getViewer();
307
- this.substitutionsInfo = new Map();
308
- const nRows = this.df.rowCount;
309
- for (let seq1Idx = 0; seq1Idx < nRows - 1; seq1Idx++) {
310
- for (let seq2Idx = seq1Idx + 1; seq2Idx < nRows; seq2Idx++) {
311
- let substCounter = 0;
312
- const activityValSeq1 = activityValues.get(seq1Idx)!;
313
- const activityValSeq2 = activityValues.get(seq2Idx)!;
314
- const delta = activityValSeq1 - activityValSeq2;
315
- if (Math.abs(delta) < viewer.minActivityDelta)
316
- continue;
317
-
318
- let substCounterFlag = false;
319
- const tempData: { pos: string, seq1monomer: string, seq2monomer: string, seq1Idx: number, seq2Idx: number }[] =
320
- [];
321
- for (const currentPosCol of columnList) {
322
- const seq1monomer = currentPosCol.get(seq1Idx)!;
323
- const seq2monomer = currentPosCol.get(seq2Idx)!;
324
- if (seq1monomer == seq2monomer)
325
- continue;
326
-
327
- substCounter++;
328
- substCounterFlag = substCounter > viewer.maxSubstitutions;
329
- if (substCounterFlag)
330
- break;
331
-
332
- tempData.push({
333
- pos: currentPosCol.name,
334
- seq1monomer: seq1monomer,
335
- seq2monomer: seq2monomer,
336
- seq1Idx: seq1Idx,
337
- seq2Idx: seq2Idx,
338
- });
339
- }
340
-
341
- if (substCounterFlag || substCounter == 0)
342
- continue;
343
-
344
- for (const tempDataElement of tempData) {
345
- const position = tempDataElement.pos;
346
-
347
- //Working with seq1monomer
348
- const seq1monomer = tempDataElement.seq1monomer;
349
- if (!this.substitutionsInfo.has(seq1monomer))
350
- this.substitutionsInfo.set(seq1monomer, new Map());
351
-
352
- let positionsMap = this.substitutionsInfo.get(seq1monomer)!;
353
- if (!positionsMap.has(position))
354
- positionsMap.set(position, new Map());
355
-
356
- let indexes = positionsMap.get(position)!;
357
-
358
- !indexes.has(seq1Idx) ? indexes.set(seq1Idx, [seq2Idx]) : (indexes.get(seq1Idx)! as number[]).push(seq2Idx);
359
-
360
- //Working with seq2monomer
361
- const seq2monomer = tempDataElement.seq2monomer;
362
- if (!this.substitutionsInfo.has(seq2monomer))
363
- this.substitutionsInfo.set(seq2monomer, new Map());
364
-
365
- positionsMap = this.substitutionsInfo.get(seq2monomer)!;
366
- if (!positionsMap.has(position))
367
- positionsMap.set(position, new Map());
368
-
369
- indexes = positionsMap.get(position)!;
370
- !indexes.has(seq2Idx) ? indexes.set(seq2Idx, [seq1Idx]) : (indexes.get(seq2Idx)! as number[]).push(seq1Idx);
371
- }
372
- }
373
- }
374
-
375
- const TypedArray = getTypedArrayConstructor(nRows);
376
- for (const positionMap of this.substitutionsInfo.values()) {
377
- for (const indexMap of positionMap.values()) {
378
- for (const [index, indexArray] of indexMap.entries())
379
- indexMap.set(index, new TypedArray(indexArray));
380
- }
381
- }
382
- }
383
-
384
282
  initSelections(positionColumns: string[]): void {
385
283
  const tempInvariantMapSelection: type.PositionToAARList = this.invariantMapSelection;
386
284
  const mutationCliffsSelection: type.PositionToAARList = this.mutationCliffsSelection;
@@ -390,7 +288,7 @@ export class PeptidesModel {
390
288
  }
391
289
  this.invariantMapSelection = tempInvariantMapSelection;
392
290
  this.mutationCliffsSelection = mutationCliffsSelection;
393
- this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
291
+ // this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
394
292
  }
395
293
 
396
294
  joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame, alphabet: string): void {
@@ -430,8 +328,8 @@ export class PeptidesModel {
430
328
  this.sourceGrid.columns.setOrder(colNames.map((v) => v.name));
431
329
  }
432
330
 
433
- createScaledCol(activityScaling: string, splitSeqDf: DG.DataFrame): void {
434
- const scaledCol = scaleActivity(activityScaling, this.df.getCol(C.COLUMNS_NAMES.ACTIVITY));
331
+ createScaledCol(splitSeqDf: DG.DataFrame): void {
332
+ const scaledCol = scaleActivity(this.df.getCol(C.COLUMNS_NAMES.ACTIVITY), this.settings.scaling);
435
333
  //TODO: make another func
436
334
  splitSeqDf.columns.add(scaledCol);
437
335
  this.df.columns.replace(C.COLUMNS_NAMES.ACTIVITY_SCALED, scaledCol);
@@ -499,7 +397,7 @@ export class PeptidesModel {
499
397
 
500
398
  setCategoryOrder(matrixDf: DG.DataFrame): void {
501
399
  let sortArgument: string = C.COLUMNS_NAMES.MEAN_DIFFERENCE;
502
- if (this.getViewer().bidirectionalAnalysis) {
400
+ if (this.settings.isBidirectional) {
503
401
  const mdCol = this.monomerPositionStatsDf.getCol(sortArgument);
504
402
  sortArgument = 'Absolute Mean difference';
505
403
  const absMDCol = this.monomerPositionStatsDf.columns.addNewFloat(sortArgument);
@@ -535,7 +433,7 @@ export class PeptidesModel {
535
433
  const rowCount = sequenceDf.rowCount;
536
434
  for (const pos of posColCategories) {
537
435
  tempStats = DG.Stats.fromColumn(mdCol, DG.BitSet.create(rowCount, (i) => posCol.get(i) === pos));
538
- maxAtPos[pos] = this.getViewer().bidirectionalAnalysis ?
436
+ maxAtPos[pos] = this.settings.isBidirectional ?
539
437
  (tempStats.max > Math.abs(tempStats.min) ? tempStats.max : tempStats.min) :
540
438
  tempStats.max;
541
439
  }
@@ -591,6 +489,9 @@ export class PeptidesModel {
591
489
  tempDfList[index] = dfSlice;
592
490
  webLogoCol.set(index, index.toString());
593
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);
594
495
  }
595
496
  webLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
596
497
 
@@ -617,7 +518,7 @@ export class PeptidesModel {
617
518
  this.modifyClusterSelection(cluster);
618
519
  else
619
520
  this.initClusterSelection(cluster);
620
- this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
521
+ // this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
621
522
  });
622
523
  grid.onCellRender.subscribe((gridCellArgs) => {
623
524
  const gc = gridCellArgs.cell;
@@ -660,51 +561,51 @@ export class PeptidesModel {
660
561
  this.logoSummarySelection = [cluster];
661
562
  }
662
563
 
663
- setBarChartInteraction(): void {
664
- const eventAction = (ev: MouseEvent): void => {
665
- const cell = this.sourceGrid.hitTest(ev.offsetX, ev.offsetY);
666
- if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.MONOMER) {
667
- const newBarPart = this.findAARandPosition(cell, ev);
668
- this.requestBarchartAction(ev, newBarPart);
669
- }
670
- };
671
-
672
- // The following events makes the barchart interactive
673
- rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'mousemove')
674
- .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
675
- rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'click')
676
- .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
677
- }
678
-
679
- findAARandPosition(cell: DG.GridCell, ev: MouseEvent): { monomer: string, position: string } | null {
680
- const barCoords = this.barsBounds[cell.tableColumn!.name];
681
- for (const [monomer, coords] of Object.entries(barCoords)) {
682
- const isIntersectingX = ev.offsetX >= coords.x && ev.offsetX <= coords.x + coords.width;
683
- const isIntersectingY = ev.offsetY >= coords.y && ev.offsetY <= coords.y + coords.height;
684
- if (isIntersectingX && isIntersectingY)
685
- return {monomer: monomer, position: cell.tableColumn!.name};
686
- }
687
-
688
- return null;
689
- }
690
-
691
- requestBarchartAction(ev: MouseEvent, barPart: { position: string, monomer: string } | null): void {
692
- if (!barPart)
693
- return;
694
- const monomer = barPart.monomer;
695
- const position = barPart.position;
696
- if (ev.type === 'click') {
697
- ev.shiftKey ? this.modifyMonomerPositionSelection(monomer, position, true) :
698
- this.initMonomerPositionSelection(monomer, position, true);
699
- this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
700
- } else {
701
- const bar = `${monomer}:${position}`;
702
- if (this.cachedBarchartTooltip.bar == bar)
703
- ui.tooltip.show(this.cachedBarchartTooltip.tooltip!, ev.clientX, ev.clientY);
704
- else
705
- this.cachedBarchartTooltip = {bar: bar, tooltip: this.showTooltipAt(monomer, position, ev.clientX, ev.clientY)};
706
- }
707
- }
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
+ // }
708
609
 
709
610
  setCellRenderers(renderColNames: string[]): void {
710
611
  const mdCol = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
@@ -737,7 +638,6 @@ export class PeptidesModel {
737
638
  tableColName : gridTable.get(C.COLUMNS_NAMES.POSITION, tableRowIndex);
738
639
  const currentAAR: string = gridTable.get(C.COLUMNS_NAMES.MONOMER, tableRowIndex);
739
640
 
740
- const viewer = this.getViewer();
741
641
  if (this.isInvariantMap) {
742
642
  const value: number = this.monomerPositionStatsDf
743
643
  .groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.COUNT])
@@ -746,9 +646,9 @@ export class PeptidesModel {
746
646
  CR.renderInvaraintMapCell(
747
647
  canvasContext, currentAAR, currentPosition, this.invariantMapSelection, value, bound);
748
648
  } else {
749
- CR.renderMutationCliffCell(
750
- canvasContext, currentAAR, currentPosition, this.monomerPositionStatsDf, viewer.bidirectionalAnalysis,
751
- mdCol, bound, cellValue, this.mutationCliffsSelection, this.substitutionsInfo);
649
+ CR.renderMutationCliffCell(canvasContext, currentAAR, currentPosition, this.monomerPositionStatsDf,
650
+ mdCol, bound, cellValue, this.mutationCliffsSelection, this.substitutionsInfo,
651
+ this.settings.isBidirectional);
752
652
  }
753
653
  }
754
654
  args.preventDefault();
@@ -760,22 +660,40 @@ export class PeptidesModel {
760
660
 
761
661
  this.sourceGrid.setOptions({'colHeaderHeight': 130});
762
662
  this.sourceGrid.onCellRender.subscribe((gcArgs) => {
763
- const context = gcArgs.g;
663
+ const ctx = gcArgs.g;
764
664
  const bounds = gcArgs.bounds;
765
665
  const col = gcArgs.cell.tableColumn;
766
666
 
767
- context.save();
768
- context.beginPath();
769
- context.rect(bounds.x, bounds.y, bounds.width, bounds.height);
770
- context.clip();
667
+ ctx.save();
668
+ ctx.beginPath();
669
+ ctx.rect(bounds.x, bounds.y, bounds.width, bounds.height);
670
+ ctx.clip();
771
671
 
772
672
  if (gcArgs.cell.isColHeader && col?.semType == C.SEM_TYPES.MONOMER) {
773
- const barBounds = CR.renderBarchart(context, col, this.barData[col.name], bounds, this.df.filter.trueCount);
774
- this.barsBounds[col.name] = barBounds;
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);
775
693
  gcArgs.preventDefault();
776
694
  }
777
695
 
778
- context.restore();
696
+ ctx.restore();
779
697
  });
780
698
  }
781
699
 
@@ -816,14 +734,13 @@ export class PeptidesModel {
816
734
  const tooltipElements: HTMLDivElement[] = [];
817
735
  const monomerName = aar.toLowerCase();
818
736
 
819
- const monomer: bio.Monomer | null = wu(['HELM_AA', 'HELM_CHEM'])
820
- .map((monomerType) => this.monomerWorks!.getCappedMonomer(monomerType, monomerName))
821
- .find((m) => m != null) ?? null;
737
+ let mw = getMonomerWorks();
738
+ let mol = mw?.getCappedRotatedMonomer('PEPTIDE', aar);
822
739
 
823
- if (monomer) {
824
- tooltipElements.push(ui.div(monomer.n));
740
+ if (mol) {
741
+ tooltipElements.push(ui.div(monomerName));
825
742
  const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
826
- tooltipElements.push(grok.chem.svgMol(monomer.m, undefined, undefined, options));
743
+ tooltipElements.push(grok.chem.svgMol(mol, undefined, undefined, options));
827
744
  } else
828
745
  tooltipElements.push(ui.div(aar));
829
746
 
@@ -886,7 +803,7 @@ export class PeptidesModel {
886
803
  (aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean = true): void => {
887
804
  isShiftPressed ? this.modifyMonomerPositionSelection(aar, position, isInvariantMapSelection) :
888
805
  this.initMonomerPositionSelection(aar, position, isInvariantMapSelection);
889
- this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
806
+ // this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
890
807
  };
891
808
 
892
809
  this.mutationCliffsGrid.root.addEventListener('click', (ev) => {
@@ -997,18 +914,26 @@ export class PeptidesModel {
997
914
  };
998
915
 
999
916
  selection.onChanged.subscribe(() => changeSelectionBitset(selection));
917
+
1000
918
  filter.onChanged.subscribe(() => {
1001
919
  const positionList = Object.keys(this.invariantMapSelection);
1002
- for (let index = 0; index < this.df.rowCount; ++index) {
920
+ const invariantMapBitset = DG.BitSet.create(filter.length, (index) => {
1003
921
  let result = true;
1004
922
  for (const position of positionList) {
1005
923
  const aarList = this.invariantMapSelection[position];
1006
924
  result &&= aarList.length === 0 || aarList.includes(this.df.get(position, index));
1007
925
  if (!result)
1008
- break;
926
+ return result;
1009
927
  }
1010
- filter.set(index, filter.get(index) && result, false);
1011
- }
928
+ return result;
929
+ });
930
+
931
+ if (!this.isInvariantMapTrigger)
932
+ this.initBitset = filter.clone();
933
+
934
+ // filter.copyFrom(invariantMapBitset.and(this.initBitset), false);
935
+ const temp = invariantMapBitset.and(this.initBitset);
936
+ filter.init((i) => temp.get(i), false);
1012
937
  });
1013
938
  this.isBitsetChangedInitialized = true;
1014
939
  }
@@ -1051,6 +976,14 @@ export class PeptidesModel {
1051
976
  setViewerGridProps(this.mostPotentResiduesGrid);
1052
977
  if (this.df.getTag(C.TAGS.CLUSTERS))
1053
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 ?? '');
986
+ }
1054
987
  }
1055
988
 
1056
989
  getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
@@ -1066,43 +999,30 @@ export class PeptidesModel {
1066
999
  this.splitCol.compact();
1067
1000
  }
1068
1001
 
1069
- syncProperties(isSourceSAR = true): void {
1070
- if (this.mutationCliffsViewer && this.mostPotentResiduesViewer) {
1071
- const [sourceViewer, targetViewer] = isSourceSAR ? [this.mutationCliffsViewer, this.mostPotentResiduesViewer] :
1072
- [this.mostPotentResiduesViewer, this.mutationCliffsViewer];
1073
- const properties = sourceViewer.props.getProperties();
1074
- const newProps: { [propName: string]: string | number | boolean } = {};
1075
- for (const property of properties) {
1076
- const propName = property.name;
1077
- const propVal = property.get(sourceViewer);
1078
- targetViewer.props.set(propName, propVal);
1079
- newProps[propName] = propVal;
1080
- }
1081
- this.usedProperties = newProps;
1082
- } else
1083
- console.warn('Warning: could not sync viewer properties, one of the viewers is not initialized');
1084
- }
1085
-
1086
1002
  /** Class initializer */
1087
1003
  async init(): Promise<void> {
1088
1004
  if (this.isInitialized)
1089
1005
  return;
1006
+ this.isInitialized = true;
1090
1007
 
1091
- // Get monomer library through bio library
1092
- this.monomerLib = await bio.getMonomerLib();
1093
- this.monomerLib.onChanged.subscribe(() => {
1094
- this.sourceGrid.invalidate();
1095
- });
1096
- this.monomerWorks = new bio.MonomerWorks(this.monomerLib);
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);
1097
1013
 
1098
- this.currentView = wu(grok.shell.tableViews).find(({dataFrame}) => dataFrame.tags[C.PEPTIDES_ANALYSIS] === 'true') ??
1099
- grok.shell.addTableView(this.df);
1014
+ this.df.setTag('newAnalysis', '');
1015
+ if (!this.isRibbonSet) {
1016
+ const settingsButton = ui.bigButton('Settings', () => getSettingsDialog(this), 'Peptides analysis settings');
1017
+ this.currentView.setRibbonPanels([[settingsButton]], false);
1018
+ this.isRibbonSet = true;
1019
+ }
1100
1020
  grok.shell.v = this.currentView;
1101
1021
  this.sourceGrid = this.currentView.grid;
1102
- if (this.df.tags[C.PEPTIDES_ANALYSIS] === 'true')
1022
+ if (this.df.tags[C.PEPTIDES_ANALYSIS] === '1')
1103
1023
  return;
1104
1024
 
1105
- this.df.tags[C.PEPTIDES_ANALYSIS] = 'true';
1025
+ this.df.tags[C.PEPTIDES_ANALYSIS] = '1';
1106
1026
  const scaledGridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!;
1107
1027
  scaledGridCol.name = scaledGridCol.column!.getTag('gridName');
1108
1028
  scaledGridCol.format = '#.000';
@@ -1129,9 +1049,9 @@ export class PeptidesModel {
1129
1049
 
1130
1050
  const dockManager = this.currentView.dockManager;
1131
1051
 
1132
- this.mutationCliffsViewer = await this.df.plot.fromType('peptide-sar-viewer', options) as MutationCliffsViewer;
1052
+ const mutationCliffsViewer = await this.df.plot.fromType('peptide-sar-viewer', options) as MutationCliffsViewer;
1133
1053
 
1134
- this.mostPotentResiduesViewer =
1054
+ const mostPotentResiduesViewer =
1135
1055
  await this.df.plot.fromType('peptide-sar-viewer-vertical', options) as MostPotentResiduesViewer;
1136
1056
 
1137
1057
  if (this.df.getTag(C.TAGS.CLUSTERS)) {
@@ -1142,10 +1062,9 @@ export class PeptidesModel {
1142
1062
  this.updateDefault();
1143
1063
 
1144
1064
  const mcNode =
1145
- dockManager.dock(this.mutationCliffsViewer, DG.DOCK_TYPE.DOWN, null, this.mutationCliffsViewer.name);
1065
+ dockManager.dock(mutationCliffsViewer, DG.DOCK_TYPE.DOWN, null, mutationCliffsViewer.name);
1146
1066
 
1147
- dockManager.dock(
1148
- this.mostPotentResiduesViewer, DG.DOCK_TYPE.RIGHT, mcNode, this.mostPotentResiduesViewer.name, 0.3);
1067
+ dockManager.dock(mostPotentResiduesViewer, DG.DOCK_TYPE.RIGHT, mcNode, mostPotentResiduesViewer.name, 0.3);
1149
1068
 
1150
1069
 
1151
1070
  this.sourceGrid.props.allowEdit = false;
@@ -1153,6 +1072,4 @@ export class PeptidesModel {
1153
1072
 
1154
1073
  this.invalidateGrids();
1155
1074
  }
1156
-
1157
- invalidateSourceGrid(): void {this.sourceGrid.invalidate();}
1158
1075
  }