@datagrok/helm 2.2.0 → 2.3.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 +28 -0
- 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 +15 -16
- package/src/cell-renderer.ts +13 -36
- package/src/constants.ts +0 -11
- package/src/helm-helper.ts +67 -17
- package/src/helm-monomer-placer.ts +8 -26
- package/src/helm-web-editor.ts +33 -3
- package/src/package-test.ts +12 -4
- package/src/package-utils.ts +160 -0
- package/src/package.ts +123 -197
- package/src/tests/findMonomers-tests.ts +2 -0
- package/src/tests/get-all-parts-tests.ts +5 -0
- package/src/tests/{get-molfiles.ts → get-molfiles-tests.ts} +3 -1
- package/src/tests/get-monomer-tests.ts +279 -0
- package/src/tests/helm-service-tests.ts +4 -0
- package/src/tests/helm-tests.ts +8 -1
- package/src/tests/helm-web-editor-tests.ts +55 -0
- package/src/tests/parse-helm-tests.ts +98 -0
- package/src/tests/properties-widget-tests.ts +33 -4
- package/src/tests/renderers-tests.ts +3 -1
- package/src/tests/utils.ts +6 -0
- package/src/types/index.ts +5 -0
- package/src/utils/get-hovered.ts +9 -21
- package/src/utils/get-monomer-dummy.ts +149 -0
- package/src/utils/get-monomer-of-library.ts +90 -0
- package/src/utils/get-monomer.ts +113 -0
- package/src/utils/helm-grid-cell-renderer.ts +12 -7
- package/src/utils/helm-service.ts +19 -45
- package/src/utils/index.ts +11 -17
- package/src/widgets/properties-widget.ts +29 -9
- package/tsconfig.json +1 -1
- package/vendor/helm-web-editor.development.js +39640 -0
- package/vendor/helm-web-editor.production.js +2 -0
- package/vendor/helm-web-editor.production.js.map +1 -0
- package/webpack.config.js +14 -11
- package/helm/JSDraw/Pistoia.HELM-uncompressed.js +0 -9694
- package/helm/JSDraw/Pistoia.HELM.js +0 -27
- package/helm/JSDraw/ReadMe.txt +0 -8
- package/helm/JSDraw/Scilligence.JSDraw2.Lite-uncompressed.js +0 -31126
- package/helm/JSDraw/Scilligence.JSDraw2.Lite.js +0 -12
- package/helm/JSDraw/Scilligence.JSDraw2.Resources.js +0 -762
- package/helm/JSDraw/dojo.js +0 -250
- package/helm/JSDraw/test.html +0 -24
- package/src/utils/dummy-monomer.ts +0 -69
|
@@ -2,15 +2,17 @@ import * as ui from 'datagrok-api/ui';
|
|
|
2
2
|
import * as DG from 'datagrok-api/dg';
|
|
3
3
|
import * as grok from 'datagrok-api/grok';
|
|
4
4
|
|
|
5
|
-
import {after, before, category, expect, expectObject, test} from '@datagrok-libraries/utils/src/test';
|
|
5
|
+
import {after, before, category, expect, expectObject, test, timeout} from '@datagrok-libraries/utils/src/test';
|
|
6
6
|
import {ALPHABET, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
7
7
|
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
8
8
|
import {
|
|
9
9
|
getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
|
|
10
10
|
} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
11
11
|
import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
12
|
+
import {generateLongSequence} from '@datagrok-libraries/bio/src/utils/generator';
|
|
12
13
|
|
|
13
|
-
import {
|
|
14
|
+
import {initHelmMainPackage} from './utils';
|
|
15
|
+
import {getPropertiesDict, SeqPropertiesError} from '../widgets/properties-widget';
|
|
14
16
|
|
|
15
17
|
const enum MolProps {
|
|
16
18
|
formula = 'formula',
|
|
@@ -25,6 +27,7 @@ const enum Tests {
|
|
|
25
27
|
separatorGaps = 'separator-gaps',
|
|
26
28
|
fastaPt = 'fasta-PT',
|
|
27
29
|
fastaPtMsa = 'fasta-PT-MSA',
|
|
30
|
+
fastaRna = 'fasta-RNA',
|
|
28
31
|
fastaDna = 'fasta-DNA',
|
|
29
32
|
}
|
|
30
33
|
|
|
@@ -56,6 +59,11 @@ const TestsData: {
|
|
|
56
59
|
units: NOTATION.FASTA, alphabet: ALPHABET.PT,
|
|
57
60
|
res: {[MolProps.formula]: 'C38H58N10O12S', [MolProps.mw]: 878.99, [MolProps.extinctCoef]: 1.55}
|
|
58
61
|
},
|
|
62
|
+
[Tests.fastaRna]: {
|
|
63
|
+
seq: 'ACGUAUUCC',
|
|
64
|
+
units: NOTATION.FASTA, alphabet: ALPHABET.RNA,
|
|
65
|
+
res: {[MolProps.formula]: 'C84H107N30O65P9', [MolProps.mw]: 2855.67, [MolProps.extinctCoef]: 96.27}
|
|
66
|
+
},
|
|
59
67
|
[Tests.fastaDna]: {
|
|
60
68
|
seq: 'ACGTATTCC',
|
|
61
69
|
units: NOTATION.FASTA, alphabet: ALPHABET.DNA,
|
|
@@ -69,9 +77,14 @@ category('properties-widget', () => {
|
|
|
69
77
|
let userLibSettings: any = null;
|
|
70
78
|
|
|
71
79
|
before(async () => {
|
|
80
|
+
await initHelmMainPackage();
|
|
81
|
+
|
|
72
82
|
monomerLibHelper = await getMonomerLibHelper();
|
|
73
83
|
userLibSettings = getUserLibSettings();
|
|
84
|
+
|
|
74
85
|
await setUserLibSettingsForTests();
|
|
86
|
+
await timeout(async () => { await monomerLibHelper.awaitLoaded(); },
|
|
87
|
+
5000, 'MonomerLibHelper.awaitLoaded() timeout');
|
|
75
88
|
await monomerLibHelper.loadLibraries(true); // load default libraries
|
|
76
89
|
});
|
|
77
90
|
|
|
@@ -85,6 +98,22 @@ category('properties-widget', () => {
|
|
|
85
98
|
testPropertiesDict(testData.seq, testData.units, testData.separator, testData.alphabet, testData.res);
|
|
86
99
|
});
|
|
87
100
|
}
|
|
101
|
+
|
|
102
|
+
test('too-long', async () => {
|
|
103
|
+
const colList = generateLongSequence();
|
|
104
|
+
const df = DG.DataFrame.fromColumns(colList);
|
|
105
|
+
const col = df.getCol('MSA');
|
|
106
|
+
|
|
107
|
+
const sh = SeqHandler.forColumn(col);
|
|
108
|
+
let errCatched = true;
|
|
109
|
+
try {
|
|
110
|
+
const _actPropDict = getPropertiesDict(col.get(0), sh);
|
|
111
|
+
const k = 11;
|
|
112
|
+
} catch (err: any) {
|
|
113
|
+
if (err instanceof SeqPropertiesError) errCatched = true;
|
|
114
|
+
}
|
|
115
|
+
expect(errCatched, true);
|
|
116
|
+
});
|
|
88
117
|
});
|
|
89
118
|
|
|
90
119
|
function testPropertiesDict(
|
|
@@ -95,7 +124,7 @@ function testPropertiesDict(
|
|
|
95
124
|
col.setTag(DG.TAGS.UNITS, units);
|
|
96
125
|
if (separator) col.setTag(bioTAGS.separator, separator);
|
|
97
126
|
if (alphabet) col.setTag(bioTAGS.alphabet, alphabet);
|
|
98
|
-
const
|
|
99
|
-
const actPropDict = getPropertiesDict(seq,
|
|
127
|
+
const sh = SeqHandler.forColumn(col);
|
|
128
|
+
const actPropDict = getPropertiesDict(seq, sh);
|
|
100
129
|
expectObject(actPropDict, expPropDict);
|
|
101
130
|
}
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
|
|
16
16
|
} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
17
17
|
|
|
18
|
-
import {awaitGrid} from './utils';
|
|
18
|
+
import {awaitGrid, initHelmMainPackage} from './utils';
|
|
19
19
|
import {TAGS as helmTAGS} from '../constants';
|
|
20
20
|
|
|
21
21
|
import {_package} from '../package-test';
|
|
@@ -26,6 +26,8 @@ category('renderers', () => {
|
|
|
26
26
|
let userLibSettings: UserLibSettings;
|
|
27
27
|
|
|
28
28
|
before(async () => {
|
|
29
|
+
await initHelmMainPackage();
|
|
30
|
+
|
|
29
31
|
monomerLibHelper = await getMonomerLibHelper();
|
|
30
32
|
userLibSettings = await getUserLibSettings();
|
|
31
33
|
|
package/src/tests/utils.ts
CHANGED
|
@@ -3,6 +3,12 @@ import * as ui from 'datagrok-api/ui';
|
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
5
|
import {delay, testEvent} from '@datagrok-libraries/utils/src/test';
|
|
6
|
+
import {getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export async function initHelmMainPackage(): Promise<void> {
|
|
10
|
+
await getHelmHelper();
|
|
11
|
+
}
|
|
6
12
|
|
|
7
13
|
export async function awaitGrid(grid: DG.Grid, timeout: number = 5000): Promise<void> {
|
|
8
14
|
await delay(100);
|
package/src/utils/get-hovered.ts
CHANGED
|
@@ -2,15 +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
|
|
6
|
-
import * as JSDraw2 from 'JSDraw2';
|
|
5
|
+
import {HelmType, PolymerType, Atom, Mol} from '@datagrok-libraries/bio/src/helm/types';
|
|
7
6
|
|
|
8
|
-
import {
|
|
7
|
+
import {helmTypeToPolymerType} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
|
|
9
8
|
|
|
10
9
|
import {HelmMonomerPlacer, ISeqMonomer} from '../helm-monomer-placer';
|
|
11
10
|
|
|
12
11
|
export function getHoveredMonomerFromEditorMol(
|
|
13
|
-
argsX: number, argsY: number, gridCell: DG.GridCell, mol:
|
|
12
|
+
argsX: number, argsY: number, gridCell: DG.GridCell, mol: Mol<HelmType>
|
|
14
13
|
): ISeqMonomer | null {
|
|
15
14
|
let hoveredSeqMonomer: ISeqMonomer | null = null;
|
|
16
15
|
|
|
@@ -80,32 +79,21 @@ export function getHoveredMonomerFallback(
|
|
|
80
79
|
}
|
|
81
80
|
left = (argsX >= sumLengths[left]) ? left : left - 1; // correct left to between sumLengths
|
|
82
81
|
if (left >= 0)
|
|
83
|
-
hoveredSeqMonomer = getSeqMonomerFromHelm(allParts[0], allParts[left]
|
|
82
|
+
hoveredSeqMonomer = getSeqMonomerFromHelm(allParts[0], allParts[left]);
|
|
84
83
|
return hoveredSeqMonomer;
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
function getSeqMonomerFromHelmAtom(atom:
|
|
88
|
-
|
|
89
|
-
switch (atom.bio.type) {
|
|
90
|
-
case 'HELM_BASE':
|
|
91
|
-
case 'HELM_SUGAR': // r - ribose, d - deoxyribose
|
|
92
|
-
case 'HELM_LINKER': // p - phosphate
|
|
93
|
-
polymerType = 'RNA';
|
|
94
|
-
break;
|
|
95
|
-
case 'HELM_AA':
|
|
96
|
-
polymerType = 'PEPTIDE';
|
|
97
|
-
break;
|
|
98
|
-
default:
|
|
99
|
-
polymerType = 'PEPTIDE';
|
|
100
|
-
}
|
|
86
|
+
function getSeqMonomerFromHelmAtom(atom: Atom<HelmType>): ISeqMonomer {
|
|
87
|
+
const polymerType = helmTypeToPolymerType(atom.bio.type);
|
|
101
88
|
return {symbol: atom.elem, polymerType: polymerType};
|
|
102
89
|
}
|
|
103
90
|
|
|
104
91
|
function getSeqMonomerFromHelm(
|
|
105
|
-
helmPrefix: string, symbol: string
|
|
92
|
+
helmPrefix: string, symbol: string
|
|
106
93
|
): ISeqMonomer {
|
|
107
94
|
let resSeqMonomer: ISeqMonomer | undefined = undefined;
|
|
108
|
-
|
|
95
|
+
const polymerTypeList: PolymerType[] = ['RNA', 'PEPTIDE', 'CHEM', 'BLOB', 'G'];
|
|
96
|
+
for (const polymerType of polymerTypeList) {
|
|
109
97
|
if (helmPrefix.startsWith(polymerType))
|
|
110
98
|
resSeqMonomer = {symbol: symbol, polymerType: polymerType};
|
|
111
99
|
}
|
|
@@ -0,0 +1,149 @@
|
|
|
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 {
|
|
6
|
+
HelmType, IWebEditorMonomer, MonomerType, PolymerType, WebEditorRGroups
|
|
7
|
+
} from '@datagrok-libraries/bio/src/helm/types';
|
|
8
|
+
|
|
9
|
+
import {Monomer} from '@datagrok-libraries/bio/src/types/index';
|
|
10
|
+
import {
|
|
11
|
+
HELM_REQUIRED_FIELD as REQ, HELM_OPTIONAL_FIELDS as OPT, HELM_RGROUP_FIELDS as RGP,
|
|
12
|
+
} from '@datagrok-libraries/bio/src/utils/const';
|
|
13
|
+
|
|
14
|
+
export function getRS(smiles: string) {
|
|
15
|
+
const newS = smiles.match(/(?<=\[)[^\][]*(?=])/gm);
|
|
16
|
+
const res: { [name: string]: string } = {};
|
|
17
|
+
let el = '';
|
|
18
|
+
let digit;
|
|
19
|
+
if (!!newS) {
|
|
20
|
+
for (let i = 0; i < newS.length; i++) {
|
|
21
|
+
if (newS[i] != null) {
|
|
22
|
+
if (/\d/.test(newS[i])) {
|
|
23
|
+
digit = newS[i][newS[i].length - 1];
|
|
24
|
+
newS[i] = newS[i].replace(/[0-9]/g, '');
|
|
25
|
+
for (let j = 0; j < newS[i].length; j++) {
|
|
26
|
+
if (newS[i][j] != ':')
|
|
27
|
+
el += newS[i][j];
|
|
28
|
+
}
|
|
29
|
+
res['R' + digit] = el;
|
|
30
|
+
el = '';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return res;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* eslint-disable camelcase */
|
|
39
|
+
export abstract class DummyWebEditorMonomer implements IWebEditorMonomer {
|
|
40
|
+
//@formatter:off
|
|
41
|
+
public abstract get backgroundcolor(): string | undefined;
|
|
42
|
+
public abstract get linecolor(): string | undefined;
|
|
43
|
+
public abstract get textcolor(): string | undefined;
|
|
44
|
+
//@formatter:on
|
|
45
|
+
|
|
46
|
+
get issmiles(): boolean { return !!this.smiles; }
|
|
47
|
+
|
|
48
|
+
/** R-Group index os single digit only is allowed in Pistoia code */
|
|
49
|
+
public readonly at: WebEditorRGroups = {
|
|
50
|
+
R1: 'H', R2: 'H', R3: 'H', R4: 'H', R5: 'H', R6: 'H', R7: 'H', R8: 'H', R9: 'H'
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
public get rs(): number { return Object.keys(this.at).length; }
|
|
54
|
+
|
|
55
|
+
protected constructor(
|
|
56
|
+
public readonly biotype: string,
|
|
57
|
+
/** symbol */ public readonly id: string,
|
|
58
|
+
/** name */ public readonly n: string | undefined = 'missing',
|
|
59
|
+
/** molfile */ public readonly m: string | undefined = undefined,
|
|
60
|
+
/* Pistoia.HELM deletes .type and .mt in Monomers.addOneMonomer() */
|
|
61
|
+
/** polymer type */ public readonly type: PolymerType | undefined = undefined,
|
|
62
|
+
/** monomer type */ public readonly mt: MonomerType | undefined = undefined,
|
|
63
|
+
public readonly smiles?: string,
|
|
64
|
+
) {
|
|
65
|
+
if (!this.id)
|
|
66
|
+
throw new Error('Invalid arg undefined [id].');
|
|
67
|
+
|
|
68
|
+
// return new Proxy(this, {
|
|
69
|
+
// get: (target: any, prop: string | symbol, _receiver: any) => {
|
|
70
|
+
// const logPrefix = `${this.toLog()}.proxy.get( '${String(prop)}' )`;
|
|
71
|
+
// switch (prop) {
|
|
72
|
+
// case 'id':
|
|
73
|
+
// case 'at':
|
|
74
|
+
// case 'na':
|
|
75
|
+
// case 'nature':
|
|
76
|
+
// case 'backgroundcolor':
|
|
77
|
+
// case 'linecolor':
|
|
78
|
+
// case 'textcolor': {
|
|
79
|
+
// // these attributes are known and handled
|
|
80
|
+
// return target[prop];
|
|
81
|
+
// }
|
|
82
|
+
// case 'rs': {
|
|
83
|
+
// /* R-Group count (?), 2 is default for monomer "?" */
|
|
84
|
+
// throw new Error('Not supported get "rs" attribute.');
|
|
85
|
+
// }
|
|
86
|
+
// default:
|
|
87
|
+
// // Unexpected attribute requested
|
|
88
|
+
// _package.logger.warning(`${logPrefix}`);
|
|
89
|
+
// return target[prop];
|
|
90
|
+
// }
|
|
91
|
+
// },
|
|
92
|
+
// set: (target, prop, value: any, _receiver: any): boolean => {
|
|
93
|
+
// throw new Error(`Not supported set '${String(prop)}' attribute, any.`);
|
|
94
|
+
// },
|
|
95
|
+
// apply: (target: any, thisArg: any, argArray: any[]): any | undefined => {
|
|
96
|
+
// throw new Error(`Not supported call (apply) '${thisArg.toString()}' method, any.`);
|
|
97
|
+
// }
|
|
98
|
+
// });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private static objCounter: number = -1;
|
|
102
|
+
private readonly objId: number = ++DummyWebEditorMonomer.objCounter;
|
|
103
|
+
|
|
104
|
+
private readonly className: string = 'DummyWebEditorMonomer';
|
|
105
|
+
|
|
106
|
+
protected toLog(): string {
|
|
107
|
+
return `Helm: ${this.className}<${this.objId}>`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export class GapWebEditorMonomer extends DummyWebEditorMonomer {
|
|
112
|
+
public readonly backgroundcolor: string = '#FFFFFF';
|
|
113
|
+
public readonly linecolor: string = '#808080';
|
|
114
|
+
public readonly textcolor: string = '#808080';
|
|
115
|
+
|
|
116
|
+
constructor(biotype: string, id: string) {
|
|
117
|
+
super(biotype, id, 'gap');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export class AmbiguousWebEditorMonomer extends DummyWebEditorMonomer {
|
|
122
|
+
public readonly backgroundcolor: string = '#808080';
|
|
123
|
+
public readonly linecolor: string = '#000000';
|
|
124
|
+
public readonly textcolor: string = '#000000';
|
|
125
|
+
|
|
126
|
+
constructor(biotype: string, id: string) {
|
|
127
|
+
super(biotype, id, 'ambiguous');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export class MissingWebEditorMonomer extends DummyWebEditorMonomer {
|
|
132
|
+
public readonly backgroundcolor: string = '#FF4444';
|
|
133
|
+
public readonly linecolor: string = '#800000';
|
|
134
|
+
public readonly textcolor: string = '#FFFFFF';
|
|
135
|
+
|
|
136
|
+
constructor(biotype: string, id: string) {
|
|
137
|
+
super(biotype, id, 'missing');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export class BrokenWebEditorMonomer extends DummyWebEditorMonomer {
|
|
142
|
+
public readonly backgroundcolor: string = '#FFFF44';
|
|
143
|
+
public readonly linecolor: string = '#800000';
|
|
144
|
+
public readonly textcolor: string = '#000000';
|
|
145
|
+
|
|
146
|
+
constructor(biotype: string, id: string) {
|
|
147
|
+
super(biotype, id, 'broken');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
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 {
|
|
6
|
+
HelmType, IWebEditorMonomer, IMonomerColors, MonomerType, PolymerType, WebEditorRGroups
|
|
7
|
+
} from '@datagrok-libraries/bio/src/helm/types';
|
|
8
|
+
import {Monomer} from '@datagrok-libraries/bio/src/types';
|
|
9
|
+
import {
|
|
10
|
+
HELM_OPTIONAL_FIELDS as OPT, HELM_REQUIRED_FIELD as REQ, HELM_RGROUP_FIELDS as RGP
|
|
11
|
+
} from '@datagrok-libraries/bio/src/utils/const';
|
|
12
|
+
import {BrokenWebEditorMonomer, getRS, MissingWebEditorMonomer} from './get-monomer-dummy';
|
|
13
|
+
|
|
14
|
+
function getMonomerColors(monomer: Monomer): IMonomerColors | null {
|
|
15
|
+
const currentMonomerSchema = 'default';
|
|
16
|
+
let monomerSchema: string = currentMonomerSchema;
|
|
17
|
+
if (!monomer.meta || !monomer.meta.colors) return null;
|
|
18
|
+
|
|
19
|
+
const monomerColors: { [colorSchemaName: string]: any } = monomer.meta.colors;
|
|
20
|
+
|
|
21
|
+
if (!(currentMonomerSchema in monomerColors)) monomerSchema = 'default';
|
|
22
|
+
|
|
23
|
+
const res = monomerColors[monomerSchema];
|
|
24
|
+
|
|
25
|
+
return !res ? null : {
|
|
26
|
+
textcolor: res.text ?? res.textColor,
|
|
27
|
+
linecolor: res.line ?? res.lineColor,
|
|
28
|
+
backgroundcolor: res.background ?? res.backgroundColor
|
|
29
|
+
} as IMonomerColors;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class LibraryWebEditorMonomer implements IWebEditorMonomer {
|
|
33
|
+
public get rs(): number { return Object.keys(this.at).length; }
|
|
34
|
+
|
|
35
|
+
public get issmiles(): boolean { return !!this.smiles; }
|
|
36
|
+
|
|
37
|
+
public linecolor?: string;
|
|
38
|
+
public backgroundcolor?: string;
|
|
39
|
+
public textcolor?: string;
|
|
40
|
+
|
|
41
|
+
/* eslint-disable max-params */
|
|
42
|
+
protected constructor(
|
|
43
|
+
public readonly id: string,
|
|
44
|
+
public readonly m: string,
|
|
45
|
+
public readonly n: string,
|
|
46
|
+
public readonly na: string | undefined,
|
|
47
|
+
public readonly type: PolymerType,
|
|
48
|
+
public readonly mt: MonomerType,
|
|
49
|
+
public readonly at: WebEditorRGroups,
|
|
50
|
+
public readonly smiles?: string,
|
|
51
|
+
) /* eslint-enable max-params */ {}
|
|
52
|
+
|
|
53
|
+
static fromMonomer(biotype: HelmType, monomer: Monomer): IWebEditorMonomer {
|
|
54
|
+
let at: WebEditorRGroups = {};
|
|
55
|
+
const symbol = monomer[REQ.SYMBOL];
|
|
56
|
+
const smiles = monomer[REQ.SMILES];
|
|
57
|
+
if (monomer.rgroups.length > 0) {
|
|
58
|
+
monomer.rgroups.forEach((it) => {
|
|
59
|
+
at[it[RGP.LABEL]] = it[RGP.CAP_GROUP_NAME];
|
|
60
|
+
});
|
|
61
|
+
} else if (smiles) {
|
|
62
|
+
// Generate R-Groups from SMILES
|
|
63
|
+
at = getRS(smiles);
|
|
64
|
+
} else if (!monomer.lib) {
|
|
65
|
+
// missing
|
|
66
|
+
return new MissingWebEditorMonomer(biotype, symbol);
|
|
67
|
+
} else {
|
|
68
|
+
// broken
|
|
69
|
+
return new BrokenWebEditorMonomer(biotype, symbol);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const res = new LibraryWebEditorMonomer(
|
|
73
|
+
monomer[REQ.SYMBOL],
|
|
74
|
+
monomer[REQ.MOLFILE],
|
|
75
|
+
monomer[REQ.NAME],
|
|
76
|
+
monomer[OPT.NATURAL_ANALOG],
|
|
77
|
+
monomer[REQ.POLYMER_TYPE],
|
|
78
|
+
monomer[REQ.MONOMER_TYPE],
|
|
79
|
+
at);
|
|
80
|
+
|
|
81
|
+
const colors = getMonomerColors(monomer);
|
|
82
|
+
if (colors) {
|
|
83
|
+
res.textcolor = colors?.textcolor;
|
|
84
|
+
res.linecolor = colors?.linecolor;
|
|
85
|
+
res.backgroundcolor = colors?.backgroundcolor;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return res;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
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 {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
|
|
6
|
+
import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
|
|
7
|
+
import {Atom, HelmType, IWebEditorMonomer, GetMonomerResType} from '@datagrok-libraries/bio/src/helm/types';
|
|
8
|
+
import {helmTypeToPolymerType} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
|
|
9
|
+
import {
|
|
10
|
+
HELM_REQUIRED_FIELD as REQ
|
|
11
|
+
} from '@datagrok-libraries/bio/src/utils/const';
|
|
12
|
+
import {HelmTypes} from '@datagrok-libraries/bio/src/helm/consts';
|
|
13
|
+
|
|
14
|
+
import {AmbiguousWebEditorMonomer, GapWebEditorMonomer, MissingWebEditorMonomer} from './get-monomer-dummy';
|
|
15
|
+
import {LibraryWebEditorMonomer} from './get-monomer-of-library';
|
|
16
|
+
import {OrgHelmModule, ScilModule} from '../types';
|
|
17
|
+
|
|
18
|
+
import {_package} from '../package';
|
|
19
|
+
|
|
20
|
+
declare const org: OrgHelmModule;
|
|
21
|
+
declare const scil: ScilModule;
|
|
22
|
+
|
|
23
|
+
const monomerRe = /[\w()]+/;
|
|
24
|
+
//** Do not mess with monomer symbol with parenthesis enclosed in square brackets */
|
|
25
|
+
const ambMonomerRe = RegExp(String.raw`\(${monomerRe}(,${monomerRe})+\)`);
|
|
26
|
+
|
|
27
|
+
type GetMonomerOverridingFunc = (
|
|
28
|
+
a: Atom<HelmType> | HelmType, name: string | undefined, monomerLib: IMonomerLib,
|
|
29
|
+
originalGetMonomer: (a: Atom<HelmType> | HelmType, name: string) => GetMonomerResType) => GetMonomerResType;
|
|
30
|
+
|
|
31
|
+
export function getMonomerOverrideAndLogAlert(
|
|
32
|
+
monomerLib: IMonomerLib, getMonomerOverriding: GetMonomerOverridingFunc, trigger: () => void, logger: ILogger,
|
|
33
|
+
): void {
|
|
34
|
+
const logPrefix = 'Helm: overrideGetMonomerAndAlert()';
|
|
35
|
+
const monomers = org.helm.webeditor.Monomers;
|
|
36
|
+
const getMonomerOriginal = monomers.getMonomer.bind(monomers);
|
|
37
|
+
const alertOriginal = scil.Utils.alert;
|
|
38
|
+
try {
|
|
39
|
+
org.helm.webeditor.Monomers.getMonomer =
|
|
40
|
+
(a: Atom<HelmType> | HelmType, name?: string): GetMonomerResType => {
|
|
41
|
+
return getMonomerOverriding(a, name, monomerLib, getMonomerOriginal);
|
|
42
|
+
};
|
|
43
|
+
// Preventing alert message box for missing monomers with compressed Scilligence.JSDraw2.Lite.js
|
|
44
|
+
scil.Utils.alert = (s: string): void => {
|
|
45
|
+
logger.warning(`${logPrefix}, scil.Utils.alert() s = 's'.`);
|
|
46
|
+
};
|
|
47
|
+
trigger();
|
|
48
|
+
} finally {
|
|
49
|
+
monomers.getMonomer = getMonomerOriginal;
|
|
50
|
+
scil.Utils.alert = alertOriginal;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Inputs logic */
|
|
55
|
+
export function getMonomerHandleArgs(
|
|
56
|
+
a: Atom<HelmType> | HelmType, name?: string
|
|
57
|
+
): [/** biotype */ HelmType, /** elem */ string] {
|
|
58
|
+
let biotype: HelmType;
|
|
59
|
+
let elem: string;
|
|
60
|
+
if ((a as Atom<HelmType>).T === 'ATOM') {
|
|
61
|
+
biotype = (a as Atom<HelmType>).biotype()!;
|
|
62
|
+
elem = (a as Atom<HelmType>).elem;
|
|
63
|
+
} else {
|
|
64
|
+
biotype = a as HelmType;
|
|
65
|
+
elem = org.helm.webeditor.IO.trimBracket(name!);
|
|
66
|
+
}
|
|
67
|
+
return [biotype, elem];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
/** Substitutes {@link org.helm.webeditor.Monomers.getMonomer()} */
|
|
72
|
+
export function getWebEditorMonomer(
|
|
73
|
+
monomerLib: IMonomerLib,
|
|
74
|
+
a: Atom<HelmType> | HelmType, argName?: string,
|
|
75
|
+
): IWebEditorMonomer | null {
|
|
76
|
+
const [biotype, elem] = getMonomerHandleArgs(a, argName);
|
|
77
|
+
const pt = helmTypeToPolymerType(biotype);
|
|
78
|
+
|
|
79
|
+
/** Get or create {@link Monomer} object (in case it is missing in monomer library current config) */
|
|
80
|
+
let m: Monomer | null = monomerLib.getMonomer(pt, elem);
|
|
81
|
+
if (m && biotype == HelmTypes.LINKER && m[REQ.RGROUPS].length != 2) {
|
|
82
|
+
// Web Editor expects null
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
if (m && biotype == HelmTypes.SUGAR && m[REQ.RGROUPS].length != 3) {
|
|
86
|
+
// Web Editor expects null
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (!m /* && biotype != HelmTypes.LINKER*/)
|
|
90
|
+
m = monomerLib.addMissingMonomer(pt, elem);
|
|
91
|
+
|
|
92
|
+
/** Get or create {@link org,helm.WebEditorMonomer} */
|
|
93
|
+
let resWem: IWebEditorMonomer | null = m.wem ?? null;
|
|
94
|
+
if (!resWem) {
|
|
95
|
+
if (elem === '*')
|
|
96
|
+
resWem = m.wem = new GapWebEditorMonomer(biotype, elem);
|
|
97
|
+
else if (
|
|
98
|
+
(biotype === 'HELM_NUCLETIDE' && elem === 'N') ||
|
|
99
|
+
(biotype === 'HELM_AA' && elem === 'X') ||
|
|
100
|
+
(biotype === 'HELM_CHEM' && false) || // TODO: Ambiguous monomer for CHEM
|
|
101
|
+
ambMonomerRe.test(elem) // e.g. (A,R,_)
|
|
102
|
+
)
|
|
103
|
+
resWem = m.wem = new AmbiguousWebEditorMonomer(biotype, elem);
|
|
104
|
+
else if (!m.lib)
|
|
105
|
+
resWem = m.wem = new MissingWebEditorMonomer(biotype, elem);
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if (!resWem)
|
|
109
|
+
resWem = m.wem = LibraryWebEditorMonomer.fromMonomer(biotype, m);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return resWem;
|
|
113
|
+
}
|
|
@@ -2,9 +2,9 @@ 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 * as JSDraw2 from 'JSDraw2';
|
|
6
5
|
import wu from 'wu';
|
|
7
6
|
|
|
7
|
+
import {HelmType, Mol} from '@datagrok-libraries/bio/src/helm/types';
|
|
8
8
|
import {
|
|
9
9
|
CellRendererBackAsyncBase, RenderServiceBase
|
|
10
10
|
} from '@datagrok-libraries/bio/src/utils/cell-renderer-async-base';
|
|
@@ -16,7 +16,6 @@ import {_getHelmService} from '../package-utils';
|
|
|
16
16
|
import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
|
|
17
17
|
import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
|
|
18
18
|
import {getGridCellRendererBack} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
19
|
-
import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
|
|
20
19
|
|
|
21
20
|
import {_package, getMonomerLib} from '../package';
|
|
22
21
|
|
|
@@ -44,15 +43,19 @@ class WrapLogger implements ILogger {
|
|
|
44
43
|
|
|
45
44
|
export class HelmGridCellRendererBack extends CellRendererBackAsyncBase<HelmProps, HelmAux> {
|
|
46
45
|
private _auxList: (HelmAux | null)[];
|
|
47
|
-
private readonly monomerLib: IMonomerLib;
|
|
48
46
|
|
|
49
47
|
constructor(
|
|
50
48
|
gridCol: DG.GridColumn | null,
|
|
51
49
|
tableCol: DG.Column<string>,
|
|
52
50
|
) {
|
|
53
51
|
super(gridCol, tableCol, new WrapLogger(_package.logger) /* _package.logger */, true);
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
|
|
53
|
+
const monomerLib = getMonomerLib();
|
|
54
|
+
if (!monomerLib)
|
|
55
|
+
throw new Error('Helm package is not initialized yet.');
|
|
56
|
+
this.subs.push(monomerLib.onChanged.subscribe(() => {
|
|
57
|
+
this.reset();
|
|
58
|
+
}));
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
protected override reset(): void {
|
|
@@ -138,7 +141,7 @@ export class HelmGridCellRendererBack extends CellRendererBackAsyncBase<HelmProp
|
|
|
138
141
|
const argsX = (e.offsetX - gcb.x) * dpr;
|
|
139
142
|
const argsY = (e.offsetY - gcb.y) * dpr;
|
|
140
143
|
|
|
141
|
-
const editorMol:
|
|
144
|
+
const editorMol: Mol<HelmType> | null = aux.mol;
|
|
142
145
|
if (!editorMol) {
|
|
143
146
|
this.logger.warning(`${logPrefix}, editorMol of the cell not found.`);
|
|
144
147
|
return; // The gridCell is not rendered yet
|
|
@@ -146,7 +149,9 @@ export class HelmGridCellRendererBack extends CellRendererBackAsyncBase<HelmProp
|
|
|
146
149
|
const seqMonomer: ISeqMonomer | null = getHoveredMonomerFromEditorMol(argsX, argsY, gridCell, editorMol);
|
|
147
150
|
|
|
148
151
|
if (seqMonomer) {
|
|
149
|
-
const
|
|
152
|
+
const monomerLib = getMonomerLib();
|
|
153
|
+
const tooltipEl = monomerLib ? monomerLib.getTooltip(seqMonomer.polymerType, seqMonomer.symbol) :
|
|
154
|
+
ui.divText('Monomer library is not available');
|
|
150
155
|
ui.tooltip.show(tooltipEl, e.x + 16, e.y + 16);
|
|
151
156
|
} else {
|
|
152
157
|
// Tooltip for missing monomers
|