@datagrok/bio 2.15.13 → 2.16.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 +25 -0
- package/detectors.js +16 -11
- package/dist/455.js.map +1 -1
- package/dist/980.js +1 -1
- package/dist/980.js.map +1 -1
- package/dist/package-test.js +6 -6
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +3 -3
- package/dist/package.js.map +1 -1
- package/package.json +14 -14
- package/src/analysis/sequence-activity-cliffs.ts +9 -8
- package/src/analysis/sequence-diversity-viewer.ts +6 -4
- package/src/analysis/sequence-similarity-viewer.ts +9 -6
- package/src/analysis/sequence-space.ts +3 -2
- package/src/calculations/monomerLevelMols.ts +4 -5
- package/src/demo/bio01-similarity-diversity.ts +4 -1
- package/src/package-test.ts +1 -1
- package/src/package-types.ts +34 -2
- package/src/package.ts +60 -76
- package/src/substructure-search/substructure-search.ts +15 -9
- package/src/tests/WebLogo-layout-tests.ts +1 -1
- package/src/tests/WebLogo-positions-test.ts +11 -5
- package/src/tests/WebLogo-project-tests.ts +1 -1
- package/src/tests/activity-cliffs-utils.ts +11 -14
- package/src/tests/bio-tests.ts +85 -79
- package/src/tests/checkInputColumn-tests.ts +15 -10
- package/src/tests/converters-test.ts +12 -5
- package/src/tests/detectors-benchmark-tests.ts +5 -2
- package/src/tests/detectors-tests.ts +51 -44
- package/src/tests/detectors-weak-and-likely-tests.ts +12 -5
- package/src/tests/fasta-export-tests.ts +13 -5
- package/src/tests/helm-tests.ts +85 -0
- package/src/tests/mm-distance-tests.ts +14 -7
- package/src/tests/monomer-libraries-tests.ts +1 -1
- package/src/tests/msa-tests.ts +33 -24
- package/src/tests/renderers-monomer-placer-tests.ts +2 -5
- package/src/tests/renderers-test.ts +15 -9
- package/src/tests/scoring.ts +9 -6
- package/src/tests/seq-handler-get-helm-tests.ts +7 -5
- package/src/tests/seq-handler-get-region-tests.ts +9 -3
- package/src/tests/seq-handler-splitted-tests.ts +11 -5
- package/src/tests/seq-handler-tests.ts +17 -10
- package/src/tests/sequence-space-utils.ts +9 -4
- package/src/tests/splitters-test.ts +5 -4
- package/src/tests/substructure-filters-tests.ts +22 -23
- package/src/tests/to-atomic-level-tests.ts +5 -3
- package/src/tests/to-atomic-level-ui-tests.ts +4 -1
- package/src/tests/utils/detectors-utils.ts +4 -4
- package/src/utils/calculate-scores.ts +11 -9
- package/src/utils/cell-renderer-custom.ts +27 -17
- package/src/utils/cell-renderer.ts +14 -8
- package/src/utils/check-input-column.ts +13 -9
- package/src/utils/context-menu.ts +4 -4
- package/src/utils/convert.ts +21 -14
- package/src/utils/get-region-func-editor.ts +8 -5
- package/src/utils/get-region.ts +4 -5
- package/src/utils/helm-to-molfile/converter/helm.ts +4 -4
- package/src/utils/helm-to-molfile/utils.ts +5 -6
- package/src/utils/macromolecule-column-widget.ts +6 -7
- package/src/utils/monomer-cell-renderer-base.ts +8 -1
- package/src/utils/monomer-lib/lib-manager.ts +3 -2
- package/src/utils/monomer-lib/monomer-colors.ts +10 -10
- package/src/utils/monomer-lib/monomer-lib-base.ts +6 -1
- package/src/utils/monomer-lib/monomer-lib.ts +15 -9
- package/src/utils/multiple-sequence-alignment-ui.ts +30 -30
- package/src/utils/save-as-fasta.ts +19 -12
- package/src/utils/seq-helper/seq-handler.ts +836 -0
- package/src/utils/seq-helper/seq-helper.ts +43 -19
- package/src/utils/sequence-to-mol.ts +7 -8
- package/src/utils/split-to-monomers.ts +7 -2
- package/src/utils/types.ts +8 -7
- package/src/utils/ui-utils.ts +2 -2
- package/src/viewers/web-logo-viewer.ts +18 -16
- package/src/widgets/bio-substructure-filter-helm.ts +5 -2
- package/src/widgets/bio-substructure-filter.ts +14 -24
- package/src/widgets/composition-analysis-widget.ts +6 -6
- package/src/widgets/representations.ts +7 -4
- package/src/tests/detectors-custom-notation-tests.ts +0 -37
- package/src/utils/cyclized.ts +0 -89
- package/src/utils/dimerized.ts +0 -10
|
@@ -3,7 +3,7 @@ import * as ui from 'datagrok-api/ui';
|
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
5
|
import {TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
6
|
-
import {
|
|
6
|
+
import {ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
|
|
7
7
|
|
|
8
8
|
import {_package} from '../package';
|
|
9
9
|
|
|
@@ -33,12 +33,14 @@ export class GetRegionFuncEditor {
|
|
|
33
33
|
}();
|
|
34
34
|
|
|
35
35
|
constructor(
|
|
36
|
-
private readonly call: DG.FuncCall
|
|
36
|
+
private readonly call: DG.FuncCall,
|
|
37
|
+
private readonly seqHelper: ISeqHelper,
|
|
37
38
|
) {
|
|
38
39
|
const getDesc = (paramName: string) => this.call.inputParams[paramName].property.description;
|
|
39
40
|
|
|
40
41
|
this.inputs.table = ui.input.table('Table', {value: this.call.inputParams['table'].value ?? grok.shell.tv.dataFrame});
|
|
41
42
|
|
|
43
|
+
//@formatter:off
|
|
42
44
|
const seqColValue = this.call.inputParams['sequence'].value ??
|
|
43
45
|
this.inputs.table.value!.columns.bySemType(DG.SEMTYPE.MACROMOLECULE);
|
|
44
46
|
this.inputs.sequence = ui.input.column('Sequence', {table: grok.shell.tv.dataFrame, value: seqColValue,
|
|
@@ -52,6 +54,7 @@ export class GetRegionFuncEditor {
|
|
|
52
54
|
this.inputs.name = ui.input.string('Column name', {value: this.getDefaultName(),
|
|
53
55
|
onValueChanged: this.nameInputChanged.bind(this), clearIcon: true});
|
|
54
56
|
this.inputs.name.onInput.subscribe(() => this.nameInputInput.bind(this)); // To catch clear event
|
|
57
|
+
//@formatter:on
|
|
55
58
|
|
|
56
59
|
// tooltips
|
|
57
60
|
for (const paramName in this.call.inputParams) {
|
|
@@ -65,7 +68,7 @@ export class GetRegionFuncEditor {
|
|
|
65
68
|
|
|
66
69
|
private sequenceInputChanged(): void {
|
|
67
70
|
const seqCol = this.inputs.sequence.value;
|
|
68
|
-
const sh = seqCol ?
|
|
71
|
+
const sh = seqCol ? this.seqHelper.getSeqHandler(seqCol) : null;
|
|
69
72
|
this.updateRegionItems();
|
|
70
73
|
this.updateStartEndInputItems();
|
|
71
74
|
this.updateRegion(true);
|
|
@@ -84,7 +87,7 @@ export class GetRegionFuncEditor {
|
|
|
84
87
|
this.inputs.start.value = reg?.start;
|
|
85
88
|
this.inputs.end.value = reg?.end;
|
|
86
89
|
} else {
|
|
87
|
-
const sh =
|
|
90
|
+
const sh = this.seqHelper.getSeqHandler(this.inputs.sequence.value!);
|
|
88
91
|
this.inputs.start.value = sh.posList[0];
|
|
89
92
|
this.inputs.end.value = sh.posList[sh.posList.length - 1];
|
|
90
93
|
}
|
|
@@ -117,7 +120,7 @@ export class GetRegionFuncEditor {
|
|
|
117
120
|
|
|
118
121
|
private updateStartEndInputItems(): void {
|
|
119
122
|
const seqCol = this.inputs.sequence.value;
|
|
120
|
-
const sh = seqCol ?
|
|
123
|
+
const sh = seqCol ? this.seqHelper.getSeqHandler(seqCol) : null;
|
|
121
124
|
|
|
122
125
|
const startSE = (this.inputs.start.input as HTMLSelectElement);
|
|
123
126
|
const endSE = (this.inputs.end.input as HTMLSelectElement);
|
package/src/utils/get-region.ts
CHANGED
|
@@ -2,11 +2,10 @@ import * as grok from 'datagrok-api/grok';
|
|
|
2
2
|
import * as ui from 'datagrok-api/ui';
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {TaskBarProgressIndicator} from 'datagrok-api/dg';
|
|
5
|
+
import {_package} from '../package';
|
|
7
6
|
|
|
8
7
|
export function getRegionUI(col: DG.Column<string>): void {
|
|
9
|
-
const sh =
|
|
8
|
+
const sh = _package.seqHelper.getSeqHandler(col);
|
|
10
9
|
|
|
11
10
|
const nameInput = ui.input.string('Name', {value: ''});
|
|
12
11
|
const startPositionInput = ui.input.choice('Start Position', {value: sh.posList[0], items: sh.posList,
|
|
@@ -23,7 +22,7 @@ export function getRegionUI(col: DG.Column<string>): void {
|
|
|
23
22
|
startPositionInput,
|
|
24
23
|
endPositionInput,
|
|
25
24
|
])).onOK(() => {
|
|
26
|
-
const pi = TaskBarProgressIndicator.create('Getting region...');
|
|
25
|
+
const pi = DG.TaskBarProgressIndicator.create('Getting region...');
|
|
27
26
|
try {
|
|
28
27
|
const name: string = nameInput.value ?? getDefaultName();
|
|
29
28
|
const regCol = getRegionDo(col, name, startPositionInput.value, endPositionInput.value);
|
|
@@ -39,7 +38,7 @@ export function getRegionUI(col: DG.Column<string>): void {
|
|
|
39
38
|
export function getRegionDo(
|
|
40
39
|
col: DG.Column<string>, startPosName: string | null, endPosName: string | null, name: string | null
|
|
41
40
|
): DG.Column<string> {
|
|
42
|
-
const sh =
|
|
41
|
+
const sh = _package.seqHelper.getSeqHandler(col);
|
|
43
42
|
|
|
44
43
|
let startPosIdx: number | null = null;
|
|
45
44
|
let endPosIdx: number | null = null;
|
|
@@ -21,8 +21,8 @@ export class Helm {
|
|
|
21
21
|
* complex polymer scope) */
|
|
22
22
|
readonly bondData: Bond[][];
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
public readonly simplePolymers: SimplePolymer[];
|
|
25
|
+
public readonly connectionList?: ConnectionList;
|
|
26
26
|
|
|
27
27
|
/** Maps global monomer index to r-group ids (starting from 1) participating
|
|
28
28
|
* in connection */
|
|
@@ -74,8 +74,8 @@ export class Helm {
|
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
private getMonomerIdxShifts(): {[simplePolymerId: string]: number} {
|
|
78
|
-
const result: {[simplePolymerId: string]: number} = {};
|
|
77
|
+
private getMonomerIdxShifts(): { [simplePolymerId: string]: number } {
|
|
78
|
+
const result: { [simplePolymerId: string]: number } = {};
|
|
79
79
|
let shift = 0;
|
|
80
80
|
this.simplePolymers.forEach((simplePolymer) => {
|
|
81
81
|
result[simplePolymer.id] = shift;
|
|
@@ -6,14 +6,14 @@ import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types';
|
|
|
6
6
|
|
|
7
7
|
import {SeqHelper} from '../seq-helper';
|
|
8
8
|
|
|
9
|
-
import {_package
|
|
9
|
+
import {_package} from '../../package';
|
|
10
10
|
|
|
11
11
|
/** Translate HELM column into molfile column and append to the dataframe */
|
|
12
12
|
export async function getMolColumnFromHelm(
|
|
13
13
|
df: DG.DataFrame, helmCol: DG.Column<string>, chiralityEngine: boolean = true, monomerLib: IMonomerLibBase
|
|
14
14
|
): Promise<DG.Column<string>> {
|
|
15
|
-
const seqHelper =
|
|
16
|
-
const converter = seqHelper.getHelmToMolfileConverter(monomerLib);
|
|
15
|
+
const seqHelper: SeqHelper = _package.seqHelper as SeqHelper;
|
|
16
|
+
const converter = await seqHelper.getHelmToMolfileConverter(monomerLib);
|
|
17
17
|
const molCol = converter.convertToRdKitBeautifiedMolfileColumn(helmCol, chiralityEngine, _package.rdKitModule, monomerLib);
|
|
18
18
|
molCol.semType = DG.SEMTYPE.MOLECULE;
|
|
19
19
|
return molCol;
|
|
@@ -22,9 +22,8 @@ export async function getMolColumnFromHelm(
|
|
|
22
22
|
export async function getSmilesColumnFromHelm(
|
|
23
23
|
helmCol: DG.Column<string>
|
|
24
24
|
): Promise<DG.Column<string>> {
|
|
25
|
-
const seqHelper =
|
|
26
|
-
const
|
|
27
|
-
const converter = seqHelper.getHelmToMolfileConverter(monomerLib);
|
|
25
|
+
const seqHelper: SeqHelper = _package.seqHelper as SeqHelper;
|
|
26
|
+
const converter = await seqHelper.getHelmToMolfileConverter(_package.monomerLib);
|
|
28
27
|
const smilesCol = converter.convertToSmiles(helmCol);
|
|
29
28
|
smilesCol.semType = DG.SEMTYPE.MOLECULE;
|
|
30
29
|
return smilesCol;
|
|
@@ -3,7 +3,7 @@ import * as ui from 'datagrok-api/ui';
|
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
5
|
import {TAGS as wlTAGS} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
6
|
-
import {
|
|
6
|
+
import {ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
|
|
7
7
|
|
|
8
8
|
import {WebLogoViewer} from '../viewers/web-logo-viewer';
|
|
9
9
|
|
|
@@ -13,18 +13,17 @@ import {_package} from '../package';
|
|
|
13
13
|
export class MacromoleculeColumnWidget extends DG.Widget {
|
|
14
14
|
private viewed: boolean = false;
|
|
15
15
|
|
|
16
|
-
private readonly seqCol: DG.Column<string>;
|
|
17
|
-
|
|
18
16
|
private wlViewer: WebLogoViewer | null = null;
|
|
19
17
|
|
|
20
|
-
constructor(
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly seqCol: DG.Column<string>,
|
|
20
|
+
private readonly seqHelper: ISeqHelper,
|
|
21
|
+
) {
|
|
21
22
|
super(ui.divV([]));
|
|
22
|
-
|
|
23
|
-
this.seqCol = seqCol;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
async init(): Promise<void> {
|
|
27
|
-
const sh =
|
|
26
|
+
const sh = this.seqHelper.getSeqHandler(this.seqCol);
|
|
28
27
|
const pkgTooltipWebLogo = _package.properties.tooltipWebLogo;
|
|
29
28
|
const colTooltipWebLogo = this.seqCol.getTag(wlTAGS.tooltipWebLogo);
|
|
30
29
|
|
|
@@ -5,13 +5,15 @@ import * as ui from 'datagrok-api/ui';
|
|
|
5
5
|
import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
6
6
|
import {CellRendererBackBase} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
7
7
|
import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types/index';
|
|
8
|
+
import {ISeqHelper, getSeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
|
|
8
9
|
|
|
9
10
|
import {_package} from '../package';
|
|
10
11
|
|
|
11
12
|
export abstract class CellRendererWithMonomerLibBackBase extends CellRendererBackBase<string> {
|
|
12
13
|
protected monomerLib: IMonomerLibBase | null = null;
|
|
14
|
+
protected seqHelper: ISeqHelper | null = null;
|
|
13
15
|
|
|
14
|
-
constructor(
|
|
16
|
+
protected constructor(
|
|
15
17
|
gridCol: DG.GridColumn | null,
|
|
16
18
|
tableCol: DG.Column<string>,
|
|
17
19
|
) {
|
|
@@ -26,5 +28,10 @@ export abstract class CellRendererWithMonomerLibBackBase extends CellRendererBac
|
|
|
26
28
|
this.gridCol?.grid?.invalidate();
|
|
27
29
|
}));
|
|
28
30
|
});
|
|
31
|
+
getSeqHelper().then((seqHelper) => {
|
|
32
|
+
this.seqHelper = seqHelper;
|
|
33
|
+
this.dirty = true;
|
|
34
|
+
this.gridCol?.grid?.invalidate();
|
|
35
|
+
});
|
|
29
36
|
}
|
|
30
37
|
}
|
|
@@ -35,7 +35,8 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
35
35
|
|
|
36
36
|
public get eventManager(): IMonomerLibFileEventManager { return this._eventManager; }
|
|
37
37
|
|
|
38
|
-
public async awaitLoaded(timeout: number =
|
|
38
|
+
public async awaitLoaded(timeout: number = Infinity): Promise<void> {
|
|
39
|
+
timeout = timeout === Infinity ? 1 * 60000 : timeout; // Limit the max lib loaded timeout
|
|
39
40
|
return await Promise.race([
|
|
40
41
|
(async () => {
|
|
41
42
|
const fileManager = await this.getFileManager();
|
|
@@ -273,7 +274,7 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
273
274
|
/** Changes userLibSettings set only HELMCoreLibrary.json, polytool-lib.json */
|
|
274
275
|
async loadMonomerLibForTests(): Promise<void> {
|
|
275
276
|
await setUserLibSettings(LIB_SETTINGS_FOR_TESTS);
|
|
276
|
-
await this.awaitLoaded();
|
|
277
|
+
await this.awaitLoaded(10000);
|
|
277
278
|
await this.loadMonomerLib(true); // load default libraries
|
|
278
279
|
}
|
|
279
280
|
|
|
@@ -8,20 +8,20 @@ import {HelmType} from '@datagrok-libraries/bio/src/helm/types';
|
|
|
8
8
|
export const naturalMonomerColors = {
|
|
9
9
|
[HelmTypes.BASE]: {
|
|
10
10
|
// Chromatogram palette // HELMWebEditor monomerColors
|
|
11
|
-
A: "
|
|
12
|
-
G: "
|
|
13
|
-
T: "
|
|
14
|
-
C: "
|
|
15
|
-
U: "
|
|
11
|
+
A: "#20E040", // "#A0A0FF",
|
|
12
|
+
G: "#040404", // "#FF7070",
|
|
13
|
+
T: "#FF8080", // "#A0FFA0",
|
|
14
|
+
C: "#2060FF", // "#FF8C4B",
|
|
15
|
+
U: "#FF8080", // "#FF8080"
|
|
16
16
|
},
|
|
17
17
|
|
|
18
18
|
[HelmTypes.NUCLEOTIDE]: {
|
|
19
19
|
// Chromatogram palette // HELMWebEditor monomerColors
|
|
20
|
-
A: "
|
|
21
|
-
G: "
|
|
22
|
-
T: "
|
|
23
|
-
C: "
|
|
24
|
-
U: "
|
|
20
|
+
A: "#20E040", // "#A0A0FF",
|
|
21
|
+
G: "#040404", // "#FF7070",
|
|
22
|
+
T: "#FF8080", // "#A0FFA0",
|
|
23
|
+
C: "#2060FF", // "#FF8C4B",
|
|
24
|
+
U: "#FF8080", // "#FF8080"
|
|
25
25
|
},
|
|
26
26
|
|
|
27
27
|
[HelmTypes.LINKER]: {
|
|
@@ -42,11 +42,16 @@ export class MonomerLibBase implements IMonomerLibBase {
|
|
|
42
42
|
|
|
43
43
|
constructor(
|
|
44
44
|
protected _monomers: MonomerLibDataType,
|
|
45
|
+
public readonly source: string,
|
|
45
46
|
) {
|
|
46
47
|
this._isEmpty = !this._monomers || Object.keys(this._monomers).length === 0 ||
|
|
47
48
|
Object.entries(this._monomers).every(([_, ptMonomers]) => Object.keys(ptMonomers).length === 0);
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
getMonomerSymbolsByType(polymerType: PolymerType): string[] {
|
|
52
|
+
return Object.keys(this._monomers[polymerType]);
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
/** Creates missing {@link Monomer} */
|
|
51
56
|
addMissingMonomer(polymerType: PolymerType, monomerSymbol: string): Monomer {
|
|
52
57
|
let mSet = this._monomers[polymerType];
|
|
@@ -212,7 +217,7 @@ export class MonomerLibBase implements IMonomerLibBase {
|
|
|
212
217
|
if (rgbM)
|
|
213
218
|
return [parseInt(rgbM[1]), parseInt(rgbM[2]), parseInt(rgbM[3])];
|
|
214
219
|
|
|
215
|
-
const n = DG.Color.fromHtml(
|
|
220
|
+
const n = DG.Color.fromHtml(html);
|
|
216
221
|
return [DG.Color.r(n), DG.Color.g(n), DG.Color.b(n)];
|
|
217
222
|
};
|
|
218
223
|
|
|
@@ -39,10 +39,10 @@ export class MonomerLib extends MonomerLibBase implements IMonomerLib {
|
|
|
39
39
|
|
|
40
40
|
constructor(
|
|
41
41
|
monomers: MonomerLibDataType,
|
|
42
|
-
|
|
42
|
+
source: string,
|
|
43
43
|
public readonly error: string | undefined = undefined,
|
|
44
44
|
) {
|
|
45
|
-
super(monomers);
|
|
45
|
+
super(monomers, source);
|
|
46
46
|
for (const [_monomerType, monomersOfType] of Object.entries(this._monomers)) {
|
|
47
47
|
for (const [_monomerSymbol, monomer] of Object.entries(monomersOfType))
|
|
48
48
|
monomer.lib = this;
|
|
@@ -112,10 +112,6 @@ export class MonomerLib extends MonomerLibBase implements IMonomerLib {
|
|
|
112
112
|
return res;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
getMonomerSymbolsByType(polymerType: PolymerType): string[] {
|
|
116
|
-
return Object.keys(this._monomers[polymerType]);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
115
|
/** Get a list of monomers with specified element attached to specified
|
|
120
116
|
* R-group
|
|
121
117
|
* WARNING: RGroup numbering starts from 1, not 0*/
|
|
@@ -241,21 +237,31 @@ export class MonomerLib extends MonomerLibBase implements IMonomerLib {
|
|
|
241
237
|
return resStr;
|
|
242
238
|
}
|
|
243
239
|
|
|
244
|
-
override(data: MonomerLibData): IMonomerLibBase {
|
|
245
|
-
return new OverriddenMonomerLib(data, this);
|
|
240
|
+
override(data: MonomerLibData, source: string): IMonomerLibBase {
|
|
241
|
+
return new OverriddenMonomerLib(data, source, this);
|
|
246
242
|
}
|
|
247
243
|
}
|
|
248
244
|
|
|
249
245
|
class OverriddenMonomerLib extends MonomerLibBase {
|
|
250
246
|
constructor(
|
|
251
247
|
private readonly data: MonomerLibData,
|
|
248
|
+
source: string,
|
|
252
249
|
private readonly base: MonomerLibBase
|
|
253
250
|
) {
|
|
254
|
-
super(data);
|
|
251
|
+
super(data, source);
|
|
255
252
|
}
|
|
256
253
|
|
|
257
254
|
get onChanged(): Observable<any> { return this.base.onChanged; }
|
|
258
255
|
|
|
256
|
+
override getMonomerSymbolsByType(polymerType: PolymerType): string[] {
|
|
257
|
+
const resList = this.base.getMonomerSymbolsByType(polymerType);
|
|
258
|
+
for (const overrideSymbol of Object.keys(this.data[polymerType] ?? {})) {
|
|
259
|
+
if (!resList.includes(overrideSymbol))
|
|
260
|
+
resList.push(overrideSymbol);
|
|
261
|
+
}
|
|
262
|
+
return resList;
|
|
263
|
+
}
|
|
264
|
+
|
|
259
265
|
addMissingMonomer(polymerType: PolymerType, monomerSymbol: string): Monomer {
|
|
260
266
|
return this.base.addMissingMonomer(polymerType, monomerSymbol);
|
|
261
267
|
}
|
|
@@ -5,21 +5,20 @@ import * as ui from 'datagrok-api/ui';
|
|
|
5
5
|
import {ColumnInputOptions} from '@datagrok-libraries/utils/src/type-declarations';
|
|
6
6
|
import {delay} from '@datagrok-libraries/utils/src/test';
|
|
7
7
|
import {ALPHABET, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
8
|
-
import {
|
|
8
|
+
import {ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
|
|
9
9
|
|
|
10
10
|
import {MsaWarning, runKalign} from './multiple-sequence-alignment';
|
|
11
11
|
import {pepseaMethods, runPepsea} from './pepsea';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import {checkInputColumn} from './check-input-column';
|
|
13
|
+
import {MultipleSequenceAlignmentUIOptions} from './types';
|
|
14
14
|
import {kalignVersion, msaDefaultOptions} from './constants';
|
|
15
15
|
import {awaitContainerStart} from './docker';
|
|
16
16
|
|
|
17
|
-
import {_package} from '../package';
|
|
18
|
-
|
|
19
17
|
import '../../css/msa.css';
|
|
18
|
+
import {_package} from '../package';
|
|
20
19
|
|
|
21
20
|
export async function multipleSequenceAlignmentUI(
|
|
22
|
-
options:
|
|
21
|
+
options: MultipleSequenceAlignmentUIOptions, seqHelper: ISeqHelper,
|
|
23
22
|
): Promise<DG.Column> {
|
|
24
23
|
return new Promise(async (resolve, reject) => {
|
|
25
24
|
options.clustersCol ??= null;
|
|
@@ -70,20 +69,22 @@ export async function multipleSequenceAlignmentUI(
|
|
|
70
69
|
|
|
71
70
|
let prevSeqCol = seqCol;
|
|
72
71
|
const colInput = ui.input.column(
|
|
73
|
-
'Sequence', {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
72
|
+
'Sequence', {
|
|
73
|
+
table: table, value: seqCol, onValueChanged: async (value: DG.Column<any>) => {
|
|
74
|
+
if (!value || value.semType !== DG.SEMTYPE.MACROMOLECULE) {
|
|
75
|
+
okBtn.disabled = true;
|
|
76
|
+
await delay(0); // to
|
|
77
|
+
colInput.value = prevSeqCol as DG.Column<string>;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
prevSeqCol = value;
|
|
81
|
+
okBtn.disabled = false;
|
|
82
|
+
performAlignment = await onColInputChange(
|
|
83
|
+
colInput.value, table, seqHelper, pepseaInputRootStyles, kalignInputRootStyles,
|
|
84
|
+
methodInput, clustersColInput, gapOpenInput, gapExtendInput, terminalGapInput,
|
|
85
|
+
);
|
|
86
|
+
}, filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE
|
|
87
|
+
} as ColumnInputOptions
|
|
87
88
|
) as DG.InputBase<DG.Column<string>>;
|
|
88
89
|
colInput.setTooltip('Sequences column to use for alignment');
|
|
89
90
|
const clustersColInput = ui.input.column('Clusters', {table: table, value: options.clustersCol!});
|
|
@@ -103,7 +104,7 @@ export async function multipleSequenceAlignmentUI(
|
|
|
103
104
|
//if column is specified (from tests), run alignment and resolve with the result
|
|
104
105
|
if (options.col) {
|
|
105
106
|
performAlignment = await onColInputChange(
|
|
106
|
-
options.col, table, pepseaInputRootStyles, kalignInputRootStyles,
|
|
107
|
+
options.col, table, seqHelper, pepseaInputRootStyles, kalignInputRootStyles,
|
|
107
108
|
methodInput, clustersColInput, gapOpenInput, gapExtendInput, terminalGapInput,
|
|
108
109
|
);
|
|
109
110
|
await onDialogOk(colInput, table, performAlignment, resolve, reject);
|
|
@@ -146,7 +147,7 @@ async function onDialogOk(
|
|
|
146
147
|
|
|
147
148
|
|
|
148
149
|
async function onColInputChange(
|
|
149
|
-
col: DG.Column<string>, table: DG.DataFrame,
|
|
150
|
+
col: DG.Column<string>, table: DG.DataFrame, seqHelper: ISeqHelper,
|
|
150
151
|
pepseaInputRootStyles: CSSStyleDeclaration[], kalignInputRootStyles: CSSStyleDeclaration[],
|
|
151
152
|
methodInput: DG.InputBase<string | null>, clustersColInput: DG.InputBase<DG.Column<any> | null>,
|
|
152
153
|
gapOpenInput: DG.InputBase<number | null>, gapExtendInput: DG.InputBase<number | null>,
|
|
@@ -157,20 +158,19 @@ async function onColInputChange(
|
|
|
157
158
|
return;
|
|
158
159
|
const unusedName = table.columns.getUnusedName(`msa(${col.name})`);
|
|
159
160
|
|
|
160
|
-
if (
|
|
161
|
-
[NOTATION.FASTA, NOTATION.SEPARATOR], [ALPHABET.DNA, ALPHABET.RNA, ALPHABET.PT]
|
|
161
|
+
if (checkInputColumn(col, col.name, seqHelper,
|
|
162
|
+
[NOTATION.FASTA, NOTATION.SEPARATOR], [ALPHABET.DNA, ALPHABET.RNA, ALPHABET.PT])[0]
|
|
162
163
|
) { // Kalign - natural alphabets. if the notation is separator, convert to fasta and then run kalign
|
|
163
164
|
switchDialog(pepseaInputRootStyles, kalignInputRootStyles, 'kalign');
|
|
164
165
|
gapOpenInput.value = null;
|
|
165
166
|
gapExtendInput.value = null;
|
|
166
167
|
terminalGapInput.value = null;
|
|
167
|
-
const potentialColSh =
|
|
168
|
+
const potentialColSh = seqHelper.getSeqHandler(col);
|
|
168
169
|
const performCol: DG.Column<string> = potentialColSh.isFasta() ? col :
|
|
169
170
|
potentialColSh.convert(NOTATION.FASTA);
|
|
170
171
|
return async () => await runKalign(performCol, false, unusedName, clustersColInput.value);
|
|
171
|
-
} else if (
|
|
172
|
-
|
|
173
|
-
) { // PepSeA branch - Helm notation or separator notation with unknown alphabets
|
|
172
|
+
} else if (checkInputColumn(col, col.name, seqHelper, [NOTATION.HELM], [])[0]) {
|
|
173
|
+
// PepSeA branch - Helm notation or separator notation with unknown alphabets
|
|
174
174
|
switchDialog(pepseaInputRootStyles, kalignInputRootStyles, 'pepsea');
|
|
175
175
|
gapOpenInput.value ??= msaDefaultOptions.pepsea.gapOpen;
|
|
176
176
|
gapExtendInput.value ??= msaDefaultOptions.pepsea.gapExtend;
|
|
@@ -180,9 +180,9 @@ async function onColInputChange(
|
|
|
180
180
|
return runPepsea(col, unusedName, methodInput.value!,
|
|
181
181
|
gapOpenInput.value!, gapExtendInput.value!, clustersColInput.value);
|
|
182
182
|
};
|
|
183
|
-
} else if (
|
|
183
|
+
} else if (checkInputColumn(col, col.name, seqHelper, [NOTATION.SEPARATOR], [ALPHABET.UN])[0]) {
|
|
184
184
|
//if the column is separator with unknown alphabet, it might be helm. check if it can be converted to helm
|
|
185
|
-
const potentialColSh =
|
|
185
|
+
const potentialColSh = seqHelper.getSeqHandler(col);
|
|
186
186
|
const helmCol = potentialColSh.convert(NOTATION.HELM);
|
|
187
187
|
switchDialog(pepseaInputRootStyles, kalignInputRootStyles, 'pepsea');
|
|
188
188
|
gapOpenInput.value ??= msaDefaultOptions.pepsea.gapOpen;
|
|
@@ -3,13 +3,16 @@ import * as ui from 'datagrok-api/ui';
|
|
|
3
3
|
import * as grok from 'datagrok-api/grok';
|
|
4
4
|
|
|
5
5
|
import wu from 'wu';
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
8
|
+
import {ISeqHandler} from '@datagrok-libraries/bio/src/utils/macromolecule/seq-handler';
|
|
9
|
+
|
|
10
|
+
import {_package} from '../package';
|
|
8
11
|
|
|
9
12
|
const FASTA_LINE_WIDTH = 60;
|
|
10
13
|
|
|
11
14
|
/** Shows dialog to select id columns list and seq column, builds and downloads FASTA content */
|
|
12
|
-
export function saveAsFastaUI() {
|
|
15
|
+
export function saveAsFastaUI(): void {
|
|
13
16
|
// Use grid for column order adjusted by user
|
|
14
17
|
const grid: DG.Grid = grok.shell.tv.grid;
|
|
15
18
|
|
|
@@ -20,23 +23,27 @@ export function saveAsFastaUI() {
|
|
|
20
23
|
.find((gcol: DG.GridColumn) => gcol.name.toLowerCase().indexOf('id') !== -1);
|
|
21
24
|
const idDefaultValue = defaultIdGCol ? [defaultIdGCol.name] : [];
|
|
22
25
|
|
|
23
|
-
const idGColListInput = ui.input.multiChoice('Seq id columns', {
|
|
24
|
-
|
|
26
|
+
const idGColListInput = ui.input.multiChoice('Seq id columns', {
|
|
27
|
+
value: idDefaultValue,
|
|
28
|
+
items: idGColList.map((gcol: DG.GridColumn) => gcol.name)
|
|
29
|
+
});
|
|
25
30
|
|
|
26
31
|
const seqGColList: DG.GridColumn[] = wu.count(0).take(grid.columns.length)/* range rom 0 to grid.columns.length */
|
|
27
32
|
.map((colI: number) => grid.columns.byIndex(colI)!)
|
|
28
33
|
.filter((gc: DG.GridColumn) => {
|
|
29
34
|
const col: DG.Column | null = gc.column;
|
|
30
35
|
if (col && col.semType === DG.SEMTYPE.MACROMOLECULE) {
|
|
31
|
-
const sh =
|
|
36
|
+
const sh = _package.seqHelper.getSeqHandler(col);
|
|
32
37
|
return sh.isFasta();
|
|
33
38
|
}
|
|
34
39
|
return false;
|
|
35
40
|
}).toArray();
|
|
36
41
|
|
|
37
42
|
const seqDefaultValue = seqGColList.length > 0 ? seqGColList[0].name : [];
|
|
38
|
-
const seqColInput = ui.input.choice('Seq column', {
|
|
39
|
-
|
|
43
|
+
const seqColInput = ui.input.choice('Seq column', {
|
|
44
|
+
value: seqDefaultValue,
|
|
45
|
+
items: seqGColList.map((gCol: DG.GridColumn) => gCol.name)
|
|
46
|
+
});
|
|
40
47
|
|
|
41
48
|
const lineWidthInput = ui.input.int('FASTA line width', {value: FASTA_LINE_WIDTH});
|
|
42
49
|
|
|
@@ -56,7 +63,8 @@ export function saveAsFastaUI() {
|
|
|
56
63
|
if (!valueSeqCol)
|
|
57
64
|
grok.shell.warning(`Seq column is mandatory to save as FASTA.`);
|
|
58
65
|
|
|
59
|
-
const
|
|
66
|
+
const seqHandler = _package.seqHelper.getSeqHandler(valueSeqCol!);
|
|
67
|
+
const resFastaTxt: string = saveAsFastaDo(valueIdColList, seqHandler, valueLineWidth);
|
|
60
68
|
|
|
61
69
|
const aEl: HTMLAnchorElement = document.createElement('a');
|
|
62
70
|
aEl.setAttribute('href', `data:text/plain;charset=utf-8,${encodeURIComponent(resFastaTxt)}`);
|
|
@@ -75,17 +83,16 @@ export function saveAsFastaUI() {
|
|
|
75
83
|
* @return {string} FASTA content
|
|
76
84
|
*/
|
|
77
85
|
export function saveAsFastaDo(
|
|
78
|
-
idColList: DG.Column[],
|
|
86
|
+
idColList: DG.Column[], seqHandler: ISeqHandler, lineWidth: number = FASTA_LINE_WIDTH, lineSeparator: string = '\n',
|
|
79
87
|
): string {
|
|
80
|
-
const sh = SeqHandler.forColumn(seqCol);
|
|
81
88
|
const fastaLines: string[] = [];
|
|
82
89
|
|
|
83
|
-
for (let rowIdx: number = 0; rowIdx <
|
|
90
|
+
for (let rowIdx: number = 0; rowIdx < seqHandler.length; rowIdx++) {
|
|
84
91
|
// multiple identifiers separated by vertical bars
|
|
85
92
|
// https://en.wikipedia.org/wiki/FASTA_format
|
|
86
93
|
|
|
87
94
|
const seqId: string = idColList.map((col) => col.get(rowIdx).toString()).join('|');
|
|
88
|
-
const srcSS =
|
|
95
|
+
const srcSS = seqHandler.getSplitted(rowIdx);
|
|
89
96
|
const seqLineList: string[] = wrapSequence(srcSS, lineWidth);
|
|
90
97
|
|
|
91
98
|
fastaLines.push(`>${seqId}${lineSeparator}`);
|