@datagrok/peptides 1.0.1 → 1.0.2
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/dist/package-test.js +8275 -3759
- package/dist/package.js +19735 -14773
- package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +306 -215
- package/files/aligned.csv +648 -648
- package/files/aligned_2.csv +541 -10275
- package/files/aligned_3.csv +335 -0
- package/helm/JSDraw/Pistoia.HELM.js +27 -0
- package/package.json +17 -12
- package/src/__jest__/remote.test.ts +31 -13
- package/src/model.ts +321 -342
- package/src/package-test.ts +0 -1
- package/src/package.ts +13 -109
- package/src/tests/core.ts +48 -16
- package/src/tests/utils.ts +0 -11
- package/src/utils/cell-renderer.ts +92 -275
- package/src/utils/constants.ts +5 -4
- package/src/utils/misc.ts +103 -16
- package/src/utils/multiple-sequence-alignment.ts +1 -1
- package/src/utils/peptide-similarity-space.ts +1 -1
- package/src/utils/types.ts +7 -5
- package/src/viewers/peptide-space-viewer.ts +3 -3
- package/src/viewers/sar-viewer.ts +34 -23
- package/src/widgets/analyze-peptides.ts +23 -17
- package/src/widgets/distribution.ts +0 -1
- package/src/widgets/manual-alignment.ts +4 -4
- package/src/widgets/subst-table.ts +5 -2
- package/{test-Peptides-34f75e5127b8-4210edfc.html → test-Peptides-4f0c8bae6479-74cbfe68.html} +8 -8
- package/detectors.js +0 -9
- package/src/monomer-library.ts +0 -193
- package/src/tests/msa-tests.ts +0 -27
- package/src/utils/chem-palette.ts +0 -280
- package/src/viewers/stacked-barchart-viewer.ts +0 -321
- package/src/widgets/multiple-sequence-alignment.ts +0 -9
- package/src/widgets/peptide-molecule.ts +0 -82
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import * as grok from 'datagrok-api/grok';
|
|
2
|
-
import * as ui from 'datagrok-api/ui';
|
|
3
|
-
import * as DG from 'datagrok-api/dg';
|
|
4
|
-
|
|
5
|
-
import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
|
|
6
|
-
import {MonomerLibrary} from '../monomer-library';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export class ChemPalette {
|
|
10
|
-
cp: StringDictionary = {};
|
|
11
|
-
isInit: boolean = false;
|
|
12
|
-
monomerLib: MonomerLibrary | null = null;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Creates an instance of ChemPalette.
|
|
16
|
-
*
|
|
17
|
-
* @param {string} scheme Color scheme to use.
|
|
18
|
-
* @param {boolean} [grouping=false] Is grouping enabled.
|
|
19
|
-
* @memberof ChemPalette
|
|
20
|
-
*/
|
|
21
|
-
private constructor(scheme: string, grouping = false) {
|
|
22
|
-
if (scheme == 'grok')
|
|
23
|
-
this.cp = ChemPalette.getDatagrok(grouping);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Renders 2D representation of a amino acid residue in a tooltip.
|
|
28
|
-
*
|
|
29
|
-
* @param {DG.GridCell} cell Grid cell to show tooltip over.
|
|
30
|
-
* @param {number} x x coordinate of the mouse pointer.
|
|
31
|
-
* @param {number} y y coordinate of the mouse pointer.
|
|
32
|
-
* @param {MonomerLibrary} monomerLib Monomer Library instance
|
|
33
|
-
*/
|
|
34
|
-
static showTooltip(cell: DG.GridCell, x: number, y: number, monomerLib: MonomerLibrary): void {
|
|
35
|
-
const s = cell.cell.value as string;
|
|
36
|
-
let toDisplay = [ui.divText(s)];
|
|
37
|
-
const [, aarOuter, aarInner] = ChemPalette.getColorAAPivot(s);
|
|
38
|
-
for (const aar of [aarOuter, aarInner]) {
|
|
39
|
-
if (monomerLib.monomerNames.includes(aar)) {
|
|
40
|
-
if (aar in ChemPalette.AANames)
|
|
41
|
-
toDisplay = [ui.divText(ChemPalette.AANames[aar])];
|
|
42
|
-
|
|
43
|
-
if (aar in ChemPalette.AAFullNames)
|
|
44
|
-
toDisplay = [ui.divText(ChemPalette.AANames[ChemPalette.AAFullNames[aar]])];
|
|
45
|
-
|
|
46
|
-
const options = {
|
|
47
|
-
autoCrop: true,
|
|
48
|
-
autoCropMargin: 0,
|
|
49
|
-
suppressChiralText: true,
|
|
50
|
-
};
|
|
51
|
-
const sketch = grok.chem.svgMol(monomerLib.getMonomerMol(aar), undefined, undefined, options);
|
|
52
|
-
if (toDisplay.length == 2)
|
|
53
|
-
toDisplay.push(ui.divText('Modified'));
|
|
54
|
-
|
|
55
|
-
toDisplay.push(sketch);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
ui.tooltip.show(ui.divV(toDisplay), x, y);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Retursn divided amino with its content in the bracket, if the conetent is number, then its omitted
|
|
63
|
-
*
|
|
64
|
-
* @param {string} c raw amino
|
|
65
|
-
* @return {[string, string]} outer and inner content
|
|
66
|
-
*/
|
|
67
|
-
static getInnerOuter(c: string): [string, string] {
|
|
68
|
-
let isInner = 0;
|
|
69
|
-
let inner = '';
|
|
70
|
-
let outer = '';
|
|
71
|
-
|
|
72
|
-
for (const char of c) {
|
|
73
|
-
if (char == '(')
|
|
74
|
-
isInner++;
|
|
75
|
-
else if (char == ')')
|
|
76
|
-
isInner--;
|
|
77
|
-
else if (isInner)
|
|
78
|
-
inner += char;
|
|
79
|
-
else
|
|
80
|
-
outer += char;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return !isNaN(parseInt(inner)) ? [outer, ''] : [outer, inner];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
static getColorAAPivot(monomer: string = '', scheme: 'grok' = 'grok'): [string, string, string, number] {
|
|
87
|
-
const chemPaletteInstance = ChemPalette.getPalette(scheme);
|
|
88
|
-
let [outerMonomer, innerMonomer] = ChemPalette.getInnerOuter(monomer);
|
|
89
|
-
outerMonomer = (outerMonomer.length > 6 ? `${outerMonomer.slice(0, 3)}...` : outerMonomer);
|
|
90
|
-
innerMonomer = (innerMonomer.length > 6 ? `${innerMonomer.slice(0, 3)}...` : innerMonomer);
|
|
91
|
-
|
|
92
|
-
if (monomer.length == 1 || monomer[1] == '(') {
|
|
93
|
-
const amino = monomer[0]?.toUpperCase()!;
|
|
94
|
-
return amino in chemPaletteInstance ?
|
|
95
|
-
[chemPaletteInstance[amino], amino, innerMonomer, 1]:
|
|
96
|
-
[ChemPalette.undefinedColor, outerMonomer, innerMonomer, 1];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (monomer[0] == 'd' && monomer[1]! in chemPaletteInstance) {
|
|
100
|
-
if (monomer.length == 2 || monomer[2] == '(') {
|
|
101
|
-
const amino = monomer[1]?.toUpperCase()!;
|
|
102
|
-
return amino in chemPaletteInstance ?
|
|
103
|
-
[chemPaletteInstance[amino], amino, innerMonomer, 2]:
|
|
104
|
-
[ChemPalette.undefinedColor, outerMonomer, innerMonomer, 2];
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (monomer.substring(0, 3) in ChemPalette.AAFullNames) {
|
|
109
|
-
if (monomer.length == 3 || monomer[3] == '(') {
|
|
110
|
-
const amino = ChemPalette.AAFullNames[monomer.substring(0, 3)];
|
|
111
|
-
return amino in chemPaletteInstance ?
|
|
112
|
-
[chemPaletteInstance[amino], amino, innerMonomer, 3]:
|
|
113
|
-
[ChemPalette.undefinedColor, outerMonomer, innerMonomer, 3];
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (monomer[0]?.toLowerCase() == monomer[0]) {
|
|
118
|
-
if (monomer.substring(1, 3) in ChemPalette.AAFullNames) {
|
|
119
|
-
if (monomer.length == 4 || monomer[4] == '(') {
|
|
120
|
-
const amino = ChemPalette.AAFullNames[monomer.substring(1, 3)];
|
|
121
|
-
return amino in chemPaletteInstance ?
|
|
122
|
-
[chemPaletteInstance[amino], amino, innerMonomer, 4]:
|
|
123
|
-
[ChemPalette.undefinedColor, outerMonomer, innerMonomer, 4];
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return [ChemPalette.undefinedColor, outerMonomer, innerMonomer, 0];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
static colourPalette: {[key: string]: string[]} = {
|
|
132
|
-
'orange': ['rgb(255,187,120)', 'rgb(245,167,100)', 'rgb(235,137,70)', 'rgb(205, 111, 71)'],
|
|
133
|
-
'all_green': ['rgb(44,160,44)', 'rgb(74,160,74)', 'rgb(23,103,57)', 'rgb(30,110,96)', 'rgb(60,131,95)',
|
|
134
|
-
'rgb(24,110,79)', 'rgb(152,223,138)', 'rgb(182, 223, 138)', 'rgb(152, 193, 138)'],
|
|
135
|
-
'all_blue': ['rgb(31,119,180)', 'rgb(23,190,207)', 'rgb(122, 102, 189)', 'rgb(158,218,229)', 'rgb(141, 124, 217)',
|
|
136
|
-
'rgb(31, 120, 150)'],
|
|
137
|
-
'magenta': ['rgb(162,106,192)', 'rgb(197,165,224)', 'rgb(208,113,218)'],
|
|
138
|
-
'red': ['rgb(214,39,40)', 'rgb(255,152,150)'],
|
|
139
|
-
'st_blue': ['rgb(23,190,207)', 'rgb(158,218,229)', 'rgb(31,119,180)'],
|
|
140
|
-
'dark_blue': ['rgb(31,119,180)', 'rgb(31, 120, 150)'],
|
|
141
|
-
'light_blue': ['rgb(23,190,207)', 'rgb(158,218,229)', 'rgb(108, 218, 229)', 'rgb(23,190,227)'],
|
|
142
|
-
'lilac_blue': ['rgb(124,102,211)', 'rgb(149,134,217)', 'rgb(97, 81, 150)'],
|
|
143
|
-
'dark_green': ['rgb(23,103,57)', 'rgb(30,110,96)', 'rgb(60,131,95)', 'rgb(24,110,79)'],
|
|
144
|
-
'green': ['rgb(44,160,44)', 'rgb(74,160,74)'],
|
|
145
|
-
'light_green': ['rgb(152,223,138)', 'rgb(182, 223, 138)', 'rgb(152, 193, 138)'],
|
|
146
|
-
'st_green': ['rgb(44,160,44)', 'rgb(152,223,138)', 'rgb(39, 174, 96)', 'rgb(74,160,74)'],
|
|
147
|
-
'pink': ['rgb(247,182,210)'],
|
|
148
|
-
'brown': ['rgb(140,86,75)', 'rgb(102, 62, 54)'],
|
|
149
|
-
'gray': ['rgb(127,127,127)', 'rgb(199,199,199)', 'rgb(196,156,148)', 'rgb(222, 222, 180)'],
|
|
150
|
-
'yellow': ['rgb(188,189,34)'],
|
|
151
|
-
'white': ['rgb(230,230,230)'],
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
static grokGroups: {[key: string]: string[]} = {
|
|
155
|
-
'yellow': ['C', 'U'],
|
|
156
|
-
'red': ['G', 'P'],
|
|
157
|
-
'all_green': ['A', 'V', 'I', 'L', 'M', 'F', 'Y', 'W'],
|
|
158
|
-
'light_blue': ['R', 'H', 'K'],
|
|
159
|
-
'dark_blue': ['D', 'E'],
|
|
160
|
-
'orange': ['S', 'T', 'N', 'Q'],
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
static undefinedColor = 'rgb(100,100,100)';
|
|
164
|
-
|
|
165
|
-
static makePalette(dt: {[key: string]: string[]}, simplified = false, grouping = false): StringDictionary {
|
|
166
|
-
const palette: { [key: string]: string } = {};
|
|
167
|
-
const groups = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
168
|
-
let currentGroup = 0;
|
|
169
|
-
for (const [color, monomers] of Object.entries(dt)) {
|
|
170
|
-
monomers.forEach((monomer, index) => {
|
|
171
|
-
palette[grouping ? groups[currentGroup] : monomer] = ChemPalette.colourPalette[color][simplified ? 0 : index];
|
|
172
|
-
});
|
|
173
|
-
currentGroup++;
|
|
174
|
-
}
|
|
175
|
-
return palette;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
static AANames: StringDictionary = {
|
|
179
|
-
'G': 'Glycine',
|
|
180
|
-
'L': 'Leucine',
|
|
181
|
-
'Y': 'Tyrosine',
|
|
182
|
-
'S': 'Serine',
|
|
183
|
-
'E': 'Glutamic acid',
|
|
184
|
-
'Q': 'Glutamine',
|
|
185
|
-
'D': 'Aspartic acid',
|
|
186
|
-
'N': 'Asparagine',
|
|
187
|
-
'F': 'Phenylalanine',
|
|
188
|
-
'A': 'Alanine',
|
|
189
|
-
'K': 'Lysine',
|
|
190
|
-
'R': 'Arginine',
|
|
191
|
-
'H': 'Histidine',
|
|
192
|
-
'C': 'Cysteine',
|
|
193
|
-
'V': 'Valine',
|
|
194
|
-
'P': 'Proline',
|
|
195
|
-
'W': 'Tryptophan',
|
|
196
|
-
'I': 'Isoleucine',
|
|
197
|
-
'M': 'Methionine',
|
|
198
|
-
'T': 'Threonine',
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
static AASmiles: StringDictionary = {
|
|
202
|
-
'G': 'NCC(=O)O',
|
|
203
|
-
'L': 'N[C@H](CC(C)C)C(=O)O',
|
|
204
|
-
'Y': 'NC(CC1=CC=C(O)C=C1)C(=O)O',
|
|
205
|
-
'S': 'NC(CO)C(=O)O',
|
|
206
|
-
'E': 'N[C@@H](CCC(O)=O)C(=O)O',
|
|
207
|
-
'Q': 'N[C@@H](CCC(N)=O)C(=O)O',
|
|
208
|
-
'D': 'N[C@@H](CC(O)=O)C(=O)O',
|
|
209
|
-
'N': 'N[C@@H](CC(N)=O)C(=O)O',
|
|
210
|
-
'F': 'NC(CC1=CC=CC=C1)C(=O)O',
|
|
211
|
-
'A': 'N[C@H](C)C(=O)O',
|
|
212
|
-
'K': 'NC(CCCCN)C(=O)O',
|
|
213
|
-
'R': 'N[C@H](CCCNC(=N)C)C(=O)O',
|
|
214
|
-
'H': 'NC(CC1=CN=C[N]1)C(=O)O',
|
|
215
|
-
'C': 'N[C@@H](CS)C(=O)O',
|
|
216
|
-
'V': 'NC(C(C)C)C(=O)O',
|
|
217
|
-
'P': 'N(CCC1)C1C(=O)O',
|
|
218
|
-
'W': 'N[C@@H](Cc1c2ccccc2n([H])c1)C(=O)O',
|
|
219
|
-
'I': 'N[C@H]([C@H](C)CC)C(=O)O',
|
|
220
|
-
'M': 'NC(CCSC)C(=O)O',
|
|
221
|
-
'T': 'NC(C(O)C)C(=O)O',
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
static AASmilesTruncated: StringDictionary = {
|
|
225
|
-
'G': '*C*',
|
|
226
|
-
'L': 'CC(C)C[C@H](*)*',
|
|
227
|
-
'Y': 'C1=CC(=CC=C1CC(*)*)O',
|
|
228
|
-
'S': 'OCC(*)C*',
|
|
229
|
-
'E': '*[C@@H](CCC(O)=O)*',
|
|
230
|
-
'Q': '*N[C@@H](CCC(N)=O)*',
|
|
231
|
-
'D': '*[C@@H](CC(O)=O)*',
|
|
232
|
-
'N': '*[C@@H](CC(N)=O)*',
|
|
233
|
-
'F': 'C1=CC=C(C=C1)CC(*)*',
|
|
234
|
-
'A': 'C[C@H](*)*',
|
|
235
|
-
'K': 'C(CCN)CC(*)*',
|
|
236
|
-
'R': '*[C@H](CCCNC(=N)C)*',
|
|
237
|
-
'H': 'C1=C(NC=N1)CC(*)*',
|
|
238
|
-
'C': 'C([C@@H](*)*)S',
|
|
239
|
-
'V': 'CC(C)C(*)*',
|
|
240
|
-
'P': 'C1CCN(*)C1*',
|
|
241
|
-
'W': '*[C@@H](Cc1c2ccccc2n([H])c1)*',
|
|
242
|
-
'I': 'CC[C@H](C)[C@H](*)*',
|
|
243
|
-
'M': 'CSCCC(*)*',
|
|
244
|
-
'T': 'CC(O)C(*)*',
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
static AAFullNames: StringDictionary = {
|
|
248
|
-
'Ala': 'A',
|
|
249
|
-
'Arg': 'R',
|
|
250
|
-
'Asn': 'N',
|
|
251
|
-
'Asp': 'D',
|
|
252
|
-
'Cys': 'C',
|
|
253
|
-
'Gln': 'Q',
|
|
254
|
-
'Glu': 'E',
|
|
255
|
-
'Gly': 'G',
|
|
256
|
-
'His': 'H',
|
|
257
|
-
'Ile': 'I',
|
|
258
|
-
'Leu': 'L',
|
|
259
|
-
'Lys': 'K',
|
|
260
|
-
'Met': 'M',
|
|
261
|
-
'Phe': 'F',
|
|
262
|
-
'Pro': 'P',
|
|
263
|
-
'Ser': 'S',
|
|
264
|
-
'Thr': 'T',
|
|
265
|
-
'Trp': 'W',
|
|
266
|
-
'Tyr': 'Y',
|
|
267
|
-
'Val': 'V',
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
static getDatagrok(grouping = false): StringDictionary {
|
|
271
|
-
return ChemPalette.makePalette(ChemPalette.grokGroups, false, grouping);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
static getPalette(scheme: 'grok'): StringDictionary {
|
|
275
|
-
if (scheme == 'grok')
|
|
276
|
-
return ChemPalette.getDatagrok();
|
|
277
|
-
else
|
|
278
|
-
throw new Error(`ChemPalette: scheme \`${scheme}\` does not exist`);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
@@ -1,321 +0,0 @@
|
|
|
1
|
-
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
import * as rxjs from 'rxjs';
|
|
3
|
-
import {MonomerLibrary} from '../monomer-library';
|
|
4
|
-
|
|
5
|
-
import * as C from '../utils/constants';
|
|
6
|
-
import * as type from '../utils/types';
|
|
7
|
-
import {PeptidesModel} from '../model';
|
|
8
|
-
|
|
9
|
-
export function addViewerToHeader(grid: DG.Grid, barchart: StackedBarChart): void {
|
|
10
|
-
if (grid.temp['containsBarchart'])
|
|
11
|
-
return;
|
|
12
|
-
|
|
13
|
-
const eventAction = (ev: MouseEvent): void => {
|
|
14
|
-
const cell = grid.hitTest(ev.offsetX, ev.offsetY);
|
|
15
|
-
if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.AMINO_ACIDS) {
|
|
16
|
-
const newBarPart = barchart.findAARandPosition(cell, ev);
|
|
17
|
-
// barchart._currentBarPart = newBarPart;
|
|
18
|
-
barchart.requestAction(ev, newBarPart);
|
|
19
|
-
barchart.computeData();
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
// The following events makes the barchart interactive
|
|
24
|
-
rxjs.fromEvent<MouseEvent>(grid.overlay, 'mousemove').subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
25
|
-
rxjs.fromEvent<MouseEvent>(grid.overlay, 'click').subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
|
|
26
|
-
rxjs.fromEvent<MouseEvent>(grid.overlay, 'mouseout').subscribe(() => barchart.computeData());
|
|
27
|
-
|
|
28
|
-
barchart.tableCanvas = grid.canvas;
|
|
29
|
-
|
|
30
|
-
//Setting grid options
|
|
31
|
-
grid.setOptions({'colHeaderHeight': 130});
|
|
32
|
-
|
|
33
|
-
grid.onCellTooltip((cell, x, y) => {
|
|
34
|
-
const colSemType = cell.tableColumn?.semType as C.SEM_TYPES;
|
|
35
|
-
if (colSemType == C.SEM_TYPES.ALIGNED_SEQUENCE || colSemType == C.SEM_TYPES.AMINO_ACIDS) {
|
|
36
|
-
if (!cell.isColHeader) {
|
|
37
|
-
const monomerLib = cell.cell.dataFrame.temp[MonomerLibrary.id];
|
|
38
|
-
PeptidesModel.chemPalette.showTooltip(cell, x, y, monomerLib);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return true;
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
grid.onCellRender.subscribe((args) => {
|
|
45
|
-
const context = args.g;
|
|
46
|
-
const boundX = args.bounds.x;
|
|
47
|
-
const boundY = args.bounds.y;
|
|
48
|
-
const boundWidth = args.bounds.width;
|
|
49
|
-
const boundHeight = args.bounds.height;
|
|
50
|
-
const cell = args.cell;
|
|
51
|
-
context.save();
|
|
52
|
-
context.beginPath();
|
|
53
|
-
context.rect(boundX, boundY, boundWidth, boundHeight);
|
|
54
|
-
context.clip();
|
|
55
|
-
|
|
56
|
-
if (cell.isColHeader && barchart.aminoColumnNames.includes(cell.gridColumn.name)) {
|
|
57
|
-
barchart.renderBarToCanvas(context, cell, boundX, boundY, boundWidth, boundHeight);
|
|
58
|
-
args.preventDefault();
|
|
59
|
-
}
|
|
60
|
-
context.restore();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
grid.temp['containsBarchart'] = true;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export class StackedBarChart extends DG.JsViewer {
|
|
67
|
-
dataEmptyAA: string;
|
|
68
|
-
tableCanvas: HTMLCanvasElement | undefined;
|
|
69
|
-
aminoColumnNames: string[] = [];
|
|
70
|
-
ord: { [Key: string]: number; } = {};
|
|
71
|
-
aminoColumnIndices: {[Key: string]: number} = {};
|
|
72
|
-
aggregatedFilterTables: type.DataFrameDict = {};
|
|
73
|
-
max = 0;
|
|
74
|
-
barStats: {[Key: string]: type.BarChart.BarStatsObject[]} = {};
|
|
75
|
-
selected: type.BarChart.BarPart[] = [];
|
|
76
|
-
aggregatedSelectedTables: type.DataFrameDict = {};
|
|
77
|
-
model!: PeptidesModel;
|
|
78
|
-
|
|
79
|
-
constructor() {
|
|
80
|
-
super();
|
|
81
|
-
this.dataEmptyAA = this.string('dataEmptyAA', '-');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
init(): void {
|
|
85
|
-
const groups: {[key: string]: string[]} = {
|
|
86
|
-
'yellow': ['C', 'U'],
|
|
87
|
-
'red': ['G', 'P'],
|
|
88
|
-
'all_green': ['A', 'V', 'I', 'L', 'M', 'F', 'Y', 'W'],
|
|
89
|
-
'light_blue': ['R', 'H', 'K'],
|
|
90
|
-
'dark_blue': ['D', 'E'],
|
|
91
|
-
'orange': ['S', 'T', 'N', 'Q'],
|
|
92
|
-
};
|
|
93
|
-
let i = 0;
|
|
94
|
-
|
|
95
|
-
for (const value of Object.values(groups)) {
|
|
96
|
-
for (const obj of value)
|
|
97
|
-
this.ord[obj] = i++;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
this.aminoColumnNames = [];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Stream subscriptions
|
|
104
|
-
async onTableAttached(): Promise<void> {
|
|
105
|
-
this.init();
|
|
106
|
-
this.model = await PeptidesModel.getInstance(this.dataFrame);
|
|
107
|
-
// this.controller.init(this.dataFrame);
|
|
108
|
-
if (this.dataFrame) {
|
|
109
|
-
this.subs.push(DG.debounce(this.dataFrame.selection.onChanged, 50).subscribe((_) => this.computeData()));
|
|
110
|
-
this.subs.push(DG.debounce(this.dataFrame.filter.onChanged, 50).subscribe((_) => this.computeData()));
|
|
111
|
-
this.subs.push(DG.debounce(this.dataFrame.onValuesChanged, 50).subscribe(() => this.computeData()));
|
|
112
|
-
}
|
|
113
|
-
this.computeData();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Cancel subscriptions when the viewer is detached
|
|
117
|
-
detach(): void {
|
|
118
|
-
this.subs.forEach((sub) => sub.unsubscribe());
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
computeData(): void {
|
|
122
|
-
this.aminoColumnNames = [];
|
|
123
|
-
this.aminoColumnIndices = {};
|
|
124
|
-
|
|
125
|
-
this.dataFrame.columns.names().forEach((name: string) => {
|
|
126
|
-
if (this.dataFrame.getCol(name).semType === C.SEM_TYPES.AMINO_ACIDS &&
|
|
127
|
-
!this.dataFrame.getCol(name).categories.includes('COOH') &&
|
|
128
|
-
!this.dataFrame.getCol(name).categories.includes('NH2')) {
|
|
129
|
-
this.aminoColumnIndices[name] = this.aminoColumnNames.length + 1;
|
|
130
|
-
this.aminoColumnNames.push(name);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
this.aggregatedFilterTables = {};
|
|
135
|
-
this.aggregatedSelectedTables = {};
|
|
136
|
-
//TODO: optimize it, why store so many tables?
|
|
137
|
-
this.aminoColumnNames.forEach((name) => {
|
|
138
|
-
this.aggregatedFilterTables[name] = this.dataFrame
|
|
139
|
-
.groupBy([name])
|
|
140
|
-
.whereRowMask(this.dataFrame.filter)
|
|
141
|
-
.add('count', name, `${name}_count`)
|
|
142
|
-
.aggregate();
|
|
143
|
-
|
|
144
|
-
this.aggregatedSelectedTables[name] = this.dataFrame
|
|
145
|
-
.groupBy([name])
|
|
146
|
-
.whereRowMask(this.dataFrame.selection)
|
|
147
|
-
.add('count', name, `${name}_count`)
|
|
148
|
-
.aggregate();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
this.barStats = {};
|
|
152
|
-
|
|
153
|
-
for (const [name, df] of Object.entries(this.aggregatedFilterTables)) {
|
|
154
|
-
const colData: {'name': string, 'count': number, 'selectedCount': number}[] = [];
|
|
155
|
-
const aminoCol = df.getCol(name);
|
|
156
|
-
const aminoCountCol = df.getCol(`${name}_count`);
|
|
157
|
-
this.barStats[name] = colData;
|
|
158
|
-
|
|
159
|
-
for (let i = 0; i < df.rowCount; i++) {
|
|
160
|
-
const amino = aminoCol.get(i);
|
|
161
|
-
const aminoCount = aminoCountCol.get(i);
|
|
162
|
-
const aminoObj = {'name': amino, 'count': aminoCount, 'selectedCount': 0};
|
|
163
|
-
const aggSelectedAminoCol = this.aggregatedSelectedTables[name].getCol(`${name}`);
|
|
164
|
-
const aggSelectedCountCol = this.aggregatedSelectedTables[name].getCol(`${name}_count`);
|
|
165
|
-
|
|
166
|
-
if (!amino || amino === this.dataEmptyAA)
|
|
167
|
-
continue;
|
|
168
|
-
|
|
169
|
-
colData.push(aminoObj);
|
|
170
|
-
|
|
171
|
-
for (let j = 0; j < aggSelectedCountCol.length; j++) {
|
|
172
|
-
const selectedAmino = aggSelectedAminoCol.get(j);
|
|
173
|
-
const curAmino = (selectedAmino);
|
|
174
|
-
if (curAmino == amino) {
|
|
175
|
-
aminoObj['selectedCount'] = aggSelectedCountCol.get(j);
|
|
176
|
-
break;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
colData.sort((o1, o2) => this.ord[o2['name']] - this.ord[o1['name']]);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
this.max = this.dataFrame.filter.trueCount;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
renderBarToCanvas(g: CanvasRenderingContext2D, cell: DG.GridCell, x: number, y: number, w: number, h: number): void {
|
|
188
|
-
const name = cell.tableColumn!.name;
|
|
189
|
-
const colNameSize = g.measureText(name).width;
|
|
190
|
-
const barData = this.barStats[name];
|
|
191
|
-
const margin = 0.2;
|
|
192
|
-
const innerMargin = 0.02;
|
|
193
|
-
const selectLineRatio = 0.1;
|
|
194
|
-
let sum = 0;
|
|
195
|
-
|
|
196
|
-
barData.forEach((obj) => {
|
|
197
|
-
sum += obj['count'];
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
x = x + w * margin;
|
|
201
|
-
y = y + h * margin / 4;
|
|
202
|
-
w = w - w * margin * 2;
|
|
203
|
-
h = h - h * margin;
|
|
204
|
-
const barWidth = w - 10;
|
|
205
|
-
g.fillStyle = 'black';
|
|
206
|
-
g.textBaseline = 'top';
|
|
207
|
-
g.font = `${h * margin / 2}px`;
|
|
208
|
-
g.fillText(name, x + (w - colNameSize) / 2, y + h + h * margin / 4);
|
|
209
|
-
|
|
210
|
-
barData.forEach((obj) => {
|
|
211
|
-
const sBarHeight = h * obj['count'] / this.max;
|
|
212
|
-
const gapSize = sBarHeight * innerMargin;
|
|
213
|
-
const verticalShift = (this.max - sum) / this.max;
|
|
214
|
-
const [color, aarOuter] = PeptidesModel.chemPalette.getColorAAPivot(obj['name']);
|
|
215
|
-
const textSize = g.measureText(aarOuter);
|
|
216
|
-
const fontSize = 11;
|
|
217
|
-
const leftMargin = (w - (aarOuter.length > 1 ? fontSize : textSize.width - 8)) / 2;
|
|
218
|
-
const subBartHeight = sBarHeight - gapSize;
|
|
219
|
-
const yStart = h * verticalShift + gapSize / 2;
|
|
220
|
-
const xStart = (w - barWidth) / 2;
|
|
221
|
-
const absX = x + leftMargin;
|
|
222
|
-
const absY = y + yStart + subBartHeight / 2 + (aarOuter.length == 1 ? + 4 : 0);
|
|
223
|
-
const eps = 0.1;
|
|
224
|
-
|
|
225
|
-
g.strokeStyle = color;
|
|
226
|
-
g.fillStyle = color;
|
|
227
|
-
if (textSize.width <= subBartHeight) {
|
|
228
|
-
const origTransform = g.getTransform();
|
|
229
|
-
|
|
230
|
-
if (color != PeptidesModel.chemPalette.undefinedColor) {
|
|
231
|
-
g.fillRect(x + xStart, y + yStart, barWidth, subBartHeight);
|
|
232
|
-
g.fillStyle = 'black';
|
|
233
|
-
} else
|
|
234
|
-
g.strokeRect(x + xStart + 0.5, y + yStart, barWidth - 1, subBartHeight);
|
|
235
|
-
|
|
236
|
-
g.font = `${fontSize}px monospace`;
|
|
237
|
-
g.textAlign = 'center';
|
|
238
|
-
g.textBaseline = 'bottom';
|
|
239
|
-
|
|
240
|
-
if (aarOuter.length > 1) {
|
|
241
|
-
g.translate(absX, absY);
|
|
242
|
-
g.rotate(Math.PI / 2);
|
|
243
|
-
g.translate(-absX, -absY);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
g.fillText(aarOuter, absX, absY);
|
|
247
|
-
g.setTransform(origTransform);
|
|
248
|
-
} else
|
|
249
|
-
g.fillRect(x + xStart, y + yStart, barWidth, subBartHeight);
|
|
250
|
-
|
|
251
|
-
if (obj['selectedCount'] > eps) {
|
|
252
|
-
g.fillStyle = 'rgb(255,165,0)';
|
|
253
|
-
g.fillRect(
|
|
254
|
-
x + xStart - w * selectLineRatio * 2,
|
|
255
|
-
y + yStart,
|
|
256
|
-
barWidth * selectLineRatio,
|
|
257
|
-
h * obj['selectedCount'] / this.max - gapSize,
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
sum -= obj['count'];
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
//TODO: refactor and simplify it
|
|
266
|
-
findAARandPosition(cell: DG.GridCell, mouseEvent: MouseEvent): {colName: string, aaName: string} | null {
|
|
267
|
-
if (!cell.tableColumn?.name || !this.aminoColumnNames.includes(cell.tableColumn.name))
|
|
268
|
-
return null;
|
|
269
|
-
|
|
270
|
-
const offsetX = mouseEvent.offsetX;
|
|
271
|
-
const offsetY = mouseEvent.offsetY;
|
|
272
|
-
const colName = cell.tableColumn?.name;
|
|
273
|
-
const innerMargin = 0.02;
|
|
274
|
-
const margin = 0.2;
|
|
275
|
-
const bound = cell.bounds;
|
|
276
|
-
const height = 130;
|
|
277
|
-
const x = bound.x + bound.width * margin;
|
|
278
|
-
const y = height * margin / 4;
|
|
279
|
-
const w = bound.width - bound.width * margin * 2;
|
|
280
|
-
const h = height - height * margin;
|
|
281
|
-
const barData = this.barStats[colName];
|
|
282
|
-
const barWidth = w - 10;
|
|
283
|
-
let sum = 0;
|
|
284
|
-
|
|
285
|
-
barData.forEach((obj) => {
|
|
286
|
-
sum += obj['count'];
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
const xStart = x + (w - barWidth) / 2;
|
|
290
|
-
for (const obj of barData) {
|
|
291
|
-
const sBarHeight = h * obj['count'] / this.max;
|
|
292
|
-
const gapSize = sBarHeight * innerMargin;
|
|
293
|
-
const verticalShift = (this.max - sum) / this.max;
|
|
294
|
-
const subBartHeight = sBarHeight - gapSize;
|
|
295
|
-
const yStart = y + h * verticalShift + gapSize / 2;
|
|
296
|
-
|
|
297
|
-
const isIntersectingX = offsetX >= xStart && offsetX <= xStart + barWidth;
|
|
298
|
-
const isIntersectingY = offsetY >= yStart && offsetY <= yStart + subBartHeight;
|
|
299
|
-
|
|
300
|
-
if (isIntersectingX && isIntersectingY)
|
|
301
|
-
return {'colName': colName, 'aaName': obj['name']};
|
|
302
|
-
|
|
303
|
-
sum -= obj['count'];
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/** Requests highlight/select/filter action based on currentBarPart */
|
|
310
|
-
requestAction(event: MouseEvent, barPart: {colName: string, aaName: string} | null): void {
|
|
311
|
-
if (!barPart)
|
|
312
|
-
return;
|
|
313
|
-
const aar = barPart['aaName'];
|
|
314
|
-
const position = barPart['colName'];
|
|
315
|
-
if (event.type === 'click') {
|
|
316
|
-
event.shiftKey ? this.model.modifyCurrentSelection(aar, position) :
|
|
317
|
-
this.model.initCurrentSelection(aar, position);
|
|
318
|
-
} else
|
|
319
|
-
this.model.showTooltip(aar, position, event.clientX, event.clientY);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
import {runKalign} from '../utils/multiple-sequence-alignment';
|
|
3
|
-
|
|
4
|
-
export async function msaWidget(col: DG.Column): Promise<DG.DataFrame> {
|
|
5
|
-
const msaCol = await runKalign(col, true);
|
|
6
|
-
const table = col.dataFrame;
|
|
7
|
-
table.columns.add(msaCol);
|
|
8
|
-
return table;
|
|
9
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import * as grok from 'datagrok-api/grok';
|
|
2
|
-
import * as ui from 'datagrok-api/ui';
|
|
3
|
-
import * as DG from 'datagrok-api/dg';
|
|
4
|
-
import * as C from '../utils/constants';
|
|
5
|
-
import {PeptidesModel} from '../model';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* 3D representation widget of peptide molecule.
|
|
9
|
-
*
|
|
10
|
-
* @export
|
|
11
|
-
* @param {string} pep Peptide string.
|
|
12
|
-
* @return {Promise<DG.Widget>} Widget.
|
|
13
|
-
*/
|
|
14
|
-
export async function peptideMoleculeWidget(pep: string, currentTable: DG.DataFrame): Promise<DG.Widget> {
|
|
15
|
-
const pi = DG.TaskBarProgressIndicator.create('Creating NGL view');
|
|
16
|
-
const separator = currentTable.columns.bySemType(C.SEM_TYPES.ALIGNED_SEQUENCE)!.tags[C.TAGS.SEPARATOR];
|
|
17
|
-
|
|
18
|
-
let widgetHost;
|
|
19
|
-
let smiles = '';
|
|
20
|
-
let molfileStr = '';
|
|
21
|
-
try {
|
|
22
|
-
try {
|
|
23
|
-
const params = {table: currentTable};
|
|
24
|
-
const result = await grok.functions.call('Customerextensions:getPeptideStructure', params) as string[];
|
|
25
|
-
if (result.length !== 0) {
|
|
26
|
-
smiles = result[0];
|
|
27
|
-
molfileStr = result[1];
|
|
28
|
-
throw new Error(`Found structure in DB`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
smiles = getMolecule(pep, separator);
|
|
32
|
-
if (smiles == '')
|
|
33
|
-
throw new Error('Couldn\'t get smiles');
|
|
34
|
-
|
|
35
|
-
molfileStr = (await grok.functions.call('Peptides:SmiTo3D', {smiles})) as string;
|
|
36
|
-
} catch (e) {
|
|
37
|
-
console.warn(e);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
molfileStr = molfileStr.replaceAll('\\n', '\n');
|
|
42
|
-
const stringBlob = new Blob([molfileStr], {type: 'text/plain'});
|
|
43
|
-
const nglHost = ui.div([], {classes: 'd4-ngl-viewer', id: 'ngl-3d-host'});
|
|
44
|
-
|
|
45
|
-
//@ts-ignore
|
|
46
|
-
const stage = new NGL.Stage(nglHost, {backgroundColor: 'white'});
|
|
47
|
-
//@ts-ignore
|
|
48
|
-
stage.loadFile(stringBlob, {ext: 'sdf'}).then(function(comp: NGL.StructureComponent) {
|
|
49
|
-
stage.setSize(300, 300);
|
|
50
|
-
comp.addRepresentation('ball+stick');
|
|
51
|
-
comp.autoView();
|
|
52
|
-
});
|
|
53
|
-
const sketch = grok.chem.svgMol(molfileStr);
|
|
54
|
-
const panel = ui.divH([sketch]);
|
|
55
|
-
|
|
56
|
-
widgetHost = ui.div([panel, nglHost]);
|
|
57
|
-
} catch (e) {
|
|
58
|
-
widgetHost = ui.divText('Couldn\'t get peptide structure');
|
|
59
|
-
}
|
|
60
|
-
} catch (e) {
|
|
61
|
-
widgetHost = ui.divText('Couldn\'t get peptide structure');
|
|
62
|
-
}
|
|
63
|
-
pi.close();
|
|
64
|
-
return new DG.Widget(widgetHost);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function getMolecule(pep: string, separator: string): string {
|
|
68
|
-
const split = pep.split(separator);
|
|
69
|
-
const mols = [];
|
|
70
|
-
const chemPalette = PeptidesModel.chemPalette;
|
|
71
|
-
for (let i = 1; i < split.length - 1; i++) {
|
|
72
|
-
if (split[i] in chemPalette.AASmiles) {
|
|
73
|
-
const aar = chemPalette.AASmiles[split[i]];
|
|
74
|
-
mols[i] = aar.substring(0, aar.length - 1);
|
|
75
|
-
} else if (!split[i] || split[i] == '-')
|
|
76
|
-
mols[i] = '';
|
|
77
|
-
else
|
|
78
|
-
return '';
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return mols.join('') + 'O';
|
|
82
|
-
}
|