@datagrok/bio 2.13.8 → 2.14.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/.eslintrc.json +10 -2
- package/CHANGELOG.md +15 -0
- package/dist/248.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/files/monomer-sets/monomer-set-PEPTIDE.json +93 -0
- package/files/monomer-sets/monomer-set-RNA.json +29 -0
- package/files/schemas/monomer-set-schema.json +48 -0
- package/package.json +5 -5
- package/src/analysis/sequence-similarity-viewer.ts +5 -2
- package/src/demo/bio01-similarity-diversity.ts +3 -2
- package/src/demo/bio01b-hierarchical-clustering-and-activity-cliffs.ts +1 -1
- package/src/package.ts +12 -7
- package/src/tests/activity-cliffs-tests.ts +2 -2
- package/src/tests/detectors-tests.ts +14 -7
- package/src/tests/monomer-libraries-tests.ts +6 -6
- package/src/tests/renderers-monomer-placer-tests.ts +2 -2
- package/src/tests/scoring.ts +2 -2
- package/src/tests/substructure-filters-tests.ts +2 -2
- package/src/tests/to-atomic-level-tests.ts +3 -3
- package/src/utils/helm-to-molfile/converter/converter.ts +1 -1
- package/src/utils/monomer-lib/lib-manager.ts +118 -21
- package/src/utils/monomer-lib/library-file-manager/event-manager.ts +25 -8
- package/src/utils/monomer-lib/library-file-manager/file-manager.ts +122 -35
- package/src/utils/monomer-lib/library-file-manager/ui.ts +1 -1
- package/src/utils/monomer-lib/monomer-lib.ts +23 -6
- package/src/utils/monomer-lib/monomer-set.ts +61 -0
- package/src/widgets/bio-substructure-filter-helm.ts +2 -2
- package/src/widgets/bio-substructure-filter.ts +19 -14
- package/webpack.config.js +3 -2
|
@@ -5,9 +5,9 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
|
|
6
6
|
import {delay} from '@datagrok-libraries/utils/src/test';
|
|
7
7
|
import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
|
|
8
|
-
import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
|
|
8
|
+
import {IMonomerLib, IMonomerSet} from '@datagrok-libraries/bio/src/types';
|
|
9
9
|
import {
|
|
10
|
-
getUserLibSettings, setUserLibSettings, LIB_PATH
|
|
10
|
+
getUserLibSettings, setUserLibSettings, LIB_PATH, SETS_PATH
|
|
11
11
|
} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
12
12
|
import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
|
|
13
13
|
import {
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
16
16
|
|
|
17
17
|
import {MonomerLib} from './monomer-lib';
|
|
18
|
+
import {MonomerSet} from './monomer-set';
|
|
18
19
|
import {MonomerLibFileManager} from './library-file-manager/file-manager';
|
|
19
20
|
import {MonomerLibFileEventManager} from './library-file-manager/event-manager';
|
|
20
21
|
|
|
@@ -27,6 +28,7 @@ declare const window: MonomerLibWindowType;
|
|
|
27
28
|
* the platform */
|
|
28
29
|
export class MonomerLibManager implements IMonomerLibHelper {
|
|
29
30
|
private readonly _monomerLib = new MonomerLib({}, 'MAIN');
|
|
31
|
+
private readonly _monomerSets = new MonomerSet('MAIN', []);
|
|
30
32
|
|
|
31
33
|
private _eventManager: MonomerLibFileEventManager;
|
|
32
34
|
|
|
@@ -54,13 +56,33 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
54
56
|
private readonly logger: ILogger,
|
|
55
57
|
) {}
|
|
56
58
|
|
|
59
|
+
private static objCounter: number = -1;
|
|
60
|
+
private readonly objId: number = (() => {
|
|
61
|
+
if (++MonomerLibManager.objCounter > 0)
|
|
62
|
+
throw new Error('MonomerLibManager MUST be a singleton.');
|
|
63
|
+
return MonomerLibManager.objCounter;
|
|
64
|
+
})();
|
|
65
|
+
|
|
66
|
+
protected toLog(): string {
|
|
67
|
+
return `MonomerLibManager<${this.objId}>`;
|
|
68
|
+
}
|
|
69
|
+
|
|
57
70
|
/** Singleton monomer library
|
|
58
71
|
* @return {MonomerLibManager} MonomerLibHelper instance
|
|
59
72
|
*/
|
|
60
|
-
|
|
73
|
+
getMonomerLib(): IMonomerLib {
|
|
61
74
|
return this._monomerLib;
|
|
62
75
|
}
|
|
63
76
|
|
|
77
|
+
/** @deprecated Use {@link v} */
|
|
78
|
+
getBioLib(): IMonomerLib {
|
|
79
|
+
return this.getMonomerLib();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getMonomerSets(): IMonomerSet {
|
|
83
|
+
return this._monomerSets;
|
|
84
|
+
}
|
|
85
|
+
|
|
64
86
|
/** Instance promise of {@link getFileManager} */
|
|
65
87
|
private _fileManagerPromise?: Promise<MonomerLibFileManager>;
|
|
66
88
|
|
|
@@ -81,17 +103,21 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
81
103
|
/** Loads libraries based on settings in user storage {@link LIB_STORAGE_NAME}
|
|
82
104
|
* @param {boolean} reload Clean {@link monomerLib} before load libraries [false]
|
|
83
105
|
*/
|
|
84
|
-
async
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
106
|
+
async loadMonomerLib(reload: boolean = false): Promise<void> {
|
|
107
|
+
const logPrefix = `${this.toLog()}.loadMonomerLib()`;
|
|
108
|
+
this.logger.debug(`${logPrefix}, start`);
|
|
109
|
+
this.loadLibrariesPromise = this.loadLibrariesPromise.then(async () => {
|
|
110
|
+
this.logger.debug(`${logPrefix}, IN`);
|
|
111
|
+
|
|
89
112
|
const pi = DG.TaskBarProgressIndicator.create('Loading monomers ...');
|
|
90
113
|
try {
|
|
91
|
-
const [libFileNameList, settings]: [string[], UserLibSettings] =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
114
|
+
const [[libFileNameList,], settings]: [[string[],], UserLibSettings] =
|
|
115
|
+
await Promise.all([
|
|
116
|
+
await this.getFileManager().then((fileManager) => {
|
|
117
|
+
return [fileManager.getValidLibraryPaths(),];
|
|
118
|
+
}),
|
|
119
|
+
getUserLibSettings(),
|
|
120
|
+
]);
|
|
95
121
|
|
|
96
122
|
const filteredLibFnList = libFileNameList.filter((libFileName) => {
|
|
97
123
|
const isFileIncluded = !settings.exclude.includes(libFileName);
|
|
@@ -100,32 +126,97 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
100
126
|
return isFileIncluded && isExplicit;
|
|
101
127
|
});
|
|
102
128
|
|
|
103
|
-
let
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
129
|
+
let completedFileCount: number = 0;
|
|
130
|
+
const allCount: number = filteredLibFnList.length;
|
|
131
|
+
const [libs,]: [IMonomerLib[],] = await Promise.all([
|
|
132
|
+
Promise.all(filteredLibFnList.map((libFileName) => {
|
|
107
133
|
return this.readLibrary(LIB_PATH, libFileName)
|
|
108
134
|
.catch((err: any) => {
|
|
109
135
|
const errMsg: string = `Loading monomers from '${libFileName}' error: ` +
|
|
110
136
|
`${err instanceof Error ? err.message : err.toString()}`;
|
|
111
137
|
return new MonomerLib({}, libFileName, errMsg);
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
|
|
138
|
+
})
|
|
139
|
+
.finally(() => {
|
|
140
|
+
pi.update(Math.round(100 * (++completedFileCount) / allCount),
|
|
141
|
+
`Loading monomers ${completedFileCount}/${allCount}`);
|
|
115
142
|
});
|
|
116
|
-
}));
|
|
143
|
+
})),]);
|
|
117
144
|
this._monomerLib.updateLibs(libs, reload);
|
|
118
145
|
} catch (err: any) {
|
|
146
|
+
// WARNING: This function is not allowed to throw any exception,
|
|
147
|
+
// because it will prevent further handling monomer library settings
|
|
148
|
+
// through blocking this.loadLibrariesPromise
|
|
149
|
+
|
|
119
150
|
const errMsg: string = 'Loading monomer libraries error: ' +
|
|
120
151
|
`${err instanceof Error ? err.message : err.toString()}`;
|
|
121
152
|
grok.shell.warning(errMsg);
|
|
122
153
|
|
|
123
154
|
const errStack = err instanceof Error ? err.stack : undefined;
|
|
124
|
-
|
|
155
|
+
this.logger.error(errMsg, undefined, errStack);
|
|
125
156
|
} finally {
|
|
126
157
|
pi.close();
|
|
158
|
+
this.logger.debug(`${logPrefix}, OUT`);
|
|
127
159
|
}
|
|
128
160
|
});
|
|
161
|
+
this.logger.debug(`${logPrefix}, end`);
|
|
162
|
+
return this.loadLibrariesPromise;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** @deprecated Use {@link loadMonomerLib} */
|
|
166
|
+
async loadLibraries(reload?: boolean): Promise<void> {
|
|
167
|
+
return this.loadMonomerLib(reload);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private loadSetsPromise: Promise<void> = Promise.resolve();
|
|
171
|
+
|
|
172
|
+
async loadMonomerSets(reload: boolean = false): Promise<void> {
|
|
173
|
+
const logPrefix = `${this.toLog()}.loadMonomerSets()`;
|
|
174
|
+
|
|
175
|
+
this.logger.debug(`${logPrefix}, start`);
|
|
176
|
+
this.loadSetsPromise = this.loadSetsPromise.then(async () => {
|
|
177
|
+
this.logger.debug(`${logPrefix}, IN`);
|
|
178
|
+
const pi = DG.TaskBarProgressIndicator.create(`Loading monomer sets ...`);
|
|
179
|
+
try {
|
|
180
|
+
const [[setFileNameList,]]: [[string[],],] = await Promise.all([
|
|
181
|
+
await this.getFileManager().then((fileManager) => {
|
|
182
|
+
return [fileManager.getValidSetPaths(),];
|
|
183
|
+
})
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
// TODO: Filter for settings
|
|
187
|
+
const filteredSetFnList = setFileNameList.filter((setFileName) => true);
|
|
188
|
+
|
|
189
|
+
let completedFileCount: number = 0;
|
|
190
|
+
const allCount: number = filteredSetFnList.length;
|
|
191
|
+
const [sets,]: [IMonomerSet[],] = await Promise.all([
|
|
192
|
+
Promise.all(filteredSetFnList.map((setFileName) => {
|
|
193
|
+
// TODO: handle whether files are in place
|
|
194
|
+
return this.readSet(SETS_PATH, setFileName)
|
|
195
|
+
.catch((err: any) => {
|
|
196
|
+
const errMsg: string = `Loading monomer sets from '${setFileName}' error: ` +
|
|
197
|
+
`${err instanceof Error ? err.message : err.toString()}`;
|
|
198
|
+
return new MonomerSet('Broken monomer set', [], setFileName, errMsg);
|
|
199
|
+
})
|
|
200
|
+
.finally(() => {
|
|
201
|
+
pi.update(Math.round(100 * (++completedFileCount) / allCount),
|
|
202
|
+
`Loading monomers ${completedFileCount}/${allCount}`);
|
|
203
|
+
});
|
|
204
|
+
})),]);
|
|
205
|
+
this._monomerSets.updateSets(sets);
|
|
206
|
+
} catch (err: any) {
|
|
207
|
+
const errMsg: string = 'Loading monomer sets error: ' +
|
|
208
|
+
`${err instanceof Error ? err.message : err.toString()}`;
|
|
209
|
+
grok.shell.warning(errMsg);
|
|
210
|
+
|
|
211
|
+
const errStack = err instanceof Error ? err.stack : undefined;
|
|
212
|
+
this.logger.error(errMsg, undefined, errStack);
|
|
213
|
+
} finally {
|
|
214
|
+
pi.close();
|
|
215
|
+
this.logger.debug(`${logPrefix}, OUT`);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
this.logger.debug(`${logPrefix}, end`);
|
|
219
|
+
return this.loadSetsPromise;
|
|
129
220
|
}
|
|
130
221
|
|
|
131
222
|
/** Reads library from file shares, handles .json and .sdf
|
|
@@ -139,6 +230,12 @@ export class MonomerLibManager implements IMonomerLibHelper {
|
|
|
139
230
|
return lib;
|
|
140
231
|
}
|
|
141
232
|
|
|
233
|
+
async readSet(path: string, fileName: string): Promise<IMonomerSet> {
|
|
234
|
+
const fileManager = await this.getFileManager();
|
|
235
|
+
const set: IMonomerSet = await fileManager.loadSetFromFile(this._monomerLib, path, fileName);
|
|
236
|
+
return set;
|
|
237
|
+
}
|
|
238
|
+
|
|
142
239
|
/** Reset user settings to the specified library. WARNING: clears user * settings */
|
|
143
240
|
public async selectSpecifiedLibraries(libFileNameList: string[]): Promise<void> {
|
|
144
241
|
const invalidNames = await this.getInvalidFileNames(libFileNameList);
|
|
@@ -20,42 +20,59 @@ export class MonomerLibFileEventManager implements IMonomerLibFileEventManager {
|
|
|
20
20
|
return MonomerLibFileEventManager._instance;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
private
|
|
23
|
+
private _libFilesUpdateSubject$ = new BehaviorSubject<string[]>([]);
|
|
24
|
+
private _setFilesUpdateSubject$ = new BehaviorSubject<string[]>([]);
|
|
25
|
+
|
|
24
26
|
private _addLibraryFilesSubject$ = new Subject<void>();
|
|
25
27
|
private _librarySelectionSubject$ = new Subject<[string, boolean]>();
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
return this.
|
|
29
|
+
getValidLibPathList(): string[] {
|
|
30
|
+
return this._libFilesUpdateSubject$.getValue();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getValidSetPathList(): string[] {
|
|
34
|
+
return this._setFilesUpdateSubject$.getValue();
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
// TODO: remove after adding init from user data storage
|
|
32
38
|
// WARNING: a temporary solution
|
|
33
39
|
async getValidLibraryPathsAsynchronously(): Promise<string[]> {
|
|
34
40
|
return new Promise((resolve) => {
|
|
35
|
-
this.
|
|
41
|
+
const sub = this._libFilesUpdateSubject$.pipe<string[]>(
|
|
36
42
|
skip(1)
|
|
37
43
|
).subscribe((fileNames) => {
|
|
38
44
|
resolve(fileNames);
|
|
45
|
+
sub.unsubscribe();
|
|
39
46
|
});
|
|
40
47
|
});
|
|
41
48
|
}
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
this.
|
|
50
|
+
changeValidLibPathList(newLibPathList: string[]): void {
|
|
51
|
+
this._libFilesUpdateSubject$.next(newLibPathList);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
changeValidSetPathList(newSetPathList: string[]): void {
|
|
55
|
+
this._setFilesUpdateSubject$.next(newSetPathList);
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
get updateUIControlsRequested$(): Observable<string[]> {
|
|
48
|
-
return this.
|
|
59
|
+
return this._libFilesUpdateSubject$.pipe(
|
|
49
60
|
// debounceTime(1000)
|
|
50
61
|
);
|
|
51
62
|
}
|
|
52
63
|
|
|
53
64
|
get updateValidLibraryFileListRequested$(): Observable<string[]> {
|
|
54
|
-
return this.
|
|
65
|
+
return this._libFilesUpdateSubject$.pipe(
|
|
55
66
|
// debounceTime(3000)
|
|
56
67
|
);
|
|
57
68
|
}
|
|
58
69
|
|
|
70
|
+
get updateValidSetFileListRequested$(): Observable<string[]> {
|
|
71
|
+
return this._setFilesUpdateSubject$.pipe(
|
|
72
|
+
// debounceTime(1000)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
get addLibraryFileRequested$(): Observable<void> {
|
|
60
77
|
return this._addLibraryFilesSubject$.pipe(
|
|
61
78
|
// debounceTime(1000)
|
|
@@ -5,9 +5,10 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
|
|
6
6
|
import {JSONSchemaType} from 'ajv';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {MonomerType, PolymerType} from '@datagrok-libraries/bio/src/helm/types';
|
|
9
|
+
import {IMonomerLib, IMonomerLinkData, IMonomerSet, Monomer} from '@datagrok-libraries/bio/src/types';
|
|
9
10
|
import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
|
|
10
|
-
import {LIB_PATH} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
11
|
+
import {LIB_PATH, SETS_PATH} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
11
12
|
import {
|
|
12
13
|
HELM_REQUIRED_FIELD as REQ,
|
|
13
14
|
} from '@datagrok-libraries/bio/src/utils/const';
|
|
@@ -19,6 +20,7 @@ import {MonomerLib} from '../monomer-lib';
|
|
|
19
20
|
import {HELM_JSON_SCHEMA_PATH} from './consts';
|
|
20
21
|
import {MonomerLibFileEventManager} from './event-manager';
|
|
21
22
|
import {MonomerLibFileValidator} from './file-validator';
|
|
23
|
+
import {MonomerSet, MonomerSetPlaceholder} from '../monomer-set';
|
|
22
24
|
|
|
23
25
|
import {_package} from '../../../package';
|
|
24
26
|
|
|
@@ -34,8 +36,11 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
|
34
36
|
public readonly eventManager: MonomerLibFileEventManager,
|
|
35
37
|
private readonly logger: ILogger,
|
|
36
38
|
) {
|
|
37
|
-
this.eventManager.updateValidLibraryFileListRequested$.subscribe(
|
|
38
|
-
|
|
39
|
+
const _libSub = this.eventManager.updateValidLibraryFileListRequested$.subscribe(() => {
|
|
40
|
+
this.updateValidLibList().then(() => {});
|
|
41
|
+
});
|
|
42
|
+
const _setSub = this.eventManager.updateValidSetFileListRequested$.subscribe(() => {
|
|
43
|
+
this.updateValidSetList().then(() => {});
|
|
39
44
|
});
|
|
40
45
|
}
|
|
41
46
|
|
|
@@ -68,7 +73,7 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
|
68
73
|
|
|
69
74
|
await this.validateAgainstHELM(fileContent, fileName);
|
|
70
75
|
await grok.dapi.files.writeAsText(LIB_PATH + `${fileName}`, fileContent);
|
|
71
|
-
await this.
|
|
76
|
+
await this.updateValidLibList();
|
|
72
77
|
const fileExists = await grok.dapi.files.exists(LIB_PATH + `${fileName}`);
|
|
73
78
|
if (!fileExists)
|
|
74
79
|
grok.shell.error(`Failed to add ${fileName} library`);
|
|
@@ -83,7 +88,7 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
|
83
88
|
async deleteLibraryFile(fileName: string): Promise<void> {
|
|
84
89
|
try {
|
|
85
90
|
await grok.dapi.files.delete(LIB_PATH + `${fileName}`);
|
|
86
|
-
await this.
|
|
91
|
+
await this.updateValidLibList();
|
|
87
92
|
grok.shell.info(`Deleted ${fileName} library`);
|
|
88
93
|
} catch (e) {
|
|
89
94
|
console.error(e);
|
|
@@ -115,8 +120,31 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
|
115
120
|
return new MonomerLib(monomers, fileName);
|
|
116
121
|
}
|
|
117
122
|
|
|
123
|
+
async loadSetFromFile(monomerLib: IMonomerLib, path: string, fileName: string): Promise<IMonomerSet> {
|
|
124
|
+
let raw: any = {};
|
|
125
|
+
const fileSource = new DG.FileSource(path);
|
|
126
|
+
const content = await fileSource.readAsText(fileName);
|
|
127
|
+
raw = JSON.parse(content);
|
|
128
|
+
|
|
129
|
+
const description = raw['description'];
|
|
130
|
+
const placeholders = Object.entries(raw['placeholders']).map(([k, v]: [string, any]) => {
|
|
131
|
+
const placeholderSymbol = k;
|
|
132
|
+
const polymerType = v['polymerType'] as PolymerType;
|
|
133
|
+
const monomerType = v['monomerType'] as MonomerType;
|
|
134
|
+
const monomerLinks = v['set'] as IMonomerLinkData[];
|
|
135
|
+
|
|
136
|
+
return new MonomerSetPlaceholder(monomerLib, placeholderSymbol, polymerType, monomerType, monomerLinks);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return new MonomerSet(description, placeholders);
|
|
140
|
+
}
|
|
141
|
+
|
|
118
142
|
getValidLibraryPaths(): string[] {
|
|
119
|
-
return this.eventManager.
|
|
143
|
+
return this.eventManager.getValidLibPathList();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
getValidSetPaths(): string[] {
|
|
147
|
+
return this.eventManager.getValidSetPathList();
|
|
120
148
|
}
|
|
121
149
|
|
|
122
150
|
// TODO: remove after adding init from user data storage
|
|
@@ -125,21 +153,21 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
|
125
153
|
return await this.eventManager.getValidLibraryPathsAsynchronously();
|
|
126
154
|
}
|
|
127
155
|
|
|
128
|
-
private async
|
|
129
|
-
const logPrefix: string = `${this.toLog()}.
|
|
156
|
+
private async updateValidLibList(): Promise<void> {
|
|
157
|
+
const logPrefix: string = `${this.toLog()}.updateValidLibList()`;
|
|
130
158
|
this.logger.debug(`${logPrefix}, start`);
|
|
131
159
|
this.filesPromise = this.filesPromise.then(async () => {
|
|
132
160
|
this.logger.debug(`${logPrefix}, IN`);
|
|
133
161
|
const invalidFiles = [] as string[];
|
|
134
162
|
// console.log(`files before validation:`, this.libraryEventManager.getValidFilesPathList());
|
|
135
|
-
const
|
|
163
|
+
const libPathList = await this.getLibFileListAtLocation();
|
|
136
164
|
|
|
137
|
-
if (!this.
|
|
165
|
+
if (!this.libListHasChanged(libPathList)) {
|
|
138
166
|
this.logger.debug(`${logPrefix}, end, not changed`);
|
|
139
167
|
return;
|
|
140
168
|
}
|
|
141
169
|
|
|
142
|
-
for (const path of
|
|
170
|
+
for (const path of libPathList) {
|
|
143
171
|
if (!path.endsWith('.json')) {
|
|
144
172
|
invalidFiles.push(path);
|
|
145
173
|
continue;
|
|
@@ -150,16 +178,16 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
|
150
178
|
invalidFiles.push(path);
|
|
151
179
|
}
|
|
152
180
|
|
|
153
|
-
const
|
|
181
|
+
const validLibPathList = libPathList.filter((path) => !invalidFiles.includes(path));
|
|
154
182
|
|
|
155
|
-
if (this.
|
|
156
|
-
this.eventManager.
|
|
157
|
-
this.libHelper.
|
|
183
|
+
if (this.libListHasChanged(validLibPathList)) {
|
|
184
|
+
this.eventManager.changeValidLibPathList(validLibPathList);
|
|
185
|
+
this.libHelper.loadMonomerLib(true);
|
|
158
186
|
}
|
|
159
187
|
// console.log(`files after validation:`, this.libraryEventManager.getValidFilesPathList());
|
|
160
188
|
|
|
161
|
-
if (
|
|
162
|
-
this.logger.warning(`Wrong validation: ${
|
|
189
|
+
if (validLibPathList.some((el) => !el.endsWith('.json')))
|
|
190
|
+
this.logger.warning(`Wrong validation: ${validLibPathList}`);
|
|
163
191
|
|
|
164
192
|
if (invalidFiles.length > 0) {
|
|
165
193
|
const message = `Invalid monomer library files in ${LIB_PATH}` +
|
|
@@ -174,8 +202,50 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
|
174
202
|
return this.filesPromise;
|
|
175
203
|
}
|
|
176
204
|
|
|
177
|
-
private
|
|
178
|
-
const
|
|
205
|
+
private async updateValidSetList(): Promise<void> {
|
|
206
|
+
const logPrefix: string = `${this.toLog()}.updateValidSetList()`;
|
|
207
|
+
_package.logger.debug(`${logPrefix}, start`);
|
|
208
|
+
this.filesPromise = this.filesPromise.then(async () => {
|
|
209
|
+
_package.logger.debug(`${logPrefix}, IN`);
|
|
210
|
+
const invalidFiles = [] as string[];
|
|
211
|
+
const setPathList: string[] = await this.getSetFileListAtLocation();
|
|
212
|
+
|
|
213
|
+
if (!this.setListHasChanged(setPathList)) {
|
|
214
|
+
_package.logger.debug(`${logPrefix}, end, not changed`);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
for (const path of setPathList) {
|
|
219
|
+
if (!path.endsWith('.json')) {
|
|
220
|
+
invalidFiles.push(path);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const fileContent = await grok.dapi.files.readAsText(SETS_PATH + `${path}`);
|
|
225
|
+
// TODO: Validate monomer set
|
|
226
|
+
// if (!this.isValidMonomerSet(fileContent, path))
|
|
227
|
+
// invalidFiles.push(path);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const validSetPathList = setPathList.filter((path) => !invalidFiles.includes(path));
|
|
231
|
+
if (this.setListHasChanged(validSetPathList)) {
|
|
232
|
+
this.eventManager.changeValidSetPathList(validSetPathList);
|
|
233
|
+
this.libHelper.loadMonomerSets(true);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
_package.logger.debug(`${logPrefix}, OUT`);
|
|
237
|
+
});
|
|
238
|
+
_package.logger.debug(`${logPrefix}, end`);
|
|
239
|
+
return this.filesPromise;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private libListHasChanged(newList: string[]): boolean {
|
|
243
|
+
const currentList = this.eventManager.getValidLibPathList();
|
|
244
|
+
return newList.length !== currentList.length || newList.some((el, i) => el !== currentList[i]);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private setListHasChanged(newList: string[]): boolean {
|
|
248
|
+
const currentList = this.eventManager.getValidSetPathList();
|
|
179
249
|
return newList.length !== currentList.length || newList.some((el, i) => el !== currentList[i]);
|
|
180
250
|
}
|
|
181
251
|
|
|
@@ -189,31 +259,48 @@ export class MonomerLibFileManager implements IMonomerLibFileManager {
|
|
|
189
259
|
return this.fileValidator.validateFile(fileContent, fileName);
|
|
190
260
|
}
|
|
191
261
|
|
|
192
|
-
/** Get relative paths for files in LIB_PATH */
|
|
193
|
-
private async
|
|
194
|
-
const logPrefix = `${this.toLog()}.
|
|
262
|
+
/** Get relative paths for files in LIB_PATH, SET_PATH */
|
|
263
|
+
private async getLibFileListAtLocation(): Promise<string[]> {
|
|
264
|
+
const logPrefix = `${this.toLog()}.getLibFileListAtLocation()`;
|
|
195
265
|
this.logger.debug(`${logPrefix}, start`);
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
});
|
|
266
|
+
|
|
267
|
+
const libPaths = await grok.dapi.files.list(LIB_PATH)
|
|
268
|
+
.then((l) => l.map((fi) => fi.fullPath));
|
|
200
269
|
|
|
201
270
|
const checkForUi = false;
|
|
202
|
-
const
|
|
271
|
+
const existingLibPaths: string[] = [];
|
|
203
272
|
if (checkForUi) {
|
|
204
273
|
// WARNING: an extra sanity check,
|
|
205
274
|
// caused by unexpected behavior of grok.dapi.files.list() when it returns non-existent paths
|
|
206
|
-
for (const path of
|
|
275
|
+
for (const path of libPaths) {
|
|
207
276
|
const exists = await grok.dapi.files.exists(path);
|
|
208
277
|
if (exists)
|
|
209
|
-
|
|
278
|
+
existingLibPaths.push(path);
|
|
210
279
|
}
|
|
211
280
|
} else
|
|
212
|
-
|
|
281
|
+
existingLibPaths.push(...libPaths);
|
|
213
282
|
|
|
214
|
-
return
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
283
|
+
return existingLibPaths.map((p) => /* relative to LIB_PATH */ p.substring(LIB_PATH.length));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private async getSetFileListAtLocation(): Promise<string[]> {
|
|
287
|
+
const logPrefix = `${this.toLog()}.getSetFileListAtLocation()`;
|
|
288
|
+
this.logger.debug(`${logPrefix}, start`);
|
|
289
|
+
|
|
290
|
+
const setPaths = await grok.dapi.files.list(SETS_PATH)
|
|
291
|
+
.then((l) => l.map((fi) => fi.fullPath));
|
|
292
|
+
|
|
293
|
+
const checkForUi = false;
|
|
294
|
+
const existingSetPaths: string[] = [];
|
|
295
|
+
if (checkForUi) {
|
|
296
|
+
for (const path of setPaths) {
|
|
297
|
+
const exists = await grok.dapi.files.exists(path);
|
|
298
|
+
if (exists)
|
|
299
|
+
existingSetPaths.push(path);
|
|
300
|
+
}
|
|
301
|
+
} else
|
|
302
|
+
existingSetPaths.push(...setPaths);
|
|
303
|
+
|
|
304
|
+
return existingSetPaths.map((p) => /* relative to SET_PATH */ p.substring(SETS_PATH.length));
|
|
218
305
|
}
|
|
219
306
|
}
|
|
@@ -157,7 +157,7 @@ class LibraryControlsManager {
|
|
|
157
157
|
this.updateLibrarySettings(isMonomerLibrarySelected, libFileName);
|
|
158
158
|
await setUserLibSettings(this.userLibSettings);
|
|
159
159
|
const monomerLibHelper = await getMonomerLibHelper();
|
|
160
|
-
await monomerLibHelper.
|
|
160
|
+
await monomerLibHelper.loadMonomerLib(true);
|
|
161
161
|
grok.shell.info('Monomer library user settings saved');
|
|
162
162
|
}
|
|
163
163
|
|
|
@@ -6,13 +6,14 @@ import * as DG from 'datagrok-api/dg';
|
|
|
6
6
|
import wu from 'wu';
|
|
7
7
|
import {Observable, Subject} from 'rxjs';
|
|
8
8
|
|
|
9
|
-
import {MonomerType, PolymerType} from '@datagrok-libraries/bio/src/helm/types';
|
|
10
|
-
import {IMonomerLib, Monomer, MonomerLibSummaryType, RGroup} from '@datagrok-libraries/bio/src/types';
|
|
9
|
+
import {HelmType, MonomerSetType, MonomerType, PolymerType} from '@datagrok-libraries/bio/src/helm/types';
|
|
10
|
+
import {IMonomerLib, IMonomerSet, Monomer, MonomerLibSummaryType, RGroup} from '@datagrok-libraries/bio/src/types';
|
|
11
11
|
import {HELM_REQUIRED_FIELD as REQ, HELM_RGROUP_FIELDS as RGP} from '@datagrok-libraries/bio/src/utils/const';
|
|
12
12
|
import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
|
|
13
13
|
import {GapOriginals} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
14
14
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
15
15
|
import {PolymerTypes} from '@datagrok-libraries/bio/src/helm/consts';
|
|
16
|
+
import {helmTypeToPolymerType} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
|
|
16
17
|
|
|
17
18
|
import '../../../css/cell-renderer.css';
|
|
18
19
|
|
|
@@ -102,6 +103,20 @@ export class MonomerLib implements IMonomerLib {
|
|
|
102
103
|
return res;
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
private _monomerSets: { [biotype: string /*HelmType*/]: MonomerSetType } | null = null;
|
|
107
|
+
|
|
108
|
+
getMonomerSet(biotype: HelmType): MonomerSetType | null {
|
|
109
|
+
const polymerType: PolymerType = helmTypeToPolymerType(biotype);
|
|
110
|
+
if (!this._monomerSets)
|
|
111
|
+
this._monomerSets = {};
|
|
112
|
+
if (!(biotype in this._monomerSets)) {
|
|
113
|
+
for (const [monomerSymbol, monomer] of Object.entries(this._monomers[polymerType])) {
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return this._monomerSets[biotype];
|
|
118
|
+
}
|
|
119
|
+
|
|
105
120
|
getPolymerTypes(): PolymerType[] {
|
|
106
121
|
return Object.keys(this._monomers) as PolymerType[];
|
|
107
122
|
}
|
|
@@ -151,7 +166,7 @@ export class MonomerLib implements IMonomerLib {
|
|
|
151
166
|
return this._onChanged;
|
|
152
167
|
}
|
|
153
168
|
|
|
154
|
-
private
|
|
169
|
+
private _updateLibInt(lib: IMonomerLib): void {
|
|
155
170
|
const typesNew = lib.getPolymerTypes();
|
|
156
171
|
const types = this.getPolymerTypes();
|
|
157
172
|
|
|
@@ -169,13 +184,15 @@ export class MonomerLib implements IMonomerLib {
|
|
|
169
184
|
}
|
|
170
185
|
|
|
171
186
|
public update(lib: IMonomerLib): void {
|
|
172
|
-
this.
|
|
187
|
+
this._updateLibInt(lib);
|
|
173
188
|
this._onChanged.next();
|
|
174
189
|
}
|
|
175
190
|
|
|
176
191
|
public updateLibs(libList: IMonomerLib[], reload: boolean = false): void {
|
|
177
|
-
if (reload)
|
|
178
|
-
|
|
192
|
+
if (reload)
|
|
193
|
+
this._monomers = {};
|
|
194
|
+
for (const lib of libList)
|
|
195
|
+
if (!lib.error) this._updateLibInt(lib);
|
|
179
196
|
this._onChanged.next();
|
|
180
197
|
}
|
|
181
198
|
|
|
@@ -0,0 +1,61 @@
|
|
|
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 type {MonomerType, PolymerType} from '@datagrok-libraries/bio/src/helm/types';
|
|
6
|
+
import {
|
|
7
|
+
IMonomerLib, IMonomerLinkData, IMonomerSet, IMonomerSetPlaceholder, Monomer
|
|
8
|
+
} from '@datagrok-libraries/bio/src/types';
|
|
9
|
+
import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
|
|
10
|
+
|
|
11
|
+
export class MonomerSetPlaceholder implements IMonomerSetPlaceholder {
|
|
12
|
+
public readonly monomers: Monomer[];
|
|
13
|
+
|
|
14
|
+
public readonly error: string | null = null;
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
private readonly monomerLib: IMonomerLib,
|
|
18
|
+
public symbol: string,
|
|
19
|
+
public readonly polymerType: PolymerType,
|
|
20
|
+
public readonly monomerType: MonomerType,
|
|
21
|
+
public readonly monomerLinks: IMonomerLinkData[],
|
|
22
|
+
) {
|
|
23
|
+
try {
|
|
24
|
+
this.monomers = this.monomerLinks.map((mLink) => {
|
|
25
|
+
const resM = this.monomerLib.getMonomer(this.polymerType, mLink.symbol);
|
|
26
|
+
if (!resM)
|
|
27
|
+
throw new Error('Monomer not found: ');
|
|
28
|
+
if (resM.lib?.source != mLink.source)
|
|
29
|
+
throw new Error(`Monomer '${symbol}' found in different library.`);
|
|
30
|
+
return resM;
|
|
31
|
+
});
|
|
32
|
+
} catch (err: any) {
|
|
33
|
+
const [errMsg, errStack] = errInfo(err);
|
|
34
|
+
this.error = errMsg;
|
|
35
|
+
this.monomers = [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class MonomerSet implements IMonomerSet {
|
|
41
|
+
public constructor(
|
|
42
|
+
public readonly description: string,
|
|
43
|
+
public placeholders: IMonomerSetPlaceholder[],
|
|
44
|
+
public readonly source: string | undefined = undefined,
|
|
45
|
+
public readonly error: string | undefined = undefined,
|
|
46
|
+
) {}
|
|
47
|
+
|
|
48
|
+
updateSets(setList: IMonomerSet[], reload: boolean = false): void {
|
|
49
|
+
if (reload)
|
|
50
|
+
this.placeholders = [];
|
|
51
|
+
for (const _set of setList)
|
|
52
|
+
if (!_set.error) this._updateSetInt(_set);
|
|
53
|
+
|
|
54
|
+
// TODO: File onChanged
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private _updateSetInt(set: IMonomerSet): void {
|
|
58
|
+
for (const setPh of set.placeholders)
|
|
59
|
+
this.placeholders.push(setPh);
|
|
60
|
+
}
|
|
61
|
+
}
|