@datagrok/bio 2.9.0 → 2.10.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 +16 -1
- package/dist/452.js +1 -1
- package/dist/452.js.map +1 -1
- 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/package.json +4 -4
- package/src/analysis/sequence-space.ts +5 -6
- package/src/apps/get-region-app.ts +56 -0
- package/src/apps/web-logo-app.ts +3 -6
- package/src/package-test.ts +1 -0
- package/src/package-types.ts +13 -0
- package/src/package.ts +152 -52
- package/src/tests/converters-test.ts +2 -3
- package/src/tests/scoring.ts +8 -4
- package/src/tests/units-handler-get-region.ts +116 -0
- package/src/utils/cell-renderer.ts +5 -32
- package/src/utils/context-menu.ts +2 -3
- package/src/utils/convert.ts +6 -7
- package/src/utils/get-region-func-editor.ts +241 -0
- package/src/utils/get-region.ts +65 -0
- package/src/utils/multiple-sequence-alignment-ui.ts +21 -17
- package/src/viewers/web-logo-viewer.ts +8 -6
|
@@ -143,8 +143,11 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
143
143
|
// Cell renderer settings
|
|
144
144
|
const tempMonomerWidth: string | null = tableColTemp[tempTAGS.monomerWidth];
|
|
145
145
|
const monomerWidth: string = (tempMonomerWidth != null) ? tempMonomerWidth : 'short';
|
|
146
|
-
if (monomerWidth === 'short')
|
|
147
|
-
|
|
146
|
+
if (monomerWidth === 'short') {
|
|
147
|
+
// Renderer can start to work before Bio package initialized, in that time _package.properties is null.
|
|
148
|
+
// TODO: Render function is available but package init method is not completed
|
|
149
|
+
maxLengthOfMonomer = tableColTemp[mmcrTemps.maxMonomerLength] ?? _package.properties?.MaxMonomerLength ?? 4;
|
|
150
|
+
}
|
|
148
151
|
|
|
149
152
|
|
|
150
153
|
let seqColTemp: MonomerPlacer = tableCol.temp[tempTAGS.bioSeqCol];
|
|
@@ -206,36 +209,6 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
206
209
|
((tempReferenceSequence != null) && (tempReferenceSequence != '')) ?
|
|
207
210
|
tempReferenceSequence : tempCurrentWord ?? '');
|
|
208
211
|
|
|
209
|
-
// let maxLengthWords: { [pos: number]: number } = {};
|
|
210
|
-
// if (tableCol.getTag(rndrTAGS.calculatedCellRender) !== splitLimit.toString()) {
|
|
211
|
-
// let sampleCount = 0;
|
|
212
|
-
// while (sampleCount < Math.min(tableCol.length, 100)) {
|
|
213
|
-
// const rowIdx: number = sampleCount;
|
|
214
|
-
// const column = tableCol.get(rowIdx);
|
|
215
|
-
// const subParts: string[] = splitterFunc(column);
|
|
216
|
-
// for (const [index, amino] of subParts.entries()) {
|
|
217
|
-
// const textSize = monomerToShortFunction(amino, maxLengthOfMonomer).length * 7 + gapRenderer;
|
|
218
|
-
// if (textSize > (maxLengthWords[index] ?? 0))
|
|
219
|
-
// maxLengthWords[index] = textSize;
|
|
220
|
-
// if (index > maxIndex) maxIndex = index;
|
|
221
|
-
// }
|
|
222
|
-
// sampleCount += 1;
|
|
223
|
-
// }
|
|
224
|
-
// const minLength = 3 * 7;
|
|
225
|
-
// for (let i = 0; i <= maxIndex; i++) {
|
|
226
|
-
// if (maxLengthWords[i] < minLength) maxLengthWords[i] = minLength;
|
|
227
|
-
// const maxLengthWordSum: { [pos: number]: number } = {};
|
|
228
|
-
// maxLengthWordSum[0] = maxLengthWords[0];
|
|
229
|
-
// for (let i = 1; i <= maxIndex; i++) maxLengthWordSum[i] = maxLengthWordSum[i - 1] + maxLengthWords[i];
|
|
230
|
-
// colTemp[tempTAGS.bioSumMaxLengthWords] = maxLengthWordSum;
|
|
231
|
-
// colTemp[tempTAGS.bioMaxIndex] = maxIndex;
|
|
232
|
-
// colTemp[tempTAGS.bioMaxLengthWords] = maxLengthWords;
|
|
233
|
-
// tableCol.setTag(rndrTAGS.calculatedCellRender, splitLimit.toString());
|
|
234
|
-
// }
|
|
235
|
-
// } else {
|
|
236
|
-
// maxLengthWords = colTemp[tempTAGS.bioMaxLengthWords];
|
|
237
|
-
// }
|
|
238
|
-
|
|
239
212
|
const subParts: ISeqSplitted = splitterFunc(value);
|
|
240
213
|
/* let x1 = x; */
|
|
241
214
|
let color = undefinedColor;
|
|
@@ -4,7 +4,6 @@ import * as ui from 'datagrok-api/ui';
|
|
|
4
4
|
|
|
5
5
|
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
6
6
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
7
|
-
import {NotationConverter} from '@datagrok-libraries/bio/src/utils/notation-converter';
|
|
8
7
|
|
|
9
8
|
import {_package} from '../package';
|
|
10
9
|
|
|
@@ -15,9 +14,9 @@ export function addCopyMenuUI(cell: DG.Cell, menu: DG.Menu): void {
|
|
|
15
14
|
|
|
16
15
|
menu.group('Copy')
|
|
17
16
|
.items(tgtNotationList, (tgtNotation) => {
|
|
18
|
-
const
|
|
17
|
+
const ncUH = UnitsHandler.getOrCreate(cell.column);
|
|
19
18
|
const separator = tgtNotation === NOTATION.SEPARATOR ? _package.properties.DefaultSeparator : undefined;
|
|
20
|
-
const converter =
|
|
19
|
+
const converter = ncUH.getConverter(tgtNotation as NOTATION, separator);
|
|
21
20
|
const tgtSeq = converter(cell.value);
|
|
22
21
|
|
|
23
22
|
if (!navigator.clipboard) {
|
package/src/utils/convert.ts
CHANGED
|
@@ -5,7 +5,6 @@ import * as grok from 'datagrok-api/grok';
|
|
|
5
5
|
import $ from 'cash-dom';
|
|
6
6
|
import {Subscription} from 'rxjs';
|
|
7
7
|
import {NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
8
|
-
import {NotationConverter} from '@datagrok-libraries/bio/src/utils/notation-converter';
|
|
9
8
|
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
10
9
|
import {expect} from '@datagrok-libraries/utils/src/test';
|
|
11
10
|
|
|
@@ -22,8 +21,8 @@ export function convert(col?: DG.Column): void {
|
|
|
22
21
|
let tgtCol = col ?? grok.shell.t.columns.bySemType('Macromolecule')!;
|
|
23
22
|
if (!tgtCol)
|
|
24
23
|
throw new Error('No column with Macromolecule semantic type found');
|
|
25
|
-
let
|
|
26
|
-
let currentNotation: NOTATION =
|
|
24
|
+
let converterUH = UnitsHandler.getOrCreate(tgtCol);
|
|
25
|
+
let currentNotation: NOTATION = converterUH.notation;
|
|
27
26
|
const dialogHeader = ui.divText(
|
|
28
27
|
'Current notation: ' + currentNotation,
|
|
29
28
|
{
|
|
@@ -47,8 +46,8 @@ export function convert(col?: DG.Column): void {
|
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
tgtCol = newCol;
|
|
50
|
-
|
|
51
|
-
currentNotation =
|
|
49
|
+
converterUH = UnitsHandler.getOrCreate(tgtCol);
|
|
50
|
+
currentNotation = converterUH.notation;
|
|
52
51
|
if (currentNotation === NOTATION.HELM)
|
|
53
52
|
separatorInput.value = '/'; // helm monomers can have - in the name like D-aThr;
|
|
54
53
|
dialogHeader.textContent = 'Current notation: ' + currentNotation;
|
|
@@ -117,8 +116,8 @@ export function convert(col?: DG.Column): void {
|
|
|
117
116
|
* @param {string | null} separator Separator for SEPARATOR notation
|
|
118
117
|
*/
|
|
119
118
|
export async function convertDo(srcCol: DG.Column, targetNotation: NOTATION, separator?: string): Promise<DG.Column> {
|
|
120
|
-
const
|
|
121
|
-
const newColumn =
|
|
119
|
+
const converterUH = UnitsHandler.getOrCreate(srcCol);
|
|
120
|
+
const newColumn = converterUH.convert(targetNotation, separator);
|
|
122
121
|
srcCol.dataFrame.columns.add(newColumn);
|
|
123
122
|
|
|
124
123
|
// Call detector directly to escape some error on detectSemanticTypes
|
|
@@ -0,0 +1,241 @@
|
|
|
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
|
+
|
|
5
|
+
import {TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
6
|
+
|
|
7
|
+
export interface GetRegionParams {
|
|
8
|
+
table: DG.DataFrame,
|
|
9
|
+
sequence: DG.Column<string>,
|
|
10
|
+
start: string | null,
|
|
11
|
+
end: string | null,
|
|
12
|
+
/** Name for the column with sequence of the region */ name: string | null,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
16
|
+
import {_package} from '../package';
|
|
17
|
+
|
|
18
|
+
export interface SeqRegion {
|
|
19
|
+
name: string,
|
|
20
|
+
description: string,
|
|
21
|
+
start: string,
|
|
22
|
+
end: string,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class GetRegionFuncEditor {
|
|
26
|
+
inputs = new class {
|
|
27
|
+
table: DG.InputBase<DG.DataFrame | null>;
|
|
28
|
+
sequence: DG.InputBase<DG.Column | null>;
|
|
29
|
+
region: DG.InputBase<SeqRegion>;
|
|
30
|
+
start: DG.InputBase<string>;
|
|
31
|
+
end: DG.InputBase<string>;
|
|
32
|
+
name: DG.InputBase;
|
|
33
|
+
}();
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
private readonly call: DG.FuncCall
|
|
37
|
+
) {
|
|
38
|
+
const getDesc = (paramName: string) => this.call.inputParams[paramName].property.description;
|
|
39
|
+
|
|
40
|
+
this.inputs.table = ui.tableInput('Table',
|
|
41
|
+
this.call.inputParams['table'].value ?? grok.shell.tv.dataFrame, undefined,
|
|
42
|
+
() => {});
|
|
43
|
+
|
|
44
|
+
const seqColValue = this.call.inputParams['sequence'].value ??
|
|
45
|
+
this.inputs.table.value!.columns.bySemType(DG.SEMTYPE.MACROMOLECULE);
|
|
46
|
+
const seqColOptions = {filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE};
|
|
47
|
+
this.inputs.sequence = ui.columnInput('Sequence', grok.shell.tv.dataFrame, seqColValue,
|
|
48
|
+
this.sequenceInputChanged.bind(this), seqColOptions);
|
|
49
|
+
this.inputs.start = ui.choiceInput(
|
|
50
|
+
'Start', undefined, [], this.startInputChanged.bind(this)) as unknown as DG.InputBase<string>;
|
|
51
|
+
this.inputs.end = ui.choiceInput(
|
|
52
|
+
'End', undefined, [], this.endInputChanged.bind(this)) as unknown as DG.InputBase<string>;
|
|
53
|
+
|
|
54
|
+
this.inputs.region = ui.choiceInput<SeqRegion>('Region', null as unknown as SeqRegion, [],
|
|
55
|
+
this.regionInputChanged.bind(this)) as DG.InputBase<SeqRegion>;
|
|
56
|
+
|
|
57
|
+
this.inputs.name = ui.stringInput('Name', '', () => {},
|
|
58
|
+
{placeholder: this.getDefaultName()});
|
|
59
|
+
|
|
60
|
+
// tooltips
|
|
61
|
+
for (const paramName in this.call.inputParams) {
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
ui.tooltip.bind(this.inputs[paramName].captionLabel, getDesc(paramName));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// initial
|
|
67
|
+
this.sequenceInputChanged();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private sequenceInputChanged(): void {
|
|
71
|
+
const seqCol = this.inputs.sequence.value;
|
|
72
|
+
const uh = seqCol ? UnitsHandler.getOrCreate(seqCol) : null;
|
|
73
|
+
this.updateRegionItems();
|
|
74
|
+
this.updateStartEndInputItems();
|
|
75
|
+
this.updateRegion(true);
|
|
76
|
+
this.updateNameInputPlaceholder();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private fixRegion: boolean = false;
|
|
80
|
+
|
|
81
|
+
private regionInputChanged(): void {
|
|
82
|
+
this.fixRegion = true;
|
|
83
|
+
try {
|
|
84
|
+
const regJsonStr = this.inputs.region.stringValue;
|
|
85
|
+
const reg: SeqRegion | null = regJsonStr ? JSON.parse(regJsonStr) as SeqRegion : null;
|
|
86
|
+
|
|
87
|
+
if (reg !== null) {
|
|
88
|
+
this.inputs.start.value = reg?.start;
|
|
89
|
+
this.inputs.end.value = reg?.end;
|
|
90
|
+
} else {
|
|
91
|
+
const uh = UnitsHandler.getOrCreate(this.inputs.sequence.value!);
|
|
92
|
+
this.inputs.start.value = uh.posList[0];
|
|
93
|
+
this.inputs.end.value = uh.posList[uh.posList.length - 1];
|
|
94
|
+
}
|
|
95
|
+
} finally {
|
|
96
|
+
this.fixRegion = false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private startInputChanged(): void {
|
|
101
|
+
this.updateRegion(false);
|
|
102
|
+
this.updateNameInputPlaceholder();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private endInputChanged(): void {
|
|
106
|
+
this.updateRegion(false);
|
|
107
|
+
this.updateNameInputPlaceholder();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private updateStartEndInputItems(): void {
|
|
111
|
+
const seqCol = this.inputs.sequence.value;
|
|
112
|
+
const uh = seqCol ? UnitsHandler.getOrCreate(seqCol) : null;
|
|
113
|
+
|
|
114
|
+
const startSE = (this.inputs.start.input as HTMLSelectElement);
|
|
115
|
+
const endSE = (this.inputs.end.input as HTMLSelectElement);
|
|
116
|
+
for (let i = startSE.options.length - 1; i >= 0; --i) startSE.options.remove(i);
|
|
117
|
+
for (let i = endSE.options.length - 1; i >= 0; --i) endSE.options.remove(i);
|
|
118
|
+
for (const pos of uh?.posList ?? []) {
|
|
119
|
+
const startPosOE = document.createElement('option');
|
|
120
|
+
const endPosOE = document.createElement('option');
|
|
121
|
+
startPosOE.text = endPosOE.text = pos;
|
|
122
|
+
startPosOE.value = endPosOE.value = pos;
|
|
123
|
+
startSE.options.add(startPosOE);
|
|
124
|
+
endSE.options.add(endPosOE);
|
|
125
|
+
}
|
|
126
|
+
startSE.value = uh?.posList[0] ?? '';
|
|
127
|
+
endSE.value = uh?.posList[uh?.posList.length - 1] ?? '';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private updateRegionItems(): void {
|
|
131
|
+
const seqCol = this.inputs.sequence.value;
|
|
132
|
+
const regionsTagTxt: string | null = seqCol ? seqCol.getTag(bioTAGS.regions) : null;
|
|
133
|
+
const regionList: SeqRegion[] | null = regionsTagTxt ? JSON.parse(regionsTagTxt) : null;
|
|
134
|
+
|
|
135
|
+
const regionSE = (this.inputs.region.input as HTMLSelectElement);
|
|
136
|
+
for (let i = regionSE.options.length - 1; i >= 0; --i) regionSE.options.remove(i);
|
|
137
|
+
|
|
138
|
+
const nullOE = document.createElement('option');
|
|
139
|
+
nullOE.text = '';
|
|
140
|
+
nullOE.value = JSON.stringify(null);
|
|
141
|
+
regionSE.options.add(nullOE);
|
|
142
|
+
|
|
143
|
+
if (regionList != null) {
|
|
144
|
+
this.inputs.region.root.style.removeProperty('display');
|
|
145
|
+
for (const region of regionList) {
|
|
146
|
+
const regionOE = document.createElement('option');
|
|
147
|
+
regionOE.text = `${region.name}: ${region.start}-${region.end}`;
|
|
148
|
+
regionOE.value = JSON.stringify(region);
|
|
149
|
+
regionSE.options.add(regionOE);
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
this.inputs.region.root.style.display = 'none';
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private updateRegion(reset: boolean): void {
|
|
157
|
+
const startPos: string = this.inputs.start.stringValue ?? '';
|
|
158
|
+
const endPos: string = this.inputs.end.stringValue ?? '';
|
|
159
|
+
|
|
160
|
+
if (!this.fixRegion) {
|
|
161
|
+
const regionSE = (this.inputs.region.input as HTMLSelectElement);
|
|
162
|
+
regionSE.selectedIndex = -1;
|
|
163
|
+
for (let i = regionSE.options.length - 1; i >= 0; --i) {
|
|
164
|
+
const regionOE = regionSE.options[i];
|
|
165
|
+
const reg: SeqRegion = JSON.parse(regionOE.value);
|
|
166
|
+
if (reg && startPos === reg.start && endPos === reg.end) regionSE.selectedIndex = i;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private updateNameInputPlaceholder(): void {
|
|
172
|
+
// @ts-ignore
|
|
173
|
+
this.inputs.name.input.attributes['placeholder'].value = this.getDefaultName();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private getDefaultName(): string {
|
|
177
|
+
const regionJsonStr = this.inputs.region.stringValue;
|
|
178
|
+
const reg: SeqRegion | null = regionJsonStr ? JSON.parse(regionJsonStr) : null;
|
|
179
|
+
|
|
180
|
+
const seqCol: DG.Column<string> = this.inputs.sequence.value!;
|
|
181
|
+
|
|
182
|
+
const startPos: string = this.inputs.start.stringValue ?? '';
|
|
183
|
+
const endPos: string = this.inputs.end.stringValue ?? '';
|
|
184
|
+
|
|
185
|
+
return reg != null ? `${seqCol.name}(${reg.name}): ${reg.start}-${reg.end}` :
|
|
186
|
+
`${seqCol?.name}: (${startPos}-${endPos})`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private getParams(): {} {
|
|
190
|
+
return {
|
|
191
|
+
table: this.inputs.table.value!,
|
|
192
|
+
sequence: this.inputs.sequence.value!,
|
|
193
|
+
start: this.getStart(),
|
|
194
|
+
end: this.getEnd(),
|
|
195
|
+
name: this.getName() ?? this.getDefaultName(),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private getStart(): string | null {
|
|
200
|
+
return this.inputs.start.stringValue;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private getEnd(): string | null {
|
|
204
|
+
return this.inputs.end.stringValue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private getName(): string | null {
|
|
208
|
+
const str = this.inputs.name.stringValue;
|
|
209
|
+
return str == '' ? null : str;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// -- UI --
|
|
213
|
+
|
|
214
|
+
public dialog(): void {
|
|
215
|
+
const inputsForm = ui.inputs(Object.values(this.inputs), {style: {minWidth: '320px'}});
|
|
216
|
+
ui.dialog({title: 'Get Region'})
|
|
217
|
+
.add(inputsForm)
|
|
218
|
+
.onOK(async () => {
|
|
219
|
+
(async () => {
|
|
220
|
+
const callParams = this.getParams();
|
|
221
|
+
await this.call.func.prepare(callParams).call(true);
|
|
222
|
+
})()
|
|
223
|
+
.catch((err: any) => { _package.handleErrorUI(err); });
|
|
224
|
+
})
|
|
225
|
+
.show();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
public widget(): DG.Widget {
|
|
229
|
+
const inputsForm = ui.inputs(Object.entries(this.inputs)
|
|
230
|
+
.filter(([inputName, input]) => !['table', 'sequence'].includes(inputName))
|
|
231
|
+
.map(([inputName, input]) => input));
|
|
232
|
+
const doBtn = ui.button('Get Region', () => {
|
|
233
|
+
(async () => {
|
|
234
|
+
const callParams = this.getParams();
|
|
235
|
+
await this.call.func.prepare(callParams).call(true);
|
|
236
|
+
})()
|
|
237
|
+
.catch((err: any) => { _package.handleErrorUI(err); });
|
|
238
|
+
});
|
|
239
|
+
return DG.Widget.fromRoot(ui.divV([inputsForm, ui.div(doBtn)]));
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
|
|
5
|
+
import {TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
6
|
+
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
7
|
+
import {getRegion} from '../package';
|
|
8
|
+
import {TaskBarProgressIndicator} from 'datagrok-api/dg';
|
|
9
|
+
|
|
10
|
+
export function getRegionUI(col: DG.Column<string>): void {
|
|
11
|
+
const uh = UnitsHandler.getOrCreate(col);
|
|
12
|
+
|
|
13
|
+
const nameInput = ui.stringInput('Name', '');
|
|
14
|
+
const startPositionInput = ui.choiceInput('Start Position', uh.posList[0], uh.posList,
|
|
15
|
+
() => { /* TODO: update name placeholder with getDefaultName() */ });
|
|
16
|
+
const endPositionInput = ui.choiceInput('End Position', uh.posList[uh.posList.length], uh.posList,
|
|
17
|
+
() => { /* TODO: update name placeholder with getDefaultName() */ });
|
|
18
|
+
|
|
19
|
+
const getDefaultName = (): string => {
|
|
20
|
+
return `${col.name}:${startPositionInput.value}-${endPositionInput.value}`;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
ui.dialog({title: 'Get Region'}).add(ui.inputs([
|
|
24
|
+
nameInput,
|
|
25
|
+
startPositionInput,
|
|
26
|
+
endPositionInput,
|
|
27
|
+
])).onOK(() => {
|
|
28
|
+
const pi = TaskBarProgressIndicator.create('Getting region...');
|
|
29
|
+
try {
|
|
30
|
+
const name: string = nameInput.value ?? getDefaultName();
|
|
31
|
+
const regCol = getRegionDo(col, name, startPositionInput.value, endPositionInput.value);
|
|
32
|
+
col.dataFrame.columns.add(regCol);
|
|
33
|
+
regCol.setTag(DG.TAGS.CELL_RENDERER, 'sequence');
|
|
34
|
+
} catch (err: any) {
|
|
35
|
+
grok.shell.error(err.toString());
|
|
36
|
+
} finally { pi.close(); }
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** {@link startPosName} and {@link endPosName} are according positionNames tag (or default ['1', '2',...]) */
|
|
41
|
+
export function getRegionDo(
|
|
42
|
+
col: DG.Column<string>, startPosName: string | null, endPosName: string | null, name: string | null
|
|
43
|
+
): DG.Column<string> {
|
|
44
|
+
const uh = UnitsHandler.getOrCreate(col);
|
|
45
|
+
|
|
46
|
+
let startPosIdx: number | null = null;
|
|
47
|
+
let endPosIdx: number | null = null;
|
|
48
|
+
|
|
49
|
+
for (let posJ: number = 0; posJ < uh.posList.length; ++posJ) {
|
|
50
|
+
if (uh.posList[posJ] == startPosName) startPosIdx = posJ;
|
|
51
|
+
if (uh.posList[posJ] == endPosName) endPosIdx = posJ;
|
|
52
|
+
}
|
|
53
|
+
if (startPosIdx === null && startPosName !== null)
|
|
54
|
+
throw new Error(`Start position ${startPosName} not found.`);
|
|
55
|
+
if (endPosIdx === null && endPosName !== null)
|
|
56
|
+
throw new Error(`End position ${endPosName} not found.`);
|
|
57
|
+
|
|
58
|
+
if (uh.posList.length < endPosIdx!)
|
|
59
|
+
throw new Error(`End position ${endPosIdx} exceeds positions length`);
|
|
60
|
+
|
|
61
|
+
const regColName: string = !!name ? name : `${col.name}: (${startPosName ?? ''}-${endPosName ?? ''})`;
|
|
62
|
+
|
|
63
|
+
const regCol = uh.getRegion(startPosIdx, endPosIdx, regColName);
|
|
64
|
+
return regCol;
|
|
65
|
+
}
|
|
@@ -3,15 +3,19 @@ import * as DG from 'datagrok-api/dg';
|
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
|
|
5
5
|
import {ALPHABET, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
6
|
+
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
7
|
+
import {ColumnInputOptions} from '@datagrok-libraries/utils/src/type-declarations';
|
|
8
|
+
|
|
6
9
|
import {runKalign} from './multiple-sequence-alignment';
|
|
7
10
|
import {pepseaMethods, runPepsea} from './pepsea';
|
|
8
11
|
import {checkInputColumnUI} from './check-input-column';
|
|
9
|
-
import {NotationConverter} from '@datagrok-libraries/bio/src/utils/notation-converter';
|
|
10
|
-
import {_package} from '../package';
|
|
11
12
|
import {multipleSequenceAlginmentUIOptions} from './types';
|
|
12
13
|
import {kalignVersion, msaDefaultOptions} from './constants';
|
|
14
|
+
|
|
15
|
+
import {_package} from '../package';
|
|
16
|
+
|
|
13
17
|
import '../../css/msa.css';
|
|
14
|
-
|
|
18
|
+
|
|
15
19
|
export class MsaWarning extends Error {
|
|
16
20
|
constructor(message: string, options?: ErrorOptions) {
|
|
17
21
|
super(message, options);
|
|
@@ -66,12 +70,12 @@ export async function multipleSequenceAlignmentUI(
|
|
|
66
70
|
//TODO: remove when the new version of datagrok-api is available
|
|
67
71
|
//TODO: allow only macromolecule colums to be chosen
|
|
68
72
|
const colInput = ui.columnInput('Sequence', table, seqCol, async () => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
performAlignment = await onColInputChange(
|
|
74
|
+
colInput.value, table, pepseaInputRootStyles, kalignInputRootStyles,
|
|
75
|
+
methodInput, clustersColInput, gapOpenInput, gapExtendInput, terminalGapInput,
|
|
76
|
+
);
|
|
77
|
+
//@ts-ignore
|
|
78
|
+
}, {filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE} as ColumnInputOptions
|
|
75
79
|
) as DG.InputBase<DG.Column<string>>;
|
|
76
80
|
colInput.setTooltip('Sequences column to use for alignment');
|
|
77
81
|
const clustersColInput = ui.columnInput('Clusters', table, options.clustersCol);
|
|
@@ -100,7 +104,7 @@ export async function multipleSequenceAlignmentUI(
|
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
async function onDialogOk(
|
|
103
|
-
colInput: DG.InputBase<
|
|
107
|
+
colInput: DG.InputBase<DG.Column<any>>,
|
|
104
108
|
table: DG.DataFrame,
|
|
105
109
|
performAlignment: (() => Promise<DG.Column<string> | null>) | undefined,
|
|
106
110
|
resolve: (value: DG.Column<any>) => void,
|
|
@@ -151,9 +155,9 @@ async function onColInputChange(
|
|
|
151
155
|
gapOpenInput.value = null;
|
|
152
156
|
gapExtendInput.value = null;
|
|
153
157
|
terminalGapInput.value = null;
|
|
154
|
-
const
|
|
155
|
-
const performCol: DG.Column<string> =
|
|
156
|
-
|
|
158
|
+
const potentialColUH = UnitsHandler.getOrCreate(col);
|
|
159
|
+
const performCol: DG.Column<string> = potentialColUH.isFasta() ? col :
|
|
160
|
+
potentialColUH.convert(NOTATION.FASTA);
|
|
157
161
|
return async () => await runKalign(performCol, false, unusedName, clustersColInput.value);
|
|
158
162
|
} else if (checkInputColumnUI(col, col.name,
|
|
159
163
|
[NOTATION.HELM], [], false)
|
|
@@ -163,18 +167,18 @@ async function onColInputChange(
|
|
|
163
167
|
gapExtendInput.value ??= msaDefaultOptions.pepsea.gapExtend;
|
|
164
168
|
|
|
165
169
|
return async () => await runPepsea(col, unusedName, methodInput.value!,
|
|
166
|
-
|
|
170
|
+
gapOpenInput.value!, gapExtendInput.value!, clustersColInput.value);
|
|
167
171
|
} else if (checkInputColumnUI(col, col.name, [NOTATION.SEPARATOR], [ALPHABET.UN], false)) {
|
|
168
172
|
//if the column is separator with unknown alphabet, it might be helm. check if it can be converted to helm
|
|
169
|
-
const
|
|
170
|
-
const helmCol =
|
|
173
|
+
const potentialColUH = UnitsHandler.getOrCreate(col);
|
|
174
|
+
const helmCol = potentialColUH.convert(NOTATION.HELM);
|
|
171
175
|
switchDialog(pepseaInputRootStyles, kalignInputRootStyles, 'pepsea');
|
|
172
176
|
gapOpenInput.value ??= msaDefaultOptions.pepsea.gapOpen;
|
|
173
177
|
gapExtendInput.value ??= msaDefaultOptions.pepsea.gapExtend;
|
|
174
178
|
// convert to helm and assign alignment function to PepSea
|
|
175
179
|
|
|
176
180
|
return async () => await runPepsea(helmCol, unusedName, methodInput.value!,
|
|
177
|
-
|
|
181
|
+
gapOpenInput.value!, gapExtendInput.value!, clustersColInput.value);
|
|
178
182
|
} else {
|
|
179
183
|
gapOpenInput.value = null;
|
|
180
184
|
gapExtendInput.value = null;
|
|
@@ -8,14 +8,15 @@ import {fromEvent, Observable, Subject, Unsubscribable} from 'rxjs';
|
|
|
8
8
|
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
9
9
|
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
10
10
|
import {
|
|
11
|
-
monomerToShort, pickUpPalette, pickUpSeqCol, TAGS as bioTAGS
|
|
11
|
+
monomerToShort, pickUpPalette, pickUpSeqCol, TAGS as bioTAGS, positionSeparator
|
|
12
12
|
} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
13
13
|
import {
|
|
14
|
-
FilterSources, HorizontalAlignments, IWebLogoViewer, PositionHeight, PositionMarginStates,
|
|
15
|
-
|
|
14
|
+
FilterSources, HorizontalAlignments, IWebLogoViewer, PositionHeight, PositionMarginStates,
|
|
15
|
+
VerticalAlignments, WebLogoProps, WebLogoPropsDefault
|
|
16
16
|
} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
17
17
|
import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
18
18
|
import {intToHtmlA} from '@datagrok-libraries/utils/src/color';
|
|
19
|
+
import {TAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
19
20
|
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
20
21
|
|
|
21
22
|
import {_package} from '../package';
|
|
@@ -560,8 +561,8 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
560
561
|
}).reduce((max, l) => Math.max(max, l), 0);
|
|
561
562
|
|
|
562
563
|
/** positionNames and positionLabel can be set up through the column's tags only */
|
|
563
|
-
const positionNamesTxt = this.seqCol.getTag(
|
|
564
|
-
const positionLabelsTxt = this.seqCol.getTag(
|
|
564
|
+
const positionNamesTxt = this.seqCol.getTag(TAGS.positionNames);
|
|
565
|
+
const positionLabelsTxt = this.seqCol.getTag(TAGS.positionLabels);
|
|
565
566
|
this.positionNames = !!positionNamesTxt ? positionNamesTxt.split(positionSeparator).map((v) => v.trim()) :
|
|
566
567
|
[...Array(maxLength).keys()].map((jPos) => `${jPos + 1}`)/* fallback if tag is not provided */;
|
|
567
568
|
this.positionLabels = !!positionLabelsTxt ? positionLabelsTxt.split(positionSeparator).map((v) => v.trim()) :
|
|
@@ -1034,7 +1035,8 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1034
1035
|
min: this.slider.min, max: this.slider.max,
|
|
1035
1036
|
maxRange: this.slider.maxRange
|
|
1036
1037
|
};
|
|
1037
|
-
_package.logger.debug(
|
|
1038
|
+
_package.logger.debug(
|
|
1039
|
+
`Bio: WebLogoViewer<${this.viewerId}>.sliderOnValuesChanged( ${JSON.stringify(val)} ), start`);
|
|
1038
1040
|
this.render(WlRenderLevel.Layout, 'sliderOnValuesChanged').then(() => {});
|
|
1039
1041
|
} catch (err: any) {
|
|
1040
1042
|
const errMsg = errorToConsole(err);
|