@datagrok/sequence-translator 1.6.3 → 1.7.0

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,10 +1,17 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
2
  import * as grok from 'datagrok-api/grok';
3
+ import * as ui from 'datagrok-api/ui';
3
4
  import {getMonomerPairs, getRules, Rules} from './pt-rules';
4
5
  import {_package, applyNotationProviderForCyclized} from '../../package';
5
6
  import {getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
6
7
  import {doPolyToolConvert} from './pt-conversion';
7
8
  import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
9
+ import {RuleCards} from './pt-rule-cards';
10
+
11
+
12
+ const TAB_LINKS = 'Links';
13
+ const TAB_REACTIONS = 'Reactions';
14
+ const TAB_DIMERS = 'Dimers';
8
15
 
9
16
  export class RulesManager {
10
17
  rules: Rules;
@@ -16,7 +23,7 @@ export class RulesManager {
16
23
  homoDimerInput: DG.InputBase;
17
24
  heteroDimerInput: DG.InputBase;
18
25
 
19
- currentTab = '';
26
+ linkCards: RuleCards[];
20
27
 
21
28
  // every rule set will have its editor instance
22
29
  private static instances: Record<string, RulesManager> = {};
@@ -24,6 +31,7 @@ export class RulesManager {
24
31
  protected constructor(rules: Rules, fileName: string) {
25
32
  this.rules = rules;
26
33
  this.linkRuleDataFrame = this.rules.getLinkRulesDf();
34
+
27
35
  this.synthRuleDataFrame = this.rules.getSynthesisRulesDf();
28
36
  this.fileName = fileName;
29
37
 
@@ -92,20 +100,16 @@ export class RulesManager {
92
100
  grok.shell.info(`Polytool rules at ${this.fileName} was updated`);
93
101
  }
94
102
 
95
- private createGridDiv(name: string, grid: DG.Grid, buttons: HTMLButtonElement[] = []) {
103
+ private createGridDiv(name: string, grid: DG.Grid, tooltipMsg?: string) {
96
104
  const header = ui.h1(name, 'polytool-grid-header');
105
+ ui.tooltip.bind(header, tooltipMsg);
97
106
  header.style.marginTop = '10px';
98
107
  header.style.marginRight = '10px';
99
108
  grid.root.style.height = '100%';
100
109
 
101
- buttons.forEach((b) => {
102
- b.style.marginLeft = '5px';
103
- b.style.marginRight = '10px';
104
- });
105
-
106
110
  const gridDiv = ui.splitV([
107
111
  ui.box(
108
- ui.divH([header, ...buttons]),
112
+ header,
109
113
  {style: {maxHeight: '60px'}},
110
114
  ),
111
115
  grid.root,
@@ -129,10 +133,10 @@ export class RulesManager {
129
133
  }
130
134
  const helmHelper = await getHelmHelper();
131
135
 
132
- const helms = doPolyToolConvert(seqs, this.rules, helmHelper);
136
+ const [helms, isLinear, positionMaps] = doPolyToolConvert(seqs, this.rules, helmHelper);
133
137
 
134
- const initCol = DG.Column.fromStrings('monomers', seqs);
135
- const helmCol = DG.Column.fromStrings('helm', helms);
138
+ const initCol = DG.Column.fromStrings('Monomers', seqs);
139
+ const helmCol = DG.Column.fromStrings('Helm', helms);
136
140
 
137
141
  applyNotationProviderForCyclized(initCol, '-');
138
142
  initCol.semType = DG.SEMTYPE.MACROMOLECULE;
@@ -158,10 +162,10 @@ export class RulesManager {
158
162
  }
159
163
  const helmHelper = await getHelmHelper();
160
164
 
161
- const helms = doPolyToolConvert(seqs, this.rules, helmHelper);
165
+ const [helms, isLinear, positionMaps] = doPolyToolConvert(seqs, this.rules, helmHelper);
162
166
 
163
- const initCol = DG.Column.fromStrings('monomers', seqs);
164
- const helmCol = DG.Column.fromStrings('helm', helms);
167
+ const initCol = DG.Column.fromStrings('Monomers', seqs);
168
+ const helmCol = DG.Column.fromStrings('Helm', helms);
165
169
 
166
170
  initCol.semType = DG.SEMTYPE.MACROMOLECULE;
167
171
  applyNotationProviderForCyclized(initCol, '-');
@@ -177,19 +181,49 @@ export class RulesManager {
177
181
  async getForm() {
178
182
  inputsTabControl: DG.TabControl;
179
183
 
180
- const saveLinksButton = ui.bigButton('Save', () => { this.save(); });
181
- const addLinkButton = ui.button('Add rule', () => { this.linkRuleDataFrame.rows.addNew(); });
182
184
  const linksGridDiv = this.createGridDiv('Rules',
183
- this.linkRuleDataFrame.plot.grid({showAddNewRowIcon: true}), [saveLinksButton, addLinkButton]);
184
- const linkExamples = this.createGridDiv('Examples', await this.getLinkExamplesGrid());
185
+ this.linkRuleDataFrame.plot.grid({showAddNewRowIcon: true}),
186
+ 'specification for monomers to link and linking positions');
187
+ const linkExamples = this.createGridDiv('Examples', await this.getLinkExamplesGrid(),
188
+ 'specification for monomers to link and linking positions');
185
189
  linksGridDiv.style.width = '50%';
186
190
  linkExamples.style.width = '50%';
187
- const links = ui.divH([linksGridDiv, linkExamples]);
191
+ const header = ui.h1('Monomers', 'polytool-grid-header');
192
+ ui.tooltip.bind(header, 'Click different cobination to see how monomers will link');
193
+ this.linkCards = await this.rules.getLinkCards();
194
+ const gridDiv: HTMLElement = ui.splitV([
195
+ ui.box(
196
+ header,
197
+ {style: {maxHeight: '30px'}},
198
+ ),
199
+ this.linkCards[0].root,
200
+ ]);
201
+
202
+ this.linkCards[0].render();
203
+ await this.linkCards[0].reset();
204
+
205
+ this.linkRuleDataFrame.currentRowIdx = 0;
206
+ this.linkRuleDataFrame.onCurrentRowChanged.subscribe(async () => {
207
+ const idx = this.linkRuleDataFrame.currentRowIdx;
208
+ if (idx !== -1) {
209
+ ui.empty(gridDiv);
210
+ gridDiv.append(ui.splitV([
211
+ ui.box(
212
+ header,
213
+ {style: {maxHeight: '30px'}},
214
+ ),
215
+ this.linkCards[idx].root,
216
+ ]));
217
+ }
218
+
219
+ this.linkCards[idx].render();
220
+ await this.linkCards[idx].reset();
221
+ });
222
+
223
+ const links = ui.splitH([linksGridDiv, gridDiv], null, true);
188
224
 
189
- const saveReactionsButton = ui.bigButton('Save', () => { this.save(); });
190
- const addReactionButton = ui.button('Add rule', () => { this.synthRuleDataFrame.rows.addNew(); });
191
225
  const reactionsGridDiv = this.createGridDiv('Rules',
192
- this.synthRuleDataFrame.plot.grid({showAddNewRowIcon: true}), [saveReactionsButton, addReactionButton]);
226
+ this.synthRuleDataFrame.plot.grid({showAddNewRowIcon: true}));
193
227
  const reactionExamples = this.createGridDiv('Examples', await this.getReactionExamplesGrid());
194
228
  reactionsGridDiv.style.width = '50%';
195
229
  reactionExamples.style.width = '50%';
@@ -206,6 +240,13 @@ export class RulesManager {
206
240
  'Dimers': dimerInputsDiv,
207
241
  }, false);
208
242
 
243
+ ui.tooltip.bind(inputsTabControl.getPane(TAB_LINKS).header,
244
+ 'Specify rules to link monomers based on HELM notation');
245
+ ui.tooltip.bind(inputsTabControl.getPane(TAB_REACTIONS).header,
246
+ 'Specify rules to perform reactions within monomers');
247
+ ui.tooltip.bind(inputsTabControl.getPane(TAB_DIMERS).header,
248
+ 'Specify symbols for homodimeric and heterodimeric codes');
249
+
209
250
  inputsTabControl.root.style.height = '100%';
210
251
  inputsTabControl.root.style.width = '100%';
211
252
  inputsTabControl.root.classList.add('rules-manager-form-tab-control');
@@ -215,6 +256,17 @@ export class RulesManager {
215
256
  inputsTabControl.root,
216
257
  ]);
217
258
 
259
+ const saveButton = ui.bigButton('Save', () => { this.save(); });
260
+ const addButton = ui.button('Add rule', () => {
261
+ const currentTab = inputsTabControl.currentPane.name;
262
+ if (currentTab == TAB_LINKS)
263
+ this.linkRuleDataFrame.rows.addNew();
264
+ else if (currentTab == TAB_REACTIONS)
265
+ this.synthRuleDataFrame.rows.addNew();
266
+ });
267
+ const topPanel = [saveButton, addButton];
268
+ this.v!.setRibbonPanels([topPanel]);
269
+
218
270
  panel.style.height = '100%';
219
271
  panel.style.alignItems = 'center';
220
272
 
@@ -0,0 +1,32 @@
1
+ .monomer-card-rule-root {
2
+ border: 2px solid var(--grey-2);
3
+ border-radius: 5px;
4
+ padding: 5px;
5
+ margin: 5px;
6
+ overflow: hidden;
7
+ align-items: center;
8
+ cursor: pointer;
9
+ width: 200px;
10
+ height: 210px;
11
+ min-width: 200px;
12
+ min-height: 210px;
13
+ }
14
+
15
+ .monomer-card-info-rules {
16
+ width: 100%;
17
+ overflow: hidden;
18
+ text-overflow: ellipsis;
19
+ margin-bottom: 10px;
20
+ }
21
+
22
+ .monomer-card-info-rules > div:nth-child(2) {
23
+ white-space: nowrap;
24
+ overflow: hidden;
25
+ text-overflow: ellipsis;
26
+ }
27
+
28
+ .monomer-card-info-rules > div:nth-child(1) {
29
+ font-weight: bold;
30
+ margin-right: 5px;
31
+ text-wrap: nowrap;
32
+ }
@@ -8,7 +8,7 @@ import {Unsubscribable} from 'rxjs';
8
8
  import {getHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
9
9
  import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
10
10
  import {ALPHABET, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
11
- import {getSeqHelper, ISeqHelper} from '@datagrok-libraries/bio/src/utils/seq-helper';
11
+ import {getSeqHelper, ISeqHelper, ToAtomicLevelRes} from '@datagrok-libraries/bio/src/utils/seq-helper';
12
12
  import {MmcrTemps} from '@datagrok-libraries/bio/src/utils/cell-renderer-consts';
13
13
  import {addMonomerHoverLink, buildMonomerHoverLink} from '@datagrok-libraries/bio/src/monomer-works/monomer-hover';
14
14
  import {getRdKitModule} from '@datagrok-libraries/bio/src/chem/rdkit-module';
@@ -23,7 +23,8 @@ import {getEnumerationChem, PT_CHEM_EXAMPLE} from './pt-enumeration-chem';
23
23
 
24
24
  import {
25
25
  PT_ERROR_DATAFRAME, PT_UI_ADD_HELM, PT_UI_DIALOG_CONVERSION, PT_UI_DIALOG_ENUMERATION,
26
- PT_UI_GET_HELM, PT_UI_HIGHLIGHT_MONOMERS, PT_UI_RULES_USED, PT_UI_USE_CHIRALITY
26
+ PT_UI_GET_HELM, PT_UI_LINEARIZE, PT_UI_LINEARIZE_TT,
27
+ PT_UI_HIGHLIGHT_MONOMERS, PT_UI_RULES_USED, PT_UI_USE_CHIRALITY
27
28
  } from './const';
28
29
 
29
30
  import {_package} from '../package';
@@ -33,12 +34,16 @@ import {MonomerMap} from '@datagrok-libraries/bio/src/monomer-works/types';
33
34
  import {ISeqMonomer} from '@datagrok-libraries/bio/src/helm/types';
34
35
  import wu from 'wu';
35
36
  import {PolymerTypes} from '@datagrok-libraries/js-draw-lite/src/types/org';
36
- import {getMonomersDictFromLib} from '@datagrok-libraries/bio/src/monomer-works/to-atomic-level';
37
+ import {_toAtomicLevel, getMonomersDictFromLib} from '@datagrok-libraries/bio/src/monomer-works/to-atomic-level';
37
38
  import {monomerSeqToMolfile} from '@datagrok-libraries/bio/src/monomer-works/to-atomic-level-utils';
38
39
  import {LRUCache} from 'lru-cache';
39
- import {getMonomerHover, ISubstruct, setMonomerHover} from '@datagrok-libraries/chem-meta/src/types';
40
+ import {addSubstructProvider, getMonomerHover, ISubstruct, setMonomerHover}
41
+ from '@datagrok-libraries/chem-meta/src/types';
40
42
  import {getMolHighlight} from '@datagrok-libraries/bio/src/monomer-works/seq-to-molfile';
41
43
  import {ChemTags} from '@datagrok-libraries/chem-meta/src/consts';
44
+ import {mergeSubstructs} from '@datagrok-libraries/chem-meta/src/types';
45
+ import {getMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
46
+ import {dealGroups, helmToMol} from './conversion/pt-atomic';
42
47
 
43
48
  type PolyToolConvertSerialized = {
44
49
  generateHelm: boolean;
@@ -99,6 +104,9 @@ export async function getPolyToolConvertDialog(srcCol?: DG.Column): Promise<DG.D
99
104
  const generateHelmInput = ui.input.bool(PT_UI_GET_HELM, {value: true});
100
105
  ui.tooltip.bind(generateHelmInput.root, PT_UI_ADD_HELM);
101
106
 
107
+ const linearizeInput = ui.input.bool(PT_UI_LINEARIZE, {value: true});
108
+ ui.tooltip.bind(linearizeInput.root, PT_UI_LINEARIZE_TT);
109
+
102
110
  const chiralityEngineInput = ui.input.bool(PT_UI_USE_CHIRALITY, {value: true});
103
111
  const highlightMonomersInput = ui.input.bool(PT_UI_HIGHLIGHT_MONOMERS, {value: true});
104
112
  let ruleFileList: string[];
@@ -112,6 +120,7 @@ export async function getPolyToolConvertDialog(srcCol?: DG.Column): Promise<DG.D
112
120
  const div = ui.divV([
113
121
  srcColInput,
114
122
  generateHelmInput,
123
+ linearizeInput,
115
124
  chiralityEngineInput,
116
125
  highlightMonomersInput,
117
126
  rulesHeader,
@@ -121,7 +130,10 @@ export async function getPolyToolConvertDialog(srcCol?: DG.Column): Promise<DG.D
121
130
  const exec = async (): Promise<void> => {
122
131
  try {
123
132
  const ruleFileList = await ruleInputs.getActive();
124
- await polyToolConvert(srcColInput.value!, generateHelmInput.value!, chiralityEngineInput.value!, ruleFileList);
133
+ await polyToolConvert(
134
+ srcColInput.value!, generateHelmInput.value!, linearizeInput.value!,
135
+ chiralityEngineInput.value!, highlightMonomersInput.value!, ruleFileList
136
+ );
125
137
  } catch (err: any) {
126
138
  defaultErrorHandler(err);
127
139
  }
@@ -250,19 +262,9 @@ async function getPolyToolEnumerationChemDialog(cell?: DG.Cell): Promise<DG.Dial
250
262
  }
251
263
  }
252
264
 
253
- function dealGroups(col: DG.Column<string>): void {
254
- for (let i = 0; i < col.length; i++) {
255
- col.set(i, col.get(i)!.replaceAll('undefined', 'H'));
256
- col.set(i, col.get(i)!.replaceAll('Oh', 'O'));
257
- col.set(i, col.get(i)!.replaceAll('0.000000 3', '0.000000 0'));
258
- col.set(i, col.get(i)!.replaceAll('?', 'O'));
259
- col.set(i, col.get(i)!.replaceAll('0 3\n', '0 0\n'));
260
- }
261
- }
262
-
263
265
  /** Returns Helm and molfile columns. */
264
- export async function polyToolConvert(
265
- seqCol: DG.Column<string>, generateHelm: boolean, chiralityEngine: boolean, ruleFiles: string[]
266
+ export async function polyToolConvert(seqCol: DG.Column<string>,
267
+ generateHelm: boolean, linearize: boolean, chiralityEngine: boolean, highlight: boolean, ruleFiles: string[]
266
268
  ): Promise<[DG.Column, DG.Column]> {
267
269
  const pi = DG.TaskBarProgressIndicator.create('PolyTool converting...');
268
270
  try {
@@ -274,7 +276,7 @@ export async function polyToolConvert(
274
276
 
275
277
  const table = seqCol.dataFrame;
276
278
  const rules = await getRules(ruleFiles);
277
- const resList = doPolyToolConvert(seqCol.toList(), rules, helmHelper);
279
+ const [resList, isLinear, positionMaps] = doPolyToolConvert(seqCol.toList(), rules, helmHelper);
278
280
 
279
281
  const resHelmColName = getUnusedName(table, `transformed(${seqCol.name})`);
280
282
  const resHelmCol = DG.Column.fromType(DG.COLUMN_TYPE.STRING, resHelmColName, resList.length)
@@ -284,25 +286,27 @@ export async function polyToolConvert(
284
286
  resHelmCol.setTag(DG.TAGS.CELL_RENDERER, 'helm');
285
287
  if (generateHelm && table) table.columns.add(resHelmCol, true);
286
288
 
287
- const seqHelper: ISeqHelper = await getSeqHelper();
289
+
288
290
  const rdKitModule: RDModule = await getRdKitModule();
291
+ const seqHelper: ISeqHelper = await getSeqHelper();
292
+
289
293
  const lib = await getOverriddenLibrary(rules);
290
294
  const resHelmColTemp = resHelmCol.temp;
291
295
  resHelmColTemp[MmcrTemps.overriddenLibrary] = lib;
292
296
  resHelmCol.temp = resHelmColTemp;
293
- const toAtomicLevelRes =
294
- await seqHelper.helmToAtomicLevel(resHelmCol, chiralityEngine, /* highlight */ generateHelm, lib);
295
- const resMolCol = toAtomicLevelRes.molCol!;
296
- dealGroups(resMolCol);
297
+
298
+ const resMolCol = await helmToMol(resHelmCol, resList,
299
+ isLinear, chiralityEngine, highlight, linearize, lib, rdKitModule, seqHelper);
297
300
  resMolCol.name = getUnusedName(table, `molfile(${seqCol.name})`);
298
301
  resMolCol.semType = DG.SEMTYPE.MOLECULE;
302
+
299
303
  if (table) {
300
304
  table.columns.add(resMolCol, true);
301
305
  await grok.data.detectSemanticTypes(table);
302
306
  }
303
307
 
304
- buildMonomerHoverLink(resHelmCol, resMolCol, lib, seqHelper, rdKitModule);
305
- buildCyclizedMonomerHoverLink(seqCol, resHelmCol, resMolCol, lib, seqHelper, rdKitModule);
308
+ //buildMonomerHoverLink(resHelmCol, resMolCol, lib, seqHelper, rdKitModule);
309
+ buildCyclizedMonomerHoverLink(seqCol, resHelmCol, resMolCol, lib, seqHelper, rdKitModule, positionMaps);
306
310
 
307
311
  return [resHelmCol, resMolCol];
308
312
  } finally {
@@ -312,7 +316,8 @@ export async function polyToolConvert(
312
316
 
313
317
  function buildCyclizedMonomerHoverLink(
314
318
  cyclizedCol: DG.Column<string>, seqCol: DG.Column<string>, molCol: DG.Column<string>,
315
- monomerLib: IMonomerLibBase, seqHelper: ISeqHelper, rdKitModule: RDModule
319
+ monomerLib: IMonomerLibBase, seqHelper: ISeqHelper, rdKitModule: RDModule,
320
+ positionMaps: number[][][]
316
321
  ): MonomerHoverLink {
317
322
  function buildMonomerMap(seqCol: DG.Column<string>, tableRowIdx: number): MonomerMap {
318
323
  const seqSH = seqHelper.getSeqHandler(seqCol);
@@ -352,6 +357,7 @@ function buildCyclizedMonomerHoverLink(
352
357
  const tableRowIdx = seqGridCell.tableRowIndex!;
353
358
  const gridRowIdx = seqGridCell.gridRow;
354
359
  const targetGridCell = grid.cell(targetGridCol.name, gridRowIdx);
360
+ const positionMap = positionMaps[gridRowIdx];
355
361
 
356
362
  const prev = getMonomerHover();
357
363
  if (!prev || (prev && (prev.dataFrameId != seqCol.dataFrame.id || prev.gridRowIdx != gridRowIdx ||
@@ -382,15 +388,16 @@ function buildCyclizedMonomerHoverLink(
382
388
  return undefined;
383
389
 
384
390
  const resSubstructList: ISubstruct[] = [];
385
- const seqMonomerList: number[] = [cyclizedMonomer.position]; // TODO: Map position of harmonized sequence
391
+ const seqMonomerList: number[] = positionMap[cyclizedMonomer.position];
392
+ console.log(seqMonomerList);
386
393
  for (const seqMonomer of seqMonomerList) {
387
- const monomerMap = molMonomerMap.get(cyclizedMonomer!.position); // single monomer
394
+ const monomerMap = molMonomerMap.get(seqMonomer); // single monomer
388
395
  if (!monomerMap) return {atoms: [], bonds: [], highlightAtomColors: [], highlightBondColors: []};
389
396
  resSubstructList.push(getMolHighlight([monomerMap], monomerLib));
390
397
  }
391
398
  //TODO: refine merge substract
392
- //const res: ISubstruct = mergeSubstructs(resSubstructList);
393
- return undefined;
399
+ const res: ISubstruct = mergeSubstructs(resSubstructList);
400
+ return res;
394
401
  }
395
402
  });
396
403
 
@@ -416,7 +423,7 @@ function buildCyclizedMonomerHoverLink(
416
423
  };
417
424
 
418
425
  addMonomerHoverLink(cyclizedCol.temp, resLink);
419
- // addSubstructProvider(molCol.temp, resLink); //
426
+ addSubstructProvider(molCol.temp, resLink);
420
427
 
421
428
  return resLink;
422
429
  }