@datagrok/peptides 0.8.9 → 0.8.13

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.
Files changed (39) hide show
  1. package/.eslintrc.json +2 -1
  2. package/dist/package-test.js +22626 -0
  3. package/dist/package.js +21429 -0
  4. package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +8840 -0
  5. package/jest.config.js +33 -0
  6. package/package.json +75 -62
  7. package/src/__jest__/remote.test.ts +50 -0
  8. package/src/__jest__/test-node.ts +96 -0
  9. package/src/model.ts +950 -86
  10. package/src/monomer-library.ts +8 -0
  11. package/src/package-test.ts +3 -2
  12. package/src/package.ts +57 -22
  13. package/src/peptides.ts +165 -119
  14. package/src/styles.css +8 -0
  15. package/src/tests/peptides-tests.ts +17 -78
  16. package/src/tests/utils.ts +1 -7
  17. package/src/utils/SAR-multiple-filter.ts +439 -0
  18. package/src/utils/SAR-multiple-selection.ts +177 -0
  19. package/src/utils/cell-renderer.ts +49 -50
  20. package/src/utils/chem-palette.ts +61 -163
  21. package/src/utils/constants.ts +56 -0
  22. package/src/utils/filtering-statistics.ts +62 -0
  23. package/src/utils/multiple-sequence-alignment.ts +33 -2
  24. package/src/utils/multivariate-analysis.ts +79 -0
  25. package/src/utils/peptide-similarity-space.ts +12 -31
  26. package/src/utils/types.ts +10 -0
  27. package/src/viewers/logo-viewer.ts +2 -1
  28. package/src/viewers/peptide-space-viewer.ts +121 -0
  29. package/src/viewers/sar-viewer.ts +111 -313
  30. package/src/viewers/stacked-barchart-viewer.ts +126 -173
  31. package/src/widgets/analyze-peptides.ts +39 -18
  32. package/src/widgets/distribution.ts +61 -0
  33. package/src/widgets/manual-alignment.ts +3 -3
  34. package/src/widgets/peptide-molecule.ts +4 -4
  35. package/src/widgets/subst-table.ts +30 -22
  36. package/test-Peptides-f8114def7953-4bf59d70.html +256 -0
  37. package/src/describe.ts +0 -534
  38. package/src/utils/split-aligned.ts +0 -72
  39. package/src/viewers/subst-viewer.ts +0 -320
@@ -0,0 +1,177 @@
1
+ import * as DG from 'datagrok-api/dg';
2
+
3
+ import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
4
+
5
+ type Operation = (op1: boolean, op2: boolean) => boolean;
6
+
7
+ /** Logical operations awailable between selection items. */
8
+ const Operations: {[op: string]: Operation} = {
9
+ and: (op1: boolean, op2: boolean) => op1 && op2,
10
+ or: (op1: boolean, op2: boolean) => op1 || op2,
11
+ };
12
+
13
+ type FilterOperation = 'and' | 'or';
14
+ type PositionFilter = {[pos: string]: Set<string>};
15
+
16
+ /** Implements multiple selection in position-residue space. */
17
+ export class MultipleSelection {
18
+ conjunction: boolean;
19
+ filter: PositionFilter;
20
+ protected operation: Operation;
21
+ protected complete: (v: boolean) => boolean;
22
+
23
+ /**
24
+ * Creates an instance of MultipleSelection.
25
+ * @param {FilterOperation} [operation='and'] Operation to apply to items.
26
+ */
27
+ constructor(operation: FilterOperation = 'and') {
28
+ this.conjunction = operation == 'and';
29
+ this.filter = {};
30
+ this.operation = Operations[operation];
31
+ this.complete = (v: boolean) => (this.conjunction && !v) || (!this.conjunction && v);
32
+ }
33
+
34
+ /**
35
+ * Adds position-residue entity into selection.
36
+ * @param {string} pos Position in a sequence.
37
+ * @param {string} res Residue at the position.
38
+ */
39
+ input(pos: string, res: string) {
40
+ if (!this.filter[pos])
41
+ this.filter[pos] = new Set([]);
42
+
43
+ if (this.filter[pos].has(res))
44
+ this.remove(pos, res);
45
+ else
46
+ this.filter[pos].add(res);
47
+ }
48
+
49
+ /**
50
+ * Removes position-residue entity from selection.
51
+ * @param {string} pos Position in a sequence.
52
+ * @param {string} res Residue at the position.
53
+ */
54
+ remove(pos: string, res: string) {
55
+ if (this.filter[pos]) {
56
+ this.filter[pos].delete(res);
57
+
58
+ if (this.filter[pos].size == 0)
59
+ delete this.filter[pos];
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Sets the particular residue at position into selection.
65
+ * @param {string} pos Position in a sequence.
66
+ * @param {string} res Residue at the position.
67
+ */
68
+ set(pos: string, res: string) {
69
+ for (const p of Object.keys(this.filter))
70
+ delete this.filter[p];
71
+
72
+ this.filter[pos] = new Set([res]);
73
+ }
74
+
75
+ /**
76
+ * Sets the particular position with the given residues into selection.
77
+ * @param {string} pos Position in a sequence.
78
+ * @param {string[]} values Residues list at the position.
79
+ */
80
+ setPos(pos: string, values: string[]) {
81
+ this.filter[pos] = new Set(values);
82
+ }
83
+
84
+ /**
85
+ * Sets the particular residue to be at given positions into selection.
86
+ * @param {string} res Residue to set.
87
+ * @param {string[]} values Positions list to assign the residue to.
88
+ */
89
+ setRes(res: string, values: string[]) {
90
+ for (const pos of values) {
91
+ if (this.filter[pos])
92
+ this.filter[pos].add(res);
93
+ else
94
+ this.filter[pos] = new Set([res]);
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Evaluates selection into a list of booleans of the same length as the given data frame.
100
+ * @param {DG.DataFrame} df Data frame to consider.
101
+ * @param {StringDictionary} [mapper={}] Optional residues mapper.
102
+ * @return {boolean[]} List of trues/falses corresponding selection constructed.
103
+ */
104
+ eval(df: DG.DataFrame, mapper: StringDictionary = {}): boolean[] {
105
+ const itemsCount = df.rowCount;
106
+ const cond = new Array<boolean>(itemsCount).fill(this.conjunction);
107
+
108
+ for (let i = 0; i < itemsCount; ++i)
109
+ cond[i] = this.match(i, df, mapper);
110
+
111
+ return cond;
112
+ }
113
+
114
+ /**
115
+ * Tests if i-th element matches filter.
116
+ * @param {number} i Element's index
117
+ * @param {DG.DataFrame} df Data frame to consider.
118
+ * @param {StringDictionary} [mapper={}] Optional residues mapper.
119
+ * @return {boolean} Result of the test.
120
+ */
121
+ match(i: number, df: DG.DataFrame, mapper: StringDictionary = {}): boolean {
122
+ let cond: boolean = this.conjunction;
123
+
124
+ for (const [posColumnName, resFilter] of Object.entries(this.filter)) {
125
+ const residue = df.get(posColumnName, i);
126
+ const isMatched = resFilter.has(mapper[residue] ?? residue);
127
+ cond = this.operation(cond, isMatched);
128
+
129
+ if (this.complete(cond))
130
+ break;
131
+ }
132
+ return cond;
133
+ }
134
+
135
+ /**
136
+ * Tests if position-residue is selected.
137
+ * @param {string} pos Position in a sequence.
138
+ * @param {string} res Residue at the position.
139
+ * @param {StringDictionary} [mapper={}] Optional residues mapper.
140
+ * @return {boolean} True if this entity is selected else false.
141
+ */
142
+ test(pos: string, res: string, mapper: StringDictionary = {}): boolean {
143
+ if (this.filter[pos])
144
+ return this.filter[pos].has(mapper[res] ?? res);
145
+
146
+ return false;
147
+ }
148
+
149
+ /**
150
+ * Make string representation of the selected items.
151
+ * @return {string} String representation.
152
+ */
153
+ toString(): string {
154
+ let repr = '';
155
+
156
+ for (const [pos, res] of Object.entries(this.filter))
157
+ repr += `${pos}: ${Array.from(res).join(',')}\n`;
158
+
159
+ return repr;
160
+ }
161
+
162
+ /**
163
+ * Turns filter into grouping query string.
164
+ * @param {string} [residueColumnName='Res'] Optional residues column name.
165
+ * @return {string} Query-formatted string.
166
+ */
167
+ toQuery(residueColumnName: string = 'Res'): string {
168
+ const alt: string[] = [];
169
+
170
+ for (const [pos, residues] of Object.entries(this.filter)) {
171
+ for (const res of residues)
172
+ alt.push(`${residueColumnName} = ${res} and Pos = ${pos}`);
173
+ }
174
+
175
+ return alt.join(' or ');
176
+ }
177
+ }
@@ -1,7 +1,7 @@
1
1
  import {ChemPalette} from './chem-palette';
2
2
  import * as DG from 'datagrok-api/dg';
3
3
 
4
- const cp = new ChemPalette('grok');
4
+ import * as C from './constants';
5
5
 
6
6
  /**
7
7
  * A function to expand column size based on its contents.
@@ -40,8 +40,8 @@ export function expandColumn(
40
40
  * @param {boolean} [grouping=false] Is grouping enabled.
41
41
  */
42
42
  export function setAARRenderer(col: DG.Column, grid: DG.Grid | null = null, grouping = false) {
43
- col.semType = 'aminoAcids';
44
- col.setTag('cell.renderer', 'aminoAcids');
43
+ col.semType = C.SEM_TYPES.AMINO_ACIDS;
44
+ col.setTag('cell.renderer', C.SEM_TYPES.AMINO_ACIDS);
45
45
  if (grouping)
46
46
  col.setTag('groups', `${grouping}`);
47
47
 
@@ -89,42 +89,35 @@ function printLeftOrCentered(
89
89
 
90
90
  if (colorPart.length >= 3) {
91
91
  if (colorPart.substring(0, 3) in ChemPalette.AAFullNames)
92
- colorPart = ChemPalette.AAFullNames[s.substring(0, 3)] + colorPart.substr(3);
92
+ colorPart = ChemPalette.AAFullNames[s.substring(0, 3)] + colorPart.substring(3);
93
93
  else if (colorPart.substring(1, 4) in ChemPalette.AAFullNames)
94
- colorPart = colorPart[0] + ChemPalette.AAFullNames[s.substring(1, 4)] + colorPart.substr(4);
94
+ colorPart = colorPart[0] + ChemPalette.AAFullNames[s.substring(1, 4)] + colorPart.substring(4);
95
95
  }
96
- let grayPart = pivot == -1 ? '' : s.substr(pivot);
96
+ let grayPart = pivot == -1 ? '' : s.substring(pivot);
97
97
  if (hideMod) {
98
98
  let end = colorPart.lastIndexOf(')');
99
99
  let beg = colorPart.indexOf('(');
100
100
  if (beg > -1 && end > -1 && end - beg > 2)
101
- colorPart = colorPart.substr(0, beg) + '(+)' + colorPart.substr(end + 1);
101
+ colorPart = colorPart.substring(0, beg) + '(+)' + colorPart.substring(end + 1);
102
102
 
103
103
 
104
104
  end = grayPart.lastIndexOf(')');
105
105
  beg = grayPart.indexOf('(');
106
106
  if (beg > -1 && end > -1 && end - beg > 2)
107
- grayPart = grayPart.substr(0, beg) + '(+)' + grayPart.substr(end + 1);
107
+ grayPart = grayPart.substring(0, beg) + '(+)' + grayPart.substring(end + 1);
108
108
  }
109
109
  const textSize = g.measureText(colorPart + grayPart);
110
110
  const indent = 5;
111
111
 
112
112
  const colorTextSize = g.measureText(colorPart);
113
+ const dy = (textSize.fontBoundingBoxAscent + textSize.fontBoundingBoxDescent) / 2;
113
114
 
114
115
  function draw(dx1: number, dx2: number) {
115
116
  g.fillStyle = color;
116
117
  g.globalAlpha = transparencyRate;
117
- g.fillText(
118
- colorPart,
119
- x + dx1,
120
- y + (textSize.fontBoundingBoxAscent + textSize.fontBoundingBoxDescent) / 2,
121
- );
118
+ g.fillText(colorPart, x + dx1, y + dy);
122
119
  g.fillStyle = ChemPalette.undefinedColor;
123
- g.fillText(
124
- grayPart,
125
- x + dx2,
126
- y + (textSize.fontBoundingBoxAscent + textSize.fontBoundingBoxDescent) / 2,
127
- );
120
+ g.fillText(grayPart, x + dx2, y + dy);
128
121
  }
129
122
 
130
123
 
@@ -132,8 +125,9 @@ function printLeftOrCentered(
132
125
  draw(indent, indent + colorTextSize.width);
133
126
  return x + colorTextSize.width + g.measureText(grayPart).width;
134
127
  } else {
135
- draw((w - textSize.width) / 2, (w - textSize.width) / 2 + colorTextSize.width);
136
- return x + (w - textSize.width) / 2 + colorTextSize.width;
128
+ const dx = (w - textSize.width) / 2;
129
+ draw(dx, dx + colorTextSize.width);
130
+ return x + dx + colorTextSize.width;
137
131
  }
138
132
  }
139
133
 
@@ -164,7 +158,7 @@ export class AminoAcidsCellRenderer extends DG.GridCellRenderer {
164
158
  * @memberof AminoAcidsCellRenderer
165
159
  */
166
160
  get cellType() {
167
- return 'aminoAcids';
161
+ return C.SEM_TYPES.AMINO_ACIDS;
168
162
  }
169
163
 
170
164
  /**
@@ -209,7 +203,7 @@ export class AminoAcidsCellRenderer extends DG.GridCellRenderer {
209
203
  render(
210
204
  g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
211
205
  cellStyle: DG.GridCellStyle) {
212
- this.chemPalette ??= new ChemPalette('grok', gridCell.tableColumn?.getTag('groups') ? true : false);
206
+ // this.chemPalette ??= new ChemPalette('grok', gridCell.tableColumn?.getTag('groups') ? true : false);
213
207
 
214
208
  y -= 2;
215
209
  g.save();
@@ -219,7 +213,7 @@ export class AminoAcidsCellRenderer extends DG.GridCellRenderer {
219
213
  g.font = `12px monospace`;
220
214
  g.textBaseline = 'top';
221
215
  const s: string = gridCell.cell.value ? gridCell.cell.value : '-';
222
- let [color, outerS, innerS, pivot] = cp.getColorAAPivot(s);
216
+ let [color, outerS, innerS, pivot] = ChemPalette.getColorAAPivot(s);
223
217
  if (innerS)
224
218
  outerS = s;
225
219
 
@@ -253,7 +247,7 @@ export class AlignedSequenceCellRenderer extends DG.GridCellRenderer {
253
247
  * @memberof AlignedSequenceCellRenderer
254
248
  */
255
249
  get cellType() {
256
- return 'alignedSequence';
250
+ return C.SEM_TYPES.ALIGNED_SEQUENCE;
257
251
  }
258
252
 
259
253
  /**
@@ -289,10 +283,10 @@ export class AlignedSequenceCellRenderer extends DG.GridCellRenderer {
289
283
  * @memberof AlignedSequenceCellRenderer
290
284
  */
291
285
  render(
292
- g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
293
- gridCell: DG.GridCell, cellStyle: DG.GridCellStyle,
286
+ g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
287
+ cellStyle: DG.GridCellStyle,
294
288
  ) {
295
- const grid = gridCell.dart.grid ? gridCell.grid : gridCell.dart.grid;
289
+ const grid = gridCell.grid;
296
290
  const cell = gridCell.cell;
297
291
  w = grid ? Math.min(grid.canvas.width - x, w) : g.canvas.width - x;
298
292
  g.save();
@@ -307,16 +301,16 @@ export class AlignedSequenceCellRenderer extends DG.GridCellRenderer {
307
301
  const subParts = s.split('-');
308
302
  const [text, simplified] = processSequence(subParts);
309
303
  const textSize = g.measureText(text.join(''));
310
- x = Math.max(x, x + (w - textSize.width) / 2);
304
+ let x1 = Math.max(x, x + (w - textSize.width) / 2);
311
305
 
312
- subParts.forEach((amino: string, index) => {
313
- let [color, outerAmino,, pivot] = cp.getColorAAPivot(amino);
306
+ subParts.forEach((amino, index) => {
307
+ let [color, outerAmino,, pivot] = ChemPalette.getColorAAPivot(amino);
314
308
  g.fillStyle = ChemPalette.undefinedColor;
315
309
  if (index + 1 < subParts.length) {
316
310
  const gap = simplified ? '' : ' ';
317
- outerAmino += `${outerAmino?'':'-'}${gap}`;
311
+ outerAmino += `${outerAmino ? '' : '-'}${gap}`;
318
312
  }
319
- x = printLeftOrCentered(x, y, w, h, g, outerAmino, color, pivot, true);
313
+ x1 = printLeftOrCentered(x1, y, w, h, g, outerAmino, color, pivot, true);
320
314
  });
321
315
 
322
316
  g.restore();
@@ -408,10 +402,9 @@ export class AlignedSequenceDifferenceCellRenderer extends DG.GridCellRenderer {
408
402
  * @memberof AlignedSequenceDifferenceCellRenderer
409
403
  */
410
404
  render(
411
- g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number,
412
- gridCell: DG.GridCell, cellStyle: DG.GridCellStyle,
413
- ) {
414
- const grid = gridCell.dart.grid ? gridCell.grid : gridCell.dart.grid;
405
+ g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
406
+ cellStyle: DG.GridCellStyle) {
407
+ const grid = gridCell.grid;
415
408
  const cell = gridCell.cell;
416
409
 
417
410
  w = grid ? Math.min(grid.canvas.width - x, w) : g.canvas.width - x;
@@ -429,26 +422,32 @@ export class AlignedSequenceDifferenceCellRenderer extends DG.GridCellRenderer {
429
422
  const subParts2 = s2.split('-');
430
423
  const [text] = processSequence(subParts1);
431
424
  const textSize = g.measureText(text.join(''));
432
- x = Math.max(x, x + (w - textSize.width) / 2);
425
+ let updatedX = Math.max(x, x + (w - (textSize.width + subParts1.length * 4)) / 2);
426
+ // 28 is the height of the two substitutions on top of each other + space
427
+ const updatedY = Math.max(y, y + (h - 28) / 2);
433
428
 
429
+ let amino2;
430
+ let updatedAmino1: string;
431
+ let updatedAmino2: string;
434
432
  subParts1.forEach((amino1: string, index) => {
435
- let amino2 = subParts2[index];
436
- const [color1, amino1Outer, amino1Inner, pivot1] = cp.getColorAAPivot(amino1);
437
- const [color2, amino2Outer, amino2Inner, pivot2] = cp.getColorAAPivot(amino2);
433
+ amino2 = subParts2[index];
434
+ const [color1, amino1Outer, amino1Inner, pivot1] = ChemPalette.getColorAAPivot(amino1);
435
+ const [color2, amino2Outer, amino2Inner, pivot2] = ChemPalette.getColorAAPivot(amino2);
438
436
 
439
- if (amino1 != amino2) {
440
- const verticalShift = 7;
437
+ updatedAmino1 = amino1Outer + (amino1Inner !== '' ? '(' + amino1Inner + ')' : '');
438
+ updatedAmino1 = updatedAmino1 === '' ? '-' : updatedAmino1;
441
439
 
442
- amino1 = amino1Outer + (amino1Inner !== '' ? '(' + amino1Inner + ')' : '');
443
- amino2 = amino2Outer + (amino2Inner !== '' ? '(' + amino2Inner + ')' : '');
444
- amino1 = amino1 === '' ? '-' : amino1;
445
- amino2 = amino2 === '' ? '-' : amino2;
440
+ if (amino1 != amino2) {
441
+ updatedAmino2 = amino2Outer + (amino2Inner !== '' ? '(' + amino2Inner + ')' : '');
442
+ updatedAmino2 = updatedAmino2 === '' ? '-' : updatedAmino2;
446
443
 
447
- const x1 = printLeftOrCentered(x, y - verticalShift, w, h, g, amino1, color1, pivot1, true);
448
- x = printLeftOrCentered(x, y + verticalShift, w, h, g, amino2, color2, pivot2, true);
449
- x = Math.max(x, x1) + 4;
444
+ const vShift = 7;
445
+ const subX0 = printLeftOrCentered(updatedX, updatedY - vShift, w, h, g, updatedAmino1, color1, pivot1, true);
446
+ const subX1 = printLeftOrCentered(updatedX, updatedY + vShift, w, h, g, updatedAmino2, color2, pivot2, true);
447
+ updatedX = Math.max(subX1, subX0);
450
448
  } else
451
- x = printLeftOrCentered(x, y, w, h, g, amino1 ? amino1 : '-', color1, pivot1, true, true, 0.5) + 4;
449
+ updatedX = printLeftOrCentered(updatedX, updatedY, w, h, g, updatedAmino1, color1, pivot1, true, true, 0.5);
450
+ updatedX += 4;
452
451
  });
453
452
  g.restore();
454
453
  }