@datagrok/peptides 1.21.3 → 1.21.5

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/peptides",
3
3
  "friendlyName": "Peptides",
4
- "version": "1.21.3",
4
+ "version": "1.21.5",
5
5
  "author": {
6
6
  "name": "Davit Rizhinashvili",
7
7
  "email": "drizhinashvili@datagrok.ai"
package/src/model.ts CHANGED
@@ -150,7 +150,7 @@ export class PeptidesModel {
150
150
  this._analysisView = grok.shell.addTableView(this.df);
151
151
  }
152
152
 
153
- if (this.df.getTag(C.TAGS.MULTIPLE_VIEWS) !== '1' && !this._layoutEventInitialized && !grok.shell.isInDemo)
153
+ if (this.df.getTag(C.TAGS.MULTIPLE_VIEWS) !== '1' && !this._layoutEventInitialized)
154
154
  grok.shell.v = this._analysisView;
155
155
 
156
156
 
package/src/package.ts CHANGED
@@ -37,6 +37,7 @@ export async function initPeptides(): Promise<void> {
37
37
  monomerWorks ??= new MonomerWorks(await grok.functions.call('Bio:getBioLib'));
38
38
  treeHelper ??= await getTreeHelper();
39
39
  await PeptideUtils.loadSeqHelper();
40
+ await PeptideUtils.loadMonomerLib();
40
41
  } catch (e) {
41
42
  grok.log.error(e as string);
42
43
  }
@@ -1,7 +1,15 @@
1
+ import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
2
+ import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
1
3
  import {getSeqHelper, ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
2
4
 
3
5
  export class PeptideUtils {
4
6
  private static _secHelper: ISeqHelper;
7
+ private static _monomerLibHelper: IMonomerLibHelper;
8
+ public static getMonomerLib(): IMonomerLib {
9
+ if (!this._monomerLibHelper)
10
+ throw new Error('MonomerLib is not initialized');
11
+ return this._monomerLibHelper.getMonomerLib();
12
+ }
5
13
  public static getSeqHelper(): ISeqHelper {
6
14
  if (!this._secHelper)
7
15
  throw new Error('SeqHelper is not initialized');
@@ -11,4 +19,9 @@ export class PeptideUtils {
11
19
  public static async loadSeqHelper(): Promise<void> {
12
20
  this._secHelper ??= await getSeqHelper();
13
21
  }
22
+
23
+ public static async loadMonomerLib(): Promise<void> {
24
+ this._monomerLibHelper ??= await getMonomerLibHelper();
25
+ await this._monomerLibHelper.awaitLoaded();
26
+ }
14
27
  }
@@ -13,6 +13,9 @@ import {MonomerPositionStats, MonomerPositionStatsCache, PositionStats} from './
13
13
  import {CLUSTER_TYPE} from '../viewers/logo-summary';
14
14
  import {MonomerPosition, MostPotentResidues, SARViewer} from '../viewers/sar-viewer';
15
15
  import {MONOMER_RENDERER_TAGS} from '@datagrok-libraries/bio/src/utils/cell-renderer';
16
+ import { getMonomerLibHelper } from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
17
+ import { PeptideUtils } from '../peptideUtils';
18
+ import { HelmTypes } from '@datagrok-libraries/bio/src/helm/consts';
16
19
 
17
20
  /**
18
21
  * Renders cell selection border.
@@ -51,7 +54,7 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
51
54
  const halfWidth = bounds.width / 2;
52
55
  const midX = Math.ceil(bounds.x + 1 + halfWidth);
53
56
  const midY = Math.ceil(bounds.y + 1 + bounds.height / 2);
54
- const maxRadius = 0.9 * halfWidth / 2; // Fill at most 90% of the half of the cell width
57
+ const maxRadius = Math.min(0.9 * halfWidth / 2, 0.9 * bounds.height / 2); // Fill at most 90% of the half of the cell width
55
58
  // render most potent residues cells according to the p-value (color) and mean difference (size)
56
59
  if (viewer instanceof MostPotentResidues) {
57
60
  const positionStats = viewer.monomerPositionStats[currentPosition];
@@ -230,6 +233,7 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
230
233
  const barWidth = (bounds.width - (leftShift + drawOptions.marginHorizontal)) * pr;
231
234
  const xStart = (bounds.x + leftShift) * pr;
232
235
 
236
+ const monomerLib = PeptideUtils.getMonomerLib();
233
237
  const monomerBounds: { [monomer: string]: DG.Rect } = {};
234
238
  for (const monomer of sortedOrder) {
235
239
  const monomerHeight = barHeight * (stats[monomer]!.count / rowCount);
@@ -246,8 +250,8 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
246
250
  ctx.lineWidth = selectionWidth;
247
251
  ctx.line(xSelection, currentY, xSelection, currentY + selectionHeight, DG.Color.rowSelection);
248
252
  }
249
-
250
- ctx.fillStyle = cp.get(monomer) ?? cp.get('other');
253
+ const monomerColor = monomerLib.getMonomerTextColor(HelmTypes.AA, monomer);
254
+ ctx.fillStyle = monomerColor;
251
255
  ctx.textAlign = 'left';
252
256
  ctx.textBaseline = 'top';
253
257
  ctx.font = drawOptions.symbolStyle;
@@ -318,6 +322,7 @@ export function setWebLogoRenderer(grid: DG.Grid, monomerPositionStats: MonomerP
318
322
  ctx.save();
319
323
  try {
320
324
  ctx.beginPath();
325
+ ctx.clearRect(bounds.x, bounds.y, bounds.width, bounds.height);
321
326
  ctx.rect(bounds.x, bounds.y, bounds.width, bounds.height);
322
327
  ctx.clip();
323
328
 
@@ -427,7 +427,7 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
427
427
 
428
428
  private resetTargetCategoryValue(): void {
429
429
  const colName = this.targetColumnName;
430
- const col = this.dataFrame.col(colName);
430
+ const col = colName ? this.dataFrame.col(colName) : null;
431
431
  this.targetCategoryInput.items = col?.categories ?? [];
432
432
  this.targetCategoryInput.value = null;
433
433
  if (!colName)
@@ -676,11 +676,11 @@ export class MonomerPosition extends SARViewer {
676
676
  if (isApplicableDataframe(this.dataFrame)) {
677
677
  this.getProperty(`${MONOMER_POSITION_PROPERTIES.COLOR}${COLUMN_NAME}`)
678
678
  ?.set(this, this.activityColumnName);
679
- this.targetColumnInput = ui.input.column('Target', {value: undefined, nullable: true, table: this.dataFrame,
679
+ this.targetColumnInput = ui.input.column('Target', {value: undefined, nullable: true, table: this.dataFrame, filter: (col: DG.Column) => col.isCategorical,
680
680
  onValueChanged: (value) => {
681
681
  const prop = this.getProperty(`${SAR_PROPERTIES.TARGET}${COLUMN_NAME}`);
682
- if (prop && prop.get(this) !== value.name)
683
- prop?.set(this, value.name ?? null);
682
+ if (prop && prop.get(this) != (value?.name ?? null))
683
+ prop?.set(this, value?.name ?? null);
684
684
  },
685
685
  });
686
686
  } else {
@@ -1338,6 +1338,7 @@ export class MostPotentResidues extends SARViewer {
1338
1338
  const afterDraw = grid.onAfterDrawContent.subscribe(() => {
1339
1339
  const monomerGCol = grid.col(C.COLUMNS_NAMES.MONOMER)!;
1340
1340
  if (monomerGCol.width === AAR_CELL_WIDTH) {
1341
+ mdCol.width = MUTATION_CLIFFS_CELL_WIDTH;
1341
1342
  afterDraw.unsubscribe();
1342
1343
  return;
1343
1344
  }
@@ -34,20 +34,25 @@ export function mutationCliffsWidget(
34
34
  addExpandIconGen('Mutation Cliffs pairs', aminoToInput.root, widgetRoot,
35
35
  () => {
36
36
  const parts = cliffsPairsWidgetParts(table, options);
37
- const div = ui.divV([parts!.aminoToInput.root, parts!.pairsGrid.root, parts!.uniqueSequencesGrid.root],
38
- {style: {width: '100%', height: '100%'}});
37
+ const div = ui.splitH(
38
+ [ui.divV([parts!.aminoToInput.root, parts!.pairsGrid.root], {style: {marginRight: '10px'}}),
39
+ ui.divV([parts!.uniqueSequencesGrid.root], {style: {marginLeft: '10px'}})],
40
+ {style: {width: '100%', height: '100%'}}, true);
39
41
 
40
42
  setTimeout(() => {
41
43
  if (document.contains(div)) {
42
44
  const dW = div.offsetWidth;
43
45
  const pairsGridParent = parts!.pairsGrid.canvas?.parentElement;
46
+ parts!.pairsGrid.props.showRowHeader = true;
47
+ parts!.uniqueSequencesGrid.props.showRowHeader = true;
44
48
  const uniqueSequencesGridParent = parts!.uniqueSequencesGrid.canvas?.parentElement;
45
49
  if (pairsGridParent && uniqueSequencesGridParent) {
46
50
  pairsGridParent.style.height = '100%';
47
51
  uniqueSequencesGridParent.style.height = '100%';
48
52
  uniqueSequencesGridParent.style.marginTop = '20px';
49
- if (dW > 200) {
50
- const macroMolWidth = Math.max(dW * 0.5, 200);
53
+ if (dW > 400) {
54
+ const macroMolWidth = Math.max(dW * 0.33, 200);
55
+
51
56
  const mutationGridCol = parts!.pairsGrid.columns.byName('Mutation');
52
57
  if (mutationGridCol)
53
58
  mutationGridCol.width = macroMolWidth;
@@ -79,6 +84,7 @@ function cliffsPairsWidgetParts(table: DG.DataFrame, options: MutationCliffsOpti
79
84
  const substitutionsArray: string[] = [];
80
85
  const deltaArray: number[] = [];
81
86
  const substitutedToArray: string[] = [];
87
+ const mutationsGroupsArray: string[] = [];
82
88
  const fromIdxArray: number[] = [];
83
89
  const toIdxArray: number[] = [];
84
90
  const alignedSeqCol = table.getCol(options.sequenceColumnName!);
@@ -89,6 +95,9 @@ function cliffsPairsWidgetParts(table: DG.DataFrame, options: MutationCliffsOpti
89
95
  const seenIndexes = new Map<number, number[]>();
90
96
  const uniqueSequencesBitSet = DG.BitSet.create(table.rowCount);
91
97
 
98
+ // one mutation position and monomer can contain multiple groups of substitutions
99
+ const mutationsGroupMap: Record<number, number> = {};
100
+
92
101
  const positionColumns: { [colName: string]: DG.Column<string> } =
93
102
  Object.fromEntries(options.positionColumns.map((col) => [col.name, col]));
94
103
  for (const pos of positions) {
@@ -106,7 +115,9 @@ function cliffsPairsWidgetParts(table: DG.DataFrame, options: MutationCliffsOpti
106
115
  for (const [referenceIdx, indexArray] of substitutionsMap.entries()) {
107
116
  if (!filteredIndexes.includes(referenceIdx))
108
117
  continue;
109
-
118
+ if (!mutationsGroupMap[referenceIdx])
119
+ mutationsGroupMap[referenceIdx] = Object.keys(mutationsGroupMap).length + 1;
120
+ const group = mutationsGroupMap[referenceIdx];
110
121
 
111
122
  const forbiddentIndexes = seenIndexes.get(referenceIdx) ?? [];
112
123
  const baseSequence = alignedSeqColCategories[alignedSeqColData[referenceIdx]];
@@ -125,6 +136,7 @@ function cliffsPairsWidgetParts(table: DG.DataFrame, options: MutationCliffsOpti
125
136
 
126
137
  seenIndexes.get(subIdx)!.push(referenceIdx);
127
138
  substitutionsArray.push(`${baseSequence}#${subSeq}`);
139
+ mutationsGroupsArray.push(`${group}`);
128
140
  deltaArray.push(baseActivity - activityScaledColData[subIdx]);
129
141
  substitutedToArray.push(posColCategories[posColData[subIdx]]);
130
142
  fromIdxArray.push(referenceIdx);
@@ -142,10 +154,12 @@ function cliffsPairsWidgetParts(table: DG.DataFrame, options: MutationCliffsOpti
142
154
 
143
155
  const substCol = DG.Column.fromStrings('Mutation', substitutionsArray);
144
156
  const activityDeltaCol = DG.Column.fromList('double', 'Delta', deltaArray);
157
+ const mutationGroupCol = DG.Column.fromStrings('Group', mutationsGroupsArray);
145
158
  const hiddenSubstToAarCol = DG.Column.fromStrings('~to', substitutedToArray);
146
159
  const toIdxCol = DG.Column.fromList(DG.COLUMN_TYPE.INT, '~toIdx', toIdxArray);
147
160
  const fromIdxCol = DG.Column.fromList(DG.COLUMN_TYPE.INT, '~fromIdx', fromIdxArray);
148
- const pairsTable = DG.DataFrame.fromColumns([substCol, activityDeltaCol, hiddenSubstToAarCol, toIdxCol, fromIdxCol]);
161
+ const pairsTable =
162
+ DG.DataFrame.fromColumns([substCol, activityDeltaCol, mutationGroupCol, hiddenSubstToAarCol, toIdxCol, fromIdxCol]);
149
163
  pairsTable.name = 'Mutation Cliff pairs';
150
164
 
151
165
  const aminoToInput = ui.input.string('Mutated to:', {value: '', onValueChanged: (value) => {