@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.
Files changed (32) hide show
  1. package/.eslintrc.json +10 -2
  2. package/CHANGELOG.md +15 -0
  3. package/dist/248.js.map +1 -1
  4. package/dist/package-test.js +6 -6
  5. package/dist/package-test.js.map +1 -1
  6. package/dist/package.js +3 -3
  7. package/dist/package.js.map +1 -1
  8. package/files/monomer-sets/monomer-set-PEPTIDE.json +93 -0
  9. package/files/monomer-sets/monomer-set-RNA.json +29 -0
  10. package/files/schemas/monomer-set-schema.json +48 -0
  11. package/package.json +5 -5
  12. package/src/analysis/sequence-similarity-viewer.ts +5 -2
  13. package/src/demo/bio01-similarity-diversity.ts +3 -2
  14. package/src/demo/bio01b-hierarchical-clustering-and-activity-cliffs.ts +1 -1
  15. package/src/package.ts +12 -7
  16. package/src/tests/activity-cliffs-tests.ts +2 -2
  17. package/src/tests/detectors-tests.ts +14 -7
  18. package/src/tests/monomer-libraries-tests.ts +6 -6
  19. package/src/tests/renderers-monomer-placer-tests.ts +2 -2
  20. package/src/tests/scoring.ts +2 -2
  21. package/src/tests/substructure-filters-tests.ts +2 -2
  22. package/src/tests/to-atomic-level-tests.ts +3 -3
  23. package/src/utils/helm-to-molfile/converter/converter.ts +1 -1
  24. package/src/utils/monomer-lib/lib-manager.ts +118 -21
  25. package/src/utils/monomer-lib/library-file-manager/event-manager.ts +25 -8
  26. package/src/utils/monomer-lib/library-file-manager/file-manager.ts +122 -35
  27. package/src/utils/monomer-lib/library-file-manager/ui.ts +1 -1
  28. package/src/utils/monomer-lib/monomer-lib.ts +23 -6
  29. package/src/utils/monomer-lib/monomer-set.ts +61 -0
  30. package/src/widgets/bio-substructure-filter-helm.ts +2 -2
  31. package/src/widgets/bio-substructure-filter.ts +19 -14
  32. 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
- getBioLib(): IMonomerLib {
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 loadLibraries(reload: boolean = false): Promise<void> {
85
- return this.loadLibrariesPromise = this.loadLibrariesPromise.then(async () => {
86
- // WARNING: This function is not allowed to throw any exception,
87
- // because it will prevent further handling monomer library settings
88
- // through blocking this.loadLibrariesPromise
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] = await Promise.all([
92
- (await this.getFileManager()).getValidLibraryPaths(),
93
- getUserLibSettings(),
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 completedLibCount: number = 0;
104
- const libs: IMonomerLib[] = await Promise.all(filteredLibFnList
105
- .map((libFileName) => {
106
- //TODO handle whether files are in place
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
- }).finally(() => {
113
- pi.update(Math.round(100 * (++completedLibCount) / filteredLibFnList.length),
114
- `Loading monomer libs ${completedLibCount}/${filteredLibFnList.length}`);
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
- _package.logger.error(errMsg, undefined, errStack);
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 _libraryFilesUpdateSubject$ = new BehaviorSubject<string[]>([]);
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
- getValidFilesPathList(): string[] {
28
- return this._libraryFilesUpdateSubject$.getValue();
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._libraryFilesUpdateSubject$.pipe<string[]>(
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
- changeValidFilesPathList(newList: string[]): void {
44
- this._libraryFilesUpdateSubject$.next(newList);
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._libraryFilesUpdateSubject$.pipe(
59
+ return this._libFilesUpdateSubject$.pipe(
49
60
  // debounceTime(1000)
50
61
  );
51
62
  }
52
63
 
53
64
  get updateValidLibraryFileListRequested$(): Observable<string[]> {
54
- return this._libraryFilesUpdateSubject$.pipe(
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 {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
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(async () => {
38
- await this.updateValidLibraryList();
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.updateValidLibraryList();
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.updateValidLibraryList();
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.getValidFilesPathList();
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 updateValidLibraryList(): Promise<void> {
129
- const logPrefix: string = `${this.toLog()}.updateValidLibraryList()`;
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 filePaths = await this.getFilePathsAtDefaultLocation();
163
+ const libPathList = await this.getLibFileListAtLocation();
136
164
 
137
- if (!this.fileListHasChanged(filePaths)) {
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 filePaths) {
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 validLibraryPaths = filePaths.filter((path) => !invalidFiles.includes(path));
181
+ const validLibPathList = libPathList.filter((path) => !invalidFiles.includes(path));
154
182
 
155
- if (this.fileListHasChanged(validLibraryPaths)) {
156
- this.eventManager.changeValidFilesPathList(validLibraryPaths);
157
- this.libHelper.loadLibraries(true);
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 (validLibraryPaths.some((el) => !el.endsWith('.json')))
162
- this.logger.warning(`Wrong validation: ${validLibraryPaths}`);
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 fileListHasChanged(newList: string[]): boolean {
178
- const currentList = this.eventManager.getValidFilesPathList();
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 getFilePathsAtDefaultLocation(): Promise<string[]> {
194
- const logPrefix = `${this.toLog()}.getFilePathsAtDefaultLocation()`;
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
- const list = await grok.dapi.files.list(LIB_PATH);
197
- const paths = list.map((fileInfo) => {
198
- return fileInfo.fullPath;
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 existingPaths = [] as string[];
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 paths) {
275
+ for (const path of libPaths) {
207
276
  const exists = await grok.dapi.files.exists(path);
208
277
  if (exists)
209
- existingPaths.push(path);
278
+ existingLibPaths.push(path);
210
279
  }
211
280
  } else
212
- existingPaths.push(...paths);
281
+ existingLibPaths.push(...libPaths);
213
282
 
214
- return existingPaths.map((path) => {
215
- // Get relative path (to LIB_PATH)
216
- return path.substring(LIB_PATH.length);
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.loadLibraries(true);
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 _updateInt(lib: IMonomerLib): void {
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._updateInt(lib);
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) this._monomers = {};
178
- for (const lib of libList) if (!lib.error) this._updateInt(lib);
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
+ }