@datagrok/bio 2.12.16 → 2.12.18
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 +20 -2
- package/dist/79.js.map +1 -1
- package/dist/package-test.js +5 -5
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +3 -3
- package/dist/package.js.map +1 -1
- package/package.json +6 -6
- package/src/package-test.ts +1 -1
- package/src/package.ts +38 -44
- package/src/tests/monomer-libraries-tests.ts +1 -4
- package/src/tests/renderers-monomer-placer-tests.ts +9 -8
- package/src/tests/renderers-test.ts +1 -1
- package/src/tests/scoring.ts +2 -2
- package/src/tests/substructure-filters-tests.ts +4 -2
- package/src/tests/to-atomic-level-tests.ts +1 -1
- package/src/tests/utils.ts +15 -0
- package/src/utils/cell-renderer.ts +45 -70
- package/src/utils/{poly-tool/cyclized.ts → cyclized.ts} +3 -7
- package/src/utils/helm-to-molfile/converter/converter.ts +10 -5
- package/src/utils/helm-to-molfile/converter/monomer-wrapper.ts +9 -9
- package/src/utils/helm-to-molfile/converter/polymer.ts +10 -3
- package/src/utils/monomer-cell-renderer.ts +18 -8
- package/src/utils/monomer-lib/lib-manager.ts +56 -18
- package/src/utils/monomer-lib/library-file-manager/event-manager.ts +15 -9
- package/src/utils/monomer-lib/library-file-manager/file-manager.ts +78 -59
- package/src/utils/monomer-lib/library-file-manager/file-validator.ts +3 -1
- package/src/utils/monomer-lib/library-file-manager/ui.ts +52 -47
- package/src/utils/monomer-lib/monomer-lib.ts +91 -10
- package/src/utils/sequence-to-mol.ts +7 -7
- package/src/widgets/bio-substructure-filter-helm.ts +5 -4
- package/src/widgets/bio-substructure-filter.ts +2 -3
- package/webpack.config.js +3 -0
- package/src/utils/poly-tool/const.ts +0 -40
- package/src/utils/poly-tool/csv-to-json-monomer-lib-converter.ts +0 -40
- package/src/utils/poly-tool/monomer-lib-handler.ts +0 -115
- package/src/utils/poly-tool/transformation.ts +0 -320
- package/src/utils/poly-tool/ui.ts +0 -59
- package/src/utils/poly-tool/utils.ts +0 -20
|
@@ -5,10 +5,13 @@ import * as OCL from 'openchemlib/full';
|
|
|
5
5
|
|
|
6
6
|
import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
|
|
7
7
|
import {RDModule, RDMol} from '@datagrok-libraries/chem-meta/src/rdkit-api';
|
|
8
|
-
import {
|
|
8
|
+
import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
|
|
9
|
+
|
|
9
10
|
import {Polymer} from './polymer';
|
|
10
11
|
import {GlobalMonomerPositionHandler} from './position-handler';
|
|
11
12
|
|
|
13
|
+
import {_package, getMonomerLibHelper} from '../../../package';
|
|
14
|
+
|
|
12
15
|
export class HelmToMolfileConverter {
|
|
13
16
|
constructor(private helmColumn: DG.Column<string>, private df: DG.DataFrame) {
|
|
14
17
|
this.helmColumn = helmColumn;
|
|
@@ -54,7 +57,7 @@ export class HelmToMolfileConverter {
|
|
|
54
57
|
async convertToRdKitBeautifiedMolfileColumn(chiralityEngine?: boolean): Promise<DG.Column<string>> {
|
|
55
58
|
const molfilesV3K = (await this.convertToMolfileV3KColumn()).toList();
|
|
56
59
|
const rdKitModule: RDModule = await grok.functions.call('Chem:getRdKitModule');
|
|
57
|
-
const beautifiedMols = molfilesV3K.map((item) =>{
|
|
60
|
+
const beautifiedMols = molfilesV3K.map((item) => {
|
|
58
61
|
if (item === '')
|
|
59
62
|
return null;
|
|
60
63
|
const mol = rdKitModule.get_mol(item);
|
|
@@ -81,6 +84,7 @@ export class HelmToMolfileConverter {
|
|
|
81
84
|
private async convertToMolfileV3KColumn(): Promise<DG.Column<string>> {
|
|
82
85
|
const polymerGraphColumn: DG.Column<string> = await this.getPolymerGraphColumn();
|
|
83
86
|
const rdKitModule = await grok.functions.call('Chem:getRdKitModule');
|
|
87
|
+
const monomerLib: IMonomerLib = (await getMonomerLibHelper()).getBioLib();
|
|
84
88
|
const molfileList = polymerGraphColumn.toList().map(
|
|
85
89
|
(pseudoMolfile: string, idx: number) => {
|
|
86
90
|
const helm = this.helmColumn.get(idx);
|
|
@@ -88,7 +92,7 @@ export class HelmToMolfileConverter {
|
|
|
88
92
|
return '';
|
|
89
93
|
let result = '';
|
|
90
94
|
try {
|
|
91
|
-
result = this.getPolymerMolfile(helm, pseudoMolfile, rdKitModule);
|
|
95
|
+
result = this.getPolymerMolfile(helm, pseudoMolfile, rdKitModule, monomerLib);
|
|
92
96
|
} catch (err: any) {
|
|
93
97
|
const [errMsg, errStack] = errInfo(err);
|
|
94
98
|
_package.logger.error(errMsg, undefined, errStack);
|
|
@@ -110,10 +114,11 @@ export class HelmToMolfileConverter {
|
|
|
110
114
|
private getPolymerMolfile(
|
|
111
115
|
helm: string,
|
|
112
116
|
polymerGraph: string,
|
|
113
|
-
rdKitModule: RDModule
|
|
117
|
+
rdKitModule: RDModule,
|
|
118
|
+
monomerLib: IMonomerLib
|
|
114
119
|
): string {
|
|
115
120
|
const globalPositionHandler = new GlobalMonomerPositionHandler(polymerGraph);
|
|
116
|
-
const polymer = new Polymer(helm, rdKitModule);
|
|
121
|
+
const polymer = new Polymer(helm, rdKitModule, monomerLib);
|
|
117
122
|
globalPositionHandler.monomerSymbols.forEach((monomerSymbol: string, monomerIdx: number) => {
|
|
118
123
|
const shift = globalPositionHandler.getMonomerShifts(monomerIdx);
|
|
119
124
|
polymer.addMonomer(monomerSymbol, monomerIdx, shift);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {Monomer} from '@datagrok-libraries/bio/src/types';
|
|
1
|
+
import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
|
|
2
2
|
import {HELM_RGROUP_FIELDS} from '@datagrok-libraries/bio/src/utils/const';
|
|
3
3
|
import {RDModule, RDMol} from '@datagrok-libraries/chem-meta/src/rdkit-api';
|
|
4
|
-
import {
|
|
4
|
+
import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
|
|
5
|
+
|
|
5
6
|
import {Helm} from './helm';
|
|
6
7
|
import {MolfileWrapper} from './mol-wrapper';
|
|
7
8
|
import {MolfileWrapperFactory} from './mol-wrapper-factory';
|
|
8
|
-
import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
|
|
9
9
|
|
|
10
10
|
export class MonomerWrapper {
|
|
11
11
|
private molfileWrapper: MolfileWrapper;
|
|
@@ -15,8 +15,9 @@ export class MonomerWrapper {
|
|
|
15
15
|
private monomerSymbol: string,
|
|
16
16
|
private monomerIdx: number,
|
|
17
17
|
private helm: Helm,
|
|
18
|
-
shift: {x: number, y: number},
|
|
19
|
-
rdKitModule: RDModule
|
|
18
|
+
shift: { x: number, y: number },
|
|
19
|
+
rdKitModule: RDModule,
|
|
20
|
+
private readonly monomerLib: IMonomerLib
|
|
20
21
|
) {
|
|
21
22
|
const libraryMonomerObject = this.getLibraryMonomerObject();
|
|
22
23
|
|
|
@@ -37,7 +38,7 @@ export class MonomerWrapper {
|
|
|
37
38
|
private convertMolfileToV3KFormat(molfileV2K: string, monomerSymbol: string, rdKitModule: RDModule): string {
|
|
38
39
|
let mol: RDMol | null = null;
|
|
39
40
|
try {
|
|
40
|
-
mol = rdKitModule.get_mol(molfileV2K, JSON.stringify({mergeQueryHs: true}))
|
|
41
|
+
mol = rdKitModule.get_mol(molfileV2K, JSON.stringify({mergeQueryHs: true}));
|
|
41
42
|
if (mol)
|
|
42
43
|
return mol.get_v3Kmolblock();
|
|
43
44
|
else
|
|
@@ -49,8 +50,7 @@ export class MonomerWrapper {
|
|
|
49
50
|
|
|
50
51
|
private getLibraryMonomerObject(): Monomer {
|
|
51
52
|
const polymerType = this.helm.getPolymerTypeByMonomerIdx(this.monomerIdx);
|
|
52
|
-
const
|
|
53
|
-
const monomer = monomerLib.getMonomer(polymerType, this.monomerSymbol);
|
|
53
|
+
const monomer = this.monomerLib.getMonomer(polymerType, this.monomerSymbol);
|
|
54
54
|
if (!monomer)
|
|
55
55
|
throw new Error(`Monomer ${this.monomerSymbol} is not found in the library`);
|
|
56
56
|
return monomer;
|
|
@@ -72,7 +72,7 @@ export class MonomerWrapper {
|
|
|
72
72
|
return result;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
private shiftCoordinates(shift: {x: number, y: number}): void {
|
|
75
|
+
private shiftCoordinates(shift: { x: number, y: number }): void {
|
|
76
76
|
this.molfileWrapper.shiftCoordinates(shift);
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
|
|
2
2
|
import {V3K_CONST} from '@datagrok-libraries/chem-meta/src/formats/molfile-const';
|
|
3
|
+
import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
|
|
4
|
+
|
|
3
5
|
import {Helm} from './helm';
|
|
4
6
|
import {MonomerWrapper} from './monomer-wrapper';
|
|
5
7
|
|
|
6
8
|
export class Polymer {
|
|
7
|
-
constructor(
|
|
9
|
+
constructor(
|
|
10
|
+
helmString: string,
|
|
11
|
+
private readonly rdKitModule: RDModule,
|
|
12
|
+
private readonly monomerLib: IMonomerLib
|
|
13
|
+
) {
|
|
8
14
|
this.helm = new Helm(helmString);
|
|
9
15
|
}
|
|
10
16
|
|
|
@@ -14,9 +20,10 @@ export class Polymer {
|
|
|
14
20
|
addMonomer(
|
|
15
21
|
monomerSymbol: string,
|
|
16
22
|
monomerIdx: number,
|
|
17
|
-
shift: {x: number, y: number},
|
|
23
|
+
shift: { x: number, y: number },
|
|
18
24
|
): void {
|
|
19
|
-
const monomerWrapper = new MonomerWrapper(
|
|
25
|
+
const monomerWrapper = new MonomerWrapper(
|
|
26
|
+
monomerSymbol, monomerIdx, this.helm, shift, this.rdKitModule, this.monomerLib);
|
|
20
27
|
|
|
21
28
|
this.monomerWrappers.push(monomerWrapper);
|
|
22
29
|
}
|
|
@@ -5,9 +5,10 @@ import * as ui from 'datagrok-api/ui';
|
|
|
5
5
|
import {ALPHABET, getPaletteByType, monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
6
6
|
import {TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
7
7
|
import {MonomerWorks} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
|
|
8
|
+
import {PolymerType} from '@datagrok-libraries/bio/src/types';
|
|
8
9
|
|
|
9
|
-
import {getMonomerLibHelper} from '../package';
|
|
10
10
|
import * as C from './constants';
|
|
11
|
+
import {getMonomerLib} from '../package';
|
|
11
12
|
|
|
12
13
|
const Tags = new class {
|
|
13
14
|
tooltipHandlerTemp = 'tooltip-handler.Monomer';
|
|
@@ -29,20 +30,29 @@ export class MonomerTooltipHandler {
|
|
|
29
30
|
!gridCell.tableColumn || !gridCell.isTableCell
|
|
30
31
|
) return false;
|
|
31
32
|
|
|
32
|
-
const alphabet = gridCell.tableColumn.getTag(bioTAGS.alphabet);
|
|
33
|
+
const alphabet = gridCell.tableColumn.getTag(bioTAGS.alphabet) as ALPHABET;
|
|
33
34
|
const monomerName = gridCell.cell.value;
|
|
34
|
-
const
|
|
35
|
-
const
|
|
35
|
+
const canvasClientRect = gridCell.grid.canvas.getBoundingClientRect();
|
|
36
|
+
const x1 = gridCell.bounds.right + canvasClientRect.left - 4;
|
|
37
|
+
const y1 = gridCell.bounds.bottom + canvasClientRect.top - 4;
|
|
38
|
+
|
|
39
|
+
const monomerLib = getMonomerLib();
|
|
40
|
+
if (!monomerLib) {
|
|
41
|
+
ui.tooltip.show(ui.divText('Monomer library is not available.'), x1, y1);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const mw = new MonomerWorks(monomerLib);
|
|
46
|
+
const polymerType: PolymerType = (alphabet === ALPHABET.DNA || alphabet === ALPHABET.RNA) ? 'RNA' :
|
|
36
47
|
alphabet === ALPHABET.PT ? 'PEPTIDE' : 'PEPTIDE';
|
|
48
|
+
// const biotype: HelmType = [ALPHABET.RNA, ALPHABET.DNA].includes(alphabet) ? HelmTypes.NUCLEOTIDE :
|
|
49
|
+
// [ALPHABET.PT].includes(alphabet) ? HelmTypes.AA : HelmTypes.AA;
|
|
37
50
|
|
|
38
|
-
const monomerMol: string | null = mw.getCappedRotatedMonomer(
|
|
51
|
+
const monomerMol: string | null = mw.getCappedRotatedMonomer(polymerType, monomerName);
|
|
39
52
|
const nameDiv = ui.div(monomerName);
|
|
40
53
|
const molDiv = !monomerMol ? null :
|
|
41
54
|
grok.chem.svgMol(monomerMol, undefined, undefined, svgMolOptions);
|
|
42
55
|
|
|
43
|
-
const canvasClientRect = gridCell.grid.canvas.getBoundingClientRect();
|
|
44
|
-
const x1 = gridCell.bounds.right + canvasClientRect.left - 4;
|
|
45
|
-
const y1 = gridCell.bounds.bottom + canvasClientRect.top - 4;
|
|
46
56
|
ui.tooltip.show(ui.divV([nameDiv, ...(molDiv ? [molDiv] : [])]), x1, y1);
|
|
47
57
|
|
|
48
58
|
return true; // To prevent default tooltip behaviour
|
|
@@ -3,33 +3,51 @@ import * as grok from 'datagrok-api/grok';
|
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {delay} from '@datagrok-libraries/utils/src/test';
|
|
7
|
+
import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
|
|
7
8
|
import {
|
|
8
9
|
getUserLibSettings, setUserLibSettings, LIB_PATH
|
|
9
10
|
} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
10
11
|
import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
|
|
11
12
|
import {
|
|
12
|
-
IMonomerLibHelper,
|
|
13
|
+
IMonomerLibFileEventManager, IMonomerLibHelper,
|
|
13
14
|
} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
15
|
+
|
|
14
16
|
import {MonomerLib} from './monomer-lib';
|
|
15
17
|
import {MonomerLibFileManager} from './library-file-manager/file-manager';
|
|
16
18
|
import {MonomerLibFileEventManager} from './library-file-manager/event-manager';
|
|
19
|
+
|
|
17
20
|
import {_package} from '../../package';
|
|
18
21
|
|
|
19
|
-
type MonomerLibWindowType = Window & { $
|
|
22
|
+
type MonomerLibWindowType = Window & { $monomerLibHelperPromise?: Promise<MonomerLibManager> };
|
|
20
23
|
declare const window: MonomerLibWindowType;
|
|
21
24
|
|
|
22
|
-
export async function getLibFileNameList(): Promise<string[]> {
|
|
23
|
-
const fileEventManager = MonomerLibFileEventManager.getInstance();
|
|
24
|
-
const fileManager = await MonomerLibFileManager.getInstance(fileEventManager);
|
|
25
|
-
return fileManager.getValidLibraryPaths();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
25
|
/** Singleton wrapper for MonomerLib, provides API for managing libraries on
|
|
29
26
|
* the platform */
|
|
30
27
|
export class MonomerLibManager implements IMonomerLibHelper {
|
|
31
28
|
private readonly _monomerLib = new MonomerLib({}, 'MAIN');
|
|
32
29
|
|
|
30
|
+
private _eventManager: MonomerLibFileEventManager;
|
|
31
|
+
|
|
32
|
+
public get eventManager(): IMonomerLibFileEventManager { return this._eventManager; }
|
|
33
|
+
|
|
34
|
+
public async awaitLoaded(timeout: number = 3000): Promise<void> {
|
|
35
|
+
return await Promise.race([
|
|
36
|
+
(async () => {
|
|
37
|
+
const fileManager = await this.getFileManager();
|
|
38
|
+
await fileManager.filesPromise;
|
|
39
|
+
return true;
|
|
40
|
+
})(),
|
|
41
|
+
(async () => {
|
|
42
|
+
await delay(timeout);
|
|
43
|
+
return false;
|
|
44
|
+
})(),
|
|
45
|
+
]).then((res) => {
|
|
46
|
+
if (!res)
|
|
47
|
+
throw new Error(`Loading monomer libraries is timeout ${timeout} ms.`);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
33
51
|
/** Protect constructor to prevent multiple instantiation. */
|
|
34
52
|
protected constructor() {}
|
|
35
53
|
|
|
@@ -40,6 +58,19 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
40
58
|
return this._monomerLib;
|
|
41
59
|
}
|
|
42
60
|
|
|
61
|
+
/** Instance promise of {@link getFileManager} */
|
|
62
|
+
private _fileManagerPromise?: Promise<MonomerLibFileManager>;
|
|
63
|
+
|
|
64
|
+
async getFileManager(): Promise<MonomerLibFileManager> {
|
|
65
|
+
if (this._fileManagerPromise === undefined) {
|
|
66
|
+
this._fileManagerPromise = (async () => {
|
|
67
|
+
const fileManager: MonomerLibFileManager = await MonomerLibFileManager.create(this, this._eventManager);
|
|
68
|
+
return fileManager;
|
|
69
|
+
})();
|
|
70
|
+
}
|
|
71
|
+
return this._fileManagerPromise;
|
|
72
|
+
}
|
|
73
|
+
|
|
43
74
|
/** Allows syncing with managing settings/loading libraries */
|
|
44
75
|
public loadLibrariesPromise: Promise<void> = Promise.resolve();
|
|
45
76
|
|
|
@@ -53,7 +84,7 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
53
84
|
// through blocking this.loadLibrariesPromise
|
|
54
85
|
try {
|
|
55
86
|
const [libFileNameList, settings]: [string[], UserLibSettings] = await Promise.all([
|
|
56
|
-
|
|
87
|
+
(await this.getFileManager()).getValidLibraryPaths(),
|
|
57
88
|
getUserLibSettings(),
|
|
58
89
|
]);
|
|
59
90
|
|
|
@@ -91,9 +122,8 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
91
122
|
* @return {Promise<IMonomerLib>} Promise of IMonomerLib
|
|
92
123
|
*/
|
|
93
124
|
async readLibrary(path: string, fileName: string): Promise<IMonomerLib> {
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
const lib: IMonomerLib = await libFileManager.loadLibraryFromFile(path, fileName);
|
|
125
|
+
const fileManager = await this.getFileManager();
|
|
126
|
+
const lib: IMonomerLib = await fileManager.loadLibraryFromFile(path, fileName);
|
|
97
127
|
return lib;
|
|
98
128
|
}
|
|
99
129
|
|
|
@@ -103,19 +133,27 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
103
133
|
if (invalidNames.length > 0)
|
|
104
134
|
throw new Error(`Cannot select libraries ${invalidNames}: no such library in the list`);
|
|
105
135
|
const settings = await getUserLibSettings();
|
|
106
|
-
settings.exclude = (await
|
|
136
|
+
settings.exclude = ((await this.getFileManager()).getValidLibraryPaths())
|
|
137
|
+
.filter((fileName) => !libFileNameList.includes(fileName));
|
|
107
138
|
await setUserLibSettings(settings);
|
|
108
139
|
}
|
|
109
140
|
|
|
110
141
|
private async getInvalidFileNames(libFileNameList: string[]): Promise<string[]> {
|
|
111
|
-
const availableFileNames = await
|
|
142
|
+
const availableFileNames = (await this.getFileManager()).getValidLibraryPaths();
|
|
112
143
|
const invalidNames = libFileNameList.filter((fileName) => !availableFileNames.includes(fileName));
|
|
113
144
|
return invalidNames;
|
|
114
145
|
}
|
|
115
146
|
|
|
116
147
|
// -- Instance singleton --
|
|
117
|
-
public static
|
|
118
|
-
|
|
119
|
-
|
|
148
|
+
public static async getInstance(): Promise<MonomerLibManager> {
|
|
149
|
+
let res = window.$monomerLibHelperPromise;
|
|
150
|
+
if (res === undefined) {
|
|
151
|
+
res = window.$monomerLibHelperPromise = (async () => {
|
|
152
|
+
const instance = new MonomerLibManager();
|
|
153
|
+
instance._eventManager = MonomerLibFileEventManager.getInstance();
|
|
154
|
+
return instance;
|
|
155
|
+
})();
|
|
156
|
+
}
|
|
157
|
+
return res;
|
|
120
158
|
}
|
|
121
159
|
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import * as
|
|
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 {BehaviorSubject, Observable, Subject} from 'rxjs';
|
|
2
6
|
import {debounceTime, tap, skip} from 'rxjs/operators';
|
|
3
7
|
|
|
4
|
-
|
|
8
|
+
import {IMonomerLibFileEventManager} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
9
|
+
|
|
10
|
+
export class MonomerLibFileEventManager implements IMonomerLibFileEventManager {
|
|
5
11
|
// WARNING: this must be a singleton because it manages the unique state
|
|
6
12
|
private constructor() {}
|
|
7
13
|
|
|
@@ -14,9 +20,9 @@ export class MonomerLibFileEventManager {
|
|
|
14
20
|
return MonomerLibFileEventManager._instance;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
|
-
private _libraryFilesUpdateSubject$ = new
|
|
18
|
-
private _addLibraryFilesSubject$ = new
|
|
19
|
-
private _librarySelectionSubject$ = new
|
|
23
|
+
private _libraryFilesUpdateSubject$ = new BehaviorSubject<string[]>([]);
|
|
24
|
+
private _addLibraryFilesSubject$ = new Subject<void>();
|
|
25
|
+
private _librarySelectionSubject$ = new Subject<[string, boolean]>();
|
|
20
26
|
|
|
21
27
|
getValidFilesPathList(): string[] {
|
|
22
28
|
return this._libraryFilesUpdateSubject$.getValue();
|
|
@@ -38,19 +44,19 @@ export class MonomerLibFileEventManager {
|
|
|
38
44
|
this._libraryFilesUpdateSubject$.next(newList);
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
get updateUIControlsRequested$():
|
|
47
|
+
get updateUIControlsRequested$(): Observable<string[]> {
|
|
42
48
|
return this._libraryFilesUpdateSubject$.pipe(
|
|
43
49
|
// debounceTime(1000)
|
|
44
50
|
);
|
|
45
51
|
}
|
|
46
52
|
|
|
47
|
-
get updateValidLibraryFileListRequested$():
|
|
53
|
+
get updateValidLibraryFileListRequested$(): Observable<string[]> {
|
|
48
54
|
return this._libraryFilesUpdateSubject$.pipe(
|
|
49
55
|
// debounceTime(3000)
|
|
50
56
|
);
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
get addLibraryFileRequested$():
|
|
59
|
+
get addLibraryFileRequested$(): Observable<void> {
|
|
54
60
|
return this._addLibraryFilesSubject$.pipe(
|
|
55
61
|
// debounceTime(1000)
|
|
56
62
|
);
|
|
@@ -60,7 +66,7 @@ export class MonomerLibFileEventManager {
|
|
|
60
66
|
this._addLibraryFilesSubject$.next();
|
|
61
67
|
}
|
|
62
68
|
|
|
63
|
-
get librarySelectionRequested$():
|
|
69
|
+
get librarySelectionRequested$(): Observable<[string, boolean]> {
|
|
64
70
|
return this._librarySelectionSubject$;
|
|
65
71
|
}
|
|
66
72
|
|
|
@@ -3,47 +3,56 @@ import * as grok from 'datagrok-api/grok';
|
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {JSONSchemaType} from 'ajv';
|
|
7
|
+
|
|
8
|
+
import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
|
|
7
9
|
import {LIB_PATH} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
8
|
-
import {MonomerLib} from '../monomer-lib';
|
|
9
10
|
import {
|
|
10
11
|
HELM_REQUIRED_FIELD as REQ,
|
|
11
12
|
} from '@datagrok-libraries/bio/src/utils/const';
|
|
12
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
IMonomerLibHelper, IMonomerLibFileManager
|
|
15
|
+
} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
16
|
+
|
|
17
|
+
import {MonomerLib} from '../monomer-lib';
|
|
13
18
|
import {HELM_JSON_SCHEMA_PATH} from './consts';
|
|
14
19
|
import {MonomerLibFileEventManager} from './event-manager';
|
|
15
|
-
|
|
16
20
|
import {MonomerLibFileValidator} from './file-validator';
|
|
17
|
-
|
|
21
|
+
|
|
22
|
+
import {_package} from '../../../package';
|
|
23
|
+
|
|
18
24
|
|
|
19
25
|
/** Singleton for adding, validation and reading of monomer library files.
|
|
20
26
|
* All files **must** be aligned to the HELM standard before adding. */
|
|
21
|
-
export class MonomerLibFileManager {
|
|
27
|
+
export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
28
|
+
public filesPromise: Promise<void> = Promise.resolve();
|
|
29
|
+
|
|
22
30
|
private constructor(
|
|
23
|
-
private
|
|
24
|
-
private
|
|
31
|
+
private readonly fileValidator: MonomerLibFileValidator,
|
|
32
|
+
private readonly libHelper: IMonomerLibHelper,
|
|
33
|
+
public readonly eventManager: MonomerLibFileEventManager,
|
|
25
34
|
) {
|
|
26
|
-
this.
|
|
35
|
+
this.eventManager.updateValidLibraryFileListRequested$.subscribe(async () => {
|
|
27
36
|
await this.updateValidLibraryList();
|
|
28
37
|
});
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
private static
|
|
40
|
+
private static objCounter: number = -1;
|
|
41
|
+
private readonly objId: number = ++MonomerLibFileManager.objCounter;
|
|
42
|
+
|
|
43
|
+
protected toLog(): string {
|
|
44
|
+
return `MonomerLibFileManager<${this.objId}>`;
|
|
45
|
+
}
|
|
32
46
|
|
|
33
|
-
|
|
34
|
-
|
|
47
|
+
/** For internal use only, get {@link IMonomerLibHelper.getFileManager} */
|
|
48
|
+
public static async create(
|
|
49
|
+
libHelper: IMonomerLibHelper, eventManager: MonomerLibFileEventManager
|
|
35
50
|
): Promise<MonomerLibFileManager> {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const helmSchemaRaw = await grok.dapi.files.readAsText(HELM_JSON_SCHEMA_PATH);
|
|
39
|
-
const helmSchema = JSON.parse(helmSchemaRaw) as JSONSchemaType<any>;
|
|
40
|
-
|
|
41
|
-
const fileValidator = new MonomerLibFileValidator(helmSchema);
|
|
42
|
-
return new MonomerLibFileManager(fileValidator, libraryEventManager);
|
|
43
|
-
})();
|
|
44
|
-
}
|
|
51
|
+
const helmSchemaRaw = await grok.dapi.files.readAsText(HELM_JSON_SCHEMA_PATH);
|
|
52
|
+
const helmSchema = JSON.parse(helmSchemaRaw) as JSONSchemaType<any>;
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
const fileValidator = new MonomerLibFileValidator(helmSchema);
|
|
55
|
+
return new MonomerLibFileManager(fileValidator, libHelper, eventManager);
|
|
47
56
|
}
|
|
48
57
|
|
|
49
58
|
/** Add standard .json monomer library */
|
|
@@ -91,72 +100,82 @@ export class MonomerLibFileManager {
|
|
|
91
100
|
const monomers: { [polymerType: string]: { [monomerSymbol: string]: Monomer } } = {};
|
|
92
101
|
const polymerTypes: string[] = [];
|
|
93
102
|
rawLibData.forEach((monomer) => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
103
|
+
const polymerType = monomer[REQ.POLYMER_TYPE];
|
|
104
|
+
const monomerSymbol = monomer[REQ.SYMBOL];
|
|
105
|
+
if (!polymerTypes.includes(polymerType)) {
|
|
106
|
+
monomers[polymerType] = {};
|
|
107
|
+
polymerTypes.push(polymerType);
|
|
97
108
|
}
|
|
98
|
-
monomers[
|
|
109
|
+
monomers[polymerType][monomerSymbol] = monomer as Monomer;
|
|
99
110
|
});
|
|
100
111
|
|
|
101
112
|
return new MonomerLib(monomers, fileName);
|
|
102
113
|
}
|
|
103
114
|
|
|
104
115
|
getValidLibraryPaths(): string[] {
|
|
105
|
-
return this.
|
|
116
|
+
return this.eventManager.getValidFilesPathList();
|
|
106
117
|
}
|
|
107
118
|
|
|
108
119
|
// TODO: remove after adding init from user data storage
|
|
109
120
|
// WARNING: a temporary solution
|
|
110
121
|
async getValidLibraryPathsAsynchronously(): Promise<string[]> {
|
|
111
|
-
return await this.
|
|
122
|
+
return await this.eventManager.getValidLibraryPathsAsynchronously();
|
|
112
123
|
}
|
|
113
124
|
|
|
114
|
-
|
|
115
125
|
private async libraryFileExists(fileName: string): Promise<boolean> {
|
|
116
126
|
return await grok.dapi.files.exists(LIB_PATH + `${fileName}`);
|
|
117
127
|
}
|
|
118
128
|
|
|
119
129
|
private async updateValidLibraryList(): Promise<void> {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
130
|
+
const logPrefix: string = `${this.toLog()}.updateValidLibraryList()`;
|
|
131
|
+
_package.logger.debug(`${logPrefix}, start`);
|
|
132
|
+
return this.filesPromise = this.filesPromise.then(async () => {
|
|
133
|
+
_package.logger.debug(`${logPrefix}, IN`);
|
|
134
|
+
const invalidFiles = [] as string[];
|
|
135
|
+
// console.log(`files before validation:`, this.libraryEventManager.getValidFilesPathList());
|
|
136
|
+
const filePaths = await this.getFilePathsAtDefaultLocation();
|
|
137
|
+
|
|
138
|
+
if (!this.fileListHasChanged(filePaths)) {
|
|
139
|
+
_package.logger.debug(`${logPrefix}, end, not changed`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
123
142
|
|
|
124
|
-
|
|
125
|
-
|
|
143
|
+
for (const path of filePaths) {
|
|
144
|
+
if (!path.endsWith('.json')) {
|
|
145
|
+
invalidFiles.push(path);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
126
148
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
continue;
|
|
149
|
+
const fileContent = await grok.dapi.files.readAsText(LIB_PATH + `${path}`);
|
|
150
|
+
if (!this.isValidHELMLibrary(fileContent, path))
|
|
151
|
+
invalidFiles.push(path);
|
|
131
152
|
}
|
|
132
153
|
|
|
133
|
-
const
|
|
134
|
-
if (!this.isValidHELMLibrary(fileContent, path))
|
|
135
|
-
invalidFiles.push(path);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const validLibraryPaths = filePaths.filter((path) => !invalidFiles.includes(path));
|
|
154
|
+
const validLibraryPaths = filePaths.filter((path) => !invalidFiles.includes(path));
|
|
139
155
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
156
|
+
if (this.fileListHasChanged(validLibraryPaths)) {
|
|
157
|
+
this.eventManager.changeValidFilesPathList(validLibraryPaths);
|
|
158
|
+
this.libHelper.loadLibraries(true);
|
|
159
|
+
}
|
|
160
|
+
// console.log(`files after validation:`, this.libraryEventManager.getValidFilesPathList());
|
|
145
161
|
|
|
146
|
-
|
|
147
|
-
|
|
162
|
+
if (validLibraryPaths.some((el) => !el.endsWith('.json')))
|
|
163
|
+
_package.logger.warning(`Wrong validation: ${validLibraryPaths}`);
|
|
148
164
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
165
|
+
if (invalidFiles.length > 0) {
|
|
166
|
+
const message = `Invalid monomer library files in ${LIB_PATH}` +
|
|
167
|
+
`, consider fixing or removing them: ${invalidFiles.join(', ')}`;
|
|
152
168
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
169
|
+
_package.logger.warning(message);
|
|
170
|
+
// grok.shell.warning(message);
|
|
171
|
+
}
|
|
172
|
+
_package.logger.debug(`${logPrefix}, OUT`);
|
|
173
|
+
});
|
|
174
|
+
_package.logger.debug(`${logPrefix}, end`);
|
|
156
175
|
}
|
|
157
176
|
|
|
158
177
|
private fileListHasChanged(newList: string[]): boolean {
|
|
159
|
-
const currentList = this.
|
|
178
|
+
const currentList = this.eventManager.getValidFilesPathList();
|
|
160
179
|
return newList.length !== currentList.length || newList.some((el, i) => el !== currentList[i]);
|
|
161
180
|
}
|
|
162
181
|
|
|
@@ -167,7 +186,7 @@ export class MonomerLibFileManager {
|
|
|
167
186
|
}
|
|
168
187
|
|
|
169
188
|
private isValidHELMLibrary(fileContent: string, fileName: string): boolean {
|
|
170
|
-
return this.
|
|
189
|
+
return this.fileValidator.validateFile(fileContent, fileName);
|
|
171
190
|
}
|
|
172
191
|
|
|
173
192
|
/** Get relative paths for files in LIB_PATH */
|
|
@@ -5,7 +5,9 @@ import addErrors from 'ajv-errors';
|
|
|
5
5
|
export class MonomerLibFileValidator {
|
|
6
6
|
private validateMonomerSchema: ValidateFunction<any>;
|
|
7
7
|
|
|
8
|
-
constructor(
|
|
8
|
+
constructor(
|
|
9
|
+
private helmMonomerSchema: JSONSchemaType<any>
|
|
10
|
+
) {
|
|
9
11
|
const ajv = new Ajv2020({allErrors: true, strictTuples: false});
|
|
10
12
|
addErrors(ajv);
|
|
11
13
|
this.validateMonomerSchema = ajv.compile(this.helmMonomerSchema);
|