@datagrok/sequence-translator 1.4.0 → 1.4.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.
@@ -1,5 +1,5 @@
1
1
  n,seqs
2
- 1,R-F-C(1)-T-G-H-F-Y-P-C(1)-meI
2
+ 1,R-F-C(1)-T-G-H-F-Y-G-H-F-Y-G-H-F-Y-P-C(1)-meI
3
3
  2,C(1)-T-G-H-F-Y-P-C(1)-meI
4
4
  3,R-F-C(1)-T-G-H-F-Y-P-C(1)
5
5
  4,C(1)-T-G-H-F-H-P-C(1)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/sequence-translator",
3
3
  "friendlyName": "Sequence Translator",
4
- "version": "1.4.0",
4
+ "version": "1.4.2",
5
5
  "author": {
6
6
  "name": "Alexey Choposky",
7
7
  "email": "achopovsky@datagrok.ai"
@@ -22,8 +22,8 @@
22
22
  }
23
23
  ],
24
24
  "dependencies": {
25
- "@datagrok-libraries/bio": "^5.42.15",
26
- "@datagrok-libraries/chem-meta": "^1.2.5",
25
+ "@datagrok-libraries/bio": "^5.44.0",
26
+ "@datagrok-libraries/chem-meta": "^1.2.7",
27
27
  "@datagrok-libraries/tutorials": "^1.4.0",
28
28
  "@datagrok-libraries/utils": "^4.3.0",
29
29
  "@types/react": "^18.0.15",
@@ -41,7 +41,7 @@
41
41
  "devDependencies": {
42
42
  "@datagrok-libraries/helm-web-editor": "^1.1.11",
43
43
  "@datagrok-libraries/js-draw-lite": "^0.0.8",
44
- "@datagrok/bio": "^2.15.1",
44
+ "@datagrok/bio": "^2.15.3",
45
45
  "@datagrok/helm": "^2.5.0",
46
46
  "@datagrok/chem": "^1.12.0",
47
47
  "@types/jquery": "^3.5.14",
@@ -19,7 +19,7 @@ export class ColoredTextInput {
19
19
  /** Resize, no scrolls */
20
20
  resizeable: boolean = true
21
21
  ) {
22
- $(this.root).addClass('colored-text-input');
22
+ $(this.root).addClass('st-colored-text-input');
23
23
  if (resizeable) {
24
24
  // make input field automatically resizeable
25
25
  this.textInputBase.onChanged.subscribe(
@@ -1,4 +1,4 @@
1
- .colored-text-input > textarea {
1
+ .st-colored-text-input > textarea {
2
2
  width: 100%;
3
3
  -webkit-text-fill-color: transparent;
4
4
  background-color: transparent;
@@ -8,7 +8,7 @@
8
8
  height: 22px; /* Fine tuned value to avoid "jumping" of the textarea upon autoresize */
9
9
  }
10
10
 
11
- .colored-text-input > div {
11
+ .st-colored-text-input > div {
12
12
  /* The values here are fine tuned to those of the ui.input textarea in order
13
13
  * to achieve precise overlap */
14
14
  overflow: auto;
@@ -25,5 +25,4 @@
25
25
  color: transparent;
26
26
  white-space: pre-wrap;
27
27
  word-wrap: break-word;
28
- padding-left: 35px;
29
28
  }
@@ -96,10 +96,10 @@ export class PatternAppRightSection {
96
96
  }
97
97
 
98
98
  const message = ui.divV([
99
- ui.divText(`Author: ${author}`),
100
99
  ui.divText(`Pattern Name: ${patternName}`),
101
- ui.divText(`Created: ${new Date(createDate).toLocaleString()}`),
102
- ui.divText(`Modified: ${new Date(modifyDate).toLocaleString()}`),
100
+ ui.divText(`Author: ${author}`),
101
+ ui.divText(`Created: ${getInfoTimestamp(new Date(createDate))}`),
102
+ ui.divText(`Modified: ${getInfoTimestamp(new Date(modifyDate))}`),
103
103
  ]);
104
104
  grok.shell.info(message);
105
105
  }
@@ -195,3 +195,6 @@ class OverwritePatternDialog {
195
195
  }
196
196
  }
197
197
 
198
+ function getInfoTimestamp(date: Date): string {
199
+ return date.toLocaleString().split(':').slice(0, -1).join(':');
200
+ }
@@ -33,7 +33,15 @@ class StructureAppLayout {
33
33
  this.onInvalidInput = new rxjs.Subject<string>();
34
34
  this.inputBase = Object.fromEntries(
35
35
  STRANDS.map(
36
- (key) => [key, ui.input.textArea(key.toUpperCase(), {value: '', onValueChanged: () => { this.onInput.next(); }})]
36
+ (key) => {
37
+ const input = ui.input.textArea(key.toUpperCase(), {value: '', onValueChanged: () => {
38
+ this.onInput.next();
39
+ // WARNING: this fine tuning is necessary to fix layout within ui.form
40
+ // js-api version ^1.21
41
+ $(input.root.getElementsByTagName('div')).css('padding-left', '38px');
42
+ }});
43
+ return [key, input];
44
+ }
37
45
  )
38
46
  );
39
47
  this.useChiralInput = ui.input.bool('Use chiral', {value: true});
@@ -165,11 +173,6 @@ class StructureAppLayout {
165
173
  }
166
174
 
167
175
  private getMolfile(ss: StrandData, as: StrandData, as2: StrandData): string {
168
- // if (ss.strand === '' && (as.strand !== '' || as2.strand !== '')) {
169
- // this.onInvalidInput.next();
170
- // return '';
171
- // }
172
-
173
176
  return getLinkedMolfile(ss, as, as2, this.useChiralInput.value!, this.th);
174
177
  }
175
178
 
@@ -24,10 +24,6 @@
24
24
  table-layout: fixed;
25
25
  }
26
26
 
27
- /* .st-translator-output-table table tbody tr td { */
28
- /* max-width: 20%; */
29
- /* } */
30
-
31
27
  .st-translator-output-table td {
32
28
  padding-top: 6px;
33
29
  padding-bottom: 6px;
@@ -10,8 +10,6 @@ import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
10
10
 
11
11
  import {DEFAULT_FORMATS} from '../../common/model/const';
12
12
  import {download} from '../../common/model/helpers';
13
- import {FormatDetector} from '../../common/model/parsing-validation/format-detector';
14
- import {SequenceValidator} from '../../common/model/parsing-validation/sequence-validator';
15
13
  import {ColoredTextInput} from '../../common/view/components/colored-input/colored-text-input';
16
14
  import {highlightInvalidSubsequence} from '../../common/view/components/colored-input/input-painters';
17
15
  import {MoleculeImage} from '../../common/view/components/molecule-img';
@@ -20,11 +18,11 @@ import {IsolatedAppUIBase} from '../../common/view/isolated-app-ui';
20
18
  import {MonomerLibViewer} from '../../common/view/monomer-lib-viewer';
21
19
  import {SequenceToMolfileConverter} from '../../structure/model/sequence-to-molfile';
22
20
  import {convert, getSupportedTargetFormats, getTranslatedSequences} from '../model/conversion-utils';
23
- import {FormatConverter} from '../model/format-converter';
24
21
  import {ITranslationHelper} from '../../../types';
25
22
 
26
23
  import {NUCLEOTIDES_FORMAT, SEQUENCE_COPIED_MSG, SEQ_TOOLTIP_MSG} from './const';
27
24
  import './style.css';
25
+ import $ from 'cash-dom';
28
26
  import {_package} from '../../../package';
29
27
 
30
28
  const enum REQUIRED_COLUMN_LABEL {
@@ -55,7 +53,12 @@ class TranslatorAppLayout {
55
53
  await this.updateMolImg();
56
54
  }
57
55
  });
58
- this.sequenceInputBase = ui.input.textArea('', {value: DEFAULT_AXOLABS_INPUT, onValueChanged: () => { this.onInput.next(); }});
56
+
57
+ $(this.formatChoiceInput.root.getElementsByTagName('select')[0]).css('width', '20%');
58
+
59
+ this.sequenceInputBase = ui.input.textArea(
60
+ '', {value: DEFAULT_AXOLABS_INPUT, onValueChanged: () => { this.onInput.next(); }}
61
+ );
59
62
 
60
63
  this.init();
61
64
 
@@ -457,9 +460,9 @@ class ColumnInputsManager {
457
460
  this.selectColumnIfTableNotNull(selectedTable, selectedColumnName, columnLabel);
458
461
 
459
462
  const input = ui.input.choice(`${columnLabel}`, {
460
- value: selectedColumnName, items: columnNames,
461
- onValueChanged: (value) => this.selectColumnIfTableNotNull(selectedTable, value, columnLabel)
462
- }
463
+ value: selectedColumnName, items: columnNames,
464
+ onValueChanged: (value) => this.selectColumnIfTableNotNull(selectedTable, value, columnLabel)
465
+ }
463
466
  );
464
467
 
465
468
  return input;
@@ -24,7 +24,7 @@ export async function demoOligoStructureUI() {
24
24
  await tryCatch(async () => {
25
25
  async function setInputValue(idx: number, sequence: string): Promise<void> {
26
26
  await delay(500);
27
- const textInputs: NodeListOf<HTMLTextAreaElement> = document.querySelectorAll('.colored-text-input > textarea');
27
+ const textInputs: NodeListOf<HTMLTextAreaElement> = document.querySelectorAll('.st-colored-text-input > textarea');
28
28
  const textarea = textInputs[idx];
29
29
  textarea.value = sequence;
30
30
  const event = new Event('input');
@@ -2,7 +2,7 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
- import {runTests, tests, TestContext} from '@datagrok-libraries/utils/src/test';
5
+ import {runTests, tests, TestContext, initAutoTests as initTests } from '@datagrok-libraries/utils/src/test';
6
6
 
7
7
  import './tests/formats-to-helm';
8
8
  import './tests/helm-to-nucleotides';
@@ -10,6 +10,7 @@ import './tests/formats-support';
10
10
  import './tests/files-tests';
11
11
  import './tests/polytool-convert-tests';
12
12
  import './tests/polytool-enumerate-tests';
13
+ import './tests/polytool-enumerate-breadth-tests';
13
14
 
14
15
  import {OligoToolkitTestPackage} from './tests/utils';
15
16
 
@@ -25,3 +26,8 @@ export async function test(category: string, test: string, testContext: TestCont
25
26
  const data = await runTests({category, test, testContext, verbose: true});
26
27
  return DG.DataFrame.fromObjects(data)!;
27
28
  }
29
+
30
+ //name: initAutoTests
31
+ export async function initAutoTests() {
32
+ await initTests(_package, _package.getModule('package-test.js'));
33
+ }
@@ -269,9 +269,7 @@ export class Chain {
269
269
  export function doPolyToolConvert(sequences: string[], rules: Rules): string[] {
270
270
  const helms = new Array<string>(sequences.length);
271
271
  for (let i = 0; i < sequences.length; i++) {
272
- if (sequences[i] === undefined)
273
- helms[i] = '';
274
- else {
272
+ if (sequences[i] === undefined) { helms[i] = ''; } else {
275
273
  const chain = Chain.fromNotation(sequences[i], rules);
276
274
  helms[i] = chain.getHelm();
277
275
  }
@@ -74,14 +74,14 @@ export class PolyToolConvertFuncEditor {
74
74
 
75
75
  public async showDialog(): Promise<DG.Column<string>> {
76
76
  const formDiv = ui.div([
77
- this.inputs.table,
78
- this.inputs.seqCol,
79
- this.inputs.generateHelm,
80
- this.inputs.chiralityEngine,
81
- this.inputs.rules.header,
82
- this.inputs.rules.form,
83
- ],
84
- {style: {minWidth: '320px'}});
77
+ this.inputs.table,
78
+ this.inputs.seqCol,
79
+ this.inputs.generateHelm,
80
+ this.inputs.chiralityEngine,
81
+ this.inputs.rules.header,
82
+ this.inputs.rules.form,
83
+ ],
84
+ {style: {minWidth: '320px'}});
85
85
 
86
86
  return new Promise((resolve, reject) => {
87
87
  ui.dialog({title: PT_UI_DIALOG_CONVERSION})
@@ -3,7 +3,6 @@ import * as grok from 'datagrok-api/grok';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
  import * as DG from 'datagrok-api/dg';
5
5
 
6
- import $ from 'cash-dom';
7
6
  import {Unsubscribable} from 'rxjs';
8
7
 
9
8
  import {getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
@@ -24,6 +23,16 @@ import {
24
23
 
25
24
  import {_package} from '../package';
26
25
 
26
+ type PolyToolConvertSerialized = {
27
+ generateHelm: boolean;
28
+ chiralityEngine: boolean;
29
+ };
30
+
31
+ type PolyToolEnumerateChemSerialized = {
32
+ mol: string;
33
+ screenLibrary: string | null;
34
+ }
35
+
27
36
  export function polyToolEnumerateChemUI(cell?: DG.Cell): void {
28
37
  getPolyToolEnumerationChemDialog(cell)
29
38
  .then((dialog) => {
@@ -80,7 +89,6 @@ export async function getPolyToolConvertDialog(targetCol?: DG.Column): Promise<D
80
89
  rulesForm
81
90
  ]);
82
91
 
83
-
84
92
  const exec = async (): Promise<void> => {
85
93
  try {
86
94
  const ruleFileList = await ruleInputs.getActive();
@@ -96,7 +104,17 @@ export async function getPolyToolConvertDialog(targetCol?: DG.Column): Promise<D
96
104
  subs.push(dialog.onClose.subscribe(() => {
97
105
  destroy();
98
106
  }));
99
-
107
+ dialog.history(
108
+ /* getInput */ (): PolyToolConvertSerialized => {
109
+ return {
110
+ generateHelm: generateHelmChoiceInput.value,
111
+ chiralityEngine: chiralityEngineInput.value,
112
+ };
113
+ },
114
+ /* applyInput */ (x: PolyToolConvertSerialized): void => {
115
+ generateHelmChoiceInput.value = x.generateHelm;
116
+ chiralityEngineInput.value = x.chiralityEngine;
117
+ });
100
118
  return dialog;
101
119
  } catch (err: any) {
102
120
  destroy(); // on failing to build a dialog
@@ -110,7 +128,6 @@ async function getPolyToolEnumerationChemDialog(cell?: DG.Cell): Promise<DG.Dial
110
128
  for (const sub of subs) sub.unsubscribe();
111
129
  };
112
130
  try {
113
-
114
131
  const [libList, helmHelper] = await Promise.all([
115
132
  getLibrariesList(), getHelmHelper()]);
116
133
 
@@ -135,15 +152,15 @@ async function getPolyToolEnumerationChemDialog(cell?: DG.Cell): Promise<DG.Dial
135
152
  molInput.setMolFile(molfileValue);
136
153
 
137
154
  //const helmInput = helmHelper.createHelmInput('Macromolecule', {value: helmValue});
138
- const screenLibrary = ui.input.choice('Library to use', {value: null, items: libList});
155
+ const screenLibraryInput = ui.input.choice('Library to use', {value: null, items: libList});
139
156
 
140
157
  molInput.root.setAttribute('style', `min-width:250px!important;`);
141
158
  molInput.root.setAttribute('style', `max-width:250px!important;`);
142
- screenLibrary.input.setAttribute('style', `min-width:250px!important;`);
159
+ screenLibraryInput.input.setAttribute('style', `min-width:250px!important;`);
143
160
 
144
161
  const div = ui.div([
145
162
  molInput.root,
146
- screenLibrary.root
163
+ screenLibraryInput.root
147
164
  ]);
148
165
 
149
166
  subs.push(grok.events.onCurrentCellChanged.subscribe(() => {
@@ -162,7 +179,7 @@ async function getPolyToolEnumerationChemDialog(cell?: DG.Cell): Promise<DG.Dial
162
179
  } else if (!molString.includes('R#')) {
163
180
  grok.shell.warning('PolyTool: no R group was provided');
164
181
  } else {
165
- const molecules = await getEnumerationChem(molString, screenLibrary.value!);
182
+ const molecules = await getEnumerationChem(molString, screenLibraryInput.value!);
166
183
  const molCol = DG.Column.fromStrings('Enumerated', molecules);
167
184
  const df = DG.DataFrame.fromColumns([molCol]);
168
185
  grok.shell.addTableView(df);
@@ -184,6 +201,17 @@ async function getPolyToolEnumerationChemDialog(cell?: DG.Cell): Promise<DG.Dial
184
201
  subs.push(dialog.onClose.subscribe(() => {
185
202
  destroy();
186
203
  }));
204
+ dialog.history(
205
+ /* getInput */ (): PolyToolEnumerateChemSerialized => {
206
+ return {
207
+ mol: molInput.getMolFile(),
208
+ screenLibrary: screenLibraryInput.value,
209
+ };
210
+ },
211
+ /* applyInput */ (x: PolyToolEnumerateChemSerialized): void => {
212
+ molInput.setMolFile(x.mol);
213
+ screenLibraryInput.value = x.screenLibrary;
214
+ });
187
215
  return dialog;
188
216
  } catch (err: any) {
189
217
  destroy();
@@ -209,7 +237,7 @@ export async function polyToolConvert(
209
237
 
210
238
  const resHelmColName = getUnusedName(table, `transformed(${seqCol.name})`);
211
239
  const resHelmCol = DG.Column.fromType(DG.COLUMN_TYPE.STRING, resHelmColName, resList.length)
212
- .init((rowIdx: number) => { return resList[rowIdx];});
240
+ .init((rowIdx: number) => { return resList[rowIdx]; });
213
241
  resHelmCol.semType = DG.SEMTYPE.MACROMOLECULE;
214
242
  resHelmCol.meta.units = NOTATION.HELM;
215
243
  resHelmCol.setTag(DG.TAGS.CELL_RENDERER, 'helm');
@@ -217,7 +245,7 @@ export async function polyToolConvert(
217
245
 
218
246
  const seqHelper: ISeqHelper = await getSeqHelper();
219
247
  const toAtomicLevelRes = await seqHelper.helmToAtomicLevel(resHelmCol, chiralityEngine, /* highlight */ generateHelm);
220
- const resMolCol = toAtomicLevelRes.molCol;
248
+ const resMolCol = toAtomicLevelRes.molCol!;
221
249
  resMolCol.name = getUnusedName(table, `molfile(${seqCol.name})`);
222
250
  resMolCol.semType = DG.SEMTYPE.MOLECULE;
223
251
  if (table) {
@@ -60,12 +60,11 @@ M END`;
60
60
 
61
61
  export async function getEnumerationChem(molString: string, screenLibrary: string):
62
62
  Promise<string[]> {
63
-
64
63
  const variableMonomers = await getAvailableMonomers(screenLibrary);
65
64
  const variableMols = await getAvailableMonomerMols(screenLibrary);
66
65
  const enumerations = new Array<string>(variableMonomers.length);
67
66
 
68
- const rdkitModule: RDModule = await grok.functions.call('Chem:getRdKitModule');
67
+ const rdkitModule: RDModule = await grok.functions.call('Chem:getRdKitModule');
69
68
  const molScaffold: RDMol = rdkitModule.get_mol(molString);
70
69
  const smiScaffold = molScaffold.get_smiles();
71
70
  molScaffold.delete();
@@ -73,7 +72,6 @@ export async function getEnumerationChem(molString: string, screenLibrary: strin
73
72
  const smilesSubsts = new Array<string>(variableMonomers.length);
74
73
 
75
74
  for (let i = 0; i < variableMonomers.length; i++) {
76
-
77
75
  const name = variableMonomers[i];
78
76
  const molBlock = variableMols[name];
79
77
  const molSubst: RDMol = rdkitModule.get_mol(molBlock);
@@ -87,18 +85,16 @@ export async function getEnumerationChem(molString: string, screenLibrary: strin
87
85
  //TODO: use RDKit linking function when exposed
88
86
  const smiResRaw = `${smiScaffold}.${smilesSubsts[i]}`.replaceAll('[1*]C', 'C([1*])').replaceAll('[1*]c', 'c([1*])').replaceAll('[1*]O', 'O([1*])').replaceAll('[1*]N', 'N([1*])');
89
87
  const smiRes = `${smiResRaw}`.replaceAll('([1*])', '9').replaceAll('[1*]', '9');
90
- molRes = rdkitModule.get_mol(smiRes, JSON.stringify({mappedDummiesAreRGroups: true}))
91
- let molV3 = molRes.get_v3Kmolblock();
88
+ molRes = rdkitModule.get_mol(smiRes, JSON.stringify({mappedDummiesAreRGroups: true}));
89
+ const molV3 = molRes.get_v3Kmolblock();
92
90
  enumerations[i] = molV3;
93
- }
94
- catch(err:any) {
91
+ } catch (err:any) {
95
92
  enumerations[i] = '';
96
- }
97
- finally {
93
+ } finally {
98
94
  molRes?.delete();
99
95
  }
100
96
  }
101
-
97
+
102
98
 
103
99
  return enumerations;
104
100
  }
@@ -10,7 +10,7 @@ import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
10
10
  import {HelmAtom, HelmMol} from '@datagrok-libraries/helm-web-editor/src/types/org-helm';
11
11
  import {getHelmHelper, HelmInputBase} from '@datagrok-libraries/bio/src/helm/helm-helper';
12
12
  import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
13
- import {HelmType, ISeqMonomer} from '@datagrok-libraries/bio/src/helm/types';
13
+ import {HelmType, PolymerType} from '@datagrok-libraries/bio/src/helm/types';
14
14
  import {helmTypeToPolymerType} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
15
15
  import {getSeqHelper, ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
16
16
  import '@datagrok-libraries/bio/src/types/input';
@@ -25,6 +25,7 @@ import {getLibrariesList} from './utils';
25
25
  import {doPolyToolEnumerateHelm, PT_HELM_EXAMPLE} from './pt-enumeration-helm';
26
26
  import {PolyToolPlaceholdersInput} from './pt-placeholders-input';
27
27
  import {defaultErrorHandler} from '../utils/err-info';
28
+ import {PolyToolPlaceholdersBreadthInput} from './pt-placeholders-breadth-input';
28
29
  import {PT_UI_DIALOG_ENUMERATION} from './const';
29
30
 
30
31
  import {_package} from '../package';
@@ -32,15 +33,17 @@ import {_package} from '../package';
32
33
  type PolyToolEnumerateInputs = {
33
34
  macromolecule: HelmInputBase;
34
35
  placeholders: PolyToolPlaceholdersInput;
36
+ placeholdersBreadth: PolyToolPlaceholdersBreadthInput;
35
37
  enumeratorType: DG.ChoiceInput<PolyToolEnumeratorType>
36
38
  trivialNameCol: InputColumnBase,
37
39
  toAtomicLevel: DG.InputBase<boolean>;
38
40
  keepOriginal: DG.InputBase<boolean>;
39
41
  };
40
42
 
41
- type PolyToolEnumerateSerialized = {
43
+ type PolyToolEnumerateHelmSerialized = {
42
44
  macromolecule: string;
43
45
  placeholders: string;
46
+ placeholdersBreadth: string;
44
47
  enumeratorType: PolyToolEnumeratorType;
45
48
  trivialNameCol: string;
46
49
  toAtomicLevel: boolean;
@@ -82,9 +85,9 @@ export async function polyToolEnumerateHelmUI(cell?: DG.Cell): Promise<void> {
82
85
  if (isFirstShow) {
83
86
  const dialogInputList = dialog.inputs;
84
87
  const dialogRootCash = $(dialog.root);
85
- const contentMaxHeight = maxHeight
86
- - dialogRootCash.find('div.d4-dialog-header').get(0)!.offsetHeight
87
- - dialogRootCash.find('div.d4-dialog-footer').get(0)!.offsetHeight;
88
+ const contentMaxHeight = maxHeight -
89
+ dialogRootCash.find('div.d4-dialog-header').get(0)!.offsetHeight -
90
+ dialogRootCash.find('div.d4-dialog-footer').get(0)!.offsetHeight;
88
91
 
89
92
  // dialog.inputs2.macromolecule.root.style.backgroundColor = '#CCFFCC';
90
93
 
@@ -136,20 +139,27 @@ async function getPolyToolEnumerateDialog(
136
139
  const trivialNameSampleDiv = ui.divText('', {style: {marginLeft: '8px', marginTop: '2px'}});
137
140
  const warningsTextDiv = ui.divText('', {style: {color: 'red'}});
138
141
  inputs = {
142
+ macromolecule: helmHelper.createHelmInput(
143
+ 'Macromolecule', {editable: false}),
144
+ placeholders: await PolyToolPlaceholdersInput.create(
145
+ 'Placeholders', {
146
+ showAddNewRowIcon: true,
147
+ showRemoveRowIcon: true,
148
+ showRowHeader: false,
149
+ showCellTooltip: false,
150
+ }),
139
151
  enumeratorType: ui.input.choice<PolyToolEnumeratorType>(
140
152
  'Enumerator type', {
141
153
  value: PolyToolEnumeratorTypes.Single,
142
154
  items: Object.values(PolyToolEnumeratorTypes)
143
155
  }) as DG.ChoiceInput<PolyToolEnumeratorType>,
144
- macromolecule: helmHelper.createHelmInput(
145
- 'Macromolecule', {editable: false}),
146
- placeholders: await PolyToolPlaceholdersInput.create(
147
- 'Placeholders', {
156
+ placeholdersBreadth: await PolyToolPlaceholdersBreadthInput.create(
157
+ 'Breadth', {
148
158
  showAddNewRowIcon: true,
149
159
  showRemoveRowIcon: true,
150
160
  showRowHeader: false,
151
161
  showCellTooltip: false,
152
- }/*, 2/**/),
162
+ }),
153
163
  toAtomicLevel: ui.input.bool(
154
164
  'To atomic level', {value: false}),
155
165
  keepOriginal: ui.input.bool(
@@ -180,9 +190,9 @@ async function getPolyToolEnumerateDialog(
180
190
  inputs.placeholders.addValidator((value: string): string | null => {
181
191
  const errors: string[] = [];
182
192
  try {
183
- const missedMonomerList: ISeqMonomer[] = [];
184
- for (const [posVal, monomerSymbolList] of Object.entries(inputs.placeholders.placeholdersValue)) {
185
- const pos = parseInt(posVal);
193
+ const missedMonomerList: { polymerType: PolymerType, symbol: string }[] = [];
194
+ for (const ph of inputs.placeholders.placeholdersValue) {
195
+ const pos = ph.position;
186
196
  if (pos >= inputs.macromolecule.molValue.atoms.length) {
187
197
  errors.push(`There is no monomer at position ${pos + 1}.`);
188
198
  continue;
@@ -190,7 +200,7 @@ async function getPolyToolEnumerateDialog(
190
200
  const a = inputs.macromolecule.molValue.atoms[pos];
191
201
  const helmType: HelmType = a.biotype()!;
192
202
  const polymerType = helmTypeToPolymerType(helmType);
193
- for (const symbol of monomerSymbolList) {
203
+ for (const symbol of ph.monomers) {
194
204
  const substituteMonomer = monomerLib.getMonomer(polymerType, symbol)!;
195
205
  // TODO: Check substitution monomer is presented in the library
196
206
  if (!substituteMonomer || !substituteMonomer.lib)
@@ -228,8 +238,8 @@ async function getPolyToolEnumerateDialog(
228
238
  const hoveredAtom = helmHelper.getHoveredAtom(argsX, argsY, mol, inputs.macromolecule.root.clientHeight);
229
239
  if (hoveredAtom) {
230
240
  const hoveredAtomContIdx = hoveredAtom._parent.atoms.indexOf(hoveredAtom);
231
- const hoveredAtomContIdxStr = (hoveredAtomContIdx + 1).toString();
232
- const substitutingMonomers = inputs.placeholders.placeholdersValue[hoveredAtomContIdx];
241
+ const substitutingMonomers = inputs.placeholders.placeholdersValue
242
+ .find((ph) => ph.position === hoveredAtomContIdx)?.monomers;
233
243
 
234
244
  if (substitutingMonomers) {
235
245
  const cnt = ui.divText(substitutingMonomers.join(', '));
@@ -259,9 +269,9 @@ async function getPolyToolEnumerateDialog(
259
269
  let rowIdx = posList.indexOf(clickedAtomContIdxStr);
260
270
  if (rowIdx === -1) {
261
271
  rowIdx = posList.findIndex((v) => isNaN(v));
262
- if (rowIdx === -1) {
272
+ if (rowIdx === -1)
263
273
  rowIdx = phDf.rows.addNew([clickedAtomContIdxStr, '']).idx;
264
- }
274
+
265
275
  phDf.set('Position', rowIdx, clickedAtomContIdxStr);
266
276
  // const tgtCell = inputs.placeholders.grid.cell('Monomers', rowIdx);
267
277
  }
@@ -377,14 +387,17 @@ async function getPolyToolEnumerateDialog(
377
387
  } else /* if (helmSelections === undefined || helmSelections.length < 1) {
378
388
  grok.shell.warning('PolyTool: no selection was provided');
379
389
  } else /**/ {
380
- if (Object.keys(inputs.placeholders.placeholdersValue).length === 0) {
390
+ if (Object.keys(inputs.placeholders.placeholdersValue).length === 0 &&
391
+ Object.keys(inputs.placeholdersBreadth.placeholdersBreadthValue).length === 0
392
+ ) {
381
393
  grok.shell.warning(`${PT_UI_DIALOG_ENUMERATION}: placeholders are empty`);
382
394
  return;
383
395
  }
384
396
  await getHelmHelper(); // initializes JSDraw and org
385
397
  const params: PolyToolEnumeratorParams = {
386
- type: inputs.enumeratorType.value!,
387
398
  placeholders: inputs.placeholders.placeholdersValue,
399
+ type: inputs.enumeratorType.value!,
400
+ placeholdersBreadth: inputs.placeholdersBreadth.placeholdersBreadthValue,
388
401
  keepOriginal: inputs.keepOriginal.value,
389
402
  };
390
403
  const enumeratorResDf = await polyToolEnumerateHelm(srcHelm, srcId, params, inputs.toAtomicLevel.value, seqHelper);
@@ -399,6 +412,7 @@ async function getPolyToolEnumerateDialog(
399
412
  .add(inputs.macromolecule)
400
413
  .add(inputs.placeholders)
401
414
  .add(inputs.enumeratorType)
415
+ .add(inputs.placeholdersBreadth)
402
416
  .add(inputs.trivialNameCol)
403
417
  .add(inputs.toAtomicLevel)
404
418
  .add(inputs.keepOriginal)
@@ -412,20 +426,22 @@ async function getPolyToolEnumerateDialog(
412
426
  destroy();
413
427
  }));
414
428
  dialog.history(
415
- /* getInput */ (): PolyToolEnumerateSerialized => {
429
+ /* getInput */ (): PolyToolEnumerateHelmSerialized => {
416
430
  return {
417
431
  macromolecule: inputs.macromolecule.stringValue,
418
432
  placeholders: inputs.placeholders.stringValue,
419
433
  enumeratorType: inputs.enumeratorType.value,
434
+ placeholdersBreadth: inputs.placeholdersBreadth.stringValue,
420
435
  trivialNameCol: inputs.trivialNameCol.stringValue,
421
436
  toAtomicLevel: inputs.toAtomicLevel.value,
422
437
  keepOriginal: inputs.keepOriginal.value,
423
438
  };
424
439
  },
425
- /* applyInput */ (x: PolyToolEnumerateSerialized): void => {
440
+ /* applyInput */ (x: PolyToolEnumerateHelmSerialized): void => {
426
441
  inputs.macromolecule.stringValue = x.macromolecule;
427
442
  inputs.placeholders.stringValue = x.placeholders;
428
443
  inputs.enumeratorType.value = x.enumeratorType;
444
+ inputs.placeholdersBreadth.stringValue = x.placeholdersBreadth;
429
445
  inputs.trivialNameCol.stringValue = x.trivialNameCol;
430
446
  inputs.toAtomicLevel.value = x.toAtomicLevel;
431
447
  inputs.keepOriginal.value = x.keepOriginal;
@@ -453,9 +469,8 @@ async function polyToolEnumerateHelm(
453
469
  if (toAtomicLevel) {
454
470
  const seqHelper: ISeqHelper = await getSeqHelper();
455
471
  const toAtomicLevelRes = await seqHelper.helmToAtomicLevel(enumHelmCol, true, true);
456
- toAtomicLevelRes.molCol.semType = DG.SEMTYPE.MOLECULE;
457
- enumeratorResDf.columns.add(toAtomicLevelRes.molCol, false);
458
- enumeratorResDf.columns.add(toAtomicLevelRes.molHighlightCol, false);
472
+ toAtomicLevelRes.molCol!.semType = DG.SEMTYPE.MOLECULE;
473
+ enumeratorResDf.columns.add(toAtomicLevelRes.molCol!, false);
459
474
  }
460
475
 
461
476
  if (srcId) {