@datagrok/bio 2.8.4 → 2.9.0
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/CHANGELOG.md +19 -9
- package/README.md +39 -20
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/dockerfiles/Dockerfile +5 -4
- package/package.json +3 -3
- package/src/analysis/sequence-activity-cliffs.ts +8 -7
- package/src/analysis/sequence-similarity-viewer.ts +8 -8
- package/src/apps/web-logo-app.ts +26 -6
- package/src/calculations/monomerLevelMols.ts +6 -3
- package/src/package-test.ts +1 -0
- package/src/package-types.ts +0 -1
- package/src/package.ts +52 -10
- package/src/substructure-search/substructure-search.ts +84 -55
- package/src/tests/activity-cliffs-tests.ts +1 -1
- package/src/tests/converters-test.ts +1 -1
- package/src/tests/detectors-tests.ts +2 -2
- package/src/tests/msa-tests.ts +2 -3
- package/src/tests/renderers-test.ts +37 -3
- package/src/tests/scoring.ts +38 -0
- package/src/tests/splitters-test.ts +27 -1
- package/src/tests/units-handler-splitted-tests.ts +19 -12
- package/src/tests/units-handler-tests.ts +15 -15
- package/src/utils/cell-renderer.ts +31 -20
- package/src/utils/monomer-cell-renderer.ts +14 -14
- package/src/utils/save-as-fasta.ts +1 -1
- package/src/utils/split-to-monomers.ts +40 -6
- package/src/utils/ui-utils.ts +4 -4
- package/src/viewers/vd-regions-viewer.ts +88 -51
- package/src/viewers/web-logo-viewer.ts +307 -310
- package/src/widgets/composition-analysis-widget.ts +6 -2
|
@@ -14,8 +14,16 @@ import {
|
|
|
14
14
|
} from '@datagrok-libraries/utils/src/test';
|
|
15
15
|
import * as C from '../utils/constants';
|
|
16
16
|
import {_package, getHelmMonomers} from '../package';
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
TAGS as bioTAGS,
|
|
19
|
+
splitterAsFasta,
|
|
20
|
+
splitterAsHelm,
|
|
21
|
+
NOTATION
|
|
22
|
+
} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
18
23
|
import {splitToMonomersUI} from '../utils/split-to-monomers';
|
|
24
|
+
import {SEMTYPE} from 'datagrok-api/dg';
|
|
25
|
+
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
26
|
+
import {TAGS} from '../utils/constants';
|
|
19
27
|
|
|
20
28
|
|
|
21
29
|
category('splitters', async () => {
|
|
@@ -35,6 +43,12 @@ category('splitters', async () => {
|
|
|
35
43
|
['M', 'MeI', 'Y', 'K', 'E', 'T', 'L', 'L', 'MeF', 'P',
|
|
36
44
|
'K', 'T', 'D', 'F', 'P', 'M', 'R', 'G', 'G', 'L', 'MeA'],
|
|
37
45
|
],
|
|
46
|
+
fastaFromHelm: [
|
|
47
|
+
'[meI][Pip][dK][Thr_PO3H2][L-hArg(Et,Et)][D-Tyr_Et][Tyr_ab-dehydroMe][dV]EN[D-Orn][D-aThr][Phe_4Me]',
|
|
48
|
+
['meI', 'Pip', 'dK', 'Thr_PO3H2', 'L-hArg(Et,Et)', 'D-Tyr_Et', 'Tyr_ab-dehydroMe', 'dV', 'E', 'N', 'D-Orn',
|
|
49
|
+
'D-aThr', 'Phe_4Me'],
|
|
50
|
+
],
|
|
51
|
+
|
|
38
52
|
helm1: [
|
|
39
53
|
'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}$$$',
|
|
40
54
|
['meI', 'hHis', 'Aca', 'N', 'T', 'dE', 'Thr_PO3H2', 'Aca', 'D-Tyr_Et',
|
|
@@ -68,6 +82,7 @@ category('splitters', async () => {
|
|
|
68
82
|
};
|
|
69
83
|
|
|
70
84
|
test('fastaMulti', async () => { await _testFastaSplitter(data.fastaMulti[0], data.fastaMulti[1]); });
|
|
85
|
+
test('fastaFromHelm', async () => { await _testFastaSplitter(data.fastaFromHelm[0], data.fastaFromHelm[1]); });
|
|
71
86
|
|
|
72
87
|
test('helm1', async () => { await _testHelmSplitter(data.helm1[0], data.helm1[1]); });
|
|
73
88
|
test('helm2', async () => { await _testHelmSplitter(data.helm2[0], data.helm2[1]); });
|
|
@@ -78,6 +93,7 @@ category('splitters', async () => {
|
|
|
78
93
|
test('testHelm2', async () => { await _testHelmSplitter(data.testHelm2[0], data.testHelm2[1]); });
|
|
79
94
|
test('testHelm3', async () => { await _testHelmSplitter(data.testHelm3[0], data.testHelm3[1]); });
|
|
80
95
|
|
|
96
|
+
|
|
81
97
|
test('splitToMonomers', async () => {
|
|
82
98
|
const df: DG.DataFrame = await grok.dapi.files.readCsv('System:AppData/Bio/samples/MSA.csv');
|
|
83
99
|
|
|
@@ -122,6 +138,16 @@ PEPTIDE1{hHis.Aca.Cys_SEt}$$$,5.72388
|
|
|
122
138
|
throw new Error(msgs.join(' '));
|
|
123
139
|
}
|
|
124
140
|
});
|
|
141
|
+
|
|
142
|
+
// test('helmAsFasta', async () => {
|
|
143
|
+
// // The columns can't be empty for UnitsHandler
|
|
144
|
+
// /* eslint-disable max-len */
|
|
145
|
+
// const srcSeq = '[meI][Pip][dK][Thr_PO3H2][L-hArg(Et,Et)][D-Tyr_Et][Tyr_ab-dehydroMe][dV]EN[D-Orn][D-aThr][Phe_4Me]';
|
|
146
|
+
// const tgtSeqA = ['meI', 'Pip', 'dK', 'Thr_PO3H2', 'L-hArg(Et,Et)', 'D-Tyr_Et', 'Tyr_ab-dehydroMe', 'dV', 'E', 'N', 'D-Orn', 'D-aThr', 'Phe_4Me'];
|
|
147
|
+
// /* eslint-enable max-len */
|
|
148
|
+
// const resSeqA = splitterAsFasta(srcSeq);
|
|
149
|
+
// expectArray(resSeqA, tgtSeqA);
|
|
150
|
+
// });
|
|
125
151
|
});
|
|
126
152
|
|
|
127
153
|
export async function _testFastaSplitter(src: string, tgt: string[]) {
|
|
@@ -2,11 +2,18 @@ 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
|
|
|
5
|
+
import wu from 'wu';
|
|
6
|
+
|
|
5
7
|
import {category, test, expect, expectArray} from '@datagrok-libraries/utils/src/test';
|
|
6
|
-
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
8
|
+
import {GapSymbols, UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
9
|
+
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
10
|
+
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
7
11
|
|
|
8
12
|
category('UnitsHandler', () => {
|
|
9
|
-
const
|
|
13
|
+
const fG = GapSymbols[NOTATION.FASTA];
|
|
14
|
+
const hG = GapSymbols[NOTATION.HELM];
|
|
15
|
+
const sG = GapSymbols[NOTATION.SEPARATOR];
|
|
16
|
+
const data: { [testName: string]: { src: { csv: string }, tgt: { splitted: (string[] | string)[] } } } = {
|
|
10
17
|
fasta: {
|
|
11
18
|
src: {
|
|
12
19
|
csv: `seq
|
|
@@ -16,9 +23,9 @@ TTCAAC`
|
|
|
16
23
|
},
|
|
17
24
|
tgt: {
|
|
18
25
|
splitted: [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
'ACGTC',
|
|
27
|
+
'CAGTGT',
|
|
28
|
+
'TTCAAC',
|
|
22
29
|
]
|
|
23
30
|
}
|
|
24
31
|
},
|
|
@@ -32,9 +39,9 @@ ACCGTACT`,
|
|
|
32
39
|
tgt: {
|
|
33
40
|
splitted: [
|
|
34
41
|
//@formatter:off
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
'AC-GT-CT',
|
|
43
|
+
'CAC-T-GT',
|
|
44
|
+
'ACCGTACT',
|
|
38
45
|
//@formatter:on
|
|
39
46
|
]
|
|
40
47
|
}
|
|
@@ -65,8 +72,8 @@ rut12-rty-her2---wert`
|
|
|
65
72
|
tgt: {
|
|
66
73
|
splitted: [
|
|
67
74
|
['abc', 'dfgg', 'abc1', 'cfr3', 'rty', 'wert'],
|
|
68
|
-
['rut12', 'her2', 'rty',
|
|
69
|
-
['rut12', 'rty', 'her2',
|
|
75
|
+
['rut12', 'her2', 'rty', sG, 'abc1', 'dfgg'],
|
|
76
|
+
['rut12', 'rty', 'her2', sG, sG, 'wert'],
|
|
70
77
|
]
|
|
71
78
|
}
|
|
72
79
|
},
|
|
@@ -99,8 +106,8 @@ PEPTIDE1{meI.hHis.Aca.Cys_SEt.T.dK.Thr_PO3H2}$$$$`
|
|
|
99
106
|
expect(col.semType, DG.SEMTYPE.MACROMOLECULE);
|
|
100
107
|
|
|
101
108
|
const uh = UnitsHandler.getOrCreate(col);
|
|
102
|
-
const
|
|
103
|
-
expectArray(
|
|
109
|
+
const resSplitted: ISeqSplitted[] = uh.splitted;
|
|
110
|
+
expectArray(resSplitted, testData.tgt.splitted);
|
|
104
111
|
});
|
|
105
112
|
}
|
|
106
113
|
});
|
|
@@ -41,25 +41,25 @@ category('UnitsHandler', () => {
|
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
test('Seq-Fasta-units', async () => {
|
|
44
|
-
const [_df, uh] = await
|
|
44
|
+
const [_df, uh] = await loadCsvWithDetection(seqDna);
|
|
45
45
|
expect(uh.notation, NOTATION.FASTA);
|
|
46
46
|
expect(uh.isMsa(), false);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
test('Seq-Fasta-MSA-units', async () => {
|
|
50
|
-
const [_df, uh] = await
|
|
50
|
+
const [_df, uh] = await loadCsvWithDetection(seqDnaMsa);
|
|
51
51
|
expect(uh.notation, NOTATION.FASTA);
|
|
52
52
|
expect(uh.isMsa(), true);
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
test('Seq-Helm', async () => {
|
|
56
|
-
const [_df, uh] = await
|
|
56
|
+
const [_df, uh] = await loadCsvWithDetection(seqHelm);
|
|
57
57
|
expect(uh.notation, NOTATION.HELM);
|
|
58
58
|
expect(uh.isHelm(), true);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
test('Seq-UN', async () => {
|
|
62
|
-
const [_df, uh] = await
|
|
62
|
+
const [_df, uh] = await loadCsvWithDetection(seqUn);
|
|
63
63
|
expect(uh.notation, NOTATION.SEPARATOR);
|
|
64
64
|
expect(uh.separator, '-');
|
|
65
65
|
expect(uh.alphabet, ALPHABET.UN);
|
|
@@ -79,15 +79,15 @@ category('UnitsHandler', () => {
|
|
|
79
79
|
return [df, uh];
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
async function loadCsvWithTag(csv: string, tag: string, value: string):
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
82
|
+
// async function loadCsvWithTag(csv: string, tag: string, value: string):
|
|
83
|
+
// Promise<[df: DG.DataFrame, uh: UnitsHandler]> {
|
|
84
|
+
// const df = DG.DataFrame.fromCsv(csv);
|
|
85
|
+
// const col = df.getCol('seq');
|
|
86
|
+
// col.setTag(tag, value);
|
|
87
|
+
// col.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
88
|
+
// if (value === NOTATION.SEPARATOR)
|
|
89
|
+
// col.setTag(TAGS.separator, '-');
|
|
90
|
+
// const uh = UnitsHandler.getOrCreate(df.getCol('seq'));
|
|
91
|
+
// return [df, uh];
|
|
92
|
+
// }
|
|
93
93
|
});
|
|
@@ -2,13 +2,12 @@ import * as grok from 'datagrok-api/grok';
|
|
|
2
2
|
import * as DG from 'datagrok-api/dg';
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import wu from 'wu';
|
|
6
|
+
|
|
6
7
|
import {printLeftOrCentered, DrawStyle} from '@datagrok-libraries/bio/src/utils/cell-renderer';
|
|
7
|
-
import * as C from './constants';
|
|
8
8
|
import {MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
|
|
9
9
|
import {
|
|
10
10
|
getPaletteByType,
|
|
11
|
-
getSplitter,
|
|
12
11
|
monomerToShort,
|
|
13
12
|
MonomerToShortFunc,
|
|
14
13
|
NOTATION,
|
|
@@ -18,8 +17,17 @@ import {
|
|
|
18
17
|
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
19
18
|
import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
|
|
20
19
|
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
Temps as mmcrTemps, Tags as mmcrTags,
|
|
23
|
+
tempTAGS, rendererSettingsChangedState
|
|
24
|
+
} from '../utils/cell-renderer-consts';
|
|
25
|
+
import * as C from './constants';
|
|
26
|
+
|
|
27
|
+
import {_package, getBioLib} from '../package';
|
|
28
|
+
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
29
|
+
import {getSplitter} from '@datagrok-libraries/bio/src/utils/macromolecule/utils';
|
|
30
|
+
|
|
23
31
|
|
|
24
32
|
type TempType = { [tagName: string]: any };
|
|
25
33
|
|
|
@@ -30,20 +38,21 @@ function getUpdatedWidth(grid: DG.Grid | null, g: CanvasRenderingContext2D, x: n
|
|
|
30
38
|
return grid ? Math.min(grid.canvas.width - x, w) : g.canvas.width - x;
|
|
31
39
|
}
|
|
32
40
|
|
|
33
|
-
export function processSequence(subParts:
|
|
34
|
-
const simplified = !subParts.some((amino, index) =>
|
|
41
|
+
export function processSequence(subParts: ISeqSplitted): [string[], boolean] {
|
|
42
|
+
const simplified = !wu.enumerate(subParts).some(([amino, index]) =>
|
|
35
43
|
amino.length > 1 &&
|
|
36
44
|
index != 0 &&
|
|
37
45
|
index != subParts.length - 1);
|
|
38
46
|
|
|
39
47
|
const text: string[] = [];
|
|
40
48
|
const gap = simplified ? '' : ' ';
|
|
41
|
-
|
|
49
|
+
for (const [amino, index] of wu.enumerate(subParts)) {
|
|
50
|
+
let aminoRes = amino;
|
|
42
51
|
if (index < subParts.length)
|
|
43
|
-
|
|
52
|
+
aminoRes += `${amino ? '' : '-'}${gap}`;
|
|
44
53
|
|
|
45
|
-
text.push(
|
|
46
|
-
}
|
|
54
|
+
text.push(aminoRes);
|
|
55
|
+
}
|
|
47
56
|
return [text, simplified];
|
|
48
57
|
}
|
|
49
58
|
|
|
@@ -188,11 +197,12 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
188
197
|
|
|
189
198
|
const separator = tableCol.getTag(bioTAGS.separator) ?? '';
|
|
190
199
|
const splitLimit = w / 5;
|
|
191
|
-
const
|
|
200
|
+
const uh = UnitsHandler.getOrCreate(tableCol);
|
|
201
|
+
const splitterFunc: SplitterFunc = uh.getSplitter(splitLimit);
|
|
192
202
|
|
|
193
203
|
const tempReferenceSequence: string | null = tableColTemp[tempTAGS.referenceSequence];
|
|
194
204
|
const tempCurrentWord: string | null = tableColTemp[tempTAGS.currentWord];
|
|
195
|
-
const referenceSequence:
|
|
205
|
+
const referenceSequence: ISeqSplitted = splitterFunc(
|
|
196
206
|
((tempReferenceSequence != null) && (tempReferenceSequence != '')) ?
|
|
197
207
|
tempReferenceSequence : tempCurrentWord ?? '');
|
|
198
208
|
|
|
@@ -226,7 +236,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
226
236
|
// maxLengthWords = colTemp[tempTAGS.bioMaxLengthWords];
|
|
227
237
|
// }
|
|
228
238
|
|
|
229
|
-
const subParts:
|
|
239
|
+
const subParts: ISeqSplitted = splitterFunc(value);
|
|
230
240
|
/* let x1 = x; */
|
|
231
241
|
let color = undefinedColor;
|
|
232
242
|
let drawStyle = DrawStyle.classic;
|
|
@@ -234,7 +244,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
234
244
|
if (aligned && aligned.includes('MSA') && units == NOTATION.SEPARATOR)
|
|
235
245
|
drawStyle = DrawStyle.MSA;
|
|
236
246
|
|
|
237
|
-
for (const [
|
|
247
|
+
for (const [amino, index] of wu.enumerate(subParts)) {
|
|
238
248
|
color = palette.get(amino);
|
|
239
249
|
g.fillStyle = undefinedColor;
|
|
240
250
|
const last = index === subParts.length - 1;
|
|
@@ -281,9 +291,10 @@ export class MacromoleculeDifferenceCellRenderer extends DG.GridCellRenderer {
|
|
|
281
291
|
_cellStyle: DG.GridCellStyle): void {
|
|
282
292
|
const grid = gridCell.grid;
|
|
283
293
|
const cell = gridCell.cell;
|
|
294
|
+
const tableCol = gridCell.tableColumn as DG.Column<string>;
|
|
284
295
|
const s: string = cell.value ?? '';
|
|
285
|
-
const separator =
|
|
286
|
-
const units: string =
|
|
296
|
+
const separator = tableCol.tags[bioTAGS.separator];
|
|
297
|
+
const units: string = tableCol.tags[DG.TAGS.UNITS];
|
|
287
298
|
w = getUpdatedWidth(grid, g, x, w);
|
|
288
299
|
//TODO: can this be replaced/merged with splitSequence?
|
|
289
300
|
const [s1, s2] = s.split('#');
|
|
@@ -300,14 +311,14 @@ export function drawMoleculeDifferenceOnCanvas(
|
|
|
300
311
|
y: number,
|
|
301
312
|
w: number,
|
|
302
313
|
h: number,
|
|
303
|
-
subParts1:
|
|
304
|
-
subParts2:
|
|
314
|
+
subParts1: ISeqSplitted,
|
|
315
|
+
subParts2: ISeqSplitted,
|
|
305
316
|
units: string,
|
|
306
317
|
fullStringLength?: boolean,
|
|
307
318
|
molDifferences?: { [key: number]: HTMLCanvasElement },
|
|
308
319
|
): void {
|
|
309
320
|
if (subParts1.length !== subParts2.length) {
|
|
310
|
-
const sequences: IComparedSequences = fillShorterSequence(subParts1, subParts2);
|
|
321
|
+
const sequences: IComparedSequences = fillShorterSequence(wu(subParts1).toArray(), wu(subParts2).toArray());
|
|
311
322
|
subParts1 = sequences.subParts1;
|
|
312
323
|
subParts2 = sequences.subParts2;
|
|
313
324
|
}
|
|
@@ -16,16 +16,18 @@ const Tags = new class {
|
|
|
16
16
|
const svgMolOptions = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
|
|
17
17
|
|
|
18
18
|
export class MonomerTooltipHandler {
|
|
19
|
-
private readonly
|
|
19
|
+
private readonly gridCol: DG.GridColumn;
|
|
20
20
|
|
|
21
|
-
constructor(
|
|
22
|
-
this.
|
|
23
|
-
this.grid.onCellTooltip(this.onCellTooltip.bind(this));
|
|
21
|
+
constructor(gridCol: DG.GridColumn) {
|
|
22
|
+
this.gridCol = gridCol;
|
|
23
|
+
this.gridCol.grid.onCellTooltip(this.onCellTooltip.bind(this));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
private onCellTooltip(gridCell: DG.GridCell, x: number, y: number): any {
|
|
27
|
-
if (
|
|
28
|
-
gridCell.
|
|
27
|
+
if (
|
|
28
|
+
gridCell.grid.dart != this.gridCol.grid.dart || gridCell.gridColumn.dart != this.gridCol.dart ||
|
|
29
|
+
!gridCell.tableColumn || !gridCell.isTableCell
|
|
30
|
+
) return false;
|
|
29
31
|
|
|
30
32
|
const alphabet = gridCell.tableColumn.getTag(bioTAGS.alphabet);
|
|
31
33
|
const monomerName = gridCell.cell.value;
|
|
@@ -46,13 +48,11 @@ export class MonomerTooltipHandler {
|
|
|
46
48
|
return true; // To prevent default tooltip behaviour
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
public static getOrCreate(
|
|
50
|
-
|
|
51
|
-
if (!
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
return gridTemp[Tags.tooltipHandlerTemp];
|
|
51
|
+
public static getOrCreate(gridCol: DG.GridColumn): MonomerTooltipHandler {
|
|
52
|
+
let res = gridCol.temp[Tags.tooltipHandlerTemp];
|
|
53
|
+
if (!res)
|
|
54
|
+
res = gridCol.temp[Tags.tooltipHandlerTemp] = new MonomerTooltipHandler(gridCol);
|
|
55
|
+
return res;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -81,7 +81,7 @@ export class MonomerCellRenderer extends DG.GridCellRenderer {
|
|
|
81
81
|
_cellStyle: DG.GridCellStyle
|
|
82
82
|
): void {
|
|
83
83
|
if (gridCell.gridRow < 0) return;
|
|
84
|
-
MonomerTooltipHandler.getOrCreate(gridCell.
|
|
84
|
+
MonomerTooltipHandler.getOrCreate(gridCell.gridColumn);
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
g.font = `12px monospace`;
|
|
@@ -107,7 +107,7 @@ export function wrapSequence(seq: string, splitter: SplitterFunc, lineWidth: num
|
|
|
107
107
|
const seqLineList: string[] = [];
|
|
108
108
|
while (seqPos < seqLength) {
|
|
109
109
|
/* join sliced monomer into line */
|
|
110
|
-
const seqLine: string[] = seqMonomerList.slice(seqPos, seqPos + lineWidth);
|
|
110
|
+
const seqLine: string[] = wu(seqMonomerList).slice(seqPos, seqPos + lineWidth).toArray();
|
|
111
111
|
const seqLineTxt: string = seqLine.map((m) => m.length > 1 ? `[${m}]` : m).join('');
|
|
112
112
|
seqLineList.push(seqLineTxt);
|
|
113
113
|
seqPos += seqLine.length;
|
|
@@ -1,10 +1,13 @@
|
|
|
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
|
+
|
|
1
5
|
import {delay} from '@datagrok-libraries/utils/src/test';
|
|
2
6
|
import {checkInputColumnUI} from './check-input-column';
|
|
3
7
|
import {splitAlignedSequences} from '@datagrok-libraries/bio/src/utils/splitter';
|
|
4
8
|
import * as C from './constants';
|
|
5
9
|
import {TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
6
|
-
import
|
|
7
|
-
import * as DG from 'datagrok-api/dg';
|
|
10
|
+
import {SEM_TYPES} from './constants';
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
export async function splitToMonomersUI(table: DG.DataFrame, seqCol: DG.Column<string>): Promise<DG.DataFrame> {
|
|
@@ -14,6 +17,7 @@ export async function splitToMonomersUI(table: DG.DataFrame, seqCol: DG.Column<s
|
|
|
14
17
|
if (!checkInputColumnUI(seqCol, 'Sequence space')) return table;
|
|
15
18
|
|
|
16
19
|
const tempDf = splitAlignedSequences(seqCol);
|
|
20
|
+
tempDf.name = 'splitToMonomers';
|
|
17
21
|
const originalDf = seqCol.dataFrame;
|
|
18
22
|
for (const tempCol of tempDf.columns) {
|
|
19
23
|
// TODO: GROK-11212
|
|
@@ -21,9 +25,39 @@ export async function splitToMonomersUI(table: DG.DataFrame, seqCol: DG.Column<s
|
|
|
21
25
|
tempCol.semType = C.SEM_TYPES.MONOMER;
|
|
22
26
|
tempCol.setTag(bioTAGS.alphabet, seqCol.getTag(bioTAGS.alphabet));
|
|
23
27
|
}
|
|
24
|
-
// Create the new data frame to enable platform to setup cell renderers
|
|
25
|
-
const newDf = originalDf.join(tempDf, [], [], undefined, undefined, DG.JOIN_TYPE.LEFT, false);
|
|
26
|
-
grok.shell.addTableView(newDf);
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
const colNameRe = /(\d+)(?: \((\d+)\))?/;
|
|
30
|
+
const generateNewColName = (srcName: string): string => {
|
|
31
|
+
colNameRe.lastIndex = 0;
|
|
32
|
+
const ma = srcName.match(colNameRe);
|
|
33
|
+
if (!ma) return srcName;
|
|
34
|
+
return `${ma[1]} (${parseInt(ma[2] ?? 0) + 1})`;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// if (tempDf.columns.length === 0) return;
|
|
38
|
+
|
|
39
|
+
for (let tempColI = 0; tempColI < tempDf.columns.length; tempColI++) {
|
|
40
|
+
const tempCol = tempDf.columns.byIndex(tempColI);
|
|
41
|
+
tempCol.semType = SEM_TYPES.MONOMER;
|
|
42
|
+
tempCol.setTag(bioTAGS.alphabet, seqCol.getTag(bioTAGS.alphabet));
|
|
43
|
+
|
|
44
|
+
const wdMax = 100;
|
|
45
|
+
let wdCount = 0;
|
|
46
|
+
while (originalDf.columns.byName(tempCol.name) && wdCount < wdMax) {
|
|
47
|
+
tempCol.name = generateNewColName(tempCol.name);
|
|
48
|
+
wdCount++;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
originalDf.columns.add(tempCol);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// originalDf.join(tempDf, [], [], undefined, undefined, DG.JOIN_TYPE.LEFT, true);
|
|
55
|
+
await grok.data.detectSemanticTypes(originalDf);
|
|
56
|
+
|
|
57
|
+
for (let tempColI = 0; tempColI < tempDf.columns.length; tempColI++) {
|
|
58
|
+
const tempCol = tempDf.columns.byIndex(tempColI);
|
|
59
|
+
tempCol.setTag(DG.TAGS.CELL_RENDERER, 'Monomer');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return originalDf;
|
|
29
63
|
}
|
package/src/utils/ui-utils.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as grok from 'datagrok-api/grok';
|
|
2
2
|
import * as DG from 'datagrok-api/dg';
|
|
3
3
|
|
|
4
|
-
export function
|
|
5
|
-
const
|
|
6
|
-
if (
|
|
4
|
+
export function getMacromoleculeColumns(): DG.Column<any>[] | any {
|
|
5
|
+
const columns = grok.shell.t.columns.bySemTypeAll(DG.SEMTYPE.MACROMOLECULE);
|
|
6
|
+
if (columns === null) {
|
|
7
7
|
grok.shell.error('Current table does not contain macromolecules');
|
|
8
8
|
return;
|
|
9
9
|
}
|
|
10
|
-
return
|
|
10
|
+
return columns;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function updateDivInnerHTML(div: HTMLElement, content: string | Node): void {
|
|
@@ -42,6 +42,24 @@ const vrt = VdRegionType;
|
|
|
42
42
|
// new VdRegion(vrt.FR, 'FR4', 'Heavy', 7, '118', null/*128*/),
|
|
43
43
|
// ];
|
|
44
44
|
|
|
45
|
+
export enum PROPS_CATS {
|
|
46
|
+
STYLE = 'Style',
|
|
47
|
+
BEHAVIOR = 'Behavior',
|
|
48
|
+
LAYOUT = 'Layout',
|
|
49
|
+
DATA = 'Data',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export enum PROPS {
|
|
53
|
+
// -- Data --
|
|
54
|
+
skipEmptyPositions = 'skipEmptyPositions',
|
|
55
|
+
regionTypes = 'regionTypes',
|
|
56
|
+
chains = 'chains',
|
|
57
|
+
|
|
58
|
+
// -- Style --
|
|
59
|
+
positionWidth = 'positionWidth',
|
|
60
|
+
positionHeight = 'positionHeight',
|
|
61
|
+
}
|
|
62
|
+
|
|
45
63
|
/** Viewer with tabs based on description of chain regions.
|
|
46
64
|
* Used to define regions of an immunoglobulin LC.
|
|
47
65
|
*/
|
|
@@ -69,20 +87,24 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
69
87
|
constructor() {
|
|
70
88
|
super();
|
|
71
89
|
|
|
90
|
+
// -- Data --
|
|
91
|
+
this.skipEmptyPositions = this.bool(PROPS.skipEmptyPositions, false,
|
|
92
|
+
{category: PROPS_CATS.DATA});
|
|
93
|
+
|
|
72
94
|
// To prevent ambiguous numbering scheme in MLB
|
|
73
|
-
this.regionTypes = this.stringList(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
this.positionWidth = this.float(
|
|
81
|
-
editor: 'slider', min: 0, max: 64,
|
|
95
|
+
this.regionTypes = this.stringList(PROPS.regionTypes, [vrt.CDR], {
|
|
96
|
+
category: PROPS_CATS.DATA, choices: Object.values(vrt).filter((t) => t != vrt.Unknown)
|
|
97
|
+
}) as VdRegionType[];
|
|
98
|
+
this.chains = this.stringList(PROPS.chains, ['Heavy', 'Light'],
|
|
99
|
+
{category: PROPS_CATS.DATA, choices: ['Heavy', 'Light']});
|
|
100
|
+
|
|
101
|
+
// -- Layout --
|
|
102
|
+
this.positionWidth = this.float(PROPS.positionWidth, 16, {
|
|
103
|
+
category: PROPS_CATS.LAYOUT, editor: 'slider', min: 0, max: 64,
|
|
82
104
|
description: 'Internal WebLogo viewers property width of position. A value of zero means autofit to the width.'
|
|
83
105
|
});
|
|
84
|
-
this.positionHeight = this.string(
|
|
85
|
-
{choices: Object.keys(PositionHeight)}) as PositionHeight;
|
|
106
|
+
this.positionHeight = this.string(PROPS.positionHeight, PositionHeight.Entropy,
|
|
107
|
+
{category: PROPS_CATS.LAYOUT, choices: Object.keys(PositionHeight)}) as PositionHeight;
|
|
86
108
|
}
|
|
87
109
|
|
|
88
110
|
public async init() {
|
|
@@ -140,28 +162,35 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
140
162
|
return;
|
|
141
163
|
}
|
|
142
164
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
break;
|
|
149
|
-
case 'sequenceColumnNamePostfix':
|
|
150
|
-
break;
|
|
151
|
-
// for (let orderI = 0; orderI < this.logos.length; orderI++) {
|
|
152
|
-
// for (let chainI = 0; chainI < this.chains.length; chainI++) {
|
|
153
|
-
// const chain: string = this.chains[chainI];
|
|
154
|
-
// this.logos[orderI][chain].setOptions({skipEmptyPositions: this.skipEmptyPositions});
|
|
155
|
-
// }
|
|
156
|
-
// }
|
|
157
|
-
// this.calcSize();
|
|
158
|
-
}
|
|
165
|
+
switch (property.name) {
|
|
166
|
+
case PROPS.regionTypes:
|
|
167
|
+
case PROPS.chains:
|
|
168
|
+
this.setData(this.dataFrame, this.regions);
|
|
169
|
+
break;
|
|
159
170
|
}
|
|
160
171
|
|
|
161
172
|
switch (property.name) {
|
|
162
|
-
case
|
|
163
|
-
|
|
164
|
-
|
|
173
|
+
case PROPS.skipEmptyPositions:
|
|
174
|
+
for (let orderI = 0; orderI < this.logos.length; ++orderI) {
|
|
175
|
+
for (const chain of this.chains)
|
|
176
|
+
this.logos[orderI][chain].setOptions({[wlPROPS.skipEmptyPositions]: this.skipEmptyPositions});
|
|
177
|
+
}
|
|
178
|
+
this.calcSize();
|
|
179
|
+
break;
|
|
180
|
+
|
|
181
|
+
case PROPS.positionWidth:
|
|
182
|
+
this.calcSize();
|
|
183
|
+
break;
|
|
184
|
+
|
|
185
|
+
case PROPS.positionHeight:
|
|
186
|
+
for (let orderI = 0; orderI < this.logos.length; ++orderI) {
|
|
187
|
+
for (const chain of this.chains)
|
|
188
|
+
this.logos[orderI][chain].setOptions({[wlPROPS.positionWidth]: this.positionWidth});
|
|
189
|
+
}
|
|
190
|
+
this.calcSize();
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
default:
|
|
165
194
|
this.setData(this.dataFrame, this.regions); // onPropertyChanged
|
|
166
195
|
break;
|
|
167
196
|
}
|
|
@@ -265,8 +294,10 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
265
294
|
this.logos = new Array(orderList.length);
|
|
266
295
|
for (let orderI = 0; orderI < orderList.length; ++orderI)
|
|
267
296
|
this.logos[orderI] = {};
|
|
268
|
-
for (const [orderI, chain, wl] of logoList)
|
|
297
|
+
for (const [orderI, chain, wl] of logoList) {
|
|
269
298
|
this.logos[orderI][chain] = wl;
|
|
299
|
+
this.viewSubs.push(wl.onFreqsCalculated.subscribe(() => { this.calcSize(); }));
|
|
300
|
+
}
|
|
270
301
|
|
|
271
302
|
// ui.tableFromMap()
|
|
272
303
|
// DG.HtmlTable.create()
|
|
@@ -336,35 +367,41 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
|
|
|
336
367
|
private calcSize() {
|
|
337
368
|
_package.logger.debug(`Bio: VdRegionsViewer.calcSize(), start`);
|
|
338
369
|
const calcSizeInt = (): void => {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const maxHeight: number = Math.min(logoHeight,
|
|
343
|
-
Math.max(...this.logos.map((wlDict) =>
|
|
344
|
-
Math.max(...Object.values(wlDict).map((wl) => wl.maxHeight)))),
|
|
345
|
-
);
|
|
370
|
+
// Postponed calcSizeInt can result call after the viewer has been closed (on tests)
|
|
371
|
+
if (!this.host) return;
|
|
346
372
|
|
|
373
|
+
const logoHeight = (this.root.clientHeight - 54) / this.chains.length;
|
|
347
374
|
let totalPos: number = 0;
|
|
348
375
|
for (let orderI = 0; orderI < this.logos.length; orderI++) {
|
|
349
|
-
for (const chain of this.chains)
|
|
350
|
-
this.logos[orderI][chain]
|
|
376
|
+
for (const chain of this.chains) {
|
|
377
|
+
const wl = this.logos[orderI][chain];
|
|
378
|
+
wl.root.style.height = `${logoHeight}px`;
|
|
379
|
+
}
|
|
351
380
|
|
|
352
381
|
totalPos += Math.max(...this.chains.map((chain) => this.logos[orderI][chain].Length));
|
|
353
382
|
}
|
|
354
383
|
|
|
355
|
-
if (this.positionWidth === 0
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
for (let
|
|
364
|
-
const chain
|
|
365
|
-
|
|
384
|
+
if (this.positionWidth === 0) {
|
|
385
|
+
if (this.logos.length > 0 && totalPos > 0) {
|
|
386
|
+
const leftPad = 22/* Chain label */;
|
|
387
|
+
const rightPad = 6 + 6 + 1;
|
|
388
|
+
const logoMargin = 8 + 1;
|
|
389
|
+
const fitPositionWidth =
|
|
390
|
+
(this.root.clientWidth - leftPad - (this.logos.length - 1) * logoMargin - rightPad) / totalPos;
|
|
391
|
+
|
|
392
|
+
for (let orderI = 0; orderI < this.logos.length; orderI++) {
|
|
393
|
+
for (const chain of this.chains) {
|
|
394
|
+
const wl = this.logos[orderI][chain];
|
|
395
|
+
wl.setOptions({[wlPROPS.positionWidth]: fitPositionWidth});
|
|
396
|
+
wl.root.style.width = `${fitPositionWidth * wl.Length}px`;
|
|
397
|
+
}
|
|
366
398
|
}
|
|
367
399
|
}
|
|
400
|
+
} else {
|
|
401
|
+
for (let orderI = 0; orderI < this.logos.length; orderI++) {
|
|
402
|
+
for (const chain of this.chains)
|
|
403
|
+
this.logos[orderI][chain].setOptions({[wlPROPS.positionWidth]: this.positionWidth});
|
|
404
|
+
}
|
|
368
405
|
}
|
|
369
406
|
|
|
370
407
|
if (this.positionWidth === 0)
|