@datagrok/peptides 1.17.2 → 1.17.3

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.
@@ -154,28 +154,25 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
154
154
  * @return - position columns.
155
155
  */
156
156
  get positionColumns(): DG.Column<string>[] {
157
- if (this._positionColumns != null) {
157
+ if (this._positionColumns != null)
158
158
  return this._positionColumns;
159
- }
160
159
 
161
160
 
162
161
  const getSharedPositionColumns = (viewerType: VIEWER_TYPE): DG.Column<string>[] | null => {
163
162
  const viewer = this.model.findViewer(viewerType) as SARViewer | LogoSummaryTable | null;
164
- if (this.sequenceColumnName === viewer?.sequenceColumnName) {
163
+ if (this.sequenceColumnName === viewer?.sequenceColumnName)
165
164
  return viewer._positionColumns;
166
- }
167
165
 
168
166
 
169
167
  return null;
170
168
  };
171
169
 
172
- if (this.model.positionColumns != null && this.sequenceColumnName === this.model.settings?.sequenceColumnName) {
170
+ if (this.model.positionColumns != null && this.sequenceColumnName === this.model.settings?.sequenceColumnName)
173
171
  this._positionColumns = this.model.positionColumns;
174
- } else if (this instanceof MonomerPosition) {
172
+ else if (this instanceof MonomerPosition)
175
173
  this._positionColumns = getSharedPositionColumns(VIEWER_TYPE.MOST_POTENT_RESIDUES);
176
- } else if (this instanceof MostPotentResidues) {
174
+ else if (this instanceof MostPotentResidues)
177
175
  this._positionColumns = getSharedPositionColumns(VIEWER_TYPE.MONOMER_POSITION);
178
- }
179
176
 
180
177
 
181
178
  this._positionColumns ??= getSharedPositionColumns(VIEWER_TYPE.LOGO_SUMMARY_TABLE) ??
@@ -191,9 +188,8 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
191
188
  * @return - monomer-position statistics.
192
189
  */
193
190
  get monomerPositionStats(): MonomerPositionStats {
194
- if (this._monomerPositionStats != null) {
191
+ if (this._monomerPositionStats != null)
195
192
  return this._monomerPositionStats;
196
- }
197
193
 
198
194
 
199
195
  const isMonomerPositionStatsEqual = (other: SARViewer | PeptidesSettings | null): boolean =>
@@ -203,21 +199,19 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
203
199
 
204
200
  const getSharedStats = (viewerType: VIEWER_TYPE): MonomerPositionStats | null => {
205
201
  const viewer = this.model.findViewer(viewerType) as SARViewer | null;
206
- if (isMonomerPositionStatsEqual(viewer)) {
202
+ if (isMonomerPositionStatsEqual(viewer))
207
203
  return viewer!._monomerPositionStats;
208
- }
209
204
 
210
205
 
211
206
  return null;
212
207
  };
213
208
 
214
- if (this.model.monomerPositionStats !== null && isMonomerPositionStatsEqual(this.model.settings)) {
209
+ if (this.model.monomerPositionStats !== null && isMonomerPositionStatsEqual(this.model.settings))
215
210
  this._monomerPositionStats = this.model.monomerPositionStats;
216
- } else if (this instanceof MonomerPosition) {
211
+ else if (this instanceof MonomerPosition)
217
212
  this._monomerPositionStats = getSharedStats(VIEWER_TYPE.MOST_POTENT_RESIDUES);
218
- } else if (this instanceof MostPotentResidues) {
213
+ else if (this instanceof MostPotentResidues)
219
214
  this._monomerPositionStats = getSharedStats(VIEWER_TYPE.MONOMER_POSITION);
220
- }
221
215
 
222
216
 
223
217
  this._monomerPositionStats ??= calculateMonomerPositionStatistics(this.getScaledActivityColumn(),
@@ -233,9 +227,8 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
233
227
  * @return - mutation cliffs.
234
228
  */
235
229
  get mutationCliffs(): type.MutationCliffs | null {
236
- if (this._mutationCliffs != null) {
230
+ if (this._mutationCliffs != null)
237
231
  return this._mutationCliffs;
238
- }
239
232
 
240
233
 
241
234
  const isMutationCliffsEqual = (v1: SARViewer, v2: SARViewer | null): boolean =>
@@ -249,19 +242,17 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
249
242
 
250
243
  const getSharedMutationCliffs = (viewerType: VIEWER_TYPE): type.MutationCliffs | null => {
251
244
  const viewer = this.model.findViewer(viewerType) as SARViewer | null;
252
- if (isMutationCliffsEqual(this, viewer)) {
245
+ if (isMutationCliffsEqual(this, viewer))
253
246
  return viewer!._mutationCliffs;
254
- }
255
247
 
256
248
 
257
249
  return null;
258
250
  };
259
251
 
260
- if (this instanceof MonomerPosition) {
252
+ if (this instanceof MonomerPosition)
261
253
  this._mutationCliffs = getSharedMutationCliffs(VIEWER_TYPE.MOST_POTENT_RESIDUES);
262
- } else if (this instanceof MostPotentResidues) {
254
+ else if (this instanceof MostPotentResidues)
263
255
  this._mutationCliffs = getSharedMutationCliffs(VIEWER_TYPE.MONOMER_POSITION);
264
- }
265
256
 
266
257
 
267
258
  return this._mutationCliffs;
@@ -316,9 +307,8 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
316
307
  */
317
308
  getScaledActivityColumn(isFiltered: boolean = false): DG.Column<number> {
318
309
  if (this.model.settings?.activityColumnName === this.activityColumnName &&
319
- this.model.settings?.activityScaling === this.activityScaling) {
310
+ this.model.settings?.activityScaling === this.activityScaling)
320
311
  this._scaledActivityColumn = this.model.getScaledActivityColumn(isFiltered);
321
- }
322
312
 
323
313
 
324
314
  this._scaledActivityColumn ??= scaleActivity(this.dataFrame.getCol(this.activityColumnName),
@@ -343,11 +333,10 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
343
333
  shiftPressed: false,
344
334
  ctrlPressed: false,
345
335
  }, notify: boolean = true): void {
346
- if (notify) {
336
+ if (notify)
347
337
  this.mutationCliffsSelection = modifySelection(this.mutationCliffsSelection, monomerPosition, options);
348
- } else {
338
+ else
349
339
  this._mutationCliffsSelection = modifySelection(this.mutationCliffsSelection, monomerPosition, options);
350
- }
351
340
  }
352
341
 
353
342
  /**
@@ -383,16 +372,14 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
383
372
  break;
384
373
  case SAR_PROPERTIES.COLUMNS:
385
374
  case SAR_PROPERTIES.AGGREGATION:
386
- if (this instanceof MostPotentResidues) {
375
+ if (this instanceof MostPotentResidues)
387
376
  this._viewerGrid = null;
388
- }
389
377
 
390
378
 
391
379
  break;
392
380
  }
393
- if (this.mutationCliffs === null && this.sequenceColumnName && this.activityColumnName) {
381
+ if (this.mutationCliffs === null && this.sequenceColumnName && this.activityColumnName)
394
382
  this.calculateMutationCliffs().then((mc) => this.mutationCliffs = mc);
395
- }
396
383
  }
397
384
 
398
385
  /**
@@ -432,9 +419,8 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
432
419
  ?.set(this, this.dataFrame.columns.bySemType(DG.SEMTYPE.MACROMOLECULE)!.name);
433
420
  this.getProperty(`${SAR_PROPERTIES.ACTIVITY}${COLUMN_NAME}`)
434
421
  ?.set(this, wu(this.dataFrame.columns.numerical).next().value.name);
435
- if (this.mutationCliffs === null && this.sequenceColumnName && this.activityColumnName) {
422
+ if (this.mutationCliffs === null && this.sequenceColumnName && this.activityColumnName)
436
423
  this.calculateMutationCliffs().then((mc) => this.mutationCliffs = mc);
437
- }
438
424
  } else {
439
425
  const msg = 'PeptidesError: dataframe is missing Macromolecule or numeric columns';
440
426
  grok.log.error(msg);
@@ -554,11 +540,10 @@ export class MonomerPosition extends SARViewer {
554
540
  shiftPressed: false,
555
541
  ctrlPressed: false,
556
542
  }, notify: boolean = true): void {
557
- if (notify) {
543
+ if (notify)
558
544
  this.invariantMapSelection = modifySelection(this.invariantMapSelection, monomerPosition, options);
559
- } else {
545
+ else
560
546
  this._invariantMapSelection = modifySelection(this.invariantMapSelection, monomerPosition, options);
561
- }
562
547
  }
563
548
 
564
549
  /**
@@ -581,9 +566,8 @@ export class MonomerPosition extends SARViewer {
581
566
  this.model.df.columns.toList().forEach((col) => {
582
567
  col.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] = null;
583
568
  });
584
- if (this.doRender) {
569
+ if (this.doRender)
585
570
  this.render();
586
- }
587
571
  }
588
572
 
589
573
  /**
@@ -596,18 +580,16 @@ export class MonomerPosition extends SARViewer {
596
580
  for (const col of splitSeqCols) {
597
581
  const colCat = col.categories;
598
582
  for (const cat of colCat) {
599
- if (cat !== '') {
583
+ if (cat !== '')
600
584
  uniqueMonomers.add(cat);
601
- }
602
585
  }
603
586
  }
604
587
 
605
588
  const monomerCol = DG.Column.fromStrings(C.COLUMNS_NAMES.MONOMER, Array.from(uniqueMonomers));
606
589
  const monomerPositionDf = DG.DataFrame.fromColumns([monomerCol]);
607
590
  monomerPositionDf.name = 'SAR';
608
- for (const col of splitSeqCols) {
591
+ for (const col of splitSeqCols)
609
592
  monomerPositionDf.columns.addNewBool(col.name);
610
- }
611
593
 
612
594
 
613
595
  return monomerPositionDf;
@@ -647,9 +629,8 @@ export class MonomerPosition extends SARViewer {
647
629
  grid.root.addEventListener('mouseleave', (_ev) => this.model.unhighlight());
648
630
  DG.debounce(grid.onCurrentCellChanged, 500).subscribe((gridCell: DG.GridCell) => {
649
631
  try {
650
- if (!this.keyPressed) {
632
+ if (!this.keyPressed)
651
633
  return;
652
- }
653
634
 
654
635
 
655
636
  if (this.currentGridCell !== null) {
@@ -671,9 +652,9 @@ export class MonomerPosition extends SARViewer {
671
652
  }
672
653
  }
673
654
  const monomerPosition = this.getMonomerPosition(gridCell);
674
- if (this.mode === SELECTION_MODE.INVARIANT_MAP) {
655
+ if (this.mode === SELECTION_MODE.INVARIANT_MAP)
675
656
  this.modifyInvariantMapSelection(monomerPosition, {shiftPressed: true, ctrlPressed: false}, true);
676
- } else {
657
+ else {
677
658
  const hasMutationCliffs = this.mutationCliffs
678
659
  ?.get(monomerPosition.monomerOrCluster)?.get(monomerPosition.positionOrClusterType)?.size;
679
660
  if (hasMutationCliffs) {
@@ -690,17 +671,15 @@ export class MonomerPosition extends SARViewer {
690
671
  });
691
672
  grid.root.addEventListener('keydown', (ev) => {
692
673
  this.keyPressed = ev.key.startsWith('Arrow');
693
- if (this.keyPressed) {
674
+ if (this.keyPressed)
694
675
  return;
695
- }
696
676
 
697
677
 
698
678
  if (ev.key === 'Escape' || (ev.code === 'KeyA' && ev.ctrlKey && ev.shiftKey)) {
699
- if (this.mode === SELECTION_MODE.INVARIANT_MAP) {
679
+ if (this.mode === SELECTION_MODE.INVARIANT_MAP)
700
680
  this._invariantMapSelection = initSelection(this.positionColumns);
701
- } else {
681
+ else
702
682
  this._mutationCliffsSelection = initSelection(this.positionColumns);
703
- }
704
683
  } else if (ev.code === 'KeyA' && ev.ctrlKey) {
705
684
  const positions = Object.keys(this.monomerPositionStats).filter((pos) => pos !== 'general');
706
685
  for (const position of positions) {
@@ -708,9 +687,9 @@ export class MonomerPosition extends SARViewer {
708
687
  .filter((monomer) => monomer !== 'general');
709
688
  for (const monomer of monomers) {
710
689
  const monomerPosition = {monomerOrCluster: monomer, positionOrClusterType: position};
711
- if (this.mode === SELECTION_MODE.INVARIANT_MAP) {
690
+ if (this.mode === SELECTION_MODE.INVARIANT_MAP)
712
691
  this.modifyInvariantMapSelection(monomerPosition, {shiftPressed: true, ctrlPressed: false}, false);
713
- } else {
692
+ else {
714
693
  this.modifyMutationCliffsSelection(monomerPosition, {
715
694
  shiftPressed: true,
716
695
  ctrlPressed: false,
@@ -718,9 +697,8 @@ export class MonomerPosition extends SARViewer {
718
697
  }
719
698
  }
720
699
  }
721
- } else {
700
+ } else
722
701
  return;
723
- }
724
702
 
725
703
 
726
704
  this.model.fireBitsetChanged(VIEWER_TYPE.MONOMER_POSITION);
@@ -728,17 +706,15 @@ export class MonomerPosition extends SARViewer {
728
706
  });
729
707
  grid.root.addEventListener('click', (ev) => {
730
708
  const gridCell = grid.hitTest(ev.offsetX, ev.offsetY);
731
- if (!gridCell?.isTableCell || gridCell?.tableColumn?.name === C.COLUMNS_NAMES.MONOMER) {
709
+ if (!gridCell?.isTableCell || gridCell?.tableColumn?.name === C.COLUMNS_NAMES.MONOMER)
732
710
  return;
733
- }
734
711
 
735
712
 
736
713
  const monomerPosition = this.getMonomerPosition(gridCell);
737
714
  if (this.mode === SELECTION_MODE.INVARIANT_MAP) {
738
715
  this.modifyInvariantMapSelection(monomerPosition, {shiftPressed: ev.shiftKey, ctrlPressed: ev.ctrlKey});
739
- if (isSelectionEmpty(this.invariantMapSelection)) {
716
+ if (isSelectionEmpty(this.invariantMapSelection))
740
717
  monomerPositionDf.currentRowIdx = -1;
741
- }
742
718
  } else {
743
719
  const hasMutationCliffs = this.mutationCliffs?.get(monomerPosition.monomerOrCluster)
744
720
  ?.get(monomerPosition.positionOrClusterType)?.size;
@@ -801,9 +777,8 @@ export class MonomerPosition extends SARViewer {
801
777
  const columnProperty = this.getProperty(MONOMER_POSITION_PROPERTIES.COLOR);
802
778
  if (columnProperty) {
803
779
  columnProperty.choices = wu(grok.shell.t.columns.numerical).toArray().map((col) => col.name);
804
- if (columnProperty.get(this) === C.COLUMNS_NAMES.ACTIVITY_SCALED) {
780
+ if (columnProperty.get(this) === C.COLUMNS_NAMES.ACTIVITY_SCALED)
805
781
  columnProperty.set(this, C.COLUMNS_NAMES.ACTIVITY);
806
- }
807
782
  }
808
783
 
809
784
  $(this.root).empty();
@@ -875,9 +850,8 @@ export class MostPotentResidues extends SARViewer {
875
850
  */
876
851
  onPropertyChanged(property: DG.Property): void {
877
852
  super.onPropertyChanged(property);
878
- if (this.doRender) {
853
+ if (this.doRender)
879
854
  this.render();
880
- }
881
855
  }
882
856
 
883
857
  /**
@@ -900,48 +874,40 @@ export class MostPotentResidues extends SARViewer {
900
874
  let i = 0;
901
875
  for (const [position, positionStats] of monomerPositionStatsEntries) {
902
876
  const generalPositionStats = positionStats.general;
903
- if (!generalPositionStats) {
877
+ if (!generalPositionStats)
904
878
  continue;
905
- }
906
879
 
907
880
 
908
- if (Object.entries(positionStats).length === 1) {
881
+ if (Object.entries(positionStats).length === 1)
909
882
  continue;
910
- }
911
883
 
912
884
 
913
885
  const filteredMonomerStats: [string, StatsItem][] = [];
914
886
  for (const [monomer, monomerStats] of Object.entries(positionStats)) {
915
- if (monomer === 'general') {
887
+ if (monomer === 'general')
916
888
  continue;
917
- }
918
889
 
919
890
 
920
- if ((monomerStats as StatsItem).count > 1 && (monomerStats as StatsItem).pValue === null) {
891
+ if ((monomerStats as StatsItem).count > 1 && (monomerStats as StatsItem).pValue === null)
921
892
  filteredMonomerStats.push([monomer, monomerStats as StatsItem]);
922
- }
923
893
 
924
894
 
925
- if ((monomerStats as StatsItem).pValue === generalPositionStats.minPValue) {
895
+ if ((monomerStats as StatsItem).pValue === generalPositionStats.minPValue)
926
896
  filteredMonomerStats.push([monomer, monomerStats as StatsItem]);
927
- }
928
897
  }
929
898
 
930
- if (filteredMonomerStats.length === 0) {
899
+ if (filteredMonomerStats.length === 0)
931
900
  continue;
932
- }
933
901
 
934
902
 
935
903
  let maxEntry: [string, StatsItem] | null = null;
936
904
  for (const [monomer, monomerStats] of filteredMonomerStats) {
937
- if (maxEntry === null || maxEntry[1].meanDifference < monomerStats.meanDifference) {
905
+ if (maxEntry === null || maxEntry[1].meanDifference < monomerStats.meanDifference)
938
906
  maxEntry = [monomer, monomerStats];
939
- }
940
907
  }
941
908
 
942
- if (maxEntry === null) {
909
+ if (maxEntry === null)
943
910
  continue;
944
- }
945
911
 
946
912
 
947
913
  posData[i] = parseInt(position);
@@ -1010,11 +976,10 @@ export class MostPotentResidues extends SARViewer {
1010
976
  highlightMonomerPosition(monomerPosition, this.dataFrame, this.monomerPositionStats);
1011
977
  this.model.isHighlighting = true;
1012
978
 
1013
- if (gridCell.tableColumn?.name === C.COLUMNS_NAMES.MONOMER) {
979
+ if (gridCell.tableColumn?.name === C.COLUMNS_NAMES.MONOMER)
1014
980
  monomerPosition.positionOrClusterType = C.COLUMNS_NAMES.MONOMER;
1015
- } else if (gridCell.tableColumn?.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE) {
981
+ else if (gridCell.tableColumn?.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE)
1016
982
  return false;
1017
- }
1018
983
 
1019
984
 
1020
985
  const columnEntries = this.getTotalViewerAggColumns();
@@ -1028,9 +993,8 @@ export class MostPotentResidues extends SARViewer {
1028
993
  });
1029
994
  DG.debounce(grid.onCurrentCellChanged, 500).subscribe((gridCell: DG.GridCell) => {
1030
995
  try {
1031
- if ((this.keyPressed && mprDf.currentCol.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE) || !this.keyPressed) {
996
+ if ((this.keyPressed && mprDf.currentCol.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE) || !this.keyPressed)
1032
997
  return;
1033
- }
1034
998
 
1035
999
 
1036
1000
  const monomerPosition = this.getMonomerPosition(gridCell);
@@ -1043,9 +1007,8 @@ export class MostPotentResidues extends SARViewer {
1043
1007
  }
1044
1008
  const hasMutationCliffs = this.mutationCliffs?.get(monomerPosition.monomerOrCluster)
1045
1009
  ?.get(monomerPosition.positionOrClusterType)?.size;
1046
- if (hasMutationCliffs) {
1010
+ if (hasMutationCliffs)
1047
1011
  this.modifyMutationCliffsSelection(monomerPosition, {shiftPressed: true, ctrlPressed: false});
1048
- }
1049
1012
 
1050
1013
 
1051
1014
  grid.invalidate();
@@ -1056,21 +1019,19 @@ export class MostPotentResidues extends SARViewer {
1056
1019
  });
1057
1020
  grid.root.addEventListener('keydown', (ev) => {
1058
1021
  this.keyPressed = ev.key.startsWith('Arrow');
1059
- if (this.keyPressed) {
1022
+ if (this.keyPressed)
1060
1023
  return;
1061
- }
1062
1024
 
1063
1025
 
1064
- if (ev.key === 'Escape' || (ev.code === 'KeyA' && ev.ctrlKey && ev.shiftKey)) {
1026
+ if (ev.key === 'Escape' || (ev.code === 'KeyA' && ev.ctrlKey && ev.shiftKey))
1065
1027
  this._mutationCliffsSelection = initSelection(this.positionColumns);
1066
- } else if (ev.code === 'KeyA' && ev.ctrlKey) {
1028
+ else if (ev.code === 'KeyA' && ev.ctrlKey) {
1067
1029
  for (let rowIdx = 0; rowIdx < mprDf.rowCount; ++rowIdx) {
1068
1030
  const monomerPosition = this.getMonomerPosition(grid.cell('Diff', rowIdx));
1069
1031
  this.modifyMutationCliffsSelection(monomerPosition, {shiftPressed: true, ctrlPressed: false}, false);
1070
1032
  }
1071
- } else {
1033
+ } else
1072
1034
  return;
1073
- }
1074
1035
 
1075
1036
 
1076
1037
  this.model.fireBitsetChanged(VIEWER_TYPE.MOST_POTENT_RESIDUES);
@@ -1079,17 +1040,15 @@ export class MostPotentResidues extends SARViewer {
1079
1040
  grid.root.addEventListener('mouseleave', (_ev) => this.model.unhighlight());
1080
1041
  grid.root.addEventListener('click', (ev) => {
1081
1042
  const gridCell = grid.hitTest(ev.offsetX, ev.offsetY);
1082
- if (!gridCell?.isTableCell || gridCell!.tableColumn!.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE) {
1043
+ if (!gridCell?.isTableCell || gridCell!.tableColumn!.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE)
1083
1044
  return;
1084
- }
1085
1045
 
1086
1046
 
1087
1047
  const monomerPosition = this.getMonomerPosition(gridCell);
1088
1048
  const hasMutationCliffs = this.mutationCliffs?.get(monomerPosition.monomerOrCluster)
1089
1049
  ?.get(monomerPosition.positionOrClusterType)?.size;
1090
- if (!hasMutationCliffs) {
1050
+ if (!hasMutationCliffs)
1091
1051
  return;
1092
- }
1093
1052
 
1094
1053
 
1095
1054
  this.modifyMutationCliffsSelection(monomerPosition, {shiftPressed: ev.shiftKey, ctrlPressed: ev.ctrlKey});
@@ -1197,18 +1156,17 @@ function renderCell(args: DG.GridCellRenderArgs, viewer: SARViewer, isInvariantM
1197
1156
  const positionCol = viewer.positionColumns.find((col) => col.name === currentPosition)!;
1198
1157
  const colorCache: { [_: string]: number } = positionCol.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] ?? {};
1199
1158
  let color: number;
1200
- if (colorCache[currentMonomer]) {
1159
+ if (colorCache[currentMonomer])
1201
1160
  color = colorCache[currentMonomer];
1202
- } else {
1161
+ else {
1203
1162
  const positionColData = positionCol.getRawData();
1204
1163
  const positionColCategories = positionCol.categories;
1205
1164
 
1206
1165
  const colorColData = colorCol!.getRawData();
1207
1166
  const colorValuesIndexes: number[] = [];
1208
1167
  for (let i = 0; i < positionCol.length; ++i) {
1209
- if (positionColCategories[positionColData[i]] === currentMonomer) {
1168
+ if (positionColCategories[positionColData[i]] === currentMonomer)
1210
1169
  colorValuesIndexes.push(i);
1211
- }
1212
1170
  }
1213
1171
  const cellColorDataCol = DG.Column.float('color', colorValuesIndexes.length)
1214
1172
  .init((i) => colorColData[colorValuesIndexes[i]]);
@@ -1220,9 +1178,8 @@ function renderCell(args: DG.GridCellRenderArgs, viewer: SARViewer, isInvariantM
1220
1178
 
1221
1179
  CR.renderInvariantMapCell(canvasContext, currentMonomer, currentPosition,
1222
1180
  (viewer as MonomerPosition).invariantMapSelection, value, bound, color);
1223
- } else {
1181
+ } else
1224
1182
  CR.renderMutationCliffCell(canvasContext, currentMonomer, currentPosition, viewer, bound);
1225
- }
1226
1183
 
1227
1184
 
1228
1185
  args.preventDefault();
@@ -96,13 +96,29 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
96
96
  const activityColumnChoice = ui.columnInput('Activity', df, defaultActivityColumn, activityScalingMethodState,
97
97
  {filter: (col: DG.Column) => col.type === DG.TYPE.INT || col.type === DG.TYPE.FLOAT});
98
98
  activityColumnChoice.setTooltip('Numerical activity column');
99
- const clustersColumnChoice = ui.columnInput('Clusters', df, null, null);
99
+ const clustersColumnChoice = ui.columnInput('Clusters', df, null, () => {
100
+ if (clustersColumnChoice.value) {
101
+ generateClustersInput.value = false;
102
+ generateClustersInput.fireChanged();
103
+ }
104
+ });
100
105
  clustersColumnChoice.setTooltip('Optional. Clusters column is used to create Logo Summary Table');
101
106
  clustersColumnChoice.nullable = true;
107
+ // clustering input
108
+ const generateClustersInput = ui.boolInput('Generate clusters', true, () => {
109
+ if (generateClustersInput.value) {
110
+ clustersColumnChoice.value = null;
111
+ clustersColumnChoice.fireChanged();
112
+ }
113
+ });
114
+ generateClustersInput
115
+ .setTooltip('Generate clusters column based on sequence space embeddings for Logo Summary Table');
102
116
  activityColumnChoice.fireChanged();
103
117
  activityScalingMethod.fireChanged();
118
+ generateClustersInput.fireChanged();
119
+
104
120
 
105
- const inputsList = [activityColumnChoice, activityScalingMethod, clustersColumnChoice];
121
+ const inputsList = [activityColumnChoice, activityScalingMethod, clustersColumnChoice, generateClustersInput];
106
122
  if (seqColInput !== null)
107
123
  inputsList.splice(0, 0, seqColInput);
108
124
 
@@ -114,7 +130,8 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
114
130
  bitsetChanged.unsubscribe();
115
131
  if (sequencesCol) {
116
132
  const model = await startAnalysis(activityColumnChoice.value!, sequencesCol, clustersColumnChoice.value, df,
117
- scaledCol, activityScalingMethod.value ?? C.SCALING_METHODS.NONE, {addSequenceSpace: true});
133
+ scaledCol, activityScalingMethod.value ?? C.SCALING_METHODS.NONE, {addSequenceSpace: true,
134
+ useEmbeddingsClusters: generateClustersInput.value ?? false});
118
135
  return model !== null;
119
136
  }
120
137
  return false;
@@ -147,7 +164,10 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): Di
147
164
  return {host: mainHost, callback: startAnalysisCallback};
148
165
  }
149
166
 
150
- type AnalysisOptions = { addSequenceSpace?: boolean };
167
+ type AnalysisOptions = {
168
+ addSequenceSpace?: boolean,
169
+ useEmbeddingsClusters?: boolean,
170
+ };
151
171
 
152
172
  /**
153
173
  * Creates dataframe to use in analysis, model instance and adds viewers
@@ -163,7 +183,7 @@ type AnalysisOptions = { addSequenceSpace?: boolean };
163
183
  export async function startAnalysis(activityColumn: DG.Column<number>, peptidesCol: DG.Column<string>,
164
184
  clustersColumn: DG.Column | null, sourceDf: DG.DataFrame, scaledCol: DG.Column<number>, scaling: C.SCALING_METHODS,
165
185
  options: AnalysisOptions = {}): Promise<PeptidesModel | null> {
166
- let model = null;
186
+ let model: PeptidesModel | null = null;
167
187
  if (activityColumn.type !== DG.COLUMN_TYPE.FLOAT && activityColumn.type !== DG.COLUMN_TYPE.INT) {
168
188
  grok.shell.error('The activity column must be of numeric type!');
169
189
  return model;
@@ -187,7 +207,8 @@ export async function startAnalysis(activityColumn: DG.Column<number>, peptidesC
187
207
 
188
208
  const settings: type.PeptidesSettings = {
189
209
  sequenceColumnName: peptidesCol.name, activityColumnName: activityColumn.name, activityScaling: scaling,
190
- columns: {}, showDendrogram: false, sequenceSpaceParams: new type.SequenceSpaceParams(),
210
+ columns: {}, showDendrogram: false,
211
+ sequenceSpaceParams: new type.SequenceSpaceParams(!!options.useEmbeddingsClusters && !clustersColumn),
191
212
  };
192
213
 
193
214
  if (clustersColumn) {
@@ -215,8 +236,20 @@ export async function startAnalysis(activityColumn: DG.Column<number>, peptidesC
215
236
  await model.addMostPotentResidues();
216
237
 
217
238
  // FIXME: enable by default for tests
218
- if (options.addSequenceSpace ?? false)
219
- model.addSequenceSpace();
239
+ if (options.addSequenceSpace ?? false) {
240
+ await model.addSequenceSpace({clusterCol: clustersColumn, clusterEmbeddings: options.useEmbeddingsClusters});
241
+ if (!clustersColumn && (options.useEmbeddingsClusters ?? false)) {
242
+ const clusterCol = model._sequenceSpaceCols
243
+ .find((col) => model!.df.col(col) && model!.df.col(col)?.type === DG.COLUMN_TYPE.STRING);
244
+ if (clusterCol) {
245
+ const lstProps: ILogoSummaryTable = {
246
+ clustersColumnName: clusterCol, sequenceColumnName: peptidesCol.name, activityScaling: scaling,
247
+ activityColumnName: activityColumn.name,
248
+ };
249
+ await model.addLogoSummaryTable(lstProps);
250
+ }
251
+ }
252
+ }
220
253
 
221
254
 
222
255
  progress.close();
@@ -9,7 +9,7 @@ import {PeptidesModel, VIEWER_TYPE} from '../model';
9
9
  import $ from 'cash-dom';
10
10
  import wu from 'wu';
11
11
  import {getTreeHelperInstance} from '../package';
12
- import { MmDistanceFunctionsNames as distFNames } from '@datagrok-libraries/ml/src/macromolecule-distance-functions';
12
+ import {MmDistanceFunctionsNames as distFNames} from '@datagrok-libraries/ml/src/macromolecule-distance-functions';
13
13
 
14
14
  type PaneInputs = { [paneName: string]: DG.InputBase[] };
15
15
  type SettingsElements = { dialog: DG.Dialog, accordion: DG.Accordion, inputs: PaneInputs };
@@ -156,7 +156,7 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
156
156
 
157
157
  // Sequence space pane options
158
158
  const modifiedSeqSpaceParams: Partial<type.SequenceSpaceParams> = {};
159
- function onSeqSpaceParamsChange(fieldName: keyof type.SequenceSpaceParams, value: any) {
159
+ function onSeqSpaceParamsChange(fieldName: keyof type.SequenceSpaceParams, value: any): void {
160
160
  correctSeqSpaceInputs();
161
161
  if (value === null || value === undefined || value === '')
162
162
  return;
@@ -174,7 +174,7 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
174
174
  result.sequenceSpaceParams = {...seqSpaceParams, ...modifiedSeqSpaceParams};
175
175
  }
176
176
 
177
- function toggleInputs(nwInputs: DG.InputBase[], condition: boolean) {
177
+ function toggleInputs(nwInputs: DG.InputBase[], condition: boolean): void {
178
178
  nwInputs.forEach((input) => {
179
179
  if (condition)
180
180
  input.root.style.display = 'flex';
@@ -184,29 +184,37 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
184
184
  }
185
185
 
186
186
  const distanceFunctionInput = ui.choiceInput(SEQUENCE_SPACE_INPUTS.DISTANCE_FUNCTION, seqSpaceParams.distanceF,
187
- [distFNames.NEEDLEMANN_WUNSCH, distFNames.HAMMING, distFNames.LEVENSHTEIN, distFNames.MONOMER_CHEMICAL_DISTANCE],
188
- () => onSeqSpaceParamsChange('distanceF', distanceFunctionInput.value));
187
+ [distFNames.NEEDLEMANN_WUNSCH, distFNames.HAMMING, distFNames.LEVENSHTEIN, distFNames.MONOMER_CHEMICAL_DISTANCE],
188
+ () => onSeqSpaceParamsChange('distanceF', distanceFunctionInput.value));
189
189
  distanceFunctionInput.setTooltip('Distance function');
190
- const gapOpenInput = ui.floatInput(SEQUENCE_SPACE_INPUTS.GAP_OPEN, seqSpaceParams.gapOpen, () => onSeqSpaceParamsChange('gapOpen', gapOpenInput.value));
191
- const gapExtendInput = ui.floatInput(SEQUENCE_SPACE_INPUTS.GAP_EXTEND, seqSpaceParams.gapExtend, () => onSeqSpaceParamsChange('gapExtend', gapExtendInput.value));
192
- const clusterEmbeddingsInput = ui.boolInput(SEQUENCE_SPACE_INPUTS.CLUSTER_EMBEDDINGS, seqSpaceParams.clusterEmbeddings ?? false,
193
- () => onSeqSpaceParamsChange('clusterEmbeddings', clusterEmbeddingsInput.value));
190
+ const gapOpenInput = ui.floatInput(SEQUENCE_SPACE_INPUTS.GAP_OPEN, seqSpaceParams.gapOpen,
191
+ () => onSeqSpaceParamsChange('gapOpen', gapOpenInput.value));
192
+ const gapExtendInput = ui.floatInput(SEQUENCE_SPACE_INPUTS.GAP_EXTEND, seqSpaceParams.gapExtend,
193
+ () => onSeqSpaceParamsChange('gapExtend', gapExtendInput.value));
194
+ const clusterEmbeddingsInput =
195
+ ui.boolInput(SEQUENCE_SPACE_INPUTS.CLUSTER_EMBEDDINGS, seqSpaceParams.clusterEmbeddings ?? false,
196
+ () => onSeqSpaceParamsChange('clusterEmbeddings', clusterEmbeddingsInput.value));
194
197
  clusterEmbeddingsInput.setTooltip('Cluster embeddings using DBSCAN algorithm');
195
- const epsilonInput = ui.floatInput(SEQUENCE_SPACE_INPUTS.EPSILON, seqSpaceParams.epsilon, () => onSeqSpaceParamsChange('epsilon', epsilonInput.value));
196
- epsilonInput.setTooltip('Epsilon parameter for DBSCAN. Minimum distance between two points to be considered as a cluster');
197
- const minPtsInput = ui.intInput(SEQUENCE_SPACE_INPUTS.MIN_PTS, seqSpaceParams.minPts, () => onSeqSpaceParamsChange('minPts', minPtsInput.value));
198
+ const epsilonInput = ui.floatInput(SEQUENCE_SPACE_INPUTS.EPSILON, seqSpaceParams.epsilon,
199
+ () => onSeqSpaceParamsChange('epsilon', epsilonInput.value));
200
+ epsilonInput.setTooltip(
201
+ 'Epsilon parameter for DBSCAN. Minimum distance between two points to be considered as a cluster');
202
+ const minPtsInput = ui.intInput(SEQUENCE_SPACE_INPUTS.MIN_PTS, seqSpaceParams.minPts,
203
+ () => onSeqSpaceParamsChange('minPts', minPtsInput.value));
198
204
  minPtsInput.setTooltip('Minimum number of points in a cluster');
199
- const fingerprintTypesInput = ui.choiceInput('Fingerprint type', seqSpaceParams.fingerprintType, ['Morgan', 'RDKit', 'Pattern'],
200
- () => onSeqSpaceParamsChange('fingerprintType', fingerprintTypesInput.value));
201
- function correctSeqSpaceInputs() {
205
+ const fingerprintTypesInput = ui.choiceInput('Fingerprint type', seqSpaceParams.fingerprintType,
206
+ ['Morgan', 'RDKit', 'Pattern'], () => onSeqSpaceParamsChange('fingerprintType', fingerprintTypesInput.value));
207
+ function correctSeqSpaceInputs(): void {
202
208
  toggleInputs([gapOpenInput, gapExtendInput], distanceFunctionInput.value === distFNames.NEEDLEMANN_WUNSCH);
203
209
  toggleInputs([epsilonInput, minPtsInput], clusterEmbeddingsInput.value === true);
204
210
  toggleInputs([fingerprintTypesInput],
205
- distanceFunctionInput.value === distFNames.MONOMER_CHEMICAL_DISTANCE || distanceFunctionInput.value === distFNames.NEEDLEMANN_WUNSCH);
211
+ distanceFunctionInput.value === distFNames.MONOMER_CHEMICAL_DISTANCE ||
212
+ distanceFunctionInput.value === distFNames.NEEDLEMANN_WUNSCH);
206
213
  }
207
214
  correctSeqSpaceInputs();
208
215
 
209
- const seqSpaceInputs = [distanceFunctionInput, fingerprintTypesInput, gapOpenInput, gapExtendInput, clusterEmbeddingsInput, epsilonInput, minPtsInput];
216
+ const seqSpaceInputs = [distanceFunctionInput, fingerprintTypesInput, gapOpenInput,
217
+ gapExtendInput, clusterEmbeddingsInput, epsilonInput, minPtsInput];
210
218
  accordion.addPane(SETTINGS_PANES.SEQUENCE_SPACE, () => ui.inputs(seqSpaceInputs), true);
211
219
  inputs[SETTINGS_PANES.SEQUENCE_SPACE] = seqSpaceInputs;
212
220
  const dialog = ui.dialog('Peptides settings').add(accordion);