@datagrok/peptides 0.8.15 → 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.
Files changed (38) hide show
  1. package/dist/package-test.js +8397 -4112
  2. package/dist/package.js +9609 -4868
  3. package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +306 -215
  4. package/files/aligned.csv +648 -648
  5. package/files/aligned_2.csv +541 -10275
  6. package/files/aligned_3.csv +335 -0
  7. package/helm/JSDraw/Pistoia.HELM.js +27 -0
  8. package/package.json +20 -14
  9. package/package.png +0 -0
  10. package/src/__jest__/remote.test.ts +31 -13
  11. package/src/model.ts +540 -646
  12. package/src/package-test.ts +3 -4
  13. package/src/package.ts +13 -131
  14. package/src/tests/core.ts +56 -20
  15. package/src/tests/peptide-space-test.ts +65 -45
  16. package/src/tests/utils.ts +15 -59
  17. package/src/utils/cell-renderer.ts +140 -262
  18. package/src/utils/constants.ts +8 -4
  19. package/src/utils/filtering-statistics.ts +21 -53
  20. package/src/utils/misc.ts +103 -16
  21. package/src/utils/multiple-sequence-alignment.ts +1 -1
  22. package/src/utils/peptide-similarity-space.ts +2 -2
  23. package/src/utils/types.ts +8 -7
  24. package/src/viewers/peptide-space-viewer.ts +62 -35
  25. package/src/viewers/sar-viewer.ts +33 -20
  26. package/src/widgets/analyze-peptides.ts +26 -10
  27. package/src/widgets/distribution.ts +171 -42
  28. package/src/widgets/manual-alignment.ts +4 -4
  29. package/src/widgets/subst-table.ts +40 -21
  30. package/src/workers/dimensionality-reducer.ts +1 -6
  31. package/{test-Peptides-e702a345ac13-2a8c8b59.html → test-Peptides-4f0c8bae6479-74cbfe68.html} +22 -20
  32. package/detectors.js +0 -9
  33. package/src/monomer-library.ts +0 -193
  34. package/src/tests/msa-tests.ts +0 -27
  35. package/src/utils/chem-palette.ts +0 -280
  36. package/src/viewers/stacked-barchart-viewer.ts +0 -362
  37. package/src/widgets/multiple-sequence-alignment.ts +0 -9
  38. 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,362 +0,0 @@
1
- import * as DG from 'datagrok-api/dg';
2
- import * as rxjs from 'rxjs';
3
- import * as ui from 'datagrok-api/ui';
4
- import {MonomerLibrary} from '../monomer-library';
5
-
6
- import * as C from '../utils/constants';
7
- import * as type from '../utils/types';
8
- import {PeptidesModel} from '../model';
9
-
10
- export function addViewerToHeader(grid: DG.Grid, barchart: StackedBarChart): void {
11
- if (grid.temp['containsBarchart'])
12
- return;
13
-
14
- const compareBarParts = (bar1: type.BarChart.BarPart | null, bar2: type.BarChart.BarPart | null): boolean =>
15
- (bar1 && bar2 && bar1.aaName === bar2.aaName && bar1.colName === bar2.colName) ?? false;
16
-
17
- const eventAction = (mouseMove: MouseEvent): void => {
18
- const cell = grid.hitTest(mouseMove.offsetX, mouseMove.offsetY);
19
- if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.AMINO_ACIDS) {
20
- const newBarPart = barchart.findAARandPosition(cell, mouseMove);
21
- const previousClickedBarPart = barchart._previousClickedBarPart;
22
- if (mouseMove.type === 'click' && compareBarParts(newBarPart, previousClickedBarPart))
23
- barchart.isSameBarClicked = true;
24
- else
25
- barchart.currentBarPart = newBarPart;
26
- barchart.requestAction(mouseMove);
27
- barchart.computeData();
28
- }
29
- };
30
-
31
- // The following events makes the barchart interactive
32
- rxjs.fromEvent<MouseEvent>(grid.overlay, 'mousemove').subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
33
- rxjs.fromEvent<MouseEvent>(grid.overlay, 'click').subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
34
- rxjs.fromEvent<MouseEvent>(grid.overlay, 'mouseout').subscribe(() => barchart.unhighlight());
35
-
36
- barchart.tableCanvas = grid.canvas;
37
-
38
- //Setting grid options
39
- grid.setOptions({'colHeaderHeight': 130});
40
-
41
- grid.onCellTooltip((cell, x, y) => {
42
- if (
43
- cell.tableColumn &&
44
- [C.SEM_TYPES.AMINO_ACIDS, C.SEM_TYPES.ALIGNED_SEQUENCE].includes(cell.tableColumn.semType as C.SEM_TYPES)
45
- ) {
46
- if (!cell.isColHeader) {
47
- const monomerLib = cell.cell.dataFrame.temp[MonomerLibrary.id];
48
- PeptidesModel.chemPalette.showTooltip(cell, x, y, monomerLib);
49
- } else if (barchart.currentBarPart) {
50
- let elements: HTMLElement[] = [];
51
- elements = elements.concat([ui.divText(barchart.currentBarPart.aaName)]);
52
- ui.tooltip.show(ui.divV(elements), x, y);
53
- }
54
- }
55
- return true;
56
- });
57
-
58
- grid.onCellRender.subscribe((args) => {
59
- const context = args.g;
60
- const boundX = args.bounds.x;
61
- const boundY = args.bounds.y;
62
- const boundWidth = args.bounds.width;
63
- const boundHeight = args.bounds.height;
64
- const cell = args.cell;
65
- context.save();
66
- context.beginPath();
67
- context.rect(boundX, boundY, boundWidth, boundHeight);
68
- context.clip();
69
-
70
- if (cell.isColHeader && barchart.aminoColumnNames.includes(cell.gridColumn.name)) {
71
- barchart.renderBarToCanvas(context, cell, boundX, boundY, boundWidth, boundHeight);
72
- args.preventDefault();
73
- }
74
- context.restore();
75
- });
76
-
77
- grid.temp['containsBarchart'] = true;
78
- //FIXME: for some reason barchat didn't show when running analysis. This fixes it, but it's bad. Find a way to fix
79
- // the problem
80
- barchart.unhighlight();
81
- }
82
-
83
- export class StackedBarChart extends DG.JsViewer {
84
- dataEmptyAA: string;
85
- _currentBarPart: type.BarChart.BarPart | null = null;
86
- tableCanvas: HTMLCanvasElement | undefined;
87
- aminoColumnNames: string[] = [];
88
- ord: { [Key: string]: number; } = {};
89
- aminoColumnIndices: {[Key: string]: number} = {};
90
- aggregatedFilterTables: type.DataFrameDict = {};
91
- max = 0;
92
- barStats: {[Key: string]: type.BarChart.BarStatsObject[]} = {};
93
- selected: type.BarChart.BarPart[] = [];
94
- aggregatedSelectedTables: type.DataFrameDict = {};
95
- controller!: PeptidesModel;
96
- isSameBarClicked: boolean = false;
97
- _previousClickedBarPart: type.BarChart.BarPart | null = null;
98
-
99
- constructor() {
100
- super();
101
- this.dataEmptyAA = this.string('dataEmptyAA', '-');
102
- }
103
-
104
- get currentBarPart(): type.BarChart.BarPart | null {return this._currentBarPart;}
105
- set currentBarPart(barPart: type.BarChart.BarPart | null) {
106
- this._currentBarPart = barPart;
107
- this.isSameBarClicked = false;
108
- }
109
-
110
- init(): void {
111
- const groups: {[key: string]: string[]} = {
112
- 'yellow': ['C', 'U'],
113
- 'red': ['G', 'P'],
114
- 'all_green': ['A', 'V', 'I', 'L', 'M', 'F', 'Y', 'W'],
115
- 'light_blue': ['R', 'H', 'K'],
116
- 'dark_blue': ['D', 'E'],
117
- 'orange': ['S', 'T', 'N', 'Q'],
118
- };
119
- let i = 0;
120
-
121
- for (const value of Object.values(groups)) {
122
- for (const obj of value)
123
- this.ord[obj] = i++;
124
- }
125
-
126
- this.aminoColumnNames = [];
127
- }
128
-
129
- // Stream subscriptions
130
- async onTableAttached(): Promise<void> {
131
- this.init();
132
- this.controller = await PeptidesModel.getInstance(this.dataFrame);
133
- // this.controller.init(this.dataFrame);
134
- if (this.dataFrame) {
135
- this.subs.push(DG.debounce(this.dataFrame.selection.onChanged, 50).subscribe((_) => this.computeData()));
136
- this.subs.push(DG.debounce(this.dataFrame.filter.onChanged, 50).subscribe((_) => this.computeData()));
137
- this.subs.push(DG.debounce(this.dataFrame.onValuesChanged, 50).subscribe(() => this.computeData()));
138
- }
139
- }
140
-
141
- // Cancel subscriptions when the viewer is detached
142
- detach(): void {
143
- this.subs.forEach((sub) => sub.unsubscribe());
144
- }
145
-
146
- computeData(): void {
147
- this.aminoColumnNames = [];
148
- this.aminoColumnIndices = {};
149
-
150
- this.dataFrame.columns.names().forEach((name: string) => {
151
- if (this.dataFrame.getCol(name).semType === C.SEM_TYPES.AMINO_ACIDS &&
152
- !this.dataFrame.getCol(name).categories.includes('COOH') &&
153
- !this.dataFrame.getCol(name).categories.includes('NH2')) {
154
- this.aminoColumnIndices[name] = this.aminoColumnNames.length + 1;
155
- this.aminoColumnNames.push(name);
156
- }
157
- });
158
-
159
- this.aggregatedFilterTables = {};
160
- this.aggregatedSelectedTables = {};
161
- //TODO: optimize it, why store so many tables?
162
- this.aminoColumnNames.forEach((name) => {
163
- this.aggregatedFilterTables[name] = this.dataFrame
164
- .groupBy([name])
165
- .whereRowMask(this.dataFrame.filter)
166
- .add('count', name, `${name}_count`)
167
- .aggregate();
168
-
169
- this.aggregatedSelectedTables[name] = this.dataFrame
170
- .groupBy([name])
171
- .whereRowMask(this.dataFrame.selection)
172
- .add('count', name, `${name}_count`)
173
- .aggregate();
174
- });
175
-
176
- this.barStats = {};
177
-
178
- for (const [name, df] of Object.entries(this.aggregatedFilterTables)) {
179
- const colData: {'name': string, 'count': number, 'selectedCount': number}[] = [];
180
- const aminoCol = df.getCol(name);
181
- const aminoCountCol = df.getCol(`${name}_count`);
182
- this.barStats[name] = colData;
183
-
184
- for (let i = 0; i < df.rowCount; i++) {
185
- const amino = aminoCol.get(i);
186
- const aminoCount = aminoCountCol.get(i);
187
- const aminoObj = {'name': amino, 'count': aminoCount, 'selectedCount': 0};
188
- const aggSelectedAminoCol = this.aggregatedSelectedTables[name].getCol(`${name}`);
189
- const aggSelectedCountCol = this.aggregatedSelectedTables[name].getCol(`${name}_count`);
190
-
191
- if (!amino || amino === this.dataEmptyAA)
192
- continue;
193
-
194
- colData.push(aminoObj);
195
-
196
- for (let j = 0; j < aggSelectedCountCol.length; j++) {
197
- const selectedAmino = aggSelectedAminoCol.get(j);
198
- const curAmino = (selectedAmino);
199
- if (curAmino == amino) {
200
- aminoObj['selectedCount'] = aggSelectedCountCol.get(j);
201
- break;
202
- }
203
- }
204
- }
205
-
206
- colData.sort((o1, o2) => this.ord[o2['name']] - this.ord[o1['name']]);
207
- }
208
-
209
- this.max = this.dataFrame.filter.trueCount;
210
- }
211
-
212
- renderBarToCanvas(g: CanvasRenderingContext2D, cell: DG.GridCell, x: number, y: number, w: number, h: number): void {
213
- const name = cell.tableColumn!.name;
214
- const colNameSize = g.measureText(name).width;
215
- const barData = this.barStats[name];
216
- const margin = 0.2;
217
- const innerMargin = 0.02;
218
- const selectLineRatio = 0.1;
219
- let sum = 0;
220
-
221
- barData.forEach((obj) => {
222
- sum += obj['count'];
223
- });
224
-
225
- x = x + w * margin;
226
- y = y + h * margin / 4;
227
- w = w - w * margin * 2;
228
- h = h - h * margin;
229
- const barWidth = w - 10;
230
- g.fillStyle = 'black';
231
- g.textBaseline = 'top';
232
- g.font = `${h * margin / 2}px`;
233
- g.fillText(name, x + (w - colNameSize) / 2, y + h + h * margin / 4);
234
-
235
- barData.forEach((obj) => {
236
- const sBarHeight = h * obj['count'] / this.max;
237
- const gapSize = sBarHeight * innerMargin;
238
- const verticalShift = (this.max - sum) / this.max;
239
- const [color, aarOuter] = PeptidesModel.chemPalette.getColorAAPivot(obj['name']);
240
- const textSize = g.measureText(aarOuter);
241
- const fontSize = 11;
242
- const leftMargin = (w - (aarOuter.length > 1 ? fontSize : textSize.width - 8)) / 2;
243
- const subBartHeight = sBarHeight - gapSize;
244
- const yStart = h * verticalShift + gapSize / 2;
245
- const xStart = (w - barWidth) / 2;
246
- const absX = x + leftMargin;
247
- const absY = y + yStart + subBartHeight / 2 + (aarOuter.length == 1 ? + 4 : 0);
248
- const eps = 0.1;
249
-
250
- g.strokeStyle = color;
251
- g.fillStyle = color;
252
- if (textSize.width <= subBartHeight) {
253
- const origTransform = g.getTransform();
254
-
255
- if (color != PeptidesModel.chemPalette.undefinedColor) {
256
- g.fillRect(x + xStart, y + yStart, barWidth, subBartHeight);
257
- g.fillStyle = 'black';
258
- } else
259
- g.strokeRect(x + xStart + 0.5, y + yStart, barWidth - 1, subBartHeight);
260
-
261
- g.font = `${fontSize}px monospace`;
262
- g.textAlign = 'center';
263
- g.textBaseline = 'bottom';
264
-
265
- if (aarOuter.length > 1) {
266
- g.translate(absX, absY);
267
- g.rotate(Math.PI / 2);
268
- g.translate(-absX, -absY);
269
- }
270
-
271
- g.fillText(aarOuter, absX, absY);
272
- g.setTransform(origTransform);
273
- } else
274
- g.fillRect(x + xStart, y + yStart, barWidth, subBartHeight);
275
-
276
- if (obj['selectedCount'] > eps) {
277
- g.fillStyle = 'rgb(255,165,0)';
278
- g.fillRect(
279
- x + xStart - w * selectLineRatio * 2,
280
- y + yStart,
281
- barWidth * selectLineRatio,
282
- h * obj['selectedCount'] / this.max - gapSize,
283
- );
284
- }
285
-
286
- sum -= obj['count'];
287
- });
288
- }
289
-
290
- findAARandPosition(cell: DG.GridCell, mouseEvent: MouseEvent): {colName: string, aaName: string} | null {
291
- if (!cell.tableColumn?.name || !this.aminoColumnNames.includes(cell.tableColumn.name))
292
- return null;
293
-
294
- const offsetX = mouseEvent.offsetX;
295
- const offsetY = mouseEvent.offsetY;
296
- const colName = cell.tableColumn?.name;
297
- const innerMargin = 0.02;
298
- const margin = 0.2;
299
- const bound = cell.bounds;
300
- const height = 130;
301
- const x = bound.x + bound.width * margin;
302
- const y = height * margin / 4;
303
- const w = bound.width - bound.width * margin * 2;
304
- const h = height - height * margin;
305
- const barData = this.barStats[colName];
306
- const barWidth = w - 10;
307
- let sum = 0;
308
-
309
- barData.forEach((obj) => {
310
- sum += obj['count'];
311
- });
312
-
313
- const xStart = x + (w - barWidth) / 2;
314
- for (const obj of barData) {
315
- const sBarHeight = h * obj['count'] / this.max;
316
- const gapSize = sBarHeight * innerMargin;
317
- const verticalShift = (this.max - sum) / this.max;
318
- const subBartHeight = sBarHeight - gapSize;
319
- const yStart = y + h * verticalShift + gapSize / 2;
320
-
321
- const isIntersectingX = offsetX >= xStart && offsetX <= xStart + barWidth;
322
- const isIntersectingY = offsetY >= yStart && offsetY <= yStart + subBartHeight;
323
-
324
- if (isIntersectingX && isIntersectingY)
325
- return {'colName': colName, 'aaName': obj['name']};
326
-
327
- sum -= obj['count'];
328
- }
329
-
330
- return null;
331
- }
332
-
333
- unhighlight(): void {
334
- ui.tooltip.hide();
335
- this.computeData();
336
- }
337
-
338
- /**
339
- * Requests highlight/select/filter action based on currentBarPart
340
- * @param event
341
- * @returns
342
- */
343
- requestAction(event: MouseEvent): void {
344
- if (!this._currentBarPart)
345
- return;
346
- let aar = this._currentBarPart['aaName'];
347
- let position = this._currentBarPart['colName'];
348
- if (event.type === 'click') {
349
- if (this.isSameBarClicked) {
350
- aar = position = C.CATEGORIES.ALL;
351
- this.currentBarPart = null;
352
- }
353
- this.controller.setSARGridCellAt(aar, position);
354
- this._previousClickedBarPart = this._currentBarPart;
355
- } else {
356
- ui.tooltip.showRowGroup(this.dataFrame, (i) => {
357
- const currentAAR = this.dataFrame.get(position, i);
358
- return currentAAR === aar;
359
- }, event.offsetX, event.offsetY);
360
- }
361
- }
362
- }
@@ -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
- }