@datagrok/bio 2.0.18 → 2.0.20
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 +664 -440
- package/dist/package.js +382 -156
- package/package.json +2 -2
- package/src/package.ts +14 -2
- package/src/substructure-search/substructure-search.ts +53 -23
- package/src/tests/Palettes-test.ts +9 -10
- package/src/tests/WebLogo-positions-test.ts +6 -8
- package/src/tests/checkInputColumn-tests.ts +10 -9
- package/src/tests/convert-test.ts +30 -31
- package/src/tests/detectors-test.ts +6 -5
- package/src/tests/renderers-test.ts +13 -12
- package/src/utils/cell-renderer.ts +8 -23
- package/src/utils/multiple-sequence-alignment.ts +5 -4
- package/src/utils/utils.ts +1 -1
- package/src/viewers/vd-regions-viewer.ts +2 -4
- package/src/widgets/bio-substructure-filter.ts +153 -0
|
@@ -110,10 +110,8 @@ export class VdRegionsViewer extends DG.JsViewer implements bio.IVdRegionsViewer
|
|
|
110
110
|
await this.buildView();
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
public override onTableAttached() {
|
|
114
|
-
|
|
115
|
-
await this.init();
|
|
116
|
-
}, 0 /* next event cycle */);
|
|
113
|
+
public override async onTableAttached() {
|
|
114
|
+
await this.init();
|
|
117
115
|
}
|
|
118
116
|
|
|
119
117
|
public override async onPropertyChanged(property: DG.Property | null) {
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Macromolecules substructure filter that uses Datagrok's collaborative filtering.
|
|
3
|
+
* 1. On onRowsFiltering event, only FILTER OUT rows that do not satisfy this filter's criteria
|
|
4
|
+
* 2. Call dataFrame.rows.requestFilter when filtering criteria changes.
|
|
5
|
+
* */
|
|
6
|
+
|
|
7
|
+
import * as ui from 'datagrok-api/ui';
|
|
8
|
+
import * as DG from 'datagrok-api/dg';
|
|
9
|
+
import * as bio from '@datagrok-libraries/bio';
|
|
10
|
+
|
|
11
|
+
import wu from 'wu';
|
|
12
|
+
import {linearSubstructureSearch} from '../substructure-search/substructure-search';
|
|
13
|
+
import {Subject, Subscription} from 'rxjs';
|
|
14
|
+
import * as C from '../utils/constants';
|
|
15
|
+
|
|
16
|
+
export class BioSubstructureFilter extends DG.Filter {
|
|
17
|
+
bioFilter: FastaFilter | SeparatorFilter | null = null;
|
|
18
|
+
bitset: DG.BitSet | null = null;
|
|
19
|
+
loader: HTMLDivElement = ui.loader();
|
|
20
|
+
onBioFilterChangedSubs?: Subscription;
|
|
21
|
+
|
|
22
|
+
get calculating(): boolean { return this.loader.style.display == 'initial'; }
|
|
23
|
+
|
|
24
|
+
set calculating(value: boolean) { this.loader.style.display = value ? 'initial' : 'none'; }
|
|
25
|
+
|
|
26
|
+
get filterSummary(): string {
|
|
27
|
+
return this.bioFilter!.substructure;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get isFiltering(): boolean {
|
|
31
|
+
return super.isFiltering && this.bioFilter!.substructure !== '';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get isReadyToApplyFilter(): boolean {
|
|
35
|
+
return !this.calculating && this.bitset != null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
constructor() {
|
|
39
|
+
super();
|
|
40
|
+
this.root = ui.divV([]);
|
|
41
|
+
this.calculating = false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
attach(dataFrame: DG.DataFrame): void {
|
|
45
|
+
super.attach(dataFrame);
|
|
46
|
+
this.column = dataFrame.columns.bySemType(DG.SEMTYPE.MACROMOLECULE);
|
|
47
|
+
this.columnName = this.column?.name;
|
|
48
|
+
const notation = this.column?.getTag(DG.TAGS.UNITS);
|
|
49
|
+
this.bioFilter = notation === bio.NOTATION.FASTA ?
|
|
50
|
+
new FastaFilter() : new SeparatorFilter(this.column!.getTag(C.TAGS.SEPARATOR));
|
|
51
|
+
this.root.appendChild(this.bioFilter!.filterPanel);
|
|
52
|
+
this.root.appendChild(this.loader);
|
|
53
|
+
|
|
54
|
+
this.onBioFilterChangedSubs?.unsubscribe();
|
|
55
|
+
const onChangedEvent: any = this.bioFilter.onChanged;
|
|
56
|
+
this.onBioFilterChangedSubs = onChangedEvent.subscribe(async (_: any) => await this._onInputChanged());
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
detach() {
|
|
60
|
+
super.detach();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
applyFilter(): void {
|
|
64
|
+
if (this.bitset && !this.isDetached)
|
|
65
|
+
this.dataFrame?.filter.and(this.bitset);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Override to save filter state. */
|
|
69
|
+
saveState(): any {
|
|
70
|
+
const state = super.saveState();
|
|
71
|
+
state.bioSubstructure = this.bioFilter?.substructure;
|
|
72
|
+
return state;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Override to load filter state. */
|
|
76
|
+
applyState(state: any): void {
|
|
77
|
+
super.applyState(state);
|
|
78
|
+
if (state.bioSubstructure)
|
|
79
|
+
this.bioFilter!.substructureInput.value = state.bioSubstructure;
|
|
80
|
+
|
|
81
|
+
const that = this;
|
|
82
|
+
if (state.bioSubstructure)
|
|
83
|
+
setTimeout(function() { that._onInputChanged(); }, 1000);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Performs the actual filtering
|
|
88
|
+
* When the results are ready, triggers `rows.requestFilter`, which in turn triggers `applyFilter`
|
|
89
|
+
* that would simply apply the bitset synchronously.
|
|
90
|
+
*/
|
|
91
|
+
async _onInputChanged(): Promise<void> {
|
|
92
|
+
if (!this.isFiltering) {
|
|
93
|
+
this.bitset = null;
|
|
94
|
+
this.dataFrame?.rows.requestFilter();
|
|
95
|
+
} else if (wu(this.dataFrame!.rows.filters).has(`${this.columnName}: ${this.filterSummary}`)) {
|
|
96
|
+
// some other filter is already filtering for the exact same thing
|
|
97
|
+
return;
|
|
98
|
+
} else {
|
|
99
|
+
this.calculating = true;
|
|
100
|
+
try {
|
|
101
|
+
this.bitset = linearSubstructureSearch(this.bioFilter!.substructure, this.column!);
|
|
102
|
+
this.calculating = false;
|
|
103
|
+
this.dataFrame?.rows.requestFilter();
|
|
104
|
+
} finally {
|
|
105
|
+
this.calculating = false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
class FastaFilter {
|
|
112
|
+
onChanged: Subject<any> = new Subject<any>();
|
|
113
|
+
substructureInput: DG.InputBase<string> = ui.stringInput('', '', () => {
|
|
114
|
+
this.onChanged.next();
|
|
115
|
+
}, {placeholder: 'Substructure'});
|
|
116
|
+
|
|
117
|
+
constructor() {
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
get filterPanel() {
|
|
121
|
+
return this.substructureInput.root;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get substructure() {
|
|
125
|
+
return this.substructureInput.value;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
class SeparatorFilter extends FastaFilter {
|
|
130
|
+
separatorInput: DG.InputBase<string> = ui.stringInput('', '', () => {
|
|
131
|
+
this.onChanged.next();
|
|
132
|
+
}, {placeholder: 'Separator'});
|
|
133
|
+
colSeparator = '';
|
|
134
|
+
|
|
135
|
+
constructor(separator: string) {
|
|
136
|
+
super();
|
|
137
|
+
this.colSeparator = separator;
|
|
138
|
+
this.separatorInput.value = separator;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
get filterPanel() {
|
|
142
|
+
return ui.divV([
|
|
143
|
+
this.substructureInput.root,
|
|
144
|
+
this.separatorInput.root
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
get substructure() {
|
|
149
|
+
return this.separatorInput.value && this.separatorInput.value !== this.colSeparator ?
|
|
150
|
+
this.substructureInput.value.replaceAll(this.separatorInput.value, this.colSeparator) :
|
|
151
|
+
this.substructureInput.value;
|
|
152
|
+
}
|
|
153
|
+
}
|