@datagrok/bio 2.12.17 → 2.12.19

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 (34) hide show
  1. package/CHANGELOG.md +21 -2
  2. package/dist/79.js.map +1 -1
  3. package/dist/package-test.js +3 -3
  4. package/dist/package-test.js.map +1 -1
  5. package/dist/package.js +3 -3
  6. package/dist/package.js.map +1 -1
  7. package/package.json +4 -4
  8. package/src/package.ts +56 -12
  9. package/src/tests/monomer-libraries-tests.ts +1 -4
  10. package/src/tests/renderers-monomer-placer-tests.ts +59 -12
  11. package/src/tests/renderers-test.ts +3 -5
  12. package/src/tests/scoring.ts +2 -2
  13. package/src/tests/substructure-filters-tests.ts +2 -0
  14. package/src/tests/to-atomic-level-tests.ts +1 -1
  15. package/src/tests/utils/sequences-generators.ts +0 -20
  16. package/src/tests/utils.ts +15 -0
  17. package/src/utils/cell-renderer.ts +39 -46
  18. package/src/utils/helm-to-molfile/converter/converter.ts +10 -5
  19. package/src/utils/helm-to-molfile/converter/monomer-wrapper.ts +9 -9
  20. package/src/utils/helm-to-molfile/converter/polymer.ts +10 -3
  21. package/src/utils/macromolecule-column-widget.ts +2 -0
  22. package/src/utils/monomer-cell-renderer.ts +18 -8
  23. package/src/utils/monomer-lib/lib-manager.ts +56 -18
  24. package/src/utils/monomer-lib/library-file-manager/event-manager.ts +15 -9
  25. package/src/utils/monomer-lib/library-file-manager/file-manager.ts +78 -59
  26. package/src/utils/monomer-lib/library-file-manager/file-validator.ts +3 -1
  27. package/src/utils/monomer-lib/library-file-manager/ui.ts +52 -47
  28. package/src/utils/monomer-lib/monomer-lib.ts +78 -9
  29. package/src/utils/multiple-sequence-alignment-ui.ts +31 -18
  30. package/src/utils/sequence-to-mol.ts +7 -7
  31. package/src/viewers/web-logo-viewer.ts +14 -4
  32. package/src/widgets/bio-substructure-filter-helm.ts +9 -3
  33. package/src/widgets/bio-substructure-filter.ts +2 -2
  34. package/webpack.config.js +1 -0
@@ -4,16 +4,15 @@ import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
5
 
6
6
  import $ from 'cash-dom';
7
- import * as rxjs from 'rxjs';
7
+ import {Subject} from 'rxjs';
8
8
  import './style.css';
9
9
 
10
10
  import {
11
11
  getUserLibSettings, setUserLibSettings
12
12
  } from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
13
13
  import {UserLibSettings} from '@datagrok-libraries/bio/src/monomer-works/types';
14
- import {MonomerLibManager} from '../lib-manager';
14
+ import {getMonomerLibHelper, IMonomerLibFileManager} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
15
15
 
16
- import {MonomerLibFileManager} from './file-manager';
17
16
  import {MonomerLibFileEventManager} from './event-manager';
18
17
 
19
18
  export async function showManageLibrariesDialog(): Promise<void> {
@@ -30,37 +29,39 @@ export async function getMonomerLibraryManagerLink(): Promise<DG.Widget> {
30
29
  }
31
30
 
32
31
  class MonomerLibraryManagerWidget {
33
- private constructor(
34
- private eventManager: MonomerLibFileEventManager
35
- ) { }
32
+ private _fileManager: IMonomerLibFileManager;
36
33
 
37
- private static _instance: MonomerLibraryManagerWidget;
34
+ private _widget: DG.Widget;
35
+ public get widget(): DG.Widget { return this._widget; }
38
36
 
39
- static async getContent(eventManager: MonomerLibFileEventManager): Promise<DG.Widget> {
40
- if (!MonomerLibraryManagerWidget._instance)
41
- MonomerLibraryManagerWidget._instance = new MonomerLibraryManagerWidget(eventManager);
37
+ private constructor() {}
42
38
 
43
- if (!MonomerLibraryManagerWidget._instance.widget)
44
- MonomerLibraryManagerWidget._instance.widget = await MonomerLibraryManagerWidget._instance.createWidget();
39
+ private static instancePromise?: Promise<MonomerLibraryManagerWidget>;
45
40
 
46
- return MonomerLibraryManagerWidget._instance.widget;
41
+ static async getInstance(): Promise<MonomerLibraryManagerWidget> {
42
+ if (MonomerLibraryManagerWidget.instancePromise === undefined) {
43
+ MonomerLibraryManagerWidget.instancePromise = (async () => {
44
+ const instance = new MonomerLibraryManagerWidget();
45
+ const libHelper = await getMonomerLibHelper();
46
+ instance._fileManager = await libHelper.getFileManager();
47
+ instance._widget = await instance.createWidget();
48
+ return instance;
49
+ })();
50
+ }
51
+ return MonomerLibraryManagerWidget.instancePromise;
47
52
  }
48
53
 
49
- private monomerLibFileManager: MonomerLibFileManager;
50
- private widget: DG.Widget | undefined;
51
-
52
54
  private async createWidget() {
53
- this.monomerLibFileManager = await MonomerLibFileManager.getInstance(this.eventManager);
54
55
  const content = await this.getWidgetContent();
55
- this.eventManager.addLibraryFileRequested$.subscribe(
56
+ const monomerLibHelper = await getMonomerLibHelper();
57
+ monomerLibHelper.eventManager.addLibraryFileRequested$.subscribe(
56
58
  async () => await this.promptToAddLibraryFiles()
57
59
  );
58
60
  return new DG.Widget(content);
59
61
  }
60
62
 
61
63
  private async getWidgetContent(): Promise<HTMLElement> {
62
- this.monomerLibFileManager = await MonomerLibFileManager.getInstance(this.eventManager);
63
- const libControlsForm = await LibraryControlsManager.createControlsForm(this.eventManager);
64
+ const libControlsForm = await LibraryControlsManager.createControlsForm();
64
65
  $(libControlsForm).addClass('monomer-lib-controls-form');
65
66
  const widgetContent = ui.divV([libControlsForm]);
66
67
  return widgetContent;
@@ -74,7 +75,7 @@ class MonomerLibraryManagerWidget {
74
75
  const name = selectedFile.name;
75
76
  const progressIndicator = DG.TaskBarProgressIndicator.create(`Adding ${name} as a monomer library`);
76
77
  try {
77
- await this.monomerLibFileManager.addLibraryFile(content, name);
78
+ await this._fileManager.addLibraryFile(content, name);
78
79
  // this.eventManager.updateLibrarySelectionStatus(name, true);
79
80
  } catch (e) {
80
81
  grok.shell.error(`File ${name} is not a valid monomer library, verify it is aligned to HELM JSON schema.`);
@@ -87,27 +88,30 @@ class MonomerLibraryManagerWidget {
87
88
  }
88
89
 
89
90
  class LibraryControlsManager {
90
- private constructor(private eventManager: MonomerLibFileEventManager) {
91
- this.eventManager.updateUIControlsRequested$.subscribe(
92
- async () => await this.updateControlsForm()
93
- );
94
- this.eventManager.librarySelectionRequested$.subscribe(
95
- async ([fileName, isSelected]) => await this.updateLibrarySelectionStatus(isSelected, fileName)
96
- );
91
+ private constructor(
92
+ private fileManager: IMonomerLibFileManager
93
+ ) {
94
+ this.fileManager.eventManager.updateUIControlsRequested$.subscribe(() => {
95
+ this.updateControlsForm();
96
+ });
97
+ this.fileManager.eventManager.librarySelectionRequested$.subscribe(async ([fileName, isSelected]) => {
98
+ await this.updateLibrarySelectionStatus(isSelected, fileName);
99
+ });
97
100
  }
98
- private monomerLibFileManager: MonomerLibFileManager;
101
+
99
102
  private userLibSettings: UserLibSettings;
100
103
 
101
- static async createControlsForm(eventManager: MonomerLibFileEventManager): Promise<HTMLElement> {
102
- const manager = new LibraryControlsManager(eventManager);
104
+ static async createControlsForm(): Promise<HTMLElement> {
105
+ const libHelper = await getMonomerLibHelper();
106
+ const fileManager = await libHelper.getFileManager();
107
+ const manager = new LibraryControlsManager(fileManager);
103
108
  await manager.initialize();
104
109
 
105
- return await manager._createControlsForm();
110
+ return manager._createControlsForm();
106
111
  }
107
112
 
108
- private async _createControlsForm(): Promise<HTMLElement> {
109
- this.monomerLibFileManager = await MonomerLibFileManager.getInstance(this.eventManager);
110
- const libraryControls = await this.createLibraryControls();
113
+ private _createControlsForm(): HTMLElement {
114
+ const libraryControls = this.createLibraryControls();
111
115
  const inputsForm = ui.form(libraryControls);
112
116
  $(inputsForm).addClass('monomer-lib-controls-form');
113
117
 
@@ -118,14 +122,13 @@ class LibraryControlsManager {
118
122
  this.userLibSettings = await getUserLibSettings();
119
123
  };
120
124
 
121
- private async updateControlsForm(): Promise<void> {
122
- const updatedForm = await this._createControlsForm();
125
+ private updateControlsForm(): void {
126
+ const updatedForm = this._createControlsForm();
123
127
  $('.monomer-lib-controls-form').replaceWith(updatedForm);
124
128
  }
125
129
 
126
- private async createLibraryControls(): Promise<DG.InputBase<boolean | null>[]> {
127
- const fileManager = await MonomerLibFileManager.getInstance(this.eventManager);
128
- const libFileNameList: string[] = fileManager.getValidLibraryPaths();
130
+ private createLibraryControls(): DG.InputBase<boolean | null>[] {
131
+ const libFileNameList: string[] = this.fileManager.getValidLibraryPaths();
129
132
  return libFileNameList.map((libFileName) => this.createLibInput(libFileName));
130
133
  }
131
134
 
@@ -134,8 +137,9 @@ class LibraryControlsManager {
134
137
  const libInput = ui.boolInput(
135
138
  libFileName,
136
139
  isMonomerLibrarySelected,
137
- (isSelected: boolean) => this.eventManager.updateLibrarySelectionStatus(libFileName, isSelected)
138
- );
140
+ (isSelected: boolean) => {
141
+ this.fileManager.eventManager.updateLibrarySelectionStatus(libFileName, isSelected);
142
+ });
139
143
  ui.tooltip.bind(libInput.root, `Include monomers from ${libFileName}`);
140
144
  const deleteIcon = ui.iconFA('trash-alt', () => this.promptForLibraryDeletion(libFileName));
141
145
  ui.tooltip.bind(deleteIcon, `Delete ${libFileName}`);
@@ -149,7 +153,8 @@ class LibraryControlsManager {
149
153
  ): Promise<void> {
150
154
  this.updateLibrarySettings(isMonomerLibrarySelected, libFileName);
151
155
  await setUserLibSettings(this.userLibSettings);
152
- await MonomerLibManager.instance.loadLibraries(true);
156
+ const monomerLibHelper = await getMonomerLibHelper();
157
+ await monomerLibHelper.loadLibraries(true);
153
158
  grok.shell.info('Monomer library user settings saved');
154
159
  }
155
160
 
@@ -172,8 +177,8 @@ class LibraryControlsManager {
172
177
  .onOK(async () => {
173
178
  try {
174
179
  const progressIndicator = DG.TaskBarProgressIndicator.create(`Deleting ${fileName} library`);
175
- this.updateLibrarySelectionStatus(false, fileName);
176
- await this.monomerLibFileManager.deleteLibraryFile(fileName);
180
+ await this.updateLibrarySelectionStatus(false, fileName);
181
+ await this.fileManager.deleteLibraryFile(fileName);
177
182
  progressIndicator.close();
178
183
  } catch (e) {
179
184
  console.error(e);
@@ -189,7 +194,7 @@ class DialogWrapper {
189
194
 
190
195
  private static _instance: DialogWrapper;
191
196
  private dialog?: DG.Dialog;
192
- private closeDialogSubject$ = new rxjs.Subject<void>();
197
+ private closeDialogSubject$ = new Subject<void>();
193
198
 
194
199
  static async showDialog(): Promise<void> {
195
200
  if (!DialogWrapper._instance) {
@@ -207,7 +212,7 @@ class DialogWrapper {
207
212
 
208
213
  private async getDialog(): Promise<DG.Dialog> {
209
214
  const eventManager = MonomerLibFileEventManager.getInstance();
210
- const widget = await MonomerLibraryManagerWidget.getContent(eventManager);
215
+ const widget = (await MonomerLibraryManagerWidget.getInstance()).widget;
211
216
  const dialog = ui.dialog(
212
217
  {
213
218
  title: 'Manage monomer libraries',
@@ -1,10 +1,17 @@
1
1
  /* Do not change these import lines to match external modules in webpack configuration */
2
2
  import * as grok from 'datagrok-api/grok';
3
3
  import * as ui from 'datagrok-api/ui';
4
+ import * as DG from 'datagrok-api/dg';
4
5
 
6
+ import wu from 'wu';
5
7
  import {Observable, Subject} from 'rxjs';
6
8
 
7
- import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types/index';
9
+ import {
10
+ IMonomerLib, Monomer, MonomerLibSummaryType, MonomerType, PolymerType, RGroup
11
+ } from '@datagrok-libraries/bio/src/types';
12
+ import {
13
+ HELM_REQUIRED_FIELD as REQ, HELM_RGROUP_FIELDS as RGP
14
+ } from '@datagrok-libraries/bio/src/utils/const';
8
15
  import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
9
16
 
10
17
  import '../../../css/cell-renderer.css';
@@ -27,18 +34,57 @@ export class MonomerLib implements IMonomerLib {
27
34
  }
28
35
  }
29
36
 
30
- getMonomer(polymerType: string, monomerSymbol: string): Monomer | null {
37
+ /** Creates missing {@link Monomer} */
38
+ addMissingMonomer(polymerType: PolymerType, monomerSymbol: string): Monomer {
39
+ let mSet = this._monomers[polymerType];
40
+ if (!mSet)
41
+ mSet = this._monomers[polymerType] = {};
42
+ const m = mSet[monomerSymbol] = {
43
+ [REQ.SYMBOL]: monomerSymbol,
44
+ [REQ.NAME]: monomerSymbol,
45
+ [REQ.MOLFILE]: '',
46
+ [REQ.AUTHOR]: 'MISSING',
47
+ [REQ.ID]: -1,
48
+ [REQ.RGROUPS]:
49
+ wu.count(1).take(9).map((i) => {
50
+ return {
51
+ /* eslint-disable no-multi-spaces */
52
+ // Samples // PEPTIDE RNA
53
+ [RGP.CAP_GROUP_SMILES]: '', // '[*:1][H]' '[*:1][H]'
54
+ [RGP.ALTERNATE_ID]: '', // 'R1-H' 'R1-H'
55
+ [RGP.CAP_GROUP_NAME]: '', // 'H' 'H'
56
+ [RGP.LABEL]: `R${i.toString()}`, // 'R1' 'R1'
57
+ /* eslint-enable no-multi-spaces */
58
+ } as RGroup;
59
+ }).toArray(),
60
+ [REQ.SMILES]: '',
61
+ [REQ.POLYMER_TYPE]: polymerType,
62
+ [REQ.MONOMER_TYPE]: undefined as unknown as MonomerType, // TODO: Can we get monomerType from atom of POM
63
+ [REQ.CREATE_DATE]: null,
64
+ } as Monomer;
65
+ return m;
66
+ }
67
+
68
+ getMonomer(polymerType: PolymerType, argMonomerSymbol: string): Monomer | null {
69
+ // Adjust RNA's 'R' for ribose to 'r' and 'P' for phosphate to 'p' for case-sensitive monomer names.
70
+ // There are uppercase 'R' and 'P' at RNA samples in test data 'helm2.csv' but lowercase in HELMCoreLibrary.json
71
+ let monomerSymbol = argMonomerSymbol;
72
+ if (polymerType == 'RNA' && monomerSymbol == 'R')
73
+ monomerSymbol = 'r';
74
+ if (polymerType == 'RNA' && monomerSymbol == 'P')
75
+ monomerSymbol = 'p';
76
+
31
77
  if (polymerType in this._monomers! && monomerSymbol in this._monomers![polymerType])
32
78
  return this._monomers![polymerType][monomerSymbol];
33
79
  else
34
80
  return null;
35
81
  }
36
82
 
37
- getPolymerTypes(): string[] {
38
- return Object.keys(this._monomers);
83
+ getPolymerTypes(): PolymerType[] {
84
+ return Object.keys(this._monomers) as PolymerType[];
39
85
  }
40
86
 
41
- getMonomerMolsByPolymerType(polymerType: string): { [monomerSymbol: string]: string } {
87
+ getMonomerMolsByPolymerType(polymerType: PolymerType): { [monomerSymbol: string]: string } {
42
88
  const res: { [monomerSymbol: string]: string } = {};
43
89
 
44
90
  Object.keys(this._monomers[polymerType] ?? {}).forEach((monomerSymbol) => {
@@ -48,14 +94,14 @@ export class MonomerLib implements IMonomerLib {
48
94
  return res;
49
95
  }
50
96
 
51
- getMonomerSymbolsByType(polymerType: string): string[] {
97
+ getMonomerSymbolsByType(polymerType: PolymerType): string[] {
52
98
  return Object.keys(this._monomers[polymerType]);
53
99
  }
54
100
 
55
101
  /** Get a list of monomers with specified element attached to specified
56
102
  * R-group
57
103
  * WARNING: RGroup numbering starts from 1, not 0*/
58
- getMonomerSymbolsByRGroup(rGroupNumber: number, polymerType: string, element?: string): string[] {
104
+ getMonomerSymbolsByRGroup(rGroupNumber: number, polymerType: PolymerType, element?: string): string[] {
59
105
  const monomerSymbols = this.getMonomerSymbolsByType(polymerType);
60
106
  let monomers = monomerSymbols.map((sym) => this.getMonomer(polymerType, sym));
61
107
  monomers = monomers.filter((el) => el !== null);
@@ -116,15 +162,38 @@ export class MonomerLib implements IMonomerLib {
116
162
  this._onChanged.next();
117
163
  }
118
164
 
165
+ getSummaryObj(): MonomerLibSummaryType {
166
+ const res: MonomerLibSummaryType = {};
167
+ const ptList: PolymerType[] = this.getPolymerTypes();
168
+ for (const pt of ptList)
169
+ res[pt] = this.getMonomerSymbolsByType(pt).length;
170
+ return res;
171
+ }
172
+
173
+ getSummaryDf(): DG.DataFrame {
174
+ const ptList = this.getPolymerTypes();
175
+
176
+ const countList: number[] = new Array<number>(ptList.length);
177
+ for (const [pt, i] of wu.enumerate(ptList))
178
+ countList[i] = this.getMonomerSymbolsByType(pt).length;
179
+
180
+ const resDf: DG.DataFrame = DG.DataFrame.fromColumns([
181
+ DG.Column.fromStrings('polymerType', ptList),
182
+ DG.Column.fromList(DG.COLUMN_TYPE.INT, 'count', countList),
183
+ ]);
184
+ return resDf;
185
+ }
186
+
187
+ /** @deprecated Keep for backward compatibility */
119
188
  getSummary(): string {
120
- const monTypeList: string[] = this.getPolymerTypes();
189
+ const monTypeList: PolymerType[] = this.getPolymerTypes();
121
190
  const resStr: string = monTypeList.length == 0 ? 'empty' : monTypeList.map((monType) => {
122
191
  return `${monType} ${this.getMonomerSymbolsByType(monType).length}`;
123
192
  }).join('\n');
124
193
  return resStr;
125
194
  }
126
195
 
127
- getTooltip(polymerType: string, monomerSymbol: string): HTMLElement {
196
+ getTooltip(polymerType: PolymerType, monomerSymbol: string): HTMLElement {
128
197
  // getTooltip(monomer: Monomer): HTMLElement;
129
198
  // getTooltip(monomerOrPolymerType: string | Monomer, symbol?: string): HTMLElement {
130
199
  // let polymerType: string;
@@ -2,9 +2,10 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as DG from 'datagrok-api/dg';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
 
5
+ import {ColumnInputOptions} from '@datagrok-libraries/utils/src/type-declarations';
6
+ import {delay} from '@datagrok-libraries/utils/src/test';
5
7
  import {ALPHABET, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
6
8
  import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
7
- import {ColumnInputOptions} from '@datagrok-libraries/utils/src/type-declarations';
8
9
 
9
10
  import {runKalign} from './multiple-sequence-alignment';
10
11
  import {pepseaMethods, runPepsea} from './pepsea';
@@ -36,9 +37,10 @@ export async function multipleSequenceAlignmentUI(
36
37
  const table = options.col?.dataFrame ?? grok.shell.t;
37
38
  const seqCol = options.col ?? table.columns.bySemType(DG.SEMTYPE.MACROMOLECULE);
38
39
  if (seqCol == null) {
39
- const errMsg = `MSAError: dataset doesn't conain any Macromolecule column`;
40
+ const errMsg: string = `Multiple sequence analysis requires a dataset with a macromolecule column.`;
40
41
  grok.shell.warning(errMsg);
41
42
  reject(new MsaWarning(errMsg));
43
+ return; // Prevents creating the MSA dialog
42
44
  }
43
45
 
44
46
  // UI for PepSea alignment
@@ -72,39 +74,50 @@ export async function multipleSequenceAlignmentUI(
72
74
 
73
75
  let performAlignment: (() => Promise<DG.Column<string> | null>) | undefined;
74
76
 
75
- //TODO: remove when the new version of datagrok-api is available
76
- //TODO: allow only macromolecule columns to be chosen
77
- const colInput = ui.columnInput('Sequence', table, seqCol, async () => {
77
+ let prevSeqCol = seqCol;
78
+ const colInput = ui.columnInput(
79
+ 'Sequence', table, seqCol,
80
+ async (valueCol: DG.Column) => {
81
+ if (!valueCol || valueCol.semType !== DG.SEMTYPE.MACROMOLECULE) {
82
+ okBtn.disabled = true;
83
+ await delay(0); // to
84
+ colInput.value = prevSeqCol as DG.Column<string>;
85
+ return;
86
+ }
87
+ prevSeqCol = valueCol;
88
+ okBtn.disabled = false;
78
89
  performAlignment = await onColInputChange(
79
90
  colInput.value, table, pepseaInputRootStyles, kalignInputRootStyles,
80
91
  methodInput, clustersColInput, gapOpenInput, gapExtendInput, terminalGapInput,
81
92
  );
82
- //@ts-ignore
83
93
  }, {filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE} as ColumnInputOptions
84
94
  ) as DG.InputBase<DG.Column<string>>;
85
95
  colInput.setTooltip('Sequences column to use for alignment');
86
96
  const clustersColInput = ui.columnInput('Clusters', table, options.clustersCol);
87
97
  clustersColInput.nullable = true;
88
- colInput.fireChanged();
98
+
99
+ const dlg = ui.dialog('MSA')
100
+ .add(colInput)
101
+ .add(clustersColInput)
102
+ .add(methodInput)
103
+ .add(msaParamsDiv)
104
+ .add(msaParamsButton)
105
+ .add(kalignVersionDiv)
106
+ .onOK(async () => { await onDialogOk(colInput, table, performAlignment, resolve, reject); });
107
+ const okBtn = dlg.getButton('OK');
108
+
109
+ colInput.fireChanged(); // changes okBtn
89
110
  //if column is specified (from tests), run alignment and resolve with the result
90
111
  if (options.col) {
91
112
  performAlignment = await onColInputChange(
92
113
  options.col, table, pepseaInputRootStyles, kalignInputRootStyles,
93
114
  methodInput, clustersColInput, gapOpenInput, gapExtendInput, terminalGapInput,
94
115
  );
95
-
96
116
  await onDialogOk(colInput, table, performAlignment, resolve, reject);
97
- return;
117
+ return; // Prevents show the dialog
98
118
  }
99
- const _dlg = ui.dialog('MSA')
100
- .add(colInput)
101
- .add(clustersColInput)
102
- .add(methodInput)
103
- .add(msaParamsDiv)
104
- .add(msaParamsButton)
105
- .add(kalignVersionDiv)
106
- .onOK(async () => { await onDialogOk(colInput, table, performAlignment, resolve, reject); })
107
- .show();
119
+
120
+ dlg.show();
108
121
  });
109
122
  }
110
123
 
@@ -4,14 +4,16 @@ import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
5
 
6
6
  import {_toAtomicLevel} from '@datagrok-libraries/bio/src/monomer-works/to-atomic-level';
7
- import {helm2mol} from './helm-to-molfile/utils';
8
7
  import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
9
- import {checkInputColumnUI} from './check-input-column';
10
- import {getMonomerLibHelper} from '../package';
11
8
  import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
12
9
  import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
13
10
 
14
- export async function sequenceToMolfile(df: DG.DataFrame, macroMolecule: DG.Column, nonlinear: boolean): Promise<void> {
11
+ import {helm2mol} from './helm-to-molfile/utils';
12
+ import {checkInputColumnUI} from './check-input-column';
13
+
14
+ export async function sequenceToMolfile(
15
+ df: DG.DataFrame, macroMolecule: DG.Column, nonlinear: boolean, monomerLib: IMonomerLib
16
+ ): Promise<void> {
15
17
  if (DG.Func.find({package: 'Chem', name: 'getRdKitModule'}).length === 0) {
16
18
  grok.shell.warning('Transformation to atomic level requires package "Chem" installed.');
17
19
  return;
@@ -20,12 +22,10 @@ export async function sequenceToMolfile(df: DG.DataFrame, macroMolecule: DG.Colu
20
22
  const seqSh = SeqHandler.forColumn(macroMolecule);
21
23
  if (!seqSh.isHelm())
22
24
  macroMolecule = seqSh.convert(NOTATION.HELM);
23
- helm2mol(df, macroMolecule);
24
- return;
25
+ return helm2mol(df, macroMolecule);
25
26
  }
26
27
  if (!checkInputColumnUI(macroMolecule, 'To Atomic Level'))
27
28
  return;
28
- const monomerLib: IMonomerLib = getMonomerLibHelper().getBioLib();
29
29
  const atomicLevelRes = await _toAtomicLevel(df, macroMolecule, monomerLib);
30
30
  if (atomicLevelRes.col !== null) {
31
31
  df.columns.add(atomicLevelRes.col, true);
@@ -953,16 +953,26 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
953
953
 
954
954
  // region updatePositions
955
955
 
956
+ /** positionNames and positionLabel can be set up through the column's tags only */
957
+ const positionNamesTxt = this.seqCol.getTag(bioTAGS.positionNames);
958
+ const positionLabelsTxt = this.seqCol.getTag(bioTAGS.positionLabels);
959
+
960
+ // WebLogo in the column tooltip / widget is limited, speed up for long sequences
961
+ let splitLimit: number | undefined = undefined;
962
+ if (!positionNamesTxt && this.endPositionName && /\d+/.test(this.endPositionName))
963
+ splitLimit = Number(this.endPositionName);
964
+ else if (positionNamesTxt && this.endPositionName) {
965
+ splitLimit = positionNamesTxt.split(positionSeparator).indexOf(this.endPositionName);
966
+ splitLimit = splitLimit !== -1 ? splitLimit : undefined;
967
+ }
968
+
956
969
  const dfFilter = this.getFilter();
957
970
  const maxLength: number = dfFilter.trueCount === 0 ? this.seqHandler!.maxLength :
958
971
  wu.count(0).take(this.seqHandler!.length).map((rowIdx) => {
959
- const mList = this.seqHandler!.getSplitted(rowIdx);
972
+ const mList = this.seqHandler!.getSplitted(rowIdx, splitLimit);
960
973
  return dfFilter.get(rowIdx) && !!mList ? mList.length : 0;
961
974
  }).reduce((max, l) => Math.max(max, l), 0);
962
975
 
963
- /** positionNames and positionLabel can be set up through the column's tags only */
964
- const positionNamesTxt = this.seqCol.getTag(bioTAGS.positionNames);
965
- const positionLabelsTxt = this.seqCol.getTag(bioTAGS.positionLabels);
966
976
  this.positionNames = !!positionNamesTxt ? positionNamesTxt.split(positionSeparator).map((v) => v.trim()) :
967
977
  [...Array(maxLength).keys()].map((jPos) => `${jPos + 1}`)/* fallback if tag is not provided */;
968
978
  this.positionLabels = !!positionLabelsTxt ? positionLabelsTxt.split(positionSeparator).map((v) => v.trim()) :
@@ -135,9 +135,15 @@ export class HelmBioFilter extends BioFilterBase<BioFilterProps> /* implements I
135
135
  }
136
136
 
137
137
  async substructureSearch(column: DG.Column): Promise<DG.BitSet | null> {
138
- await delay(10);
139
- const res = await helmSubstructureSearch(this.props.substructure, column);
140
- return res;
138
+ const logPrefix = `${this.viewerToLog()}.substructureSearch( column = <${column.name}> )`;
139
+ _package.logger.debug(`${logPrefix}, start`);
140
+ try {
141
+ await delay(10);
142
+ const res = await helmSubstructureSearch(this.props.substructure, column);
143
+ return res;
144
+ } finally {
145
+ _package.logger.debug(`${logPrefix}, end`);
146
+ }
141
147
  }
142
148
 
143
149
  // // -- IRenderer --
@@ -273,12 +273,12 @@ export class BioSubstructureFilter extends DG.Filter implements IRenderer {
273
273
  async awaitRendered(timeout: number = 10000): Promise<void> {
274
274
  const callLog = `awaitRendered( ${timeout} )`;
275
275
  const logPrefix = `${this.filterToLog()}.${callLog}`;
276
- await delay(0);
276
+ await delay(10);
277
277
  await testEvent(this.onRendered, () => {
278
278
  this.logger.debug(`${logPrefix}, ` + '_onRendered event caught');
279
279
  }, () => {
280
280
  this.invalidate(callLog);
281
- }, timeout, `${logPrefix} ${timeout} timeout`);
281
+ }, timeout, `${logPrefix} timeout`);
282
282
 
283
283
  // Rethrow stored syncer error (for test purposes)
284
284
  const viewErrors = this.filterSyncer.resetErrors();
package/webpack.config.js CHANGED
@@ -43,6 +43,7 @@ module.exports = {
43
43
  'wu': 'wu',
44
44
  'scil': 'scil',
45
45
  'org': 'org',
46
+ // 'JSDraw2': 'JSDraw2',
46
47
  },
47
48
  output: {
48
49
  filename: '[name].js',