@datagrok/bio 1.7.20 → 1.7.23
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/detectors.js +24 -12
- package/dist/package-test.js +360 -49
- package/dist/package.js +151 -48
- package/files/tests/testAlertCollection.csv +1252 -0
- package/package.json +6 -2
- package/src/package-test.ts +1 -0
- package/src/package.ts +23 -14
- package/src/tests/WebLogo-positions-test.ts +78 -6
- package/src/tests/WebLogo-test.ts +1 -0
- package/src/tests/detectors-test.ts +9 -0
- package/src/tests/fasta-handler-test.ts +141 -0
- package/src/utils/cell-renderer.ts +83 -29
- package/src/utils/constants.ts +3 -4
- package/src/viewers/vd-regions-viewer.ts +17 -0
- package/{test-Bio-4f0c8bae6479-d8016115.html → test-Bio-4f0c8bae6479-5b129baa.html} +15 -3
package/package.json
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
"name": "@datagrok/bio",
|
|
3
3
|
"beta": false,
|
|
4
4
|
"friendlyName": "Bio",
|
|
5
|
-
"
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Leonid Stolbov",
|
|
7
|
+
"email": "lstolbov@datagrok.ai"
|
|
8
|
+
},
|
|
9
|
+
"version": "1.7.23",
|
|
6
10
|
"description": "Bio is a [package](https://datagrok.ai/help/develop/develop#packages) for the [Datagrok](https://datagrok.ai) platform",
|
|
7
11
|
"repository": {
|
|
8
12
|
"type": "git",
|
|
@@ -11,7 +15,7 @@
|
|
|
11
15
|
},
|
|
12
16
|
"dependencies": {
|
|
13
17
|
"@biowasm/aioli": ">=2.4.0",
|
|
14
|
-
"@datagrok-libraries/bio": "^3.0.
|
|
18
|
+
"@datagrok-libraries/bio": "^3.0.2",
|
|
15
19
|
"@datagrok-libraries/ml": "^3.0.0",
|
|
16
20
|
"@datagrok-libraries/utils": "^1.4.0",
|
|
17
21
|
"cash-dom": "latest",
|
package/src/package-test.ts
CHANGED
|
@@ -11,6 +11,7 @@ import './tests/activity-cliffs-tests';
|
|
|
11
11
|
import './tests/splitters-test';
|
|
12
12
|
import './tests/renderers-test';
|
|
13
13
|
import './tests/convert-test';
|
|
14
|
+
import './tests/fasta-handler-test';
|
|
14
15
|
import './tests/WebLogo-positions-test';
|
|
15
16
|
|
|
16
17
|
export const _package = new DG.Package();
|
package/src/package.ts
CHANGED
|
@@ -5,7 +5,7 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
|
|
6
6
|
export const _package = new DG.Package();
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {MacromoleculeDifferenceCellRenderer, MonomerCellRenderer} from './utils/cell-renderer';
|
|
9
9
|
import {WebLogo, SeqColStats} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
10
10
|
import {VdRegionsViewer} from './viewers/vd-regions-viewer';
|
|
11
11
|
import {runKalign, testMSAEnoughMemory} from './utils/multiple-sequence-alignment';
|
|
@@ -26,17 +26,18 @@ import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
|
26
26
|
import {FastaFileHandler} from '@datagrok-libraries/bio/src/utils/fasta-handler';
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
|
|
30
29
|
//tags: init
|
|
31
30
|
export async function initBio(): Promise<void> {
|
|
32
31
|
// apparently HELMWebEditor requires dojo to be initialized first
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
const funcList: DG.Func[] = DG.Func.find({package: 'Helm', name: 'initHelm'});
|
|
33
|
+
console.debug(`Bio: initBio() funcList.length = ${funcList.length}`);
|
|
34
|
+
if (funcList.length === 1)
|
|
35
|
+
await grok.functions.call('Helm:initHelp');
|
|
36
|
+
|
|
36
37
|
return new Promise((resolve, reject) => {
|
|
37
38
|
// @ts-ignore
|
|
38
39
|
dojo.ready(function() { resolve(null); });
|
|
39
|
-
});
|
|
40
|
+
});
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
//name: Lru
|
|
@@ -240,6 +241,14 @@ export async function multipleSequenceAlignmentAny(table: DG.DataFrame, col: DG.
|
|
|
240
241
|
return msaCol;
|
|
241
242
|
}
|
|
242
243
|
|
|
244
|
+
//name: Bio | MSA
|
|
245
|
+
//tags: bio, panel
|
|
246
|
+
//input: column sequence { semType: Macromolecule }
|
|
247
|
+
//output: column result
|
|
248
|
+
export async function panelMSA(col: DG.Column): Promise<DG.Column | null> {
|
|
249
|
+
return multipleSequenceAlignmentAny(col.dataFrame, col);
|
|
250
|
+
}
|
|
251
|
+
|
|
243
252
|
//name: Composition Analysis
|
|
244
253
|
//top-menu: Bio | Composition Analysis
|
|
245
254
|
//output: viewer result
|
|
@@ -333,20 +342,20 @@ export function convertPanel(col: DG.Column): void {
|
|
|
333
342
|
convert(col);
|
|
334
343
|
}
|
|
335
344
|
|
|
336
|
-
//name:
|
|
345
|
+
//name: monomerCellRenderer
|
|
337
346
|
//tags: cellRenderer
|
|
338
|
-
//meta.cellType:
|
|
347
|
+
//meta.cellType: Monomer
|
|
339
348
|
//output: grid_cell_renderer result
|
|
340
|
-
export function
|
|
341
|
-
return new
|
|
349
|
+
export function monomerCellRenderer(): MonomerCellRenderer {
|
|
350
|
+
return new MonomerCellRenderer();
|
|
342
351
|
}
|
|
343
352
|
|
|
344
|
-
//name:
|
|
353
|
+
//name: MacromoleculeDifferenceCellRenderer
|
|
345
354
|
//tags: cellRenderer
|
|
346
|
-
//meta.cellType:
|
|
355
|
+
//meta.cellType: MacromoleculeDifference
|
|
347
356
|
//output: grid_cell_renderer result
|
|
348
|
-
export function
|
|
349
|
-
return new
|
|
357
|
+
export function macromoleculeDifferenceCellRenderer(): MacromoleculeDifferenceCellRenderer {
|
|
358
|
+
return new MacromoleculeDifferenceCellRenderer();
|
|
350
359
|
}
|
|
351
360
|
|
|
352
361
|
//name: testDetectMacromolecule
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
|
|
1
2
|
import {after, before, category, test, expect, expectObject} from '@datagrok-libraries/utils/src/test';
|
|
2
3
|
|
|
3
4
|
import * as grok from 'datagrok-api/grok';
|
|
4
5
|
import * as ui from 'datagrok-api/ui';
|
|
5
6
|
import * as DG from 'datagrok-api/dg';
|
|
6
7
|
import {PositionInfo, PositionMonomerInfo, WebLogo} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
7
|
-
|
|
8
8
|
category('WebLogo-positions', () => {
|
|
9
9
|
let tvList: DG.TableView[];
|
|
10
10
|
let dfList: DG.DataFrame[];
|
|
11
|
+
let currentView: DG.View;
|
|
11
12
|
|
|
12
13
|
const csvDf1 = `seq
|
|
13
14
|
ATC-G-TTGC--
|
|
@@ -22,13 +23,14 @@ category('WebLogo-positions', () => {
|
|
|
22
23
|
before(async () => {
|
|
23
24
|
tvList = [];
|
|
24
25
|
dfList = [];
|
|
26
|
+
currentView = grok.shell.tv;
|
|
25
27
|
});
|
|
26
28
|
|
|
27
29
|
after(async () => {
|
|
28
|
-
dfList.forEach((df: DG.DataFrame) => { grok.shell.closeTable(df);
|
|
30
|
+
dfList.forEach((df: DG.DataFrame) => { grok.shell.closeTable(df);});
|
|
29
31
|
tvList.forEach((tv: DG.TableView) => tv.close());
|
|
32
|
+
currentView = grok.shell.tv;
|
|
30
33
|
});
|
|
31
|
-
|
|
32
34
|
test('allPositions', async () => {
|
|
33
35
|
const df: DG.DataFrame = DG.DataFrame.fromCsv(csvDf1);
|
|
34
36
|
const tv: DG.TableView = grok.shell.addTableView(df);
|
|
@@ -55,12 +57,82 @@ category('WebLogo-positions', () => {
|
|
|
55
57
|
new PositionInfo('11', {'-': new PositionMonomerInfo(5)}),
|
|
56
58
|
new PositionInfo('12', {'-': new PositionMonomerInfo(5)})
|
|
57
59
|
];
|
|
60
|
+
console.log(positions);
|
|
61
|
+
expect(positions.length,resAllDf1.length);
|
|
58
62
|
// check all positions are equal resAllDf1
|
|
59
63
|
for (let i = 0; i < positions.length; i++) {
|
|
60
64
|
expect(positions[i].name, resAllDf1[i].name);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
for (const key in positions[i].freq) {
|
|
66
|
+
expect(positions[i].freq[key].count, resAllDf1[i].freq[key].count);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
});
|
|
71
|
+
test('positions with shrinkEmptyTail option', async () => {
|
|
72
|
+
const df: DG.DataFrame = DG.DataFrame.fromCsv(csvDf1);
|
|
73
|
+
const tv: DG.TableView = grok.shell.addTableView(df);
|
|
74
|
+
|
|
75
|
+
const wlViewer: WebLogo = await df.plot.fromType('WebLogo', {'shrinkEmptyTail': true}) as unknown as WebLogo;
|
|
76
|
+
tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
|
|
77
|
+
|
|
78
|
+
tvList.push(tv);
|
|
79
|
+
dfList.push(df);
|
|
80
|
+
|
|
81
|
+
const positions: PositionInfo[] = wlViewer['positions'];
|
|
82
|
+
|
|
83
|
+
const resAllDf1: PositionInfo[] = [
|
|
84
|
+
new PositionInfo('1', {'A': new PositionMonomerInfo(2), '-': new PositionMonomerInfo(3)}),
|
|
85
|
+
new PositionInfo('2', {'T': new PositionMonomerInfo(5)}),
|
|
86
|
+
new PositionInfo('3', {'C': new PositionMonomerInfo(5)}),
|
|
87
|
+
new PositionInfo('4', {'-': new PositionMonomerInfo(5)}),
|
|
88
|
+
new PositionInfo('5', {'G': new PositionMonomerInfo(5)}),
|
|
89
|
+
new PositionInfo('6', {'-': new PositionMonomerInfo(3), 'C': new PositionMonomerInfo(2)}),
|
|
90
|
+
new PositionInfo('7', {'T': new PositionMonomerInfo(5)}),
|
|
91
|
+
new PositionInfo('8', {'T': new PositionMonomerInfo(5)}),
|
|
92
|
+
new PositionInfo('9', {'G': new PositionMonomerInfo(5)}),
|
|
93
|
+
new PositionInfo('10', {'C': new PositionMonomerInfo(5)})
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
console.log(positions);
|
|
97
|
+
for (let i = 0; i < positions.length; i++) {
|
|
98
|
+
expect(positions[i].name, resAllDf1[i].name);
|
|
99
|
+
for (const key in positions[i].freq) {
|
|
100
|
+
expect(positions[i].freq[key].count, resAllDf1[i].freq[key].count);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('positions with skipEmptyPositions option', async () => {
|
|
107
|
+
const df: DG.DataFrame = DG.DataFrame.fromCsv(csvDf1);
|
|
108
|
+
const tv: DG.TableView = grok.shell.addTableView(df);
|
|
109
|
+
|
|
110
|
+
const wlViewer: WebLogo = await df.plot.fromType('WebLogo', {'skipEmptyPositions': false}) as unknown as WebLogo;
|
|
111
|
+
tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
|
|
112
|
+
|
|
113
|
+
tvList.push(tv);
|
|
114
|
+
dfList.push(df);
|
|
115
|
+
|
|
116
|
+
const positions: PositionInfo[] = wlViewer['positions'];
|
|
117
|
+
|
|
118
|
+
const resAllDf1: PositionInfo[] = [
|
|
119
|
+
new PositionInfo('1', {'A': new PositionMonomerInfo(2), '-': new PositionMonomerInfo(3)}),
|
|
120
|
+
new PositionInfo('2', {'T': new PositionMonomerInfo(5)}),
|
|
121
|
+
new PositionInfo('3', {'C': new PositionMonomerInfo(5)}),
|
|
122
|
+
new PositionInfo('5', {'G': new PositionMonomerInfo(5)}),
|
|
123
|
+
new PositionInfo('6', {'-': new PositionMonomerInfo(3), 'C': new PositionMonomerInfo(2)}),
|
|
124
|
+
new PositionInfo('7', {'T': new PositionMonomerInfo(5)}),
|
|
125
|
+
new PositionInfo('8', {'T': new PositionMonomerInfo(5)}),
|
|
126
|
+
new PositionInfo('9', {'G': new PositionMonomerInfo(5)}),
|
|
127
|
+
new PositionInfo('10', {'C': new PositionMonomerInfo(5)})
|
|
128
|
+
];
|
|
129
|
+
console.log(positions);
|
|
130
|
+
|
|
131
|
+
for (let i = 0; i < positions.length; i++) {
|
|
132
|
+
expect(positions[i].name, resAllDf1[i].name);
|
|
133
|
+
for (const key in positions[i].freq) {
|
|
134
|
+
expect(positions[i].freq[key].count, resAllDf1[i].freq[key].count);
|
|
135
|
+
}
|
|
64
136
|
}
|
|
65
137
|
|
|
66
138
|
});
|
|
@@ -125,6 +125,7 @@ MWRSWY-CKHP
|
|
|
125
125
|
testSpgi100 = 'testSpgi100',
|
|
126
126
|
testUnichemSources = 'testUnichemSources',
|
|
127
127
|
testDmvOffices = 'testDmvOffices',
|
|
128
|
+
testAlertCollection = 'testAlertCollection',
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
const samples: { [key: string]: string } = {
|
|
@@ -145,6 +146,7 @@ MWRSWY-CKHP
|
|
|
145
146
|
'testSpgi100': 'System:AppData/Bio/tests/testSpgi100.csv',
|
|
146
147
|
'testUnichemSources': 'System:AppData/Bio/tests/testUnichemSources.csv',
|
|
147
148
|
'testDmvOffices': 'System:AppData/Bio/tests/testDmvOffices.csv',
|
|
149
|
+
'testAlertCollection': 'System:AppData/Bio/tests/testAlertCollection.csv',
|
|
148
150
|
};
|
|
149
151
|
|
|
150
152
|
const _samplesDfs: { [key: string]: Promise<DG.DataFrame> } = {};
|
|
@@ -342,6 +344,9 @@ MWRSWY-CKHP
|
|
|
342
344
|
test('samplesTestSpgi100NegativePrimaryScaffoldName', async () => {
|
|
343
345
|
await _testNeg(readSamples(Samples.testSpgi100), 'Primary Scaffold Name');
|
|
344
346
|
});
|
|
347
|
+
test('samplesTestSpgi100NegativeSampleName', async () => {
|
|
348
|
+
await _testNeg(readSamples(Samples.testSpgi100), 'Sample Name');
|
|
349
|
+
});
|
|
345
350
|
|
|
346
351
|
test('samplesTestUnichemSourcesNegativeSrcUrl', async () => {
|
|
347
352
|
await _testNeg(readSamples(Samples.testUnichemSources), 'src_url');
|
|
@@ -356,6 +361,10 @@ MWRSWY-CKHP
|
|
|
356
361
|
test('samplesTestDmvOfficesNegativeCity', async () => {
|
|
357
362
|
await _testNeg(readSamples(Samples.testDmvOffices), 'City');
|
|
358
363
|
});
|
|
364
|
+
|
|
365
|
+
test('samplesTestAlertCollectionNegativeSmarts', async () => {
|
|
366
|
+
await _testNeg(readSamples(Samples.testAlertCollection), 'smarts');
|
|
367
|
+
});
|
|
359
368
|
});
|
|
360
369
|
|
|
361
370
|
export async function _testNeg(readDf: DfReaderFunc, colName: string) {
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/* Do not change these import lines to match external modules in webpack configuration */
|
|
2
|
+
import * as grok from 'datagrok-api/grok';
|
|
3
|
+
import * as ui from 'datagrok-api/ui';
|
|
4
|
+
import * as DG from 'datagrok-api/dg';
|
|
5
|
+
|
|
6
|
+
import {category, expectArray, test} from '@datagrok-libraries/utils/src/test';
|
|
7
|
+
import {FastaFileHandler} from '@datagrok-libraries/bio/src/utils/fasta-handler';
|
|
8
|
+
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
category('fastaFileHandler', () => {
|
|
12
|
+
const fastaNormalFormatting = `>description:1
|
|
13
|
+
MDYKETLLMPKTDFPMRGGLPNKEPQIQEKW
|
|
14
|
+
|
|
15
|
+
>description:2
|
|
16
|
+
MIEVFLFGIVLGLIPITLAGLFVTAYLQYRRGDQLDL
|
|
17
|
+
|
|
18
|
+
>description:3
|
|
19
|
+
MMELVLKTIIGPIVVGVVLRIVDKWLNKDK
|
|
20
|
+
|
|
21
|
+
>description:4
|
|
22
|
+
MDRTDEVSNHTHDKPTLTWFEEIFEEYHSPFHN
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
const fastaExtraSpaces = `>description:1
|
|
26
|
+
MDYKETLLMPKTDFPMRGGLPNKEPQIQEKW
|
|
27
|
+
|
|
28
|
+
>description:2
|
|
29
|
+
MI EVF LFGIVLGLI PITLAGLFVTAY LQYRRGDQLDL
|
|
30
|
+
|
|
31
|
+
>description:3
|
|
32
|
+
M MELVLKTI IGPI VVGVVLR IVDKWLNKDK
|
|
33
|
+
|
|
34
|
+
>description:4
|
|
35
|
+
MDR TDEVSNHTHDKP TLTWFEEIFEEYHSPFHN
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const fastaExtraNewlines = `>description:1
|
|
39
|
+
|
|
40
|
+
MDYKETLLMPKTDFPMRGGLPNKEPQIQEKW
|
|
41
|
+
|
|
42
|
+
>description:2
|
|
43
|
+
MIEVF
|
|
44
|
+
LFGIVLGLI
|
|
45
|
+
PITLAGLFVTA
|
|
46
|
+
YLQYRRGDQLDL
|
|
47
|
+
|
|
48
|
+
>description:3
|
|
49
|
+
M
|
|
50
|
+
ME
|
|
51
|
+
|
|
52
|
+
LVLKTIIG
|
|
53
|
+
|
|
54
|
+
PIVVGVVLRI
|
|
55
|
+
VDKWLNKDK
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
>description:4
|
|
59
|
+
|
|
60
|
+
MDRT
|
|
61
|
+
|
|
62
|
+
DEVSNHTHDKP
|
|
63
|
+
|
|
64
|
+
TLTWFEEIFEE
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
YHSPFHN
|
|
69
|
+
`;
|
|
70
|
+
// a "broken" fasta file
|
|
71
|
+
// const fastaBroken = `
|
|
72
|
+
|
|
73
|
+
// >description:1
|
|
74
|
+
// MDYKETLLM
|
|
75
|
+
// PKTDFPMRGGLPN
|
|
76
|
+
// KEPQIQEKW
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
// >description:2
|
|
81
|
+
// MIEVFL FGIVLGLIPI TLAGLFVTAYLQYRRGDQLDL
|
|
82
|
+
|
|
83
|
+
// >description:3
|
|
84
|
+
|
|
85
|
+
// M
|
|
86
|
+
// MELVLKTIIGP
|
|
87
|
+
// IVVGVVLR
|
|
88
|
+
// IVDKWLNKD
|
|
89
|
+
|
|
90
|
+
// K
|
|
91
|
+
|
|
92
|
+
// >description:4
|
|
93
|
+
// MDRTDEV
|
|
94
|
+
|
|
95
|
+
// SNHTHDKP
|
|
96
|
+
// TLTWFEEI
|
|
97
|
+
// FEE
|
|
98
|
+
|
|
99
|
+
// YHSPFHN
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
// `;
|
|
103
|
+
|
|
104
|
+
const descriptionsArray = [
|
|
105
|
+
'description:1', 'description:2', 'description:3', 'description:4',
|
|
106
|
+
];
|
|
107
|
+
const descriptionCol = DG.Column.fromStrings('description', descriptionsArray);
|
|
108
|
+
|
|
109
|
+
const sequencesArray = [
|
|
110
|
+
'MDYKETLLMPKTDFPMRGGLPNKEPQIQEKW',
|
|
111
|
+
'MIEVFLFGIVLGLIPITLAGLFVTAYLQYRRGDQLDL',
|
|
112
|
+
'MMELVLKTIIGPIVVGVVLRIVDKWLNKDK',
|
|
113
|
+
'MDRTDEVSNHTHDKPTLTWFEEIFEEYHSPFHN',
|
|
114
|
+
];
|
|
115
|
+
const sequencesCol = DG.Column.fromStrings('sequence', sequencesArray);
|
|
116
|
+
sequencesCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
117
|
+
UnitsHandler.setUnitsToFastaColumn(sequencesCol);
|
|
118
|
+
|
|
119
|
+
const fastaDf = DG.DataFrame.fromColumns([descriptionCol, sequencesCol]);
|
|
120
|
+
|
|
121
|
+
function _testColumnsParser(inputFasta: string) {
|
|
122
|
+
const ffh = new FastaFileHandler(inputFasta);
|
|
123
|
+
const parsedDescriptionsArray = ffh.descriptionsArray;
|
|
124
|
+
const parsedSequencesArray = ffh.sequencesArray;
|
|
125
|
+
expectArray(
|
|
126
|
+
[parsedDescriptionsArray, parsedSequencesArray],
|
|
127
|
+
[descriptionsArray, sequencesArray]
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// test parser
|
|
132
|
+
test('testNormalFormatting', async () => {
|
|
133
|
+
_testColumnsParser(fastaNormalFormatting);
|
|
134
|
+
});
|
|
135
|
+
test('testExtraSpaces', async () => {
|
|
136
|
+
_testColumnsParser(fastaExtraSpaces);
|
|
137
|
+
});
|
|
138
|
+
test('testExtraNewlines', async () => {
|
|
139
|
+
_testColumnsParser(fastaExtraNewlines);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
@@ -2,7 +2,7 @@ import * as C from './constants';
|
|
|
2
2
|
import * as DG from 'datagrok-api/dg';
|
|
3
3
|
import {AminoacidsPalettes} from '@datagrok-libraries/bio/src/aminoacids';
|
|
4
4
|
import {NucleotidesPalettes} from '@datagrok-libraries/bio/src/nucleotides';
|
|
5
|
-
import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
|
|
5
|
+
import {UnknownSeqPalette, UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
|
|
6
6
|
import {SplitterFunc, WebLogo} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
7
7
|
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
8
8
|
import * as ui from 'datagrok-api/ui';
|
|
@@ -44,6 +44,7 @@ export function processSequence(subParts: string[]): [string[], boolean] {
|
|
|
44
44
|
return [text, simplified];
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
|
|
47
48
|
/**
|
|
48
49
|
* A function that prints a string aligned to left or centered.
|
|
49
50
|
*
|
|
@@ -65,33 +66,46 @@ function printLeftOrCentered(
|
|
|
65
66
|
x: number, y: number, w: number, h: number,
|
|
66
67
|
g: CanvasRenderingContext2D, s: string, color = undefinedColor,
|
|
67
68
|
pivot: number = 0, left = false, transparencyRate: number = 1.0,
|
|
68
|
-
separator: string = '', last: boolean = false): number {
|
|
69
|
+
separator: string = '', last: boolean = false, drawStyle: string = 'classic', maxWord:any={}, maxWordIdx:number=0, gridCell:any = {}): number {
|
|
69
70
|
g.textAlign = 'start';
|
|
70
71
|
const colorPart = s.substring(0);
|
|
71
|
-
let grayPart =
|
|
72
|
+
let grayPart = last ? '' : separator;
|
|
73
|
+
if (drawStyle === 'msa') {
|
|
74
|
+
grayPart = ' ';
|
|
75
|
+
}
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
let textSize: any = g.measureText(colorPart + grayPart);
|
|
74
78
|
const indent = 5;
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
let colorTextSize = g.measureText(colorPart).width;
|
|
77
81
|
const dy = (textSize.fontBoundingBoxAscent + textSize.fontBoundingBoxDescent) / 2;
|
|
82
|
+
textSize = textSize.width;
|
|
83
|
+
if (drawStyle === 'msa') {
|
|
84
|
+
if (colorTextSize > maxWord) {
|
|
85
|
+
maxWord[maxWordIdx] = colorTextSize;
|
|
86
|
+
gridCell.cell.column.temp = maxWord;
|
|
87
|
+
}
|
|
88
|
+
colorTextSize = maxWord[maxWordIdx];
|
|
89
|
+
textSize = maxWord[maxWordIdx];
|
|
90
|
+
}
|
|
78
91
|
|
|
79
92
|
function draw(dx1: number, dx2: number): void {
|
|
80
93
|
g.fillStyle = color;
|
|
81
94
|
g.globalAlpha = transparencyRate;
|
|
82
95
|
g.fillText(colorPart, x + dx1, y + dy);
|
|
83
|
-
|
|
84
|
-
|
|
96
|
+
if (drawStyle === 'classic') {
|
|
97
|
+
g.fillStyle = grayColor;
|
|
98
|
+
g.fillText(grayPart, x + dx2, y + dy);
|
|
99
|
+
}
|
|
85
100
|
}
|
|
86
101
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return x + colorTextSize.width + g.measureText(grayPart).width;
|
|
102
|
+
if (left || textSize > w) {
|
|
103
|
+
draw(indent, indent + colorTextSize);
|
|
104
|
+
return x + colorTextSize + g.measureText(grayPart).width;
|
|
91
105
|
} else {
|
|
92
|
-
const dx = (w - textSize
|
|
93
|
-
draw(dx, dx + colorTextSize
|
|
94
|
-
return x + dx + colorTextSize
|
|
106
|
+
const dx = (w - textSize) / 2;
|
|
107
|
+
draw(dx, dx + colorTextSize);
|
|
108
|
+
return x + dx + colorTextSize;
|
|
95
109
|
}
|
|
96
110
|
}
|
|
97
111
|
|
|
@@ -114,7 +128,7 @@ function findMonomers(helmString: string) {
|
|
|
114
128
|
export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
115
129
|
get name(): string { return 'macromoleculeSequence'; }
|
|
116
130
|
|
|
117
|
-
get cellType(): string { return C.SEM_TYPES.
|
|
131
|
+
get cellType(): string { return C.SEM_TYPES.MACROMOLECULE; }
|
|
118
132
|
|
|
119
133
|
get defaultHeight(): number { return 30; }
|
|
120
134
|
|
|
@@ -198,15 +212,51 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
198
212
|
const separator = gridCell.cell.column.getTag('separator') ?? '';
|
|
199
213
|
const splitterFunc: SplitterFunc = WebLogo.getSplitter(units, gridCell.cell.column.getTag('separator'));
|
|
200
214
|
|
|
215
|
+
// обработка новых елементов
|
|
216
|
+
const columns = gridCell.cell.column.categories;
|
|
217
|
+
let maxLengthWords = {};
|
|
218
|
+
// check if gridCell.cell.column.temp is array
|
|
219
|
+
if (gridCell.cell.column.getTag('.calculatedCellRender') !== 'exist') {
|
|
220
|
+
for (let i = 0; i < columns.length; i++) {
|
|
221
|
+
let subParts: string[] = splitterFunc(columns[i]);
|
|
222
|
+
subParts.forEach((amino, index) => {
|
|
223
|
+
//@ts-ignore
|
|
224
|
+
let textSizeWidth = g.measureText(WebLogo.monomerToText(amino) + ' ');
|
|
225
|
+
//@ts-ignore
|
|
226
|
+
if (textSizeWidth.width > (maxLengthWords[index] ?? 0)) {
|
|
227
|
+
//@ts-ignore
|
|
228
|
+
maxLengthWords[index] = textSizeWidth.width;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
gridCell.cell.column.temp = maxLengthWords;
|
|
233
|
+
gridCell.cell.column.setTag('.calculatedCellRender', 'exist');
|
|
234
|
+
} else {
|
|
235
|
+
maxLengthWords = gridCell.cell.column.temp;
|
|
236
|
+
}
|
|
237
|
+
|
|
201
238
|
const subParts: string[] = splitterFunc(cell.value);
|
|
202
|
-
// console.log(subParts);
|
|
203
239
|
let x1 = x;
|
|
204
240
|
let color = undefinedColor;
|
|
241
|
+
// get max length word in subParts
|
|
242
|
+
let tagUnits = gridCell.cell.column.getTag(DG.TAGS.UNITS);
|
|
243
|
+
let maxLength = 0;
|
|
244
|
+
let maxWord = '';
|
|
245
|
+
let drawStyle = 'classic';
|
|
246
|
+
if (tagUnits.includes('MSA')) {
|
|
247
|
+
subParts.forEach(part => {
|
|
248
|
+
if (part.length > maxLength) {
|
|
249
|
+
maxLength = part.length;
|
|
250
|
+
maxWord = part;
|
|
251
|
+
drawStyle = 'msa';
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
}
|
|
205
255
|
subParts.forEach((amino, index) => {
|
|
206
256
|
color = palette.get(amino);
|
|
207
257
|
g.fillStyle = undefinedColor;
|
|
208
258
|
let last = index === subParts.length - 1;
|
|
209
|
-
x1 = printLeftOrCentered(x1, y, w, h, g, amino, color, 0, true, 1.0, separator, last);
|
|
259
|
+
x1 = printLeftOrCentered(x1, y, w, h, g, WebLogo.monomerToText(amino), color, 0, true, 1.0, separator, last, drawStyle, maxLengthWords, index, gridCell);
|
|
210
260
|
});
|
|
211
261
|
|
|
212
262
|
g.restore();
|
|
@@ -215,11 +265,10 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
215
265
|
}
|
|
216
266
|
}
|
|
217
267
|
|
|
268
|
+
export class MonomerCellRenderer extends DG.GridCellRenderer {
|
|
269
|
+
get name(): string {return 'MonomerCR';}
|
|
218
270
|
|
|
219
|
-
|
|
220
|
-
get name(): string {return 'aminoAcidsCR';}
|
|
221
|
-
|
|
222
|
-
get cellType(): string {return C.SEM_TYPES.AMINO_ACIDS;}
|
|
271
|
+
get cellType(): string {return C.SEM_TYPES.MONOMER;}
|
|
223
272
|
|
|
224
273
|
get defaultHeight(): number {return 15;}
|
|
225
274
|
|
|
@@ -256,10 +305,10 @@ export class AminoAcidsCellRenderer extends DG.GridCellRenderer {
|
|
|
256
305
|
}
|
|
257
306
|
}
|
|
258
307
|
|
|
259
|
-
export class
|
|
260
|
-
get name(): string {return '
|
|
308
|
+
export class MacromoleculeDifferenceCellRenderer extends DG.GridCellRenderer {
|
|
309
|
+
get name(): string {return 'MacromoleculeDifferenceCR';}
|
|
261
310
|
|
|
262
|
-
get cellType(): string {return C.SEM_TYPES.
|
|
311
|
+
get cellType(): string {return C.SEM_TYPES.MACROMOLECULE_DIFFERENCE;}
|
|
263
312
|
|
|
264
313
|
get defaultHeight(): number {return 30;}
|
|
265
314
|
|
|
@@ -295,23 +344,28 @@ export class AlignedSequenceDifferenceCellRenderer extends DG.GridCellRenderer {
|
|
|
295
344
|
//TODO: can this be replaced/merged with splitSequence?
|
|
296
345
|
const [s1, s2] = s.split('#');
|
|
297
346
|
const separator = gridCell.tableColumn!.tags[C.TAGS.SEPARATOR];
|
|
298
|
-
const
|
|
299
|
-
const
|
|
347
|
+
const units: string = gridCell.tableColumn!.tags[DG.TAGS.UNITS];
|
|
348
|
+
const splitter = WebLogo.getSplitter(units, separator);
|
|
349
|
+
const subParts1 = splitter(s1);
|
|
350
|
+
const subParts2 = splitter(s2);
|
|
300
351
|
const [text] = processSequence(subParts1);
|
|
301
352
|
const textSize = g.measureText(text.join(''));
|
|
302
353
|
let updatedX = Math.max(x, x + (w - (textSize.width + subParts1.length * 4)) / 2);
|
|
303
354
|
// 28 is the height of the two substitutions on top of each other + space
|
|
304
355
|
const updatedY = Math.max(y, y + (h - 28) / 2);
|
|
305
356
|
|
|
306
|
-
|
|
357
|
+
let palette: SeqPalette = UnknownSeqPalettes.Color;
|
|
358
|
+
if (units != 'HELM')
|
|
359
|
+
palette = getPalleteByType(units.substring(units.length - 2));
|
|
360
|
+
|
|
361
|
+
const vShift = 7;
|
|
307
362
|
for (let i = 0; i < subParts1.length; i++) {
|
|
308
363
|
const amino1 = subParts1[i];
|
|
309
364
|
const amino2 = subParts2[i];
|
|
310
365
|
const color1 = palette.get(amino1);
|
|
311
|
-
const color2 = palette.get(amino2);
|
|
312
366
|
|
|
313
367
|
if (amino1 != amino2) {
|
|
314
|
-
const
|
|
368
|
+
const color2 = palette.get(amino2);
|
|
315
369
|
const subX0 = printLeftOrCentered(updatedX, updatedY - vShift, w, h, g, amino1, color1, 0, true);
|
|
316
370
|
const subX1 = printLeftOrCentered(updatedX, updatedY + vShift, w, h, g, amino2, color2, 0, true);
|
|
317
371
|
updatedX = Math.max(subX1, subX0);
|
package/src/utils/constants.ts
CHANGED
|
@@ -23,12 +23,11 @@ export enum TAGS {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export enum SEM_TYPES {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
ALIGNED_SEQUENCE_DIFFERENCE = 'alignedSequenceDifference',
|
|
26
|
+
MONOMER = 'Monomer',
|
|
27
|
+
MACROMOLECULE_DIFFERENCE = 'MacromoleculeDifference',
|
|
29
28
|
ACTIVITY = 'activity',
|
|
30
29
|
ACTIVITY_SCALED = 'activityScaled',
|
|
31
|
-
|
|
30
|
+
MACROMOLECULE = 'Macromolecule',
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
export const STATS = 'stats';
|
|
@@ -50,6 +50,9 @@ export class VdRegionsViewer extends DG.JsViewer {
|
|
|
50
50
|
public chains: string[];
|
|
51
51
|
public sequenceColumnNamePostfix: string;
|
|
52
52
|
|
|
53
|
+
public skipEmptyPositions: boolean;
|
|
54
|
+
|
|
55
|
+
|
|
53
56
|
public get df(): DG.DataFrame {
|
|
54
57
|
return this.dataFrame;
|
|
55
58
|
}
|
|
@@ -72,6 +75,8 @@ export class VdRegionsViewer extends DG.JsViewer {
|
|
|
72
75
|
this.chains = this.stringList('chains', ['Heavy', 'Light'],
|
|
73
76
|
{choices: ['Heavy', 'Light']});
|
|
74
77
|
this.sequenceColumnNamePostfix = this.string('sequenceColumnNamePostfix', 'chain sequence');
|
|
78
|
+
|
|
79
|
+
this.skipEmptyPositions = this.bool('skipEmptyPositions', false);
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
public async init() {
|
|
@@ -119,6 +124,17 @@ export class VdRegionsViewer extends DG.JsViewer {
|
|
|
119
124
|
break;
|
|
120
125
|
case 'sequenceColumnNamePostfix':
|
|
121
126
|
break;
|
|
127
|
+
case 'skipEmptyPositions':
|
|
128
|
+
// for (let orderI = 0; orderI < this.logos.length; orderI++) {
|
|
129
|
+
// for (let chainI = 0; chainI < this.chains.length; chainI++) {
|
|
130
|
+
// const chain: string = this.chains[chainI];
|
|
131
|
+
// this.logos[orderI][chain].setOptions({skipEmptyPositions: this.skipEmptyPositions});
|
|
132
|
+
// }
|
|
133
|
+
// }
|
|
134
|
+
// this.calcSize();
|
|
135
|
+
await this.destroyView();
|
|
136
|
+
await this.buildView();
|
|
137
|
+
break;
|
|
122
138
|
}
|
|
123
139
|
}
|
|
124
140
|
}
|
|
@@ -188,6 +204,7 @@ export class VdRegionsViewer extends DG.JsViewer {
|
|
|
188
204
|
startPositionName: region!.positionStartName,
|
|
189
205
|
endPositionName: region!.positionEndName,
|
|
190
206
|
fixWidth: true,
|
|
207
|
+
skipEmptyPositions: this.skipEmptyPositions,
|
|
191
208
|
})) as unknown as WebLogo;
|
|
192
209
|
}
|
|
193
210
|
// WebLogo creation fires onRootSizeChanged event even before control being added to this.logos
|