@datagrok/bio 2.0.21 → 2.0.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/dist/package-test.js +133 -32
- package/dist/package.js +100 -10
- package/package.json +2 -2
- package/src/package-test.ts +3 -2
- package/src/package.ts +2 -2
- package/src/substructure-search/substructure-search.ts +1 -1
- package/src/tests/WebLogo-positions-test.ts +9 -6
- package/src/tests/checkInputColumn-tests.ts +9 -4
- package/src/widgets/bio-substructure-filter.ts +106 -9
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "Leonid Stolbov",
|
|
6
6
|
"email": "lstolbov@datagrok.ai"
|
|
7
7
|
},
|
|
8
|
-
"version": "2.0.
|
|
8
|
+
"version": "2.0.23",
|
|
9
9
|
"description": "Bio is a [package](https://datagrok.ai/help/develop/develop#packages) for the [Datagrok](https://datagrok.ai) platform",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"@datagrok-libraries/bio": "^5.1.1",
|
|
18
18
|
"@datagrok-libraries/chem-meta": "1.0.1",
|
|
19
19
|
"@datagrok-libraries/ml": "^6.2.0",
|
|
20
|
-
"@datagrok-libraries/utils": "^1.
|
|
20
|
+
"@datagrok-libraries/utils": "^1.11.0",
|
|
21
21
|
"@deck.gl/core": "^8.7.5",
|
|
22
22
|
"@deck.gl/layers": "^8.7.5",
|
|
23
23
|
"@luma.gl/constants": "^8.5.10",
|
package/src/package-test.ts
CHANGED
|
@@ -24,8 +24,9 @@ export {tests};
|
|
|
24
24
|
//name: test
|
|
25
25
|
//input: string category {optional: true}
|
|
26
26
|
//input: string test {optional: true}
|
|
27
|
+
//input: bool catchUnhandled {optional: true}
|
|
27
28
|
//output: dataframe result
|
|
28
|
-
export async function test(category: string, test: string): Promise<DG.DataFrame> {
|
|
29
|
-
const data = await runTests({category, test});
|
|
29
|
+
export async function test(category: string, test: string, catchUnhandled: boolean): Promise<DG.DataFrame> {
|
|
30
|
+
const data = await runTests({category, test, catchUnhandled});
|
|
30
31
|
return DG.DataFrame.fromObjects(data)!;
|
|
31
32
|
}
|
package/src/package.ts
CHANGED
|
@@ -99,7 +99,7 @@ export function checkInputColumn(
|
|
|
99
99
|
) {
|
|
100
100
|
const notationAdd = allowedNotations.length == 0 ? 'any notation' :
|
|
101
101
|
(`notation${allowedNotations.length > 1 ? 's' : ''} ${allowedNotations.map((n) => `"${n}"`).join(', ')} `);
|
|
102
|
-
msg = `${name}
|
|
102
|
+
msg = `${name} analysis is allowed for Macromolecules with ${notationAdd}.`;
|
|
103
103
|
res = false;
|
|
104
104
|
} else if (!uh.isHelm()) {
|
|
105
105
|
// alphabet is not specified for 'helm' notation
|
|
@@ -521,7 +521,7 @@ export function saveAsFasta() {
|
|
|
521
521
|
}
|
|
522
522
|
|
|
523
523
|
//name: BioSubstructureFilter
|
|
524
|
-
//description: Substructure filter for
|
|
524
|
+
//description: Substructure filter for macromolecules
|
|
525
525
|
//tags: filter
|
|
526
526
|
//output: filter result
|
|
527
527
|
//meta.semType: Macromolecule
|
|
@@ -78,7 +78,7 @@ export function linearSubstructureSearch(substructure: string, col: DG.Column):
|
|
|
78
78
|
return resultArray;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
async function helmSubstructureSearch(substructure: string, col: DG.Column): Promise<BitSet> {
|
|
81
|
+
export async function helmSubstructureSearch(substructure: string, col: DG.Column): Promise<BitSet> {
|
|
82
82
|
const helmColWithSubstructure = DG.Column.string('helm', col.length + 1)
|
|
83
83
|
.init((i) => i === col.length ? substructure : col.get(i));
|
|
84
84
|
helmColWithSubstructure.setTag(DG.TAGS.UNITS, bio.NOTATION.HELM);
|
|
@@ -38,6 +38,7 @@ ATC-G-TTGC--
|
|
|
38
38
|
seqCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
39
39
|
seqCol.setTag(DG.TAGS.UNITS, bio.NOTATION.FASTA);
|
|
40
40
|
seqCol.setTag(bio.TAGS.alphabet, bio.ALPHABET.DNA);
|
|
41
|
+
seqCol.setTag(bio.TAGS.aligned, 'SEQ.MSA');
|
|
41
42
|
|
|
42
43
|
const wlViewer: bio.WebLogo = (await df.plot.fromType('WebLogo')) as bio.WebLogo;
|
|
43
44
|
tv.dockManager.dock(wlViewer.root, DG.DOCK_TYPE.DOWN);
|
|
@@ -74,12 +75,12 @@ ATC-G-TTGC--
|
|
|
74
75
|
|
|
75
76
|
test('positions with shrinkEmptyTail option true (filterd)', async () => {
|
|
76
77
|
let csvDf2 = `seq
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
-TC-G-TTGC--
|
|
79
|
+
-TC-GCTTGC--
|
|
80
|
+
-T--C-GT-
|
|
81
|
+
-T--C-GT-
|
|
82
|
+
-T--C-GT-
|
|
83
|
+
-T--CCGT-`;
|
|
83
84
|
const df: DG.DataFrame = DG.DataFrame.fromCsv(csvDf2);
|
|
84
85
|
const tv: DG.TableView = grok.shell.addTableView(df);
|
|
85
86
|
|
|
@@ -87,6 +88,7 @@ ATC-G-TTGC--
|
|
|
87
88
|
seqCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
88
89
|
seqCol.setTag(DG.TAGS.UNITS, bio.NOTATION.FASTA);
|
|
89
90
|
seqCol.setTag(bio.TAGS.alphabet, bio.ALPHABET.DNA);
|
|
91
|
+
seqCol.setTag(bio.TAGS.aligned, 'SEQ');
|
|
90
92
|
|
|
91
93
|
df.filter.init((i) => {
|
|
92
94
|
return i > 2;
|
|
@@ -131,6 +133,7 @@ ATC-G-TTGC--
|
|
|
131
133
|
seqCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
132
134
|
seqCol.setTag(DG.TAGS.UNITS, bio.NOTATION.FASTA);
|
|
133
135
|
seqCol.setTag(bio.TAGS.alphabet, bio.ALPHABET.DNA);
|
|
136
|
+
seqCol.setTag(bio.TAGS.aligned, 'SEQ.MSA');
|
|
134
137
|
|
|
135
138
|
const wlViewer: bio.WebLogo = (await df.plot.fromType('WebLogo',
|
|
136
139
|
{'skipEmptyPositions': true})) as bio.WebLogo;
|
|
@@ -27,11 +27,13 @@ seq4`;
|
|
|
27
27
|
const df: DG.DataFrame = DG.DataFrame.fromCsv(csv);
|
|
28
28
|
const col: DG.Column = df.getCol('seq');
|
|
29
29
|
col.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
30
|
-
col.setTag(DG.TAGS.UNITS,
|
|
30
|
+
col.setTag(DG.TAGS.UNITS, bio.NOTATION.FASTA);
|
|
31
31
|
col.setTag(bio.TAGS.alphabet, bio.ALPHABET.DNA);
|
|
32
|
+
col.setTag(bio.TAGS.aligned, 'SEQ');
|
|
32
33
|
|
|
33
34
|
const [res, msg]: [boolean, string] = checkInputColumn(
|
|
34
|
-
col, 'Test', [
|
|
35
|
+
col, 'Test', [bio.NOTATION.FASTA,],
|
|
36
|
+
[bio.ALPHABET.DNA, bio.ALPHABET.RNA, bio.ALPHABET.PT]);
|
|
35
37
|
|
|
36
38
|
expect(res, true);
|
|
37
39
|
});
|
|
@@ -45,7 +47,8 @@ seq4`;
|
|
|
45
47
|
col.setTag(bio.TAGS.alphabetIsMultichar, 'true');
|
|
46
48
|
|
|
47
49
|
const [res, msg]: [boolean, string] = checkInputColumn(
|
|
48
|
-
col, 'Test', [
|
|
50
|
+
col, 'Test', [bio.NOTATION.FASTA,],
|
|
51
|
+
[bio.ALPHABET.DNA, bio.ALPHABET.RNA, bio.ALPHABET.PT]);
|
|
49
52
|
|
|
50
53
|
expect(res, false);
|
|
51
54
|
});
|
|
@@ -58,9 +61,11 @@ seq4`;
|
|
|
58
61
|
col.setTag(bio.TAGS.alphabet, 'UN');
|
|
59
62
|
col.setTag(bio.TAGS.alphabetSize, '11');
|
|
60
63
|
col.setTag(bio.TAGS.alphabetIsMultichar, 'true');
|
|
64
|
+
col.setTag(bio.TAGS.aligned, 'SEQ');
|
|
61
65
|
|
|
62
66
|
const [res, msg]: [boolean, string] = checkInputColumn(
|
|
63
|
-
col, 'Test', [
|
|
67
|
+
col, 'Test', [bio.NOTATION.FASTA,],
|
|
68
|
+
[bio.ALPHABET.DNA, bio.ALPHABET.RNA, bio.ALPHABET.PT]);
|
|
64
69
|
|
|
65
70
|
expect(res, false);
|
|
66
71
|
});
|
|
@@ -6,18 +6,21 @@
|
|
|
6
6
|
|
|
7
7
|
import * as ui from 'datagrok-api/ui';
|
|
8
8
|
import * as DG from 'datagrok-api/dg';
|
|
9
|
-
import * as bio from '@datagrok-libraries/bio';
|
|
10
9
|
|
|
10
|
+
import * as grok from 'datagrok-api/grok';
|
|
11
11
|
import wu from 'wu';
|
|
12
|
-
import {linearSubstructureSearch} from '../substructure-search/substructure-search';
|
|
12
|
+
import {helmSubstructureSearch, linearSubstructureSearch} from '../substructure-search/substructure-search';
|
|
13
13
|
import {Subject, Subscription} from 'rxjs';
|
|
14
14
|
import * as C from '../utils/constants';
|
|
15
|
+
import {updateDivInnerHTML} from '../utils/ui-utils';
|
|
16
|
+
import {NOTATION} from '@datagrok-libraries/bio';
|
|
15
17
|
|
|
16
18
|
export class BioSubstructureFilter extends DG.Filter {
|
|
17
|
-
bioFilter: FastaFilter | SeparatorFilter | null = null;
|
|
19
|
+
bioFilter: FastaFilter | SeparatorFilter | HelmFilter | null = null;
|
|
18
20
|
bitset: DG.BitSet | null = null;
|
|
19
21
|
loader: HTMLDivElement = ui.loader();
|
|
20
22
|
onBioFilterChangedSubs?: Subscription;
|
|
23
|
+
notation: string | undefined = '';
|
|
21
24
|
|
|
22
25
|
get calculating(): boolean { return this.loader.style.display == 'initial'; }
|
|
23
26
|
|
|
@@ -45,9 +48,10 @@ export class BioSubstructureFilter extends DG.Filter {
|
|
|
45
48
|
super.attach(dataFrame);
|
|
46
49
|
this.column = dataFrame.columns.bySemType(DG.SEMTYPE.MACROMOLECULE);
|
|
47
50
|
this.columnName = this.column?.name;
|
|
48
|
-
|
|
49
|
-
this.bioFilter = notation ===
|
|
50
|
-
new FastaFilter() :
|
|
51
|
+
this.notation = this.column?.getTag(DG.TAGS.UNITS);
|
|
52
|
+
this.bioFilter = this.notation === NOTATION.FASTA ?
|
|
53
|
+
new FastaFilter() : this.notation === NOTATION.SEPARATOR ?
|
|
54
|
+
new SeparatorFilter(this.column!.getTag(C.TAGS.SEPARATOR)) : new HelmFilter();
|
|
51
55
|
this.root.appendChild(this.bioFilter!.filterPanel);
|
|
52
56
|
this.root.appendChild(this.loader);
|
|
53
57
|
|
|
@@ -76,7 +80,7 @@ export class BioSubstructureFilter extends DG.Filter {
|
|
|
76
80
|
applyState(state: any): void {
|
|
77
81
|
super.applyState(state);
|
|
78
82
|
if (state.bioSubstructure)
|
|
79
|
-
this.bioFilter!.
|
|
83
|
+
this.bioFilter!.substructure = state.bioSubstructure;
|
|
80
84
|
|
|
81
85
|
const that = this;
|
|
82
86
|
if (state.bioSubstructure)
|
|
@@ -98,7 +102,9 @@ export class BioSubstructureFilter extends DG.Filter {
|
|
|
98
102
|
} else {
|
|
99
103
|
this.calculating = true;
|
|
100
104
|
try {
|
|
101
|
-
this.bitset =
|
|
105
|
+
this.bitset = this.notation === NOTATION.HELM ?
|
|
106
|
+
await helmSubstructureSearch(this.bioFilter!.substructure, this.column!) :
|
|
107
|
+
linearSubstructureSearch(this.bioFilter!.substructure, this.column!);
|
|
102
108
|
this.calculating = false;
|
|
103
109
|
this.dataFrame?.rows.requestFilter();
|
|
104
110
|
} finally {
|
|
@@ -108,13 +114,28 @@ export class BioSubstructureFilter extends DG.Filter {
|
|
|
108
114
|
}
|
|
109
115
|
}
|
|
110
116
|
|
|
111
|
-
class
|
|
117
|
+
abstract class BioFilterBase {
|
|
112
118
|
onChanged: Subject<any> = new Subject<any>();
|
|
119
|
+
|
|
120
|
+
get filterPanel() {
|
|
121
|
+
return new HTMLElement();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get substructure() {
|
|
125
|
+
return '';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
set substructure(s: string) {
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
class FastaFilter extends BioFilterBase {
|
|
113
133
|
substructureInput: DG.InputBase<string> = ui.stringInput('', '', () => {
|
|
114
134
|
this.onChanged.next();
|
|
115
135
|
}, {placeholder: 'Substructure'});
|
|
116
136
|
|
|
117
137
|
constructor() {
|
|
138
|
+
super();
|
|
118
139
|
}
|
|
119
140
|
|
|
120
141
|
get filterPanel() {
|
|
@@ -124,6 +145,10 @@ class FastaFilter {
|
|
|
124
145
|
get substructure() {
|
|
125
146
|
return this.substructureInput.value;
|
|
126
147
|
}
|
|
148
|
+
|
|
149
|
+
set substructure(s: string) {
|
|
150
|
+
this.substructureInput.value = s;
|
|
151
|
+
}
|
|
127
152
|
}
|
|
128
153
|
|
|
129
154
|
class SeparatorFilter extends FastaFilter {
|
|
@@ -150,4 +175,76 @@ class SeparatorFilter extends FastaFilter {
|
|
|
150
175
|
this.substructureInput.value.replaceAll(this.separatorInput.value, this.colSeparator) :
|
|
151
176
|
this.substructureInput.value;
|
|
152
177
|
}
|
|
178
|
+
|
|
179
|
+
set substructure(s: string) {
|
|
180
|
+
this.substructureInput.value = s;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
class HelmFilter extends BioFilterBase {
|
|
185
|
+
helmEditor: any;
|
|
186
|
+
_filterPanel = ui.div('', {style: {cursor: 'pointer'}});
|
|
187
|
+
helmSubstructure = '';
|
|
188
|
+
|
|
189
|
+
constructor() {
|
|
190
|
+
super();
|
|
191
|
+
this.init();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async init() {
|
|
195
|
+
this.helmEditor = await grok.functions.call('HELM:helmWebEditor');
|
|
196
|
+
await ui.tools.waitForElementInDom(this._filterPanel);
|
|
197
|
+
this.updateFilterPanel();
|
|
198
|
+
this._filterPanel.addEventListener('click', (event: MouseEvent) => {
|
|
199
|
+
//@ts-ignore
|
|
200
|
+
ui.dialog({showHeader: false, showFooter: true})
|
|
201
|
+
.add(this.helmEditor.editorView)
|
|
202
|
+
.onOK(() => {
|
|
203
|
+
const helmString = this.helmEditor
|
|
204
|
+
.webEditor.canvas.getHelm(true).replace(/<\/span>/g, '').replace(/<span style='background:#bbf;'>/g, '');
|
|
205
|
+
this.updateFilterPanel(helmString);
|
|
206
|
+
this.helmSubstructure = helmString;
|
|
207
|
+
this.onChanged.next();
|
|
208
|
+
}).show({modal: true, fullScreen: true});
|
|
209
|
+
});
|
|
210
|
+
ui.onSizeChanged(this._filterPanel).subscribe((_) => {
|
|
211
|
+
const helmString = this.helmEditor
|
|
212
|
+
.webEditor.canvas.getHelm(true).replace(/<\/span>/g, '').replace(/<span style='background:#bbf;'>/g, '');
|
|
213
|
+
this.updateFilterPanel(helmString);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
get filterPanel() {
|
|
218
|
+
return this._filterPanel;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
get substructure() {
|
|
222
|
+
return this.helmSubstructure;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
set substructure(s: string) {
|
|
226
|
+
this.helmEditor.editor.setHelm(s);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
updateFilterPanel(helmString?: string) {
|
|
230
|
+
const width = this._filterPanel.parentElement!.clientWidth < 100 ? 100 :
|
|
231
|
+
this._filterPanel.parentElement!.clientWidth;
|
|
232
|
+
const height = width / 2;
|
|
233
|
+
if (!helmString) {
|
|
234
|
+
const editDivStyle = {style: {
|
|
235
|
+
width: `${width}px`,
|
|
236
|
+
height: `${height/2}px`,
|
|
237
|
+
textAlign: 'center',
|
|
238
|
+
verticalAlign: 'middle',
|
|
239
|
+
lineHeight: `${height/2}px`,
|
|
240
|
+
border: '1px solid #dbdcdf'
|
|
241
|
+
}};
|
|
242
|
+
const editDiv = ui.divText('Click to edit', editDivStyle);
|
|
243
|
+
updateDivInnerHTML(this._filterPanel, editDiv);
|
|
244
|
+
} else {
|
|
245
|
+
updateDivInnerHTML(this._filterPanel, this.helmEditor.host);
|
|
246
|
+
this.helmEditor.editor.setHelm(helmString);
|
|
247
|
+
this.helmEditor.resizeEditor(width, height);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
153
250
|
}
|