@datagrok/bio 2.10.0 → 2.10.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/CHANGELOG.md +10 -0
- package/detectors.js +5 -3
- 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/files/data/sample_HELM_empty_unkn.csv +11 -0
- package/package.json +2 -2
- package/src/apps/get-region-app.ts +30 -21
- package/src/package.ts +16 -10
- package/src/tests/detectors-tests.ts +13 -2
- package/src/tests/renderers-monomer-placer.ts +25 -12
- package/src/utils/get-region-func-editor.ts +30 -10
- package/src/viewers/web-logo-viewer.ts +65 -38
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
HELM,Activity
|
|
2
|
+
,5.30751
|
|
3
|
+
PEPTIDE1{meI.hHis.Aca.Cys_SEt.T.dK.Thr_PO3H2.Aca.Tyr_PO3H2.D-Chg.dV.Phe_ab-dehydro.N.D-Orn.D-aThr.Phe_4Me}$$$$,5.72388
|
|
4
|
+
,5.18581
|
|
5
|
+
PEPTIDE1{meI.hHis.unkn1.Cys_SEt.unkn2.dK.Thr_PO3H2.Aca.Tyr_PO3H2.D-Chg.dV.Thr_PO3H2.N.D-Orn.D-aThr.Phe_4Me}$$$$,6.22350
|
|
6
|
+
PEPTIDE1{meI.hHis.Aca.N.T.dK.Thr_PO3H2.Aca.D-Tyr_Et.Tyr_ab-dehydroMe.dV.Chg.N.D-Orn.D-aThr.Phe_4Me}$$$$,3.84591
|
|
7
|
+
PEPTIDE1{meI.hHis.Aca.N.unkn3.dK.Thr_PO3H2.Aca.D-Tyr_Et.Tyr_Bn.dV.E.N.dV.Phe_4Me}$$$$,3.27920
|
|
8
|
+
,2.10585
|
|
9
|
+
PEPTIDE1{meI.hHis.Aca.N.T.dK.Thr_PO3H2.Aca.D-Tyr_Et.meQ.dV.E.N.dV.Phe_4Me}$$$$,1.80369
|
|
10
|
+
PEPTIDE1{meI.hHis.Aca.N.T.dK.Thr_PO3H2.Aca.Oic_3aS-7aS.D-Chg.dV.E.N.D-Orn.D-aThr.Phe_4Me}$$$$,6.38806
|
|
11
|
+
PEPTIDE1{meI.hHis.Aca.N.T.dK.Thr_PO3H2.Aca.meM.D-Chg.dV.E.N.D-Orn.D-aThr.Phe_4Me}$$$$,4.44165
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "Leonid Stolbov",
|
|
6
6
|
"email": "lstolbov@datagrok.ai"
|
|
7
7
|
},
|
|
8
|
-
"version": "2.10.
|
|
8
|
+
"version": "2.10.2",
|
|
9
9
|
"description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@biowasm/aioli": "^3.1.0",
|
|
37
|
-
"@datagrok-libraries/bio": "^5.38.
|
|
37
|
+
"@datagrok-libraries/bio": "^5.38.2",
|
|
38
38
|
"@datagrok-libraries/chem-meta": "^1.0.1",
|
|
39
39
|
"@datagrok-libraries/ml": "^6.3.39",
|
|
40
40
|
"@datagrok-libraries/tutorials": "^1.3.6",
|
|
@@ -7,38 +7,44 @@ import {IWebLogoViewer} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
|
7
7
|
|
|
8
8
|
import {_package} from '../package';
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const defaultData: GetRegionAppData = {
|
|
11
|
+
df: DG.DataFrame.fromCsv(`seq,value
|
|
11
12
|
ATCCGTCGT,0.5
|
|
12
13
|
TGTTCGTCA,0.4
|
|
13
14
|
ATGGTCGTA,0.7
|
|
14
|
-
ATCCGTGCA,0.1
|
|
15
|
+
ATCCGTGCA,0.1`),
|
|
16
|
+
colName: 'seq',
|
|
17
|
+
positionNames: ['1', '1A', '1C', '2', '4', '4A', '4B', '5', '6'].join(positionSeparator),
|
|
18
|
+
regions: [
|
|
19
|
+
{name: 'first region', start: '1', end: '2'},
|
|
20
|
+
{name: 'second region', start: '1C', end: '4'},
|
|
21
|
+
{name: 'overlapping second', start: '1C', end: '4A'},
|
|
22
|
+
{name: 'whole sequence', start: '1', end: '6'},
|
|
23
|
+
{name: 'bad start', start: '0', end: '6'},
|
|
24
|
+
{name: 'bad end', start: '1', end: '4C'},
|
|
25
|
+
{name: 'bad start & end', start: '0', end: '4C'},
|
|
26
|
+
]
|
|
27
|
+
};
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{name: 'second region', start: '1C', end: '4'},
|
|
21
|
-
{name: 'overlapping second', start: '1C', end: '4A'},
|
|
22
|
-
{name: 'whole sequence', start: '1', end: '6'},
|
|
23
|
-
{name: 'bad start', start: '0', end: '6'},
|
|
24
|
-
{name: 'bad end', start: '1', end: '4C'},
|
|
25
|
-
{name: 'bad start & end', start: '0', end: '4C'},
|
|
26
|
-
];
|
|
29
|
+
export type GetRegionAppData = {
|
|
30
|
+
df: DG.DataFrame, colName: string,
|
|
31
|
+
positionNames?: string, regions?: { name: string, start: string, end: string }[]
|
|
32
|
+
};
|
|
27
33
|
|
|
28
34
|
export class GetRegionApp {
|
|
29
|
-
df: DG.DataFrame;
|
|
30
35
|
view: DG.TableView;
|
|
36
|
+
data!: GetRegionAppData;
|
|
31
37
|
|
|
32
38
|
constructor(
|
|
33
39
|
private readonly urlParams: URLSearchParams,
|
|
34
40
|
private readonly funcName: string
|
|
35
41
|
) {}
|
|
36
42
|
|
|
37
|
-
async init(): Promise<void> {
|
|
38
|
-
this.
|
|
39
|
-
const seqCol = this.df.getCol(
|
|
40
|
-
seqCol.setTag(TAGS.positionNames, positionNames);
|
|
41
|
-
seqCol.setTag(TAGS.regions, JSON.stringify(regions));
|
|
43
|
+
async init(data?: GetRegionAppData): Promise<void> {
|
|
44
|
+
this.data = data ?? defaultData;
|
|
45
|
+
const seqCol = this.data.df.getCol(this.data.colName);
|
|
46
|
+
if (!!this.data.positionNames) seqCol.setTag(TAGS.positionNames, this.data.positionNames);
|
|
47
|
+
if (!!this.data.regions) seqCol.setTag(TAGS.regions, JSON.stringify(this.data.regions));
|
|
42
48
|
|
|
43
49
|
await this.buildView();
|
|
44
50
|
}
|
|
@@ -46,11 +52,14 @@ export class GetRegionApp {
|
|
|
46
52
|
// -- View --
|
|
47
53
|
|
|
48
54
|
async buildView(): Promise<void> {
|
|
49
|
-
|
|
55
|
+
// To allow showing a WebLogoViewer
|
|
56
|
+
await grok.data.detectSemanticTypes(this.data.df);
|
|
57
|
+
|
|
58
|
+
this.view = grok.shell.addTableView(this.data.df);
|
|
50
59
|
this.view.path = this.view.basePath = `func/${_package.name}.${this.funcName}`;
|
|
51
60
|
|
|
52
61
|
const viewer: DG.Viewer & IWebLogoViewer = (await this.view.dataFrame.plot
|
|
53
|
-
.fromType('WebLogo')) as DG.Viewer & IWebLogoViewer;
|
|
62
|
+
.fromType('WebLogo', {sequenceColumnName: this.data.colName})) as DG.Viewer & IWebLogoViewer;
|
|
54
63
|
this.view.dockManager.dock(viewer, DG.DOCK_TYPE.DOWN, null, 'WebLogo', 0.35);
|
|
55
64
|
}
|
|
56
65
|
}
|
package/src/package.ts
CHANGED
|
@@ -365,13 +365,13 @@ export function getRegion(
|
|
|
365
365
|
//input: string end {optional: true} [Region end position name]
|
|
366
366
|
//input: string name {optional: true} [Region column name]
|
|
367
367
|
//editor: Bio:GetRegionEditor
|
|
368
|
-
export function getRegionTopMenu(
|
|
368
|
+
export async function getRegionTopMenu(
|
|
369
369
|
table: DG.DataFrame, sequence: DG.Column,
|
|
370
370
|
start: string | undefined, end: string | undefined, name: string | undefined
|
|
371
|
-
): void {
|
|
371
|
+
): Promise<void> {
|
|
372
372
|
const regCol = getRegionDo(sequence, start ?? null, end ?? null, name ?? null);
|
|
373
373
|
sequence.dataFrame.columns.add(regCol);
|
|
374
|
-
|
|
374
|
+
await grok.data.detectSemanticTypes(sequence.dataFrame); // to set renderer
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
//top-menu: Bio | Analyze | Activity Cliffs...
|
|
@@ -656,13 +656,6 @@ export async function compositionAnalysis(): Promise<void> {
|
|
|
656
656
|
await handler(col);
|
|
657
657
|
}
|
|
658
658
|
|
|
659
|
-
//top-menu: Bio | Convert | SDF to JSON Library...
|
|
660
|
-
//name: SDF to JSON Library
|
|
661
|
-
//input: dataframe table
|
|
662
|
-
export async function sdfToJsonLib(table: DG.DataFrame) {
|
|
663
|
-
const _jsonMonomerLibrary = createJsonMonomerLibFromSdf(table);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
659
|
// 2023-05-17 Representations does not work at BioIT
|
|
667
660
|
// //name: Representations
|
|
668
661
|
// //tags: panel, widgets
|
|
@@ -915,6 +908,19 @@ export async function getRegionApp(): Promise<void> {
|
|
|
915
908
|
}
|
|
916
909
|
}
|
|
917
910
|
|
|
911
|
+
//name: getRegionHelmApp
|
|
912
|
+
export async function getRegionHelmApp(): Promise<void> {
|
|
913
|
+
const pi = DG.TaskBarProgressIndicator.create('getRegion ...');
|
|
914
|
+
try {
|
|
915
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
916
|
+
const df = await _package.files.readCsv('data/sample_HELM_empty_vals.csv');
|
|
917
|
+
const app = new GetRegionApp(urlParams, 'getRegionHelmApp');
|
|
918
|
+
await app.init({df: df, colName: 'HELM'});
|
|
919
|
+
} finally {
|
|
920
|
+
pi.close();
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
918
924
|
// -- Handle context menu --
|
|
919
925
|
|
|
920
926
|
///name: addCopyMenu
|
|
@@ -38,6 +38,7 @@ category('detectors', () => {
|
|
|
38
38
|
sepUn1 = 'sepUn1',
|
|
39
39
|
sepUn2 = 'sepUn2',
|
|
40
40
|
sepMsaDna1 = 'sepMsaDna1',
|
|
41
|
+
sepMsaUnWEmpty = 'sepMsaUnWEmpty',
|
|
41
42
|
fastaMsaDna1 = 'fastaMsaDna1',
|
|
42
43
|
fastaMsaPt1 = 'fastaMsaPt1',
|
|
43
44
|
}
|
|
@@ -109,6 +110,11 @@ rut12/rty/her2/abc/cfr3//wert/rut12`;
|
|
|
109
110
|
A-C--G-T--C-T
|
|
110
111
|
C-A-C--T--G-T
|
|
111
112
|
A-C-C-G-T-A-C-T`;
|
|
113
|
+
[csvTests.sepMsaUnWEmpty]: string = `seq
|
|
114
|
+
m1-M-m3-mon4-mon5-N-T-MON8-N9
|
|
115
|
+
m1-mon2-m3-mon4-mon5-Num--MON8-N9
|
|
116
|
+
|
|
117
|
+
mon1-M-mon3-mon4-mon5---MON8-N9`;
|
|
112
118
|
[csvTests.fastaMsaDna1]: string = `seq
|
|
113
119
|
AC-GT-CT
|
|
114
120
|
CAC-T-GT
|
|
@@ -234,6 +240,11 @@ MWRSWY-CKHP`;
|
|
|
234
240
|
await _testPos(readCsv(csvTests.fastaUn), 'seq',
|
|
235
241
|
NOTATION.FASTA, ALIGNMENT.SEQ_MSA, ALPHABET.UN, 12, true);
|
|
236
242
|
});
|
|
243
|
+
|
|
244
|
+
test('SepMsaUnWEmpty', async () => {
|
|
245
|
+
await _testPos(readCsv(csvTests.sepMsaUnWEmpty), 'seq',
|
|
246
|
+
NOTATION.SEPARATOR, ALIGNMENT.SEQ_MSA, ALPHABET.UN, 14, true);
|
|
247
|
+
});
|
|
237
248
|
test('FastaMsaDna1', async () => {
|
|
238
249
|
await _testPos(readCsv(csvTests.fastaMsaDna1), 'seq',
|
|
239
250
|
NOTATION.FASTA, ALIGNMENT.SEQ_MSA, ALPHABET.DNA, 4, false);
|
|
@@ -274,13 +285,13 @@ MWRSWY-CKHP`;
|
|
|
274
285
|
await _testDf(readSamples(Samples.fastaCsv), {
|
|
275
286
|
'Sequence': new PosCol(NOTATION.FASTA, ALIGNMENT.SEQ, ALPHABET.PT, 20, false),
|
|
276
287
|
});
|
|
277
|
-
}
|
|
288
|
+
});
|
|
278
289
|
|
|
279
290
|
test('samplesFastaFasta', async () => {
|
|
280
291
|
await _testDf(readSamples(Samples.fastaFasta), {
|
|
281
292
|
'sequence': new PosCol(NOTATION.FASTA, ALIGNMENT.SEQ, ALPHABET.PT, 20, false),
|
|
282
293
|
});
|
|
283
|
-
}
|
|
294
|
+
});
|
|
284
295
|
|
|
285
296
|
// peptidesComplex contains monomers with spaces in AlignedSequence columns, which are forbidden
|
|
286
297
|
// test('samplesPeptidesComplexPositiveAlignedSequence', async () => {
|
|
@@ -2,12 +2,14 @@ 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
|
import {category, test} from '@datagrok-libraries/utils/src/test';
|
|
7
7
|
import {MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
|
|
8
8
|
import {monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
9
9
|
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
10
10
|
|
|
11
|
+
import {_package} from '../package-test';
|
|
12
|
+
|
|
11
13
|
category('renderers: monomerPlacer', () => {
|
|
12
14
|
const tests = {
|
|
13
15
|
splitter: {
|
|
@@ -34,21 +36,27 @@ category('renderers: monomerPlacer', () => {
|
|
|
34
36
|
},
|
|
35
37
|
splitterMsa: {
|
|
36
38
|
/** For charWidth=7 and sepWidth=12, MSA
|
|
37
|
-
* Array(10) [
|
|
39
|
+
* Array(10) [5, 38, 71, 104, 137, 170, 203, 222, 255, 281]
|
|
38
40
|
*/
|
|
39
41
|
csv: 'id,seq\n' +
|
|
40
|
-
'id1,m1-M-m3-mon4-mon5-N-T-MON8-N9\n' +
|
|
41
|
-
'id2,m1-mon2-m3-mon4-mon5-Num--MON8-N9\n' +
|
|
42
|
-
'id3
|
|
42
|
+
'id1,m1-M-m3-mon4-mon5-N-T-MON8-N9\n' +
|
|
43
|
+
'id2,m1-mon2-m3-mon4-mon5-Num--MON8-N9\n' +
|
|
44
|
+
'id3,\n' + // empty
|
|
45
|
+
'id4,mon1-M-mon3-mon4-mon5---MON8-N9\n',
|
|
43
46
|
testList: [
|
|
44
47
|
{src: {row: 0, x: -1}, tgt: {pos: null}},
|
|
45
48
|
{src: {row: 1, x: 0}, tgt: {pos: null}},
|
|
46
49
|
{src: {row: 1, x: 1}, tgt: {pos: null}},
|
|
47
|
-
{src: {row: 1, x:
|
|
50
|
+
{src: {row: 1, x: 4}, tgt: {pos: null}},
|
|
51
|
+
{src: {row: 1, x: 5}, tgt: {pos: 0}},
|
|
52
|
+
{src: {row: 1, x: 37}, tgt: {pos: 0}},
|
|
53
|
+
{src: {row: 1, x: 38}, tgt: {pos: 1}},
|
|
48
54
|
{src: {row: 1, x: 170}, tgt: {pos: 4}},
|
|
49
55
|
{src: {row: 1, x: 200}, tgt: {pos: 5}},
|
|
50
|
-
{src: {row: 2, x:
|
|
51
|
-
{src: {row:
|
|
56
|
+
{src: {row: 2, x: 20}, tgt: {pos: null}}, // empty value
|
|
57
|
+
{src: {row: 3, x: 170}, tgt: {pos: 4}},
|
|
58
|
+
{src: {row: 3, x: 200}, tgt: {pos: 5}},
|
|
59
|
+
{src: {row: 3, x: 282}, tgt: {pos: null}},
|
|
52
60
|
]
|
|
53
61
|
},
|
|
54
62
|
fastaMsa: {
|
|
@@ -58,6 +66,7 @@ category('renderers: monomerPlacer', () => {
|
|
|
58
66
|
csv: `id,seq
|
|
59
67
|
id1,QQYNIYPLT
|
|
60
68
|
id2,QQWSSFPYT
|
|
69
|
+
id3,
|
|
61
70
|
id3,QHIRE--LT
|
|
62
71
|
`,
|
|
63
72
|
testList: [
|
|
@@ -67,14 +76,15 @@ id3,QHIRE--LT
|
|
|
67
76
|
{src: {row: 1, x: 19}, tgt: {pos: 0}},
|
|
68
77
|
{src: {row: 1, x: 170}, tgt: {pos: 8}},
|
|
69
78
|
{src: {row: 1, x: 171}, tgt: {pos: 8}},
|
|
70
|
-
{src: {row: 2, x:
|
|
71
|
-
{src: {row:
|
|
79
|
+
{src: {row: 2, x: 5}, tgt: {pos: null}}, // empty value
|
|
80
|
+
{src: {row: 3, x: 170}, tgt: {pos: 8}},
|
|
81
|
+
{src: {row: 3, x: 181}, tgt: {pos: null}},
|
|
72
82
|
]
|
|
73
83
|
},
|
|
74
84
|
};
|
|
75
85
|
|
|
76
86
|
for (const [testName, testData] of Object.entries(tests)) {
|
|
77
|
-
test(`
|
|
87
|
+
test(`getPosition-${testName}`, async () => {
|
|
78
88
|
const df: DG.DataFrame = DG.DataFrame.fromCsv(testData.csv);
|
|
79
89
|
await grok.data.detectSemanticTypes(df);
|
|
80
90
|
const seqCol: DG.Column = df.getCol('seq');
|
|
@@ -94,9 +104,12 @@ id3,QHIRE--LT
|
|
|
94
104
|
});
|
|
95
105
|
|
|
96
106
|
const testList = testData.testList;
|
|
107
|
+
// simulate rendering
|
|
108
|
+
for (let rowI: number = 0; rowI < seqCol.length; ++rowI)
|
|
109
|
+
colTemp.getCellMonomerLengths(rowI);
|
|
97
110
|
|
|
98
111
|
const errorList: string[] = [];
|
|
99
|
-
for (const test of testList) {
|
|
112
|
+
for (const [test, _testI] of wu.enumerate(testList)) {
|
|
100
113
|
const res = {pos: colTemp.getPosition(test.src.row, test.src.x)};
|
|
101
114
|
if (test.tgt.pos != res.pos) {
|
|
102
115
|
errorList.push(`Test src ${JSON.stringify(test.src)} expected tgt ${JSON.stringify(test.tgt)},` +
|
|
@@ -29,7 +29,7 @@ export class GetRegionFuncEditor {
|
|
|
29
29
|
region: DG.InputBase<SeqRegion>;
|
|
30
30
|
start: DG.InputBase<string>;
|
|
31
31
|
end: DG.InputBase<string>;
|
|
32
|
-
name: DG.InputBase
|
|
32
|
+
name: DG.InputBase<string>;
|
|
33
33
|
}();
|
|
34
34
|
|
|
35
35
|
constructor(
|
|
@@ -54,8 +54,9 @@ export class GetRegionFuncEditor {
|
|
|
54
54
|
this.inputs.region = ui.choiceInput<SeqRegion>('Region', null as unknown as SeqRegion, [],
|
|
55
55
|
this.regionInputChanged.bind(this)) as DG.InputBase<SeqRegion>;
|
|
56
56
|
|
|
57
|
-
this.inputs.name = ui.stringInput('
|
|
58
|
-
|
|
57
|
+
this.inputs.name = ui.stringInput('Column name', this.getDefaultName(),
|
|
58
|
+
this.nameInputChanged.bind(this), {clearIcon: true});
|
|
59
|
+
this.inputs.name.onInput(this.nameInputInput.bind(this)); // To catch clear event
|
|
59
60
|
|
|
60
61
|
// tooltips
|
|
61
62
|
for (const paramName in this.call.inputParams) {
|
|
@@ -73,7 +74,7 @@ export class GetRegionFuncEditor {
|
|
|
73
74
|
this.updateRegionItems();
|
|
74
75
|
this.updateStartEndInputItems();
|
|
75
76
|
this.updateRegion(true);
|
|
76
|
-
this.
|
|
77
|
+
this.updateNameInput();
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
private fixRegion: boolean = false;
|
|
@@ -99,12 +100,24 @@ export class GetRegionFuncEditor {
|
|
|
99
100
|
|
|
100
101
|
private startInputChanged(): void {
|
|
101
102
|
this.updateRegion(false);
|
|
102
|
-
this.
|
|
103
|
+
this.updateNameInput();
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
private endInputChanged(): void {
|
|
106
107
|
this.updateRegion(false);
|
|
107
|
-
this.
|
|
108
|
+
this.updateNameInput();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private nameInputChanged(): void {
|
|
112
|
+
if (!this.defaultNameUpdating)
|
|
113
|
+
this.defaultName = false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private nameInputInput(): void {
|
|
117
|
+
if (!this.inputs.name.value) {
|
|
118
|
+
this.defaultName = true;
|
|
119
|
+
this.inputs.name.input.focus();
|
|
120
|
+
}
|
|
108
121
|
}
|
|
109
122
|
|
|
110
123
|
private updateStartEndInputItems(): void {
|
|
@@ -168,9 +181,16 @@ export class GetRegionFuncEditor {
|
|
|
168
181
|
}
|
|
169
182
|
}
|
|
170
183
|
|
|
171
|
-
private
|
|
172
|
-
|
|
173
|
-
|
|
184
|
+
private defaultName: boolean = true;
|
|
185
|
+
private defaultNameUpdating: boolean = false;
|
|
186
|
+
|
|
187
|
+
private updateNameInput(): void {
|
|
188
|
+
this.defaultNameUpdating = true;
|
|
189
|
+
try {
|
|
190
|
+
if (this.defaultName) this.inputs.name.value = this.getDefaultName();
|
|
191
|
+
} finally {
|
|
192
|
+
this.defaultNameUpdating = false;
|
|
193
|
+
}
|
|
174
194
|
}
|
|
175
195
|
|
|
176
196
|
private getDefaultName(): string {
|
|
@@ -192,7 +212,7 @@ export class GetRegionFuncEditor {
|
|
|
192
212
|
sequence: this.inputs.sequence.value!,
|
|
193
213
|
start: this.getStart(),
|
|
194
214
|
end: this.getEnd(),
|
|
195
|
-
name: this.getName()
|
|
215
|
+
name: this.getName(),
|
|
196
216
|
};
|
|
197
217
|
}
|
|
198
218
|
|
|
@@ -144,7 +144,7 @@ export class PositionInfo {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
calcScreen(
|
|
147
|
-
posIdx: number, firstVisiblePosIdx: number,
|
|
147
|
+
isGap: (m: string) => boolean, posIdx: number, firstVisiblePosIdx: number,
|
|
148
148
|
absoluteMaxHeight: number, heightMode: PositionHeight, alphabetSizeLog: number,
|
|
149
149
|
positionWidthWithMargin: number, positionWidth: number, dpr: number, axisHeight: number
|
|
150
150
|
): void {
|
|
@@ -155,13 +155,13 @@ export class PositionInfo {
|
|
|
155
155
|
|
|
156
156
|
const entries = Object.entries(this._freqs)
|
|
157
157
|
.sort((a, b) => {
|
|
158
|
-
if (a[0]
|
|
158
|
+
if (!isGap(a[0]) && !isGap(b[0]))
|
|
159
159
|
return b[1].count - a[1].count;
|
|
160
|
-
else if (a[0]
|
|
160
|
+
else if (isGap(a[0]) && isGap(b[0]))
|
|
161
161
|
return 0;
|
|
162
|
-
else if (a[0]
|
|
162
|
+
else if (isGap(a[0]))
|
|
163
163
|
return -1;
|
|
164
|
-
else /* (b[0]
|
|
164
|
+
else /* (isGap(b[0])) */
|
|
165
165
|
return +1;
|
|
166
166
|
});
|
|
167
167
|
for (const entry of entries) {
|
|
@@ -177,10 +177,11 @@ export class PositionInfo {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
render(g: CanvasRenderingContext2D,
|
|
180
|
+
isGap: (m: string) => boolean,
|
|
180
181
|
fontStyle: string, uppercaseLetterAscent: number, uppercaseLetterHeight: number, cp: SeqPalette
|
|
181
182
|
) {
|
|
182
183
|
for (const [monomer, pmInfo] of Object.entries(this._freqs)) {
|
|
183
|
-
if (monomer
|
|
184
|
+
if (!isGap(monomer)) {
|
|
184
185
|
const monomerTxt = monomerToShort(monomer, 5);
|
|
185
186
|
const b = pmInfo.bounds!;
|
|
186
187
|
const left = b.left;
|
|
@@ -416,21 +417,33 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
416
417
|
// -- Data --
|
|
417
418
|
|
|
418
419
|
setData(): void {
|
|
419
|
-
if (this.
|
|
420
|
-
|
|
421
|
-
this.viewed = false;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
this.updateSeqCol();
|
|
420
|
+
if (!this.setDataInProgress) this.setDataInProgress = true; else return;
|
|
421
|
+
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.setData() `);
|
|
425
422
|
|
|
426
|
-
|
|
427
|
-
this.
|
|
428
|
-
|
|
429
|
-
|
|
423
|
+
this.viewPromise = this.viewPromise.then(async () => { // setData
|
|
424
|
+
if (this.viewed) {
|
|
425
|
+
await this.destroyView();
|
|
426
|
+
this.viewed = false;
|
|
427
|
+
}
|
|
428
|
+
}).then(async () => {
|
|
429
|
+
await this.detachPromise;
|
|
430
|
+
|
|
431
|
+
this.updateSeqCol();
|
|
432
|
+
}).then(async () => {
|
|
433
|
+
if (!this.viewed) {
|
|
434
|
+
await this.buildView();
|
|
435
|
+
this.viewed = true;
|
|
436
|
+
}
|
|
437
|
+
}).finally(() => {
|
|
438
|
+
this.setDataInProgress = false;
|
|
439
|
+
});
|
|
430
440
|
}
|
|
431
441
|
|
|
432
442
|
// -- View --
|
|
433
443
|
|
|
444
|
+
private viewPromise: Promise<void> = Promise.resolve();
|
|
445
|
+
private detachPromise: Promise<void> = Promise.resolve();
|
|
446
|
+
private setDataInProgress: boolean = false;
|
|
434
447
|
private viewSubs: Unsubscribable[] = [];
|
|
435
448
|
|
|
436
449
|
private async destroyView(): Promise<void> {
|
|
@@ -439,7 +452,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
439
452
|
|
|
440
453
|
const dataFrameTxt = `${this.dataFrame ? 'data' : 'null'}`;
|
|
441
454
|
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.destroyView( dataFrame = ${dataFrameTxt} ) start`);
|
|
442
|
-
super.detach();
|
|
443
455
|
|
|
444
456
|
this.viewSubs.forEach((sub) => sub.unsubscribe());
|
|
445
457
|
this.host!.remove();
|
|
@@ -525,8 +537,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
525
537
|
}
|
|
526
538
|
if (this.seqCol) {
|
|
527
539
|
try {
|
|
528
|
-
const units: string = this.seqCol!.getTag(DG.TAGS.UNITS);
|
|
529
|
-
const separator: string = this.seqCol!.getTag(bioTAGS.separator);
|
|
530
540
|
this.unitsHandler = UnitsHandler.getOrCreate(this.seqCol);
|
|
531
541
|
|
|
532
542
|
this.cp = pickUpPalette(this.seqCol);
|
|
@@ -811,10 +821,18 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
811
821
|
|
|
812
822
|
/** Remove all handlers when table is a detach */
|
|
813
823
|
public override async detach() {
|
|
814
|
-
if (this.
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
824
|
+
if (this.setDataInProgress) return;
|
|
825
|
+
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.detach(), `);
|
|
826
|
+
|
|
827
|
+
const superDetach = super.detach.bind(this);
|
|
828
|
+
this.detachPromise = this.detachPromise.then(async () => { // detach
|
|
829
|
+
await this.viewPromise;
|
|
830
|
+
if (this.viewed) {
|
|
831
|
+
await this.destroyView();
|
|
832
|
+
this.viewed = false;
|
|
833
|
+
}
|
|
834
|
+
superDetach();
|
|
835
|
+
});
|
|
818
836
|
}
|
|
819
837
|
|
|
820
838
|
private _onSizeChanged: Subject<void> = new Subject<void>();
|
|
@@ -861,8 +879,10 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
861
879
|
/** Function for removing empty positions */
|
|
862
880
|
protected _removeEmptyPositions() {
|
|
863
881
|
if (this.skipEmptyPositions) {
|
|
864
|
-
this.positions = wu(this.positions)
|
|
865
|
-
|
|
882
|
+
this.positions = wu(this.positions).filter((pi) => {
|
|
883
|
+
const gapSymbol: string = this.unitsHandler!.defaultGapSymbol;
|
|
884
|
+
return !pi.hasMonomer(gapSymbol) || pi.getFreq(gapSymbol).count !== pi.rowCount;
|
|
885
|
+
}).toArray();
|
|
866
886
|
}
|
|
867
887
|
}
|
|
868
888
|
|
|
@@ -899,7 +919,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
899
919
|
if (dfFilter.get(rowI)) {
|
|
900
920
|
const seqMList: ISeqSplitted = splitted[rowI];
|
|
901
921
|
for (let jPos = 0; jPos < length; ++jPos) {
|
|
902
|
-
const m: string = seqMList[this.startPosition + jPos] ||
|
|
922
|
+
const m: string = seqMList[this.startPosition + jPos] || this.unitsHandler.defaultGapSymbol;
|
|
903
923
|
const pmInfo = this.positions[jPos].getFreq(m);
|
|
904
924
|
pmInfo.count++;
|
|
905
925
|
}
|
|
@@ -908,7 +928,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
908
928
|
|
|
909
929
|
//#region Polish freq counts
|
|
910
930
|
for (let jPos = 0; jPos < length; jPos++) {
|
|
911
|
-
// delete this.positions[jPos].freq[
|
|
931
|
+
// delete this.positions[jPos].freq[this.unitsHandler.defaultGapSymbol];
|
|
912
932
|
this.positions[jPos].calcHeights(this.positionHeight as PositionHeight);
|
|
913
933
|
}
|
|
914
934
|
//#endregion
|
|
@@ -928,7 +948,13 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
928
948
|
const alphabetSizeLog = Math.log2(alphabetSize);
|
|
929
949
|
|
|
930
950
|
for (let jPos = Math.floor(this.slider.min); jPos <= Math.floor(this.slider.max); ++jPos) {
|
|
931
|
-
|
|
951
|
+
if (!(jPos in this.positions)) {
|
|
952
|
+
console.warn(`Bio: WebLogoViewer<${this.viewerId}>.render.calculateLayoutInt() ` +
|
|
953
|
+
`this.positions.length = ${this.positions.length}, jPos = ${jPos}`);
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
956
|
+
this.positions[jPos].calcScreen((m) => { return this.unitsHandler!.isGap(m); },
|
|
957
|
+
jPos, this.slider.min, absoluteMaxHeight, this.positionHeight,
|
|
932
958
|
alphabetSizeLog, this._positionWidthWithMargin, this._positionWidth, dpr, positionLabelsHeight);
|
|
933
959
|
}
|
|
934
960
|
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.calculateLayoutInt(), end `);
|
|
@@ -991,7 +1017,8 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
991
1017
|
const uppercaseLetterAscent = 0.25;
|
|
992
1018
|
const uppercaseLetterHeight = 12.2;
|
|
993
1019
|
for (let jPos = Math.floor(this.slider.min); jPos <= Math.floor(this.slider.max); jPos++) {
|
|
994
|
-
this.positions[jPos].render(g,
|
|
1020
|
+
this.positions[jPos].render(g, (m) => { return this.unitsHandler!.isGap(m); },
|
|
1021
|
+
fontStyle, uppercaseLetterAscent, uppercaseLetterHeight,
|
|
995
1022
|
/* this._positionWidthWithMargin, firstVisiblePosIdx,*/ this.cp);
|
|
996
1023
|
}
|
|
997
1024
|
|
|
@@ -1040,32 +1067,32 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1040
1067
|
this.render(WlRenderLevel.Layout, 'sliderOnValuesChanged').then(() => {});
|
|
1041
1068
|
} catch (err: any) {
|
|
1042
1069
|
const errMsg = errorToConsole(err);
|
|
1043
|
-
_package.logger.error(
|
|
1070
|
+
_package.logger.error(`Bio: WebLogoViewer<${this.viewerId}>.sliderOnValuesChanged() error:\n` + errMsg);
|
|
1044
1071
|
//throw err; // Do not throw to prevent disabling event handler
|
|
1045
1072
|
}
|
|
1046
1073
|
}
|
|
1047
1074
|
|
|
1048
1075
|
private dataFrameFilterOnChanged(_value: any): void {
|
|
1049
|
-
_package.logger.debug(
|
|
1076
|
+
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.dataFrameFilterChanged()`);
|
|
1050
1077
|
try {
|
|
1051
1078
|
this.updatePositions();
|
|
1052
1079
|
if (this.filterSource === FilterSources.Filtered)
|
|
1053
1080
|
this.render(WlRenderLevel.Freqs, 'dataFrameFilterOnChanged').then(() => {});
|
|
1054
1081
|
} catch (err: any) {
|
|
1055
1082
|
const errMsg = errorToConsole(err);
|
|
1056
|
-
_package.logger.error(
|
|
1083
|
+
_package.logger.error(`Bio: WebLogoViewer<${this.viewerId}>.dataFrameFilterOnChanged() error:\n` + errMsg);
|
|
1057
1084
|
//throw err; // Do not throw to prevent disabling event handler
|
|
1058
1085
|
}
|
|
1059
1086
|
}
|
|
1060
1087
|
|
|
1061
1088
|
private dataFrameSelectionOnChanged(_value: any): void {
|
|
1062
|
-
_package.logger.debug(
|
|
1089
|
+
_package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.dataFrameSelectionOnChanged()`);
|
|
1063
1090
|
try {
|
|
1064
1091
|
if (this.filterSource === FilterSources.Selected)
|
|
1065
1092
|
this.render(WlRenderLevel.Freqs, 'dataFrameSelectionOnChanged').then(() => {});
|
|
1066
1093
|
} catch (err: any) {
|
|
1067
1094
|
const errMsg = errorToConsole(err);
|
|
1068
|
-
_package.logger.error(
|
|
1095
|
+
_package.logger.error(`Bio: WebLogoViewer<${this.viewerId}>.dataFrameSelectionOnChanged() error:\n` + errMsg);
|
|
1069
1096
|
//throw err; // Do not throw to prevent disabling event handler
|
|
1070
1097
|
}
|
|
1071
1098
|
}
|
|
@@ -1099,7 +1126,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1099
1126
|
}
|
|
1100
1127
|
} catch (err: any) {
|
|
1101
1128
|
const errMsg = errorToConsole(err);
|
|
1102
|
-
_package.logger.error(
|
|
1129
|
+
_package.logger.error(`Bio: WebLogoViewer<${this.viewerId}>.canvasOnMouseMove() error:\n` + errMsg);
|
|
1103
1130
|
//throw err; // Do not throw to prevent disabling event handler
|
|
1104
1131
|
}
|
|
1105
1132
|
}
|
|
@@ -1122,7 +1149,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1122
1149
|
}
|
|
1123
1150
|
} catch (err: any) {
|
|
1124
1151
|
const errMsg = errorToConsole(err);
|
|
1125
|
-
_package.logger.error(
|
|
1152
|
+
_package.logger.error(`Bio: WebLogoViewer<${this.viewerId}>.canvasOnMouseDown() error:\n` + errMsg);
|
|
1126
1153
|
//throw err; // Do not throw to prevent disabling event handler
|
|
1127
1154
|
}
|
|
1128
1155
|
}
|
|
@@ -1136,7 +1163,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1136
1163
|
this.slider.scrollBy(this.slider.min + countOfScrollPositions);
|
|
1137
1164
|
} catch (err: any) {
|
|
1138
1165
|
const errMsg = errorToConsole(err);
|
|
1139
|
-
_package.logger.error(
|
|
1166
|
+
_package.logger.error(`Bio: WebLogoViewer<${this.viewerId}>.canvasOnWheel() error:\n` + errMsg);
|
|
1140
1167
|
//throw err; // Do not throw to prevent disabling event handler
|
|
1141
1168
|
}
|
|
1142
1169
|
}
|
|
@@ -1161,7 +1188,7 @@ export function checkSeqForMonomerAtPos(
|
|
|
1161
1188
|
): boolean {
|
|
1162
1189
|
const seqMList: ISeqSplitted = unitsHandler.splitted[rowI];
|
|
1163
1190
|
const seqM = at.pos < seqMList.length ? seqMList[at.pos] : null;
|
|
1164
|
-
return ((seqM === monomer) || (seqM === '' && monomer ===
|
|
1191
|
+
return ((seqM === monomer) || (seqM === '' && monomer === unitsHandler.defaultGapSymbol));
|
|
1165
1192
|
}
|
|
1166
1193
|
|
|
1167
1194
|
export function countForMonomerAtPosition(
|