@datagrok/bio 2.7.0 → 2.7.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/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "Leonid Stolbov",
6
6
  "email": "lstolbov@datagrok.ai"
7
7
  },
8
- "version": "2.7.0",
8
+ "version": "2.7.2",
9
9
  "description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
10
10
  "repository": {
11
11
  "type": "git",
@@ -53,9 +53,9 @@
53
53
  "source-map-loader": "^4.0.1",
54
54
  "ts-loader": "^9.2.5",
55
55
  "typescript": "^4.8.4",
56
- "webpack": "^5.76.0",
56
+ "webpack": "^5.76.3",
57
57
  "webpack-bundle-analyzer": "latest",
58
- "webpack-cli": "^4.6.0",
58
+ "webpack-cli": "^4.9.1",
59
59
  "@datagrok/chem": "1.4.21",
60
60
  "@datagrok/helm": "2.1.7"
61
61
  },
package/src/package.ts CHANGED
@@ -262,7 +262,7 @@ export function SeqActivityCliffsEditor(call: DG.FuncCall) {
262
262
  .show();
263
263
  }
264
264
 
265
- //top-menu: Bio | SAR | Activity Cliffs...
265
+ //top-menu: Bio | Analyze | Activity Cliffs...
266
266
  //name: Sequence Activity Cliffs
267
267
  //description: detect activity cliffs
268
268
  //input: dataframe table [Input data table]
@@ -356,7 +356,7 @@ export function SequenceSpaceEditor(call: DG.FuncCall) {
356
356
  .show();
357
357
  }
358
358
 
359
- //top-menu: Bio | Structure | Sequence Space...
359
+ //top-menu: Bio | Analyze | Sequence Space...
360
360
  //name: Sequence Space
361
361
  //input: dataframe table
362
362
  //input: column molecules { semType: Macromolecule }
@@ -454,7 +454,7 @@ export async function sequenceSpaceTopMenu(
454
454
  } */
455
455
  };
456
456
 
457
- //top-menu: Bio | Atomic Level | To Atomic Level...
457
+ //top-menu: Bio | Convert | To Atomic Level...
458
458
  //name: To Atomic Level
459
459
  //description: returns molfiles for each monomer from HELM library
460
460
  //input: dataframe df [Input data table]
@@ -477,8 +477,8 @@ export async function toAtomicLevel(df: DG.DataFrame, macroMolecule: DG.Column):
477
477
  grok.shell.warning(ui.list(atomicLevelRes.warnings));
478
478
  }
479
479
 
480
- //top-menu: Bio | Alignment | MSA...
481
- //name: MSA...
480
+ //top-menu: Bio | Analyze | MSA...
481
+ //name: MSA
482
482
  //tags: bio, panel
483
483
  export function multipleSequenceAlignmentDialog(): void {
484
484
  multipleSequenceAlignmentUI();
@@ -495,7 +495,7 @@ export async function alignSequences(sequenceCol: DG.Column<string> | null = nul
495
495
  return multipleSequenceAlignmentUI({col: sequenceCol, clustersCol});
496
496
  }
497
497
 
498
- //top-menu: Bio | Structure | Composition Analysis
498
+ //top-menu: Bio | Analyze | Composition
499
499
  //name: Composition Analysis
500
500
  //meta.icon: files/icons/composition-analysis.svg
501
501
  //output: viewer result
@@ -554,7 +554,7 @@ export async function compositionAnalysis(): Promise<void> {
554
554
  await handler(col);
555
555
  }
556
556
 
557
- //top-menu: Bio | Atomic Level | SDF to JSON Library...
557
+ //top-menu: Bio | Convert | SDF to JSON Library...
558
558
  //name: SDF to JSON Library
559
559
  //input: dataframe table
560
560
  export async function sdfToJsonLib(table: DG.DataFrame) {
@@ -595,7 +595,7 @@ export function importBam(fileContent: string): DG.DataFrame [] {
595
595
  return [];
596
596
  }
597
597
 
598
- //top-menu: Bio | Convert...
598
+ //top-menu: Bio | Convert | Notation...
599
599
  //name: convertDialog
600
600
  export function convertDialog() {
601
601
  const col = getMacromoleculeColumn();
@@ -675,7 +675,7 @@ export function SplitToMonomersEditor(call: DG.FuncCall): void {
675
675
  .show();
676
676
  }
677
677
 
678
- //top-menu: Bio | Split to Monomers
678
+ //top-menu: Bio | Convert | Split to Monomers...
679
679
  //name: Split to Monomers
680
680
  //input: dataframe table
681
681
  //input: column sequence { semType: Macromolecule }
@@ -702,7 +702,7 @@ export function similaritySearchViewer(): SequenceSimilarityViewer {
702
702
  return new SequenceSimilarityViewer();
703
703
  }
704
704
 
705
- //top-menu: Bio | Search | Similarity Search
705
+ //top-menu: Bio | Search | Similarity
706
706
  //name: similaritySearch
707
707
  //description: finds the most similar sequence
708
708
  //output: viewer result
@@ -720,7 +720,7 @@ export function diversitySearchViewer(): SequenceDiversityViewer {
720
720
  return new SequenceDiversityViewer();
721
721
  }
722
722
 
723
- //top-menu: Bio | Search | Diversity Search
723
+ //top-menu: Bio | Search | Diversity
724
724
  //name: diversitySearch
725
725
  //description: finds the most diverse molecules
726
726
  //output: viewer result
@@ -730,7 +730,7 @@ export function diversitySearchTopMenu() {
730
730
  view.dockManager.dock(viewer, 'down');
731
731
  }
732
732
 
733
- //top-menu: Bio | Structure | Substructure Search ...
733
+ //top-menu: Bio | Search | Substructure...
734
734
  //name: bioSubstructureSearch
735
735
  export function bioSubstructureSearch(): void {
736
736
  const col = getMacromoleculeColumn();
@@ -105,6 +105,7 @@ export async function helmSubstructureSearch(substructure: string, col: DG.Colum
105
105
  if (col.version !== col.temp[MONOMERIC_COL_TAGS.LAST_INVALIDATED_VERSION])
106
106
  await invalidateMols(col, true);
107
107
  const substructureCol: DG.Column<string> = DG.Column.string('helm', 1).init((_i) => substructure);
108
+ substructureCol.semType = DG.SEMTYPE.MACROMOLECULE;
108
109
  substructureCol.setTag(DG.TAGS.UNITS, NOTATION.HELM);
109
110
  const substructureMolsCol =
110
111
  await getMonomericMols(substructureCol, true, col.temp[MONOMERIC_COL_TAGS.MONOMERS_DICT]);
@@ -5,7 +5,7 @@ import * as ui from 'datagrok-api/ui';
5
5
  import {test, after, before, category, expect} from '@datagrok-libraries/utils/src/test';
6
6
 
7
7
  import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
8
- import {LIB_STORAGE_NAME} from '../utils/monomer-lib';
8
+ import {getLibFileNameList, getUserLibSettings, LIB_STORAGE_NAME, setUserLibSetting} from '../utils/monomer-lib';
9
9
 
10
10
 
11
11
  category('monomerLibraries', () => {
@@ -31,4 +31,17 @@ category('monomerLibraries', () => {
31
31
  const currentMonomerLib = monomerLibHelper.getBioLib();
32
32
  expect(currentMonomerLib.getPolymerTypes().length > 0, true);
33
33
  });
34
+
35
+ test('empty', async () => {
36
+ // exclude all monomer libraries for empty set
37
+ const libSettings = await getUserLibSettings();
38
+ const libFnList = await getLibFileNameList();
39
+ libSettings.exclude = libFnList;
40
+ await setUserLibSetting(libSettings);
41
+
42
+ await monomerLibHelper.loadLibraries(true);
43
+ const currentMonomerLib = monomerLibHelper.getBioLib();
44
+ expect(currentMonomerLib.getPolymerTypes().length === 0, true);
45
+ const monomerOfTypesList = currentMonomerLib.getMonomerMolsByPolymerType('PEPTIDE');
46
+ });
34
47
  });
@@ -39,18 +39,28 @@ export async function getLibFileNameList(): Promise<string[]> {
39
39
  return res;
40
40
  }
41
41
 
42
- export async function getUserLibSettings(): Promise<LibSettings> {
43
- const resStr: string = await grok.dapi.userDataStorage.getValue(LIB_STORAGE_NAME, 'Settings', true);
44
- const res: LibSettings = resStr ? JSON.parse(resStr) : {exclude: []};
45
-
46
- // Fix empty object returned in case there is no settings stored for user
47
- res.exclude = res.exclude instanceof Array ? res.exclude : [];
42
+ let userLibSettingsPromise: Promise<void> = Promise.resolve();
48
43
 
49
- return res;
44
+ export async function getUserLibSettings(): Promise<LibSettings> {
45
+ let res: LibSettings;
46
+ userLibSettingsPromise = userLibSettingsPromise.then(async () => {
47
+ const resStr: string = await grok.dapi.userDataStorage.getValue(LIB_STORAGE_NAME, 'Settings', true);
48
+ res = resStr ? JSON.parse(resStr) : {exclude: []};
49
+
50
+ // Fix empty object returned in case there is no settings stored for user
51
+ res.exclude = res.exclude instanceof Array ? res.exclude : [];
52
+ console.debug(`Bio: getUserLibSettings()\n${JSON.stringify(res, undefined, 2)}`);
53
+ });
54
+ await userLibSettingsPromise;
55
+ return res!;
50
56
  }
51
57
 
52
58
  export async function setUserLibSetting(value: LibSettings): Promise<void> {
53
- await grok.dapi.userDataStorage.postValue(LIB_STORAGE_NAME, 'Settings', JSON.stringify(value), true);
59
+ userLibSettingsPromise = userLibSettingsPromise.then(async () => {
60
+ console.debug(`Bio: setUserLibSettings()\n${JSON.stringify(value, undefined, 2)}`);
61
+ await grok.dapi.userDataStorage.postValue(LIB_STORAGE_NAME, 'Settings', JSON.stringify(value), true);
62
+ });
63
+ await userLibSettingsPromise;
54
64
  }
55
65
 
56
66
  export async function manageFiles() {
@@ -67,20 +77,19 @@ export async function getLibraryPanelUI(): Promise<DG.Widget> {
67
77
  const inputsForm: HTMLDivElement = ui.inputs([]);
68
78
  const libFileNameList: string[] = await getLibFileNameList();
69
79
 
70
- let userStoragePromise: Promise<void> = Promise.resolve();
80
+ const settings = await getUserLibSettings();
81
+
71
82
  for (const libFileName of libFileNameList) {
72
- const settings = await getUserLibSettings();
73
83
  const libInput: DG.InputBase<boolean | null> = ui.boolInput(libFileName, !settings.exclude.includes(libFileName),
74
84
  () => {
75
- userStoragePromise = userStoragePromise.then(async () => {
76
- if (libInput.value == true) {
77
- // Checked library remove from excluded list
78
- settings.exclude = settings.exclude.filter((l) => l != libFileName);
79
- } else {
80
- // Unchecked library add to excluded list
81
- if (!settings.exclude.includes(libFileName)) settings.exclude.push(libFileName);
82
- }
83
- await setUserLibSetting(settings);
85
+ if (libInput.value == true) {
86
+ // Checked library remove from excluded list
87
+ settings.exclude = settings.exclude.filter((l) => l != libFileName);
88
+ } else {
89
+ // Unchecked library add to excluded list
90
+ if (!settings.exclude.includes(libFileName)) settings.exclude.push(libFileName);
91
+ }
92
+ setUserLibSetting(settings).then(async () => {
84
93
  await MonomerLibHelper.instance.loadLibraries(true); // from libraryPanel()
85
94
  grok.shell.info('Monomer library user settings saved.');
86
95
  });
@@ -115,7 +124,7 @@ export class MonomerLib implements IMonomerLib {
115
124
  getMonomerMolsByPolymerType(polymerType: string): { [monomerSymbol: string]: string } {
116
125
  const res: { [monomerSymbol: string]: string } = {};
117
126
 
118
- Object.keys(this._monomers[polymerType]).forEach((monomerSymbol) => {
127
+ Object.keys(this._monomers[polymerType] ?? {}).forEach((monomerSymbol) => {
119
128
  res[monomerSymbol] = this._monomers[polymerType][monomerSymbol].molfile;
120
129
  });
121
130
 
@@ -193,8 +202,9 @@ export class MonomerLibHelper implements IMonomerLibHelper {
193
202
  getLibFileNameList(),
194
203
  getUserLibSettings(),
195
204
  ]);
196
- const libs: IMonomerLib[] = await Promise.all(libFileNameList
197
- .filter((libFileName) => !settings.exclude.includes(libFileName))
205
+ const filteredLibFnList = libFileNameList
206
+ .filter((libFileName) => !settings.exclude.includes(libFileName));
207
+ const libs: IMonomerLib[] = await Promise.all(filteredLibFnList
198
208
  .map((libFileName) => {
199
209
  //TODO handle whether files are in place
200
210
  return this.readLibrary(LIB_PATH, libFileName).catch((err: any) => {
@@ -62,6 +62,7 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
62
62
  // public sequenceColumnNamePostfix: string;
63
63
 
64
64
  public skipEmptyPositions: boolean;
65
+ /* A value of zero means autofit to the width. */
65
66
  public positionWidth: number;
66
67
  public positionHeight: PositionHeight;
67
68
 
@@ -76,7 +77,10 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
76
77
  // this.sequenceColumnNamePostfix = this.string('sequenceColumnNamePostfix', 'chain sequence');
77
78
 
78
79
  this.skipEmptyPositions = this.bool('skipEmptyPositions', false);
79
- this.positionWidth = this.float('positionWidth', 16);
80
+ this.positionWidth = this.float('positionWidth', 16, {
81
+ editor: 'slider', min: 0, max: 64,
82
+ description: 'Internal WebLogo viewers property width of position. A value of zero means autofit to the width.'
83
+ });
80
84
  this.positionHeight = this.string('positionHeight', PositionHeight.Entropy,
81
85
  {choices: Object.keys(PositionHeight)}) as PositionHeight;
82
86
  }
@@ -105,7 +109,6 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
105
109
 
106
110
  // this.mlbView.dockManager.dock(this.regionsFg.root, DG.DOCK_TYPE.LEFT, rootNode, 'Filter regions', 0.2);
107
111
 
108
- this.subs.push(ui.onSizeChanged(this.root).subscribe(this.rootOnSizeChanged.bind(this)));
109
112
  this.subs.push(fromEvent<MouseEvent>(this.root, 'mousemove').subscribe(this.rootOnMouseMove.bind(this)));
110
113
 
111
114
  // await this.buildView('init'); // init
@@ -213,7 +216,7 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
213
216
 
214
217
  private async destroyView(purpose: string): Promise<void> {
215
218
  // TODO: Unsubscribe from and remove all view elements
216
- console.debug(`Bio: VdRegionsViewer.destroyView( mainLayout = ${!this.mainLayout ? 'none' : 'value'} ), ` +
219
+ _package.logger.debug(`Bio: VdRegionsViewer.destroyView( mainLayout = ${!this.mainLayout ? 'none' : 'value'} ), ` +
217
220
  `purpose = '${purpose}'`);
218
221
  if (this.filterSourceInput) {
219
222
  //
@@ -232,7 +235,7 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
232
235
  }
233
236
 
234
237
  private async buildView(purpose: string): Promise<void> {
235
- console.debug(`Bio: VdRegionsViewer.buildView() begin, ` + `purpose = '${purpose}'`);
238
+ _package.logger.debug(`Bio: VdRegionsViewer.buildView() begin, ` + `purpose = '${purpose}'`);
236
239
 
237
240
  const regionsFiltered: VdRegion[] = this.regions.filter((r: VdRegion) => this.regionTypes.includes(r.type));
238
241
  const orderList: number[] = Array.from(new Set(regionsFiltered.map((r) => r.order))).sort();
@@ -252,6 +255,7 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
252
255
  positionWidth: this.positionWidth,
253
256
  positionHeight: this.positionHeight,
254
257
  }) as WebLogoViewer;
258
+ wl.onSizeChanged.subscribe(() => { this.calcSize(); });
255
259
  return [orderI, chain, wl];
256
260
  })());
257
261
  }
@@ -321,23 +325,59 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
321
325
  this.root.style.overflowX = 'auto';
322
326
 
323
327
  this.calcSize();
328
+ this.viewSubs.push(ui.onSizeChanged(this.root).subscribe(this.rootOnSizeChanged.bind(this)));
324
329
 
325
- console.debug('Bio: VdRegionsViewer.buildView() end');
330
+ _package.logger.debug('Bio: VdRegionsViewer.buildView() end');
326
331
  }
327
332
 
328
- private calcSize() {
329
- const logoHeight = (this.root.clientHeight - 54) / this.chains.length;
333
+ private calcSizeRequested: boolean = false;
330
334
 
331
- const maxHeight: number = Math.min(logoHeight,
332
- Math.max(...this.logos.map((wlDict) =>
333
- Math.max(...Object.values(wlDict).map((wl) => wl.maxHeight)))),
334
- );
335
+ private calcSize() {
336
+ _package.logger.debug(`Bio: VdRegionsViewer.calcSize(), start`);
337
+ const calcSizeInt = (): void => {
338
+ const dpr: number = window.devicePixelRatio;
339
+ const logoHeight = (this.root.clientHeight - 54) / this.chains.length;
340
+
341
+ const maxHeight: number = Math.min(logoHeight,
342
+ Math.max(...this.logos.map((wlDict) =>
343
+ Math.max(...Object.values(wlDict).map((wl) => wl.maxHeight)))),
344
+ );
345
+
346
+ let totalPos: number = 0;
347
+ for (let orderI = 0; orderI < this.logos.length; orderI++) {
348
+ for (const chain of this.chains)
349
+ this.logos[orderI][chain].root.style.height = `${maxHeight}px`;
350
+
351
+ totalPos += Math.max(...this.chains.map((chain) => this.logos[orderI][chain].Length));
352
+ }
335
353
 
336
- for (let orderI = 0; orderI < this.logos.length; orderI++) {
337
- for (let chainI = 0; chainI < this.chains.length; chainI++) {
338
- const chain: string = this.chains[chainI];
339
- this.logos[orderI][chain].root.style.height = `${maxHeight}px`;
354
+ if (this.positionWidth === 0 && this.logos.length > 0 && totalPos > 0) {
355
+ const leftPad = 22/* Chain label */;
356
+ const rightPad = 6 + 6 + 1;
357
+ const logoMargin = 8 + 1;
358
+ const fitPositionWidth =
359
+ (this.root.clientWidth - leftPad - (this.logos.length - 1) * logoMargin - rightPad) / totalPos * dpr;
360
+
361
+ for (let orderI = 0; orderI < this.logos.length; orderI++) {
362
+ for (let chainI = 0; chainI < this.chains.length; chainI++) {
363
+ const chain: string = this.chains[chainI];
364
+ this.logos[orderI][chain].setOptions({positionWidth: fitPositionWidth});
365
+ }
366
+ }
340
367
  }
368
+
369
+ if (this.positionWidth === 0)
370
+ this.host!.style.setProperty('overflow-x', 'hidden', 'important');
371
+ else
372
+ this.host!.style.removeProperty('overflow-x');
373
+ };
374
+
375
+ if (!this.calcSizeRequested) {
376
+ this.calcSizeRequested = true;
377
+ window.setTimeout(() => {
378
+ calcSizeInt();
379
+ this.calcSizeRequested = false;
380
+ }, 0 /* next event cycle */);
341
381
  }
342
382
  }
343
383
 
package/tsconfig.json CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  /* Basic Options */
6
6
  // "incremental": true, /* Enable incremental compilation */
7
- "target": "es2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
7
+ "target": "es2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
8
8
  "module": "es2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
9
9
  "lib": ["ES2022", "dom"], /* Specify library files to be included in the compilation. */
10
10
  // "allowJs": true, /* Allow javascript files to be compiled. */
@@ -66,8 +66,7 @@
66
66
 
67
67
  /* Advanced Options */
68
68
  "skipLibCheck": false, /* Skip type checking of declaration files. */
69
- "forceConsistentCasingInFileNames": true,
70
- /* Disallow inconsistently-cased references to the same file. */
69
+ "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
71
70
  //"jsx": "react"
72
71
  }
73
72
  }
package/webpack.config.js CHANGED
@@ -36,7 +36,7 @@ module.exports = {
36
36
  },
37
37
  output: {
38
38
  filename: '[name].js',
39
- library: 'bio',
39
+ library: packageName,
40
40
  libraryTarget: 'var',
41
41
  path: path.resolve(__dirname, 'dist'),
42
42
  },