@datagrok/peptides 1.3.8 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datagrok/peptides",
3
- "version": "1.3.8",
3
+ "version": "1.3.9",
4
4
  "author": {
5
5
  "name": "Volodymyr Dyma",
6
6
  "email": "vdyma@datagrok.ai"
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,15 +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';
20
19
  import {getSettingsDialog} from './widgets/settings';
21
- import {getMoomerWorks} from './package';
20
+ import {getMonomerWorks} from './package';
21
+ import * as bio from '@datagrok-libraries/bio';
22
+ import {findMutations} from './utils/algorithms';
22
23
 
23
24
  export class PeptidesModel {
24
25
  static modelName = 'peptidesModel';
@@ -51,15 +52,21 @@ export class PeptidesModel {
51
52
  isChangingEdfBitset = false;
52
53
 
53
54
  monomerMap: { [key: string]: { molfile: string, fullName: string } } = {};
54
- barData: type.MonomerDfStats = {};
55
- barsBounds: { [position: string]: type.BarCoordinates } = {};
56
- cachedBarchartTooltip: { bar: string, tooltip: null | HTMLDivElement } = {bar: '', tooltip: null};
55
+ monomerLib: bio.IMonomerLib | null = null; // To get monomers from lib(s)
56
+ monomerWorks: bio.MonomerWorks | null = null; // To get processed monomers
57
57
 
58
58
  _settings!: type.PeptidesSettings;
59
59
  isRibbonSet = false;
60
60
 
61
+ cp: bio.SeqPalette;
62
+ xorBitset?: DG.BitSet;
63
+ initBitset: DG.BitSet;
64
+ isInvariantMapTrigger: boolean = false;;
65
+
61
66
  private constructor(dataFrame: DG.DataFrame) {
62
67
  this.df = dataFrame;
68
+ this.initBitset = this.df.filter.clone();
69
+ this.cp = bio.pickUpPalette(this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE));
63
70
  }
64
71
 
65
72
  static async getInstance(dataFrame: DG.DataFrame): Promise<PeptidesModel> {
@@ -100,7 +107,9 @@ export class PeptidesModel {
100
107
  set invariantMapSelection(selection: type.PositionToAARList) {
101
108
  this._invariantMapSelection = selection;
102
109
  this.df.tags[C.TAGS.FILTER] = JSON.stringify(selection);
110
+ this.isInvariantMapTrigger = true;
103
111
  this.df.filter.fireChanged();
112
+ this.isInvariantMapTrigger = false;
104
113
  this.invalidateGrids();
105
114
  }
106
115
 
@@ -180,7 +189,7 @@ export class PeptidesModel {
180
189
 
181
190
  updateDefault(): void {
182
191
  if ((this.sourceGrid && !this._isUpdating) || !this.isInitialized) {
183
- this.isInitialized = true;
192
+ // this.isInitialized = true;
184
193
  this._isUpdating = true;
185
194
  this.initializeViewersComponents();
186
195
  //FIXME: modify during the initializeViewersComponents stages
@@ -204,7 +213,7 @@ export class PeptidesModel {
204
213
  const alphabet = col.tags['alphabet'];
205
214
  const splitSeqDf = splitAlignedSequences(col);
206
215
 
207
- this.barData = calculateBarsData(splitSeqDf.columns.toList(), this.df.selection);
216
+ // this.barData = calculateBarsData(splitSeqDf.columns.toList(), this.df.selection);
208
217
 
209
218
  const positionColumns = splitSeqDf.columns.names();
210
219
 
@@ -239,7 +248,9 @@ export class PeptidesModel {
239
248
  // SAR vertical table (naive, choose best Mean difference from pVals <= 0.01)
240
249
  const sequenceDf = this.createVerticalTable();
241
250
 
242
- 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);
243
254
 
244
255
  [this.mutationCliffsGrid, this.mostPotentResiduesGrid] =
245
256
  this.createGrids(matrixDf, sequenceDf, positionColumns, alphabet);
@@ -254,7 +265,7 @@ export class PeptidesModel {
254
265
 
255
266
  positionColumns.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
256
267
 
257
- this.setBarChartInteraction();
268
+ // this.setBarChartInteraction();
258
269
 
259
270
  this.setCellRenderers(positionColumns);
260
271
 
@@ -268,91 +279,6 @@ export class PeptidesModel {
268
279
  this.postProcessGrids();
269
280
  }
270
281
 
271
- //TODO: move out
272
- calcSubstitutions(): void {
273
- const activityValues: DG.Column<number> = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
274
- const columnList: DG.Column<string>[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER);
275
- const nCols = columnList.length;
276
- if (nCols == 0)
277
- throw new Error(`Couldn't find any column of semType '${C.SEM_TYPES.MONOMER}'`);
278
-
279
- this.substitutionsInfo = new Map();
280
- const nRows = this.df.rowCount;
281
- for (let seq1Idx = 0; seq1Idx < nRows - 1; seq1Idx++) {
282
- for (let seq2Idx = seq1Idx + 1; seq2Idx < nRows; seq2Idx++) {
283
- let substCounter = 0;
284
- const activityValSeq1 = activityValues.get(seq1Idx)!;
285
- const activityValSeq2 = activityValues.get(seq2Idx)!;
286
- const delta = activityValSeq1 - activityValSeq2;
287
- if (Math.abs(delta) < (this.settings.minActivityDelta ?? 0))
288
- continue;
289
-
290
- let substCounterFlag = false;
291
- const tempData: { pos: string, seq1monomer: string, seq2monomer: string, seq1Idx: number, seq2Idx: number }[] =
292
- [];
293
- for (const currentPosCol of columnList) {
294
- const seq1monomer = currentPosCol.get(seq1Idx)!;
295
- const seq2monomer = currentPosCol.get(seq2Idx)!;
296
- if (seq1monomer == seq2monomer)
297
- continue;
298
-
299
- substCounter++;
300
- substCounterFlag = substCounter > (this.settings.maxMutations ?? 1);
301
- if (substCounterFlag)
302
- break;
303
-
304
- tempData.push({
305
- pos: currentPosCol.name,
306
- seq1monomer: seq1monomer,
307
- seq2monomer: seq2monomer,
308
- seq1Idx: seq1Idx,
309
- seq2Idx: seq2Idx,
310
- });
311
- }
312
-
313
- if (substCounterFlag || substCounter == 0)
314
- continue;
315
-
316
- for (const tempDataElement of tempData) {
317
- const position = tempDataElement.pos;
318
-
319
- //Working with seq1monomer
320
- const seq1monomer = tempDataElement.seq1monomer;
321
- if (!this.substitutionsInfo.has(seq1monomer))
322
- this.substitutionsInfo.set(seq1monomer, new Map());
323
-
324
- let positionsMap = this.substitutionsInfo.get(seq1monomer)!;
325
- if (!positionsMap.has(position))
326
- positionsMap.set(position, new Map());
327
-
328
- let indexes = positionsMap.get(position)!;
329
-
330
- !indexes.has(seq1Idx) ? indexes.set(seq1Idx, [seq2Idx]) : (indexes.get(seq1Idx)! as number[]).push(seq2Idx);
331
-
332
- //Working with seq2monomer
333
- const seq2monomer = tempDataElement.seq2monomer;
334
- if (!this.substitutionsInfo.has(seq2monomer))
335
- this.substitutionsInfo.set(seq2monomer, new Map());
336
-
337
- positionsMap = this.substitutionsInfo.get(seq2monomer)!;
338
- if (!positionsMap.has(position))
339
- positionsMap.set(position, new Map());
340
-
341
- indexes = positionsMap.get(position)!;
342
- !indexes.has(seq2Idx) ? indexes.set(seq2Idx, [seq1Idx]) : (indexes.get(seq2Idx)! as number[]).push(seq1Idx);
343
- }
344
- }
345
- }
346
-
347
- const TypedArray = getTypedArrayConstructor(nRows);
348
- for (const positionMap of this.substitutionsInfo.values()) {
349
- for (const indexMap of positionMap.values()) {
350
- for (const [index, indexArray] of indexMap.entries())
351
- indexMap.set(index, new TypedArray(indexArray));
352
- }
353
- }
354
- }
355
-
356
282
  initSelections(positionColumns: string[]): void {
357
283
  const tempInvariantMapSelection: type.PositionToAARList = this.invariantMapSelection;
358
284
  const mutationCliffsSelection: type.PositionToAARList = this.mutationCliffsSelection;
@@ -362,7 +288,7 @@ export class PeptidesModel {
362
288
  }
363
289
  this.invariantMapSelection = tempInvariantMapSelection;
364
290
  this.mutationCliffsSelection = mutationCliffsSelection;
365
- 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);
366
292
  }
367
293
 
368
294
  joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame, alphabet: string): void {
@@ -563,6 +489,9 @@ export class PeptidesModel {
563
489
  tempDfList[index] = dfSlice;
564
490
  webLogoCol.set(index, index.toString());
565
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);
566
495
  }
567
496
  webLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
568
497
 
@@ -589,7 +518,7 @@ export class PeptidesModel {
589
518
  this.modifyClusterSelection(cluster);
590
519
  else
591
520
  this.initClusterSelection(cluster);
592
- 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);
593
522
  });
594
523
  grid.onCellRender.subscribe((gridCellArgs) => {
595
524
  const gc = gridCellArgs.cell;
@@ -632,51 +561,51 @@ export class PeptidesModel {
632
561
  this.logoSummarySelection = [cluster];
633
562
  }
634
563
 
635
- setBarChartInteraction(): void {
636
- const eventAction = (ev: MouseEvent): void => {
637
- const cell = this.sourceGrid.hitTest(ev.offsetX, ev.offsetY);
638
- if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.MONOMER) {
639
- const newBarPart = this.findAARandPosition(cell, ev);
640
- this.requestBarchartAction(ev, newBarPart);
641
- }
642
- };
643
-
644
- // The following events makes the barchart interactive
645
- rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'mousemove')
646
- .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
647
- rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'click')
648
- .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
649
- }
650
-
651
- findAARandPosition(cell: DG.GridCell, ev: MouseEvent): { monomer: string, position: string } | null {
652
- const barCoords = this.barsBounds[cell.tableColumn!.name];
653
- for (const [monomer, coords] of Object.entries(barCoords)) {
654
- const isIntersectingX = ev.offsetX >= coords.x && ev.offsetX <= coords.x + coords.width;
655
- const isIntersectingY = ev.offsetY >= coords.y && ev.offsetY <= coords.y + coords.height;
656
- if (isIntersectingX && isIntersectingY)
657
- return {monomer: monomer, position: cell.tableColumn!.name};
658
- }
659
-
660
- return null;
661
- }
662
-
663
- requestBarchartAction(ev: MouseEvent, barPart: { position: string, monomer: string } | null): void {
664
- if (!barPart)
665
- return;
666
- const monomer = barPart.monomer;
667
- const position = barPart.position;
668
- if (ev.type === 'click') {
669
- ev.shiftKey ? this.modifyMonomerPositionSelection(monomer, position, true) :
670
- this.initMonomerPositionSelection(monomer, position, true);
671
- this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
672
- } else {
673
- const bar = `${monomer}:${position}`;
674
- if (this.cachedBarchartTooltip.bar == bar)
675
- ui.tooltip.show(this.cachedBarchartTooltip.tooltip!, ev.clientX, ev.clientY);
676
- else
677
- this.cachedBarchartTooltip = {bar: bar, tooltip: this.showTooltipAt(monomer, position, ev.clientX, ev.clientY)};
678
- }
679
- }
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
+ // }
680
609
 
681
610
  setCellRenderers(renderColNames: string[]): void {
682
611
  const mdCol = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
@@ -731,22 +660,40 @@ export class PeptidesModel {
731
660
 
732
661
  this.sourceGrid.setOptions({'colHeaderHeight': 130});
733
662
  this.sourceGrid.onCellRender.subscribe((gcArgs) => {
734
- const context = gcArgs.g;
663
+ const ctx = gcArgs.g;
735
664
  const bounds = gcArgs.bounds;
736
665
  const col = gcArgs.cell.tableColumn;
737
666
 
738
- context.save();
739
- context.beginPath();
740
- context.rect(bounds.x, bounds.y, bounds.width, bounds.height);
741
- context.clip();
667
+ ctx.save();
668
+ ctx.beginPath();
669
+ ctx.rect(bounds.x, bounds.y, bounds.width, bounds.height);
670
+ ctx.clip();
742
671
 
743
672
  if (gcArgs.cell.isColHeader && col?.semType == C.SEM_TYPES.MONOMER) {
744
- const barBounds = CR.renderBarchart(context, col, this.barData[col.name], bounds, this.df.filter.trueCount);
745
- 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);
746
693
  gcArgs.preventDefault();
747
694
  }
748
695
 
749
- context.restore();
696
+ ctx.restore();
750
697
  });
751
698
  }
752
699
 
@@ -787,7 +734,7 @@ export class PeptidesModel {
787
734
  const tooltipElements: HTMLDivElement[] = [];
788
735
  const monomerName = aar.toLowerCase();
789
736
 
790
- let mw = getMoomerWorks();
737
+ let mw = getMonomerWorks();
791
738
  let mol = mw?.getCappedRotatedMonomer('PEPTIDE', aar);
792
739
 
793
740
  if (mol) {
@@ -856,7 +803,7 @@ export class PeptidesModel {
856
803
  (aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean = true): void => {
857
804
  isShiftPressed ? this.modifyMonomerPositionSelection(aar, position, isInvariantMapSelection) :
858
805
  this.initMonomerPositionSelection(aar, position, isInvariantMapSelection);
859
- 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);
860
807
  };
861
808
 
862
809
  this.mutationCliffsGrid.root.addEventListener('click', (ev) => {
@@ -967,18 +914,26 @@ export class PeptidesModel {
967
914
  };
968
915
 
969
916
  selection.onChanged.subscribe(() => changeSelectionBitset(selection));
917
+
970
918
  filter.onChanged.subscribe(() => {
971
919
  const positionList = Object.keys(this.invariantMapSelection);
972
- for (let index = 0; index < this.df.rowCount; ++index) {
920
+ const invariantMapBitset = DG.BitSet.create(filter.length, (index) => {
973
921
  let result = true;
974
922
  for (const position of positionList) {
975
923
  const aarList = this.invariantMapSelection[position];
976
924
  result &&= aarList.length === 0 || aarList.includes(this.df.get(position, index));
977
925
  if (!result)
978
- break;
926
+ return result;
979
927
  }
980
- filter.set(index, filter.get(index) && result, false);
981
- }
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);
982
937
  });
983
938
  this.isBitsetChangedInitialized = true;
984
939
  }
@@ -1021,6 +976,14 @@ export class PeptidesModel {
1021
976
  setViewerGridProps(this.mostPotentResiduesGrid);
1022
977
  if (this.df.getTag(C.TAGS.CLUSTERS))
1023
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
+ }
1024
987
  }
1025
988
 
1026
989
  getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
@@ -1040,19 +1003,26 @@ export class PeptidesModel {
1040
1003
  async init(): Promise<void> {
1041
1004
  if (this.isInitialized)
1042
1005
  return;
1006
+ this.isInitialized = true;
1007
+
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);
1043
1013
 
1044
- this.currentView = wu(grok.shell.tableViews).find(({dataFrame}) => dataFrame.tags[C.PEPTIDES_ANALYSIS] === 'true') ??
1045
- grok.shell.addTableView(this.df);
1014
+ this.df.setTag('newAnalysis', '');
1046
1015
  if (!this.isRibbonSet) {
1047
- this.currentView.setRibbonPanels([[ui.icons.settings(() => getSettingsDialog(this))]], false);
1016
+ const settingsButton = ui.bigButton('Settings', () => getSettingsDialog(this), 'Peptides analysis settings');
1017
+ this.currentView.setRibbonPanels([[settingsButton]], false);
1048
1018
  this.isRibbonSet = true;
1049
1019
  }
1050
1020
  grok.shell.v = this.currentView;
1051
1021
  this.sourceGrid = this.currentView.grid;
1052
- if (this.df.tags[C.PEPTIDES_ANALYSIS] === 'true')
1022
+ if (this.df.tags[C.PEPTIDES_ANALYSIS] === '1')
1053
1023
  return;
1054
1024
 
1055
- this.df.tags[C.PEPTIDES_ANALYSIS] = 'true';
1025
+ this.df.tags[C.PEPTIDES_ANALYSIS] = '1';
1056
1026
  const scaledGridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!;
1057
1027
  scaledGridCol.name = scaledGridCol.column!.getTag('gridName');
1058
1028
  scaledGridCol.format = '#.000';
package/src/package.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  import * as grok from 'datagrok-api/grok';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
- import * as bio from '@datagrok-libraries/bio';
6
5
  import * as C from './utils/constants';
7
6
 
8
7
  import {analyzePeptidesUI} from './widgets/peptides';
@@ -12,14 +11,15 @@ import {MutationCliffsViewer, MostPotentResiduesViewer} from './viewers/sar-view
12
11
 
13
12
  import {PeptideSpaceViewer} from './viewers/peptide-space-viewer';
14
13
  import {LogoSummary} from './viewers/logo-summary';
14
+ import {MonomerWorks} from '@datagrok-libraries/bio';
15
15
 
16
- export let monomerWorks: bio.MonomerWorks | null;
16
+ export let monomerWorks: MonomerWorks | null;
17
17
 
18
18
  export const _package = new DG.Package();
19
19
  let currentTable: DG.DataFrame;
20
20
  let alignedSequenceColumn: DG.Column;
21
21
 
22
- export function getMoomerWorks() {
22
+ export function getMonomerWorks() {
23
23
  return monomerWorks;
24
24
  };
25
25
 
@@ -42,7 +42,7 @@ export async function Peptides(): Promise<void> {
42
42
  const textLink = ui.inlineText(['For more details, see our ', wikiLink, '.']);
43
43
  if (monomerWorks == null) {
44
44
  let lib = await grok.functions.call('Bio:getBioLib');
45
- monomerWorks = new bio.MonomerWorks(lib);
45
+ monomerWorks = new MonomerWorks(lib);
46
46
  }
47
47
  const appDescription = ui.info(
48
48
  [
@@ -0,0 +1,51 @@
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as DG from 'datagrok-api/dg';
3
+
4
+ import {category, test, expect, delay, before} from '@datagrok-libraries/utils/src/test';
5
+
6
+ import {_package} from '../package-test';
7
+ import {startAnalysis} from '../widgets/peptides';
8
+ import {PeptidesModel} from '../model';
9
+ import * as C from '../utils/constants';
10
+ import {scaleActivity} from '../utils/misc';
11
+ import {ALPHABET, TAGS, NOTATION, ALIGNMENT} from '@datagrok-libraries/bio';
12
+ import {findMutations} from '../utils/algorithms';
13
+ import * as type from '../utils/types';
14
+
15
+ category('Algorithms', () => {
16
+ let activityCol: DG.Column<number>;
17
+ let monomerColumns: DG.Column<string>[];
18
+ let settings: type.PeptidesSettings;
19
+
20
+ before(async () => {
21
+ activityCol = DG.Column.fromList('int', 'test', [1, 2, 5]);
22
+ monomerColumns = [
23
+ DG.Column.fromList('string', '1', 'ABC'.split('')),
24
+ DG.Column.fromList('string', '2', 'ACC'.split('')),
25
+ DG.Column.fromList('string', '3', 'ACD'.split('')),
26
+ ];
27
+ settings = {maxMutations: 1, minActivityDelta: 2};
28
+ });
29
+
30
+ test('MutationCliffs', async () => {
31
+ const substInfo: type.SubstitutionsInfo = findMutations(activityCol, monomerColumns, settings);
32
+ expect(substInfo.has('C'), true);
33
+ expect(substInfo.has('D'), true);
34
+ expect(substInfo.has('A'), false);
35
+
36
+ const c = substInfo.get('C')!;
37
+ const d = substInfo.get('D')!;
38
+ expect(c.has('3'), true);
39
+ expect(d.has('3'), true);
40
+
41
+ const c3 = c.get('3')!;
42
+ const d3 = d.get('3')!;
43
+ expect(c3.has(2), true);
44
+ expect(d3.has(3), true);
45
+
46
+ const c32 = c3.get(2)!;
47
+ const d33 = d3.get(3)!;
48
+ expect(c32[0], 3);
49
+ expect(d33[0], 2);
50
+ });
51
+ });
package/src/tests/core.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import * as grok from 'datagrok-api/grok';
2
2
  import * as DG from 'datagrok-api/dg';
3
- import * as bio from '@datagrok-libraries/bio';
4
3
 
5
4
  import {category, test, expect, delay} from '@datagrok-libraries/utils/src/test';
6
5
 
@@ -9,6 +8,7 @@ import {startAnalysis} from '../widgets/peptides';
9
8
  import {PeptidesModel} from '../model';
10
9
  import * as C from '../utils/constants';
11
10
  import {scaleActivity} from '../utils/misc';
11
+ import {ALPHABET, TAGS, NOTATION, ALIGNMENT} from '@datagrok-libraries/bio';
12
12
 
13
13
  category('Core', () => {
14
14
  let simpleTable: DG.DataFrame;
@@ -33,9 +33,9 @@ category('Core', () => {
33
33
  simpleActivityCol = simpleTable.getCol(simpleActivityColName);
34
34
  simpleAlignedSeqCol = simpleTable.getCol(alignedSequenceCol);
35
35
  simpleAlignedSeqCol.semType = C.SEM_TYPES.MACROMOLECULE;
36
- simpleAlignedSeqCol.setTag(C.TAGS.ALPHABET, bio.ALPHABET.PT);
37
- simpleAlignedSeqCol.setTag(DG.TAGS.UNITS, bio.NOTATION.FASTA);
38
- simpleAlignedSeqCol.setTag(bio.TAGS.aligned, bio.ALIGNMENT.SEQ_MSA);
36
+ simpleAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.PT);
37
+ simpleAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
38
+ simpleAlignedSeqCol.setTag(TAGS.aligned, ALIGNMENT.SEQ_MSA);
39
39
  simpleScaledCol = scaleActivity(simpleActivityCol, '-lg');
40
40
 
41
41
  model = await startAnalysis(simpleActivityCol, simpleAlignedSeqCol, null, simpleTable, simpleScaledCol, '-lg');
@@ -53,9 +53,9 @@ category('Core', () => {
53
53
  complexActivityCol = complexTable.getCol(complexActivityColName);
54
54
  complexAlignedSeqCol = complexTable.getCol('MSA');
55
55
  complexAlignedSeqCol.semType = C.SEM_TYPES.MACROMOLECULE;
56
- complexAlignedSeqCol.setTag(C.TAGS.ALPHABET, bio.ALPHABET.UN);
57
- complexAlignedSeqCol.setTag(DG.TAGS.UNITS, bio.NOTATION.SEPARATOR);
58
- complexAlignedSeqCol.setTag(bio.TAGS.aligned, bio.ALIGNMENT.SEQ_MSA);
56
+ complexAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.UN);
57
+ complexAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.SEPARATOR);
58
+ complexAlignedSeqCol.setTag(TAGS.aligned, ALIGNMENT.SEQ_MSA);
59
59
  complexAlignedSeqCol.tags[C.TAGS.SEPARATOR] = '/';
60
60
  complexScaledCol = scaleActivity(complexActivityCol, '-lg');
61
61
 
@@ -75,9 +75,9 @@ category('Core', () => {
75
75
  simpleActivityCol = simpleTable.getCol(simpleActivityColName);
76
76
  simpleAlignedSeqCol = simpleTable.getCol(alignedSequenceCol);
77
77
  simpleAlignedSeqCol.semType = C.SEM_TYPES.MACROMOLECULE;
78
- simpleAlignedSeqCol.setTag(C.TAGS.ALPHABET, bio.ALPHABET.PT);
79
- simpleAlignedSeqCol.setTag(DG.TAGS.UNITS, bio.NOTATION.FASTA);
80
- simpleAlignedSeqCol.setTag(bio.TAGS.aligned, bio.ALIGNMENT.SEQ_MSA);
78
+ simpleAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.PT);
79
+ simpleAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
80
+ simpleAlignedSeqCol.setTag(TAGS.aligned, ALIGNMENT.SEQ_MSA);
81
81
  simpleScaledCol = scaleActivity(simpleActivityCol, '-lg');
82
82
 
83
83
  model = await startAnalysis(simpleActivityCol, simpleAlignedSeqCol, null, simpleTable, simpleScaledCol, '-lg');