@datagrok/peptides 1.9.2 → 1.11.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.
@@ -1,13 +1,14 @@
1
+ import * as grok from 'datagrok-api/grok';
1
2
  import * as DG from 'datagrok-api/dg';
2
3
 
3
- import {before, category, expect, test, testViewer} from '@datagrok-libraries/utils/src/test';
4
+ import {awaitCheck, before, category, expect, test, testViewer} from '@datagrok-libraries/utils/src/test';
4
5
  import {aligned1} from './test-data';
5
6
  import {PeptidesModel, VIEWER_TYPE} from '../model';
6
7
  import {_package} from '../package-test';
7
8
  import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
8
9
  import {scaleActivity} from '../utils/misc';
9
10
  import {startAnalysis} from '../widgets/peptides';
10
- import {MONOMER_POSITION_MODE, MonomerPosition, MostPotentResiduesViewer, showTooltip} from '../viewers/sar-viewer';
11
+ import {MONOMER_POSITION_MODE, MonomerPosition, MostPotentResidues, showTooltip} from '../viewers/sar-viewer';
11
12
  import {SCALING_METHODS} from '../utils/constants';
12
13
  import {LST_PROPERTIES, LogoSummaryTable} from '../viewers/logo-summary';
13
14
  import {PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
@@ -45,7 +46,18 @@ category('Viewers: Monomer-Position', () => {
45
46
  if (tempModel === null)
46
47
  throw new Error('Model is null');
47
48
  model = tempModel;
49
+ let overlayInit = false;
50
+ model._analysisView!.grid.onAfterDrawOverlay.subscribe(() => overlayInit = true);
51
+
48
52
  mpViewer = model.findViewer(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition;
53
+
54
+ // Ensure grid finished initializing to prevent Unhandled exceptions
55
+ let accrodionInit = false;
56
+ grok.events.onAccordionConstructed.subscribe((_) => accrodionInit = true);
57
+ await awaitCheck(() => model!.df.currentRowIdx === 0, 'Grid cell never finished initializing', 2000);
58
+ await awaitCheck(() => grok.shell.o instanceof DG.Column, 'Shell object never changed', 2000);
59
+ await awaitCheck(() => accrodionInit, 'Accordion never finished initializing', 2000);
60
+ await awaitCheck(() => overlayInit, 'Overlay never finished initializing', 2000);
49
61
  });
50
62
 
51
63
  test('Tooltip', async () => {
@@ -70,7 +82,7 @@ category('Viewers: Monomer-Position', () => {
70
82
  expect(mpViewer.mode, MONOMER_POSITION_MODE.MUTATION_CLIFFS,
71
83
  `Monomer-Position mode is not ${MONOMER_POSITION_MODE.MUTATION_CLIFFS} after switching`);
72
84
  });
73
- });
85
+ }, {clear: false});
74
86
 
75
87
  category('Viewers: Most Potent Residues', () => {
76
88
  let df: DG.DataFrame;
@@ -79,7 +91,7 @@ category('Viewers: Most Potent Residues', () => {
79
91
  let sequenceCol: DG.Column<string>;
80
92
  let clusterCol: DG.Column<any>;
81
93
  let scaledActivityCol: DG.Column<number>;
82
- let mprViewer: MostPotentResiduesViewer;
94
+ let mprViewer: MostPotentResidues;
83
95
 
84
96
  before(async () => {
85
97
  df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
@@ -94,7 +106,18 @@ category('Viewers: Most Potent Residues', () => {
94
106
  if (tempModel === null)
95
107
  throw new Error('Model is null');
96
108
  model = tempModel;
97
- mprViewer = model.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResiduesViewer;
109
+ let overlayInit = false;
110
+ model._analysisView!.grid.onAfterDrawOverlay.subscribe(() => overlayInit = true);
111
+
112
+ mprViewer = model.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues;
113
+
114
+ // Ensure grid finished initializing to prevent Unhandled exceptions
115
+ let accrodionInit = false;
116
+ grok.events.onAccordionConstructed.subscribe((_) => accrodionInit = true);
117
+ await awaitCheck(() => model!.df.currentRowIdx === 0, 'Grid cell never finished initializing', 2000);
118
+ await awaitCheck(() => grok.shell.o instanceof DG.Column, 'Shell object never changed', 2000);
119
+ await awaitCheck(() => accrodionInit, 'Accordion never finished initializing', 2000);
120
+ await awaitCheck(() => overlayInit, 'Overlay never finished initializing', 2000);
98
121
  });
99
122
 
100
123
  test('Tooltip', async () => {
@@ -127,7 +150,18 @@ category('Viewers: Logo Summary Table', () => {
127
150
  if (tempModel === null)
128
151
  throw new Error('Model is null');
129
152
  model = tempModel;
153
+ let overlayInit = false;
154
+ model._analysisView!.grid.onAfterDrawOverlay.subscribe(() => overlayInit = true);
155
+
130
156
  lstViewer = model.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable;
157
+
158
+ // Ensure grid finished initializing to prevent Unhandled exceptions
159
+ let accrodionInit = false;
160
+ grok.events.onAccordionConstructed.subscribe((_) => accrodionInit = true);
161
+ await awaitCheck(() => model!.df.currentRowIdx === 0, 'Grid cell never finished initializing', 2000);
162
+ await awaitCheck(() => grok.shell.o instanceof DG.Column, 'Shell object never changed', 2000);
163
+ await awaitCheck(() => accrodionInit, 'Accordion never finished initializing', 2000);
164
+ await awaitCheck(() => overlayInit, 'Overlay never finished initializing', 2000);
131
165
  });
132
166
 
133
167
  test('Properties', async () => {
@@ -152,4 +186,4 @@ category('Viewers: Logo Summary Table', () => {
152
186
  const tooltipElement = lstViewer.showTooltip(cluster, 0, 0);
153
187
  expect(tooltipElement !== null, true, `Tooltip is not shown for cluster '${cluster}'`);
154
188
  });
155
- });
189
+ }, {clear: false});
@@ -1,10 +1,10 @@
1
1
  import * as grok from 'datagrok-api/grok';
2
2
  import * as DG from 'datagrok-api/dg';
3
3
 
4
- import {category, test, before, expect, delay} from '@datagrok-libraries/utils/src/test';
4
+ import {category, test, before, expect, awaitCheck, expectFloat} from '@datagrok-libraries/utils/src/test';
5
5
  import {_package} from '../package-test';
6
6
  import {PeptidesModel, VIEWER_TYPE} from '../model';
7
- import {scaleActivity} from '../utils/misc';
7
+ import {getTemplate, scaleActivity} from '../utils/misc';
8
8
  import {startAnalysis} from '../widgets/peptides';
9
9
  import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
10
10
  import * as C from '../utils/constants';
@@ -14,6 +14,7 @@ import {mutationCliffsWidget} from '../widgets/mutation-cliffs';
14
14
  import {TEST_COLUMN_NAMES} from './utils';
15
15
  import wu from 'wu';
16
16
  import {LogoSummaryTable} from '../viewers/logo-summary';
17
+ import {calculateIdentity, calculateSimilarity} from '../widgets/similarity';
17
18
 
18
19
  category('Widgets: Settings', () => {
19
20
  let df: DG.DataFrame;
@@ -36,6 +37,16 @@ category('Widgets: Settings', () => {
36
37
  if (tempModel === null)
37
38
  throw new Error('Model is null');
38
39
  model = tempModel;
40
+ let overlayInit = false;
41
+ model._analysisView!.grid.onAfterDrawOverlay.subscribe(() => overlayInit = true);
42
+
43
+ // Ensure grid finished initializing to prevent Unhandled exceptions
44
+ let accrodionInit = false;
45
+ grok.events.onAccordionConstructed.subscribe((_) => accrodionInit = true);
46
+ await awaitCheck(() => model!.df.currentRowIdx === 0, 'Grid cell never finished initializing', 2000);
47
+ await awaitCheck(() => grok.shell.o instanceof DG.Column, 'Shell object never changed', 2000);
48
+ await awaitCheck(() => accrodionInit, 'Accordion never finished initializing', 2000);
49
+ await awaitCheck(() => overlayInit, 'Overlay never finished initializing', 2000);
39
50
  });
40
51
 
41
52
  test('UI', async () => {
@@ -77,15 +88,21 @@ category('Widgets: Distribution panel', () => {
77
88
  if (tempModel === null)
78
89
  throw new Error('Model is null');
79
90
  model = tempModel;
91
+ let overlayInit = false;
92
+ model._analysisView!.grid.onAfterDrawOverlay.subscribe(() => overlayInit = true);
93
+
94
+ // Ensure grid finished initializing to prevent Unhandled exceptions
95
+ let accrodionInit = false;
96
+ grok.events.onAccordionConstructed.subscribe((_) => accrodionInit = true);
97
+ await awaitCheck(() => model!.df.currentRowIdx === 0, 'Grid cell never finished initializing', 2000);
98
+ await awaitCheck(() => grok.shell.o instanceof DG.Column, 'Shell object never changed', 2000);
99
+ await awaitCheck(() => accrodionInit, 'Accordion never finished initializing', 2000);
100
+ await awaitCheck(() => overlayInit, 'Overlay never finished initializing', 2000);
80
101
  });
81
102
 
82
103
  test('UI', async () => {
83
104
  getDistributionWidget(model.df, model);
84
105
  });
85
-
86
- test('Split', async () => {
87
-
88
- }, {skipReason: 'Not implemented yet'});
89
106
  });
90
107
 
91
108
  category('Widgets: Mutation cliffs', () => {
@@ -109,19 +126,21 @@ category('Widgets: Mutation cliffs', () => {
109
126
  if (tempModel === null)
110
127
  throw new Error('Model is null');
111
128
  model = tempModel;
129
+ let overlayInit = false;
130
+ model._analysisView!.grid.onAfterDrawOverlay.subscribe(() => overlayInit = true);
131
+
132
+ // Ensure grid finished initializing to prevent Unhandled exceptions
133
+ let accrodionInit = false;
134
+ grok.events.onAccordionConstructed.subscribe((_) => accrodionInit = true);
135
+ await awaitCheck(() => model!.df.currentRowIdx === 0, 'Grid cell never finished initializing', 2000);
136
+ await awaitCheck(() => grok.shell.o instanceof DG.Column, 'Shell object never changed', 2000);
137
+ await awaitCheck(() => accrodionInit, 'Accordion never finished initializing', 2000);
138
+ await awaitCheck(() => overlayInit, 'Overlay never finished initializing', 2000);
112
139
  });
113
140
 
114
141
  test('UI', async () => {
115
142
  mutationCliffsWidget(model.df, model);
116
143
  });
117
-
118
- test('General', async () => {
119
-
120
- }, {skipReason: 'Not implemented yet'});
121
-
122
- test('Filtering', async () => {
123
-
124
- }, {skipReason: 'Not implemented yet'});
125
144
  });
126
145
 
127
146
  category('Widgets: Actions', () => {
@@ -145,6 +164,16 @@ category('Widgets: Actions', () => {
145
164
  if (tempModel === null)
146
165
  throw new Error('Model is null');
147
166
  model = tempModel;
167
+ let overlayInit = false;
168
+ model._analysisView!.grid.onAfterDrawOverlay.subscribe(() => overlayInit = true);
169
+
170
+ // Ensure grid finished initializing to prevent Unhandled exceptions
171
+ let accrodionInit = false;
172
+ grok.events.onAccordionConstructed.subscribe((_) => accrodionInit = true);
173
+ await awaitCheck(() => model!.df.currentRowIdx === 0, 'Grid cell never finished initializing', 2000);
174
+ await awaitCheck(() => grok.shell.o instanceof DG.Column, 'Shell object never changed', 2000);
175
+ await awaitCheck(() => accrodionInit, 'Accordion never finished initializing', 2000);
176
+ await awaitCheck(() => overlayInit, 'Overlay never finished initializing', 2000);
148
177
  });
149
178
 
150
179
  test('New view', async () => {
@@ -164,7 +193,7 @@ category('Widgets: Actions', () => {
164
193
  expect(currentTable.getTag(C.TAGS.UUID), newViewId, 'Current table is expected to have the same UUID as new view');
165
194
  expect(currentTable.rowCount, 1, 'Current table is expected to have 1 row');
166
195
 
167
- await delay(500);
196
+ await awaitCheck(() => currentTable.currentRowIdx === 0, 'Grid never finished initializing', 2000);
168
197
 
169
198
  const currentTableModel = currentTable.temp[PeptidesModel.modelName] as PeptidesModel;
170
199
  const lstViewer = currentTableModel.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE);
@@ -181,13 +210,15 @@ category('Widgets: Actions', () => {
181
210
  const selection = model.df.selection;
182
211
  selection.set(0, true, false);
183
212
 
184
- const lstViewer = model.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable;
213
+ const lstViewer = model.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
214
+ if (lstViewer === null)
215
+ throw new Error('Logo summary table viewer is not found');
185
216
 
186
217
  // Check that custom clusters are not created yet
187
218
  expect(wu(model.customClusters).toArray().length, 0, 'Expected to have 0 custom clusters before creating one');
188
219
 
189
220
  // Create custom cluster
190
- model._newClusterSubject.next();
221
+ lstViewer.clusterFromSelection();
191
222
  const customClusterList = wu(model.customClusters).toArray();
192
223
  expect(customClusterList.length, 1, 'Expected to have 1 custom cluster');
193
224
  const clustName = customClusterList[0].name;
@@ -198,11 +229,62 @@ category('Widgets: Actions', () => {
198
229
 
199
230
  // Remove custom cluster
200
231
  model.modifyClusterSelection(clustName);
201
- model._removeClusterSubject.next();
232
+ lstViewer.removeCluster();
202
233
  expect(wu(model.customClusters).toArray().length, 0, 'Expected to have 0 custom clusters after removing one');
203
234
  expect(model.df.col(clustName) === null, true,
204
235
  'Expected to have no custom cluster column in the table');
205
236
  expect(lstViewer.viewerGrid.table.getCol(C.LST_COLUMN_NAMES.CLUSTER).categories.indexOf(clustName) === -1, true,
206
237
  'Expected to have no custom cluster in the Logo Summary Table');
207
238
  });
239
+ }, {clear: false});
240
+
241
+
242
+ category('Widgets: Identity', () => {
243
+ let df: DG.DataFrame;
244
+ let model: PeptidesModel;
245
+ let activityCol: DG.Column<number>;
246
+ let sequenceCol: DG.Column<string>;
247
+ let clusterCol: DG.Column<any>;
248
+ let scaledActivityCol: DG.Column<number>;
249
+
250
+ before(async () => {
251
+ df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
252
+ activityCol = df.getCol(TEST_COLUMN_NAMES.ACTIVITY);
253
+ sequenceCol = df.getCol(TEST_COLUMN_NAMES.SEQUENCE);
254
+ sequenceCol.semType = DG.SEMTYPE.MACROMOLECULE;
255
+ sequenceCol.setTag(DG.TAGS.UNITS, NOTATION.HELM);
256
+ scaledActivityCol = scaleActivity(activityCol, C.SCALING_METHODS.NONE);
257
+ clusterCol = df.getCol(TEST_COLUMN_NAMES.CLUSTER);
258
+ const tempModel = await startAnalysis(activityCol, sequenceCol, clusterCol, df, scaledActivityCol,
259
+ C.SCALING_METHODS.NONE);
260
+ if (tempModel === null)
261
+ throw new Error('Model is null');
262
+ model = tempModel;
263
+ let overlayInit = false;
264
+ model._analysisView!.grid.onAfterDrawOverlay.subscribe(() => overlayInit = true);
265
+
266
+ // Ensure grid finished initializing to prevent Unhandled exceptions
267
+ let accrodionInit = false;
268
+ grok.events.onAccordionConstructed.subscribe((_) => accrodionInit = true);
269
+ await awaitCheck(() => model!.df.currentRowIdx === 0, 'Grid cell never finished initializing', 2000);
270
+ await awaitCheck(() => grok.shell.o instanceof DG.Column, 'Shell object never changed', 2000);
271
+ await awaitCheck(() => accrodionInit, 'Accordion never finished initializing', 2000);
272
+ await awaitCheck(() => overlayInit, 'Overlay never finished initializing', 2000);
273
+ });
274
+
275
+ test('Identity', async () => {
276
+ const seq = 'PEPTIDE1{meI.hHis.Aca.N.T.dE.Thr_PO3H2.Aca.D-Tyr_Et.Tyr_ab-dehydroMe.dV.E.N.D-Orn.D-aThr.Phe_4Me}$$$$';
277
+ const template = await getTemplate(seq);
278
+ const identityCol = calculateIdentity(template, model.splitSeqDf);
279
+ expect(identityCol.get(0), 1, 'Expected 1 identity score when sequence is matching template');
280
+ expectFloat(identityCol.get(3)!, 0.5625, 0.01, 'Expected 0.5625 identity score agains sequence at position 3');
281
+ });
282
+
283
+ test('Similarity', async () => {
284
+ const seq = 'PEPTIDE1{meI.hHis.Aca.N.T.dE.Thr_PO3H2.Aca.D-Tyr_Et.Tyr_ab-dehydroMe.dV.E.N.D-Orn.D-aThr.Phe_4Me}$$$$';
285
+ const template = await getTemplate(seq);
286
+ const identityCol = await calculateSimilarity(template, model.splitSeqDf);
287
+ expect(identityCol.get(0), 1, 'Expected 1 identity score when sequence is matching template');
288
+ expectFloat(identityCol.get(3)!, 0, 0.001, 'Expected 7 identity score agains sequence at position 3');
289
+ })
208
290
  });
@@ -6,7 +6,7 @@ import {PositionStats, MonomerPositionStats} from '../model';
6
6
  import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
7
7
  import {monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
8
8
 
9
- function renderCellSelection(canvasContext: CanvasRenderingContext2D, bound: DG.Rect): void {
9
+ export function renderCellSelection(canvasContext: CanvasRenderingContext2D, bound: DG.Rect): void {
10
10
  canvasContext.strokeStyle = '#000';
11
11
  canvasContext.lineWidth = 1;
12
12
  canvasContext.strokeRect(bound.x + 1, bound.y + 1, bound.width - 1, bound.height - 1);
@@ -22,7 +22,7 @@ export function setAARRenderer(col: DG.Column, alphabet: string): void {
22
22
  export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D, currentAAR: string,
23
23
  currentPosition: string, monomerPositionStats: MonomerPositionStats, bound: DG.Rect,
24
24
  mutationCliffsSelection: types.PositionToAARList, substitutionsInfo: types.MutationCliffs | null = null,
25
- twoColorMode: boolean = false): void {
25
+ twoColorMode: boolean = false, renderNums: boolean = true): void {
26
26
  const positionStats = monomerPositionStats[currentPosition];
27
27
  const pVal: number = positionStats[currentAAR].pValue;
28
28
  const currentMeanDiff = positionStats[currentAAR].meanDifference;
@@ -56,19 +56,27 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
56
56
  canvasContext.fillStyle = coef;
57
57
  canvasContext.arc(midX, midY, radius < 3 ? 3 : radius, 0, Math.PI * 2, true);
58
58
  canvasContext.closePath();
59
-
60
59
  canvasContext.fill();
61
- if (substitutionsInfo !== null && substitutionsInfo.size > 0) {
62
- canvasContext.textBaseline = 'middle';
63
- canvasContext.textAlign = 'center';
64
- canvasContext.fillStyle = DG.Color.toHtml(DG.Color.black);
65
- canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
66
- canvasContext.shadowBlur = 5;
67
- canvasContext.shadowColor = DG.Color.toHtml(DG.Color.white);
68
- let substValue = 0;
69
- substitutionsInfo.get(currentAAR)?.get(currentPosition)?.forEach((idxs) => substValue += idxs.length);
70
- if (substValue && substValue !== 0)
71
- canvasContext.fillText(substValue.toString(), midX, midY);
60
+
61
+ if (renderNums) {
62
+ const substitutions = substitutionsInfo?.get(currentAAR)?.get(currentPosition)?.entries() ?? null;
63
+ if (substitutions !== null) {
64
+ canvasContext.textBaseline = 'middle';
65
+ canvasContext.textAlign = 'center';
66
+ canvasContext.fillStyle = DG.Color.toHtml(DG.Color.black);
67
+ canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
68
+ canvasContext.shadowBlur = 5;
69
+ canvasContext.shadowColor = DG.Color.toHtml(DG.Color.white);
70
+ const uniqueValues = new Set<number>();
71
+
72
+ for (const [key, value] of substitutions) {
73
+ uniqueValues.add(key);
74
+ for (const val of value)
75
+ uniqueValues.add(val);
76
+ }
77
+ if (uniqueValues.size !== 0)
78
+ canvasContext.fillText(uniqueValues.size.toString(), midX, midY);
79
+ }
72
80
  }
73
81
 
74
82
  const aarSelection = mutationCliffsSelection[currentPosition];
@@ -105,18 +113,20 @@ export function renderLogoSummaryCell(canvasContext: CanvasRenderingContext2D, c
105
113
  }
106
114
 
107
115
 
108
- export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect, stats: PositionStats,
116
+ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect, stats: PositionStats, position: string,
109
117
  sortedOrder: string[], rowCount: number, cp: SeqPalette, monomerSelectionStats: { [monomer: string]: number } = {},
110
118
  drawOptions: types.DrawOptions = {}): { [monomer: string]: DG.Rect } {
111
- drawOptions.fontStyle ??= '16px Roboto, Roboto Local, sans-serif';
119
+ const pr = window.devicePixelRatio;
120
+ drawOptions.symbolStyle ??= '16px Roboto, Roboto Local, sans-serif';
112
121
  drawOptions.upperLetterHeight ??= 12.2;
113
122
  drawOptions.upperLetterAscent ??= 0.25;
114
123
  drawOptions.marginVertical ??= 5;
115
124
  drawOptions.marginHorizontal ??= 5;
125
+ drawOptions.textHeight ??= 13;
126
+ drawOptions.headerStyle ??= `bold ${drawOptions.textHeight * pr}px Roboto, Roboto Local, sans-serif`;
116
127
 
117
- const pr = window.devicePixelRatio;
118
- const totalSpaceBetweenLetters = (sortedOrder.length - 1) * drawOptions.upperLetterAscent;
119
- const barHeight = (bounds.height - 2 * drawOptions.marginVertical - totalSpaceBetweenLetters) * pr;
128
+ const totalSpace = (sortedOrder.length - 1) * drawOptions.upperLetterAscent; // Total space between letters
129
+ const barHeight = (bounds.height - 2 * drawOptions.marginVertical - totalSpace - 1.25 * drawOptions.textHeight) * pr;
120
130
  const leftShift = drawOptions.marginHorizontal * 2;
121
131
  const barWidth = (bounds.width - leftShift * 2) * pr;
122
132
  const xStart = (bounds.x + leftShift) * pr;
@@ -143,7 +153,7 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
143
153
  ctx.fillStyle = cp.get(monomer) ?? cp.get('other');
144
154
  ctx.textAlign = 'left';
145
155
  ctx.textBaseline = 'top';
146
- ctx.font = drawOptions.fontStyle;
156
+ ctx.font = drawOptions.symbolStyle;
147
157
  // Hacks to scale uppercase characters to target rectangle
148
158
  const widthTransform = barWidth / mTm.width;
149
159
  const heightTransfrom = monomerHeight / drawOptions.upperLetterHeight;
@@ -153,5 +163,13 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
153
163
  currentY += monomerHeight + drawOptions.upperLetterAscent * pr;
154
164
  }
155
165
 
166
+ // Drawing column header
167
+ ctx.resetTransform();
168
+ ctx.fillStyle = DG.Color.toHtml(DG.Color.black);
169
+ ctx.textAlign = 'center';
170
+ ctx.textBaseline = 'top';
171
+ ctx.font = drawOptions.headerStyle;
172
+ ctx.fillText(position, (bounds.x + bounds.width / 2) * pr, (bounds.y + bounds.height - drawOptions.textHeight) * pr);
173
+
156
174
  return monomerBounds;
157
175
  }
@@ -31,6 +31,7 @@ export enum TAGS {
31
31
  SELECTION = 'selection',
32
32
  ALPHABET = 'alphabet',
33
33
  FILTER = 'filter',
34
+ INVARIANT_MAP_SELECTION = 'invariantMapSelection',
34
35
  SAR_MODE = 'sarMode',
35
36
  CLUSTER_SELECTION = 'clusterSelection',
36
37
  VISIBLE = 'visible',
@@ -39,6 +40,8 @@ export enum TAGS {
39
40
  UUID = 'pep-uuid',
40
41
  MONOMER_POSITION_MODE = 'monomerPositionMode',
41
42
  MULTIPLE_VIEWS = 'isMultipleViews',
43
+ IDENTITY_TEMPLATE = 'Identity template',
44
+ SIMILARITY_TEMPLATE = 'Similarity template',
42
45
  }
43
46
 
44
47
  export enum SEM_TYPES {
package/src/utils/misc.ts CHANGED
@@ -1,8 +1,11 @@
1
- import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
1
+ import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
  import * as C from './constants';
5
5
  import * as type from './types';
6
+ import {getSplitterForColumn} from '@datagrok-libraries/bio/src/utils/macromolecule';
7
+ import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
8
+ import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
6
9
 
7
10
  export function getTypedArrayConstructor(
8
11
  maxNum: number): Uint8ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor {
@@ -30,10 +33,11 @@ export function scaleActivity(activityCol: DG.Column<number>, scaling: C.SCALING
30
33
  default:
31
34
  throw new Error(`ScalingError: method \`${scaling}\` is not available.`);
32
35
  }
36
+ const activityColData = activityCol.getRawData();
33
37
  const scaledCol: DG.Column<number> = DG.Column.float(C.COLUMNS_NAMES.ACTIVITY_SCALED, activityCol.length)
34
38
  .init((i) => {
35
- const val = activityCol.get(i);
36
- return val ? formula(val) : val;
39
+ const val = activityColData[i];
40
+ return val === DG.FLOAT_NULL || val === DG.INT_NULL ? val : formula(val);
37
41
  });
38
42
 
39
43
  return scaledCol;
@@ -58,11 +62,6 @@ export function calculateSelected(df: DG.DataFrame): type.MonomerSelectionStats
58
62
  return selectedObj;
59
63
  }
60
64
 
61
- // export function isGridCellInvalid(gc: DG.GridCell | null): boolean {
62
- // return !gc || !gc.cell.value || !gc.tableColumn || gc.tableRowIndex === null || gc.tableRowIndex === -1 ||
63
- // gc.cell.value === DG.INT_NULL || gc.cell.value === DG.FLOAT_NULL;
64
- // }
65
-
66
65
  export function extractColInfo(col: DG.Column<string>): type.RawColumn {
67
66
  return {
68
67
  name: col.name,
@@ -72,10 +71,43 @@ export function extractColInfo(col: DG.Column<string>): type.RawColumn {
72
71
  }
73
72
 
74
73
  export function getStatsSummary(legend: HTMLDivElement, hist: DG.Viewer<DG.IHistogramLookSettings>,
75
- statsMap: StringDictionary, isTooltip: boolean = false): HTMLDivElement {
74
+ statsMap: StringDictionary): HTMLDivElement {
76
75
  const result = ui.divV([legend, hist.root, ui.tableFromMap(statsMap)]);
77
- result.style.minWidth = '200px';
78
- if (isTooltip)
79
- hist.root.style.maxHeight = '150px';
76
+ hist.root.style.maxHeight = '75px';
80
77
  return result;
81
78
  }
79
+
80
+ export function prepareTableForHistogram(table: DG.DataFrame): DG.DataFrame {
81
+ const activityCol: DG.Column<number> = table.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
82
+ const splitCol: DG.Column<boolean> = table.getCol(C.COLUMNS_NAMES.SPLIT_COL);
83
+
84
+ const rowCount = activityCol.length;
85
+ const activityColData = activityCol.getRawData();
86
+ const expandedData: number[] = new Array(rowCount + splitCol.stats.sum);
87
+ const expandedMasks: boolean[] = new Array(expandedData.length);
88
+ for (let i = 0, j = 0; i < rowCount; ++i) {
89
+ const isSplit = splitCol.get(i)!;
90
+ expandedData[i] = activityColData[i];
91
+ expandedMasks[i] = isSplit;
92
+ if (isSplit) {
93
+ expandedData[rowCount + j] = activityColData[i];
94
+ expandedMasks[rowCount + j] = false;
95
+ ++j;
96
+ }
97
+ }
98
+
99
+ return DG.DataFrame.fromColumns([
100
+ DG.Column.fromList(DG.TYPE.FLOAT, activityCol.name, expandedData),
101
+ DG.Column.fromList(DG.TYPE.BOOL, C.COLUMNS_NAMES.SPLIT_COL, expandedMasks),
102
+ ]);
103
+ }
104
+
105
+ export async function getTemplate(sequence: string, seqCol?: DG.Column<string>): Promise<ISeqSplitted> {
106
+ if (typeof seqCol === 'undefined') {
107
+ const tempDf = DG.DataFrame.fromCsv(`sequence\n${new Array(10).fill(sequence).join('\n')}`);
108
+ await grok.data.detectSemanticTypes(tempDf);
109
+ seqCol = tempDf.getCol('sequence');
110
+ }
111
+ const splitter = getSplitterForColumn(seqCol);
112
+ return splitter(sequence);
113
+ }
@@ -4,12 +4,11 @@ import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {getSequenceMolecularWeight} from './molecular-measure';
6
6
  import {AlignedSequenceEncoder} from '@datagrok-libraries/bio/src/sequence-encoder';
7
- import {DimensionalityReducer} from '@datagrok-libraries/ml/src/reduce-dimensionality';
7
+ import {DimensionalityReducer, IReduceDimensionalityResult} from '@datagrok-libraries/ml/src/reduce-dimensionality';
8
8
  import {
9
9
  createDimensinalityReducingWorker,
10
- IReduceDimensionalityResult,
11
10
  } from '@datagrok-libraries/ml/src/workers/dimensionality-reducing-worker-creator';
12
- import {Measure, StringMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
11
+ import {DistanceMetricsSubjects, Measure, StringMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
13
12
  import {Coordinates} from '@datagrok-libraries/utils/src/type-declarations';
14
13
 
15
14
  /**
@@ -115,7 +114,7 @@ export class PeptideSimilaritySpaceWidget {
115
114
  */
116
115
  constructor(alignedSequencesColumn: DG.Column, view: DG.TableView) {
117
116
  this.availableMethods = DimensionalityReducer.availableMethods;
118
- this.availableMetrics = Measure.getMetricByDataType('String');
117
+ this.availableMetrics = Measure.getMetricByDataType(DistanceMetricsSubjects.String);
119
118
  this.method = this.availableMethods[0];
120
119
  this.metrics = this.availableMetrics[0];
121
120
  const df = alignedSequencesColumn.dataFrame;
@@ -16,7 +16,7 @@ export function getStats(data: RawData | number[], bitArray: BitArray): Stats {
16
16
 
17
17
  let selectedIndex = 0;
18
18
  let restIndex = 0;
19
- for (let i = 0; i < data.length; ++i) {
19
+ for (let i = 0; i < bitArray.length; ++i) {
20
20
  if (bitArray.getBit(i))
21
21
  selected[selectedIndex++] = data[i];
22
22
  else
@@ -29,7 +29,7 @@ export function getStats(data: RawData | number[], bitArray: BitArray): Stats {
29
29
  count: selected.length,
30
30
  pValue: testResult[currentMeanDiff >= 0 ? 'p-value more' : 'p-value less'] || 0,
31
31
  meanDifference: currentMeanDiff || 0,
32
- ratio: selected.length / data.length,
32
+ ratio: selected.length / (bitArray.length),
33
33
  };
34
34
  }
35
35
 
@@ -28,7 +28,7 @@ export type PeptidesSettings = {
28
28
  };
29
29
 
30
30
  export type DrawOptions = {
31
- fontStyle?: string,
31
+ symbolStyle?: string,
32
32
  upperLetterHeight?: number,
33
33
  upperLetterAscent?: number,
34
34
  bounds?: DG.Rect,
@@ -36,6 +36,8 @@ export type DrawOptions = {
36
36
  textBaseline?: CanvasTextBaseline,
37
37
  marginVertical?: number,
38
38
  marginHorizontal?: number,
39
+ headerStyle?: string,
40
+ textHeight?: number,
39
41
  };
40
42
 
41
43
  export type StatsInfo = {