@datagrok/sequence-translator 1.4.1 → 1.4.3

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.
@@ -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;
@@ -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(', '));
@@ -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) {
@@ -10,7 +10,7 @@ import {
10
10
 
11
11
  import {Chain} from './pt-conversion';
12
12
  import {getAvailableMonomers} from './utils';
13
- import {PolyToolEnumeratorParams, PolyToolEnumeratorTypes, PolyToolPlaceholders} from './types';
13
+ import {PolyToolEnumeratorParams, PolyToolEnumeratorTypes, PolyToolPlaceholders, PolyToolPlaceholdersBreadth} from './types';
14
14
 
15
15
  // For example keep monomers presented in HELMCoreLibrary.json only (not [NH2])
16
16
  export const PT_HELM_EXAMPLE = 'PEPTIDE1{R.[Aca].T.G.H.F.G.A.A.Y.P.E.[meI]}$$$$';
@@ -19,17 +19,21 @@ export const PT_HELM_EXAMPLE = 'PEPTIDE1{R.[Aca].T.G.H.F.G.A.A.Y.P.E.[meI]}$$$$'
19
19
  declare const JSDraw2: JSDraw2ModuleType;
20
20
  declare const org: OrgType;
21
21
 
22
- function polyToolEnumeratorCore(m: HelmMol, position: number, monomerList: string[]): HelmMol[] {
23
- const resMolList: HelmMol[] = new Array<HelmMol>(monomerList.length);
24
- for (let i = 0; i < monomerList.length; i++) {
25
- const newSymbol = monomerList[i];
26
- const resM = resMolList[i] = m.clone() as HelmMol;
27
- const oldSymbol = resM.atoms[position].elem;
28
- resM.atoms[position].elem = newSymbol;
22
+ function polyToolEnumeratorCore(m: HelmMol, start: number, end: number, monomerList: string[]): HelmMol[] {
23
+ const resMolList: HelmMol[] = new Array<HelmMol>(monomerList.length * (end - start + 1));
24
+ for (let monI: number = 0; monI < monomerList.length; ++monI) {
25
+ const posCount = end - start + 1;
26
+ for (let posI: number = 0; posI < posCount; ++posI) {
27
+ const pos = start + posI;
28
+ const newSymbol = monomerList[monI];
29
+ const resM = resMolList[monI * posCount + posI] = m.clone() as HelmMol;
30
+ const oldSymbol = resM.atoms[pos].elem;
31
+ resM.atoms[pos].elem = newSymbol;
29
32
 
30
- const idOldSymbol = oldSymbol?.length > 1 ? `[${oldSymbol}]` : oldSymbol;
31
- const idNewSymbol = newSymbol?.length > 1 ? `[${newSymbol}]` : newSymbol;
32
- resM.name = `${m.name}-${idOldSymbol}${position + 1}${idNewSymbol}`;
33
+ const idOldSymbol = oldSymbol?.length > 1 ? `[${oldSymbol}]` : oldSymbol;
34
+ const idNewSymbol = newSymbol?.length > 1 ? `[${newSymbol}]` : newSymbol;
35
+ resM.name = `${m.name}-${idOldSymbol}${pos + 1}${idNewSymbol}`;
36
+ }
33
37
  }
34
38
  return resMolList;
35
39
  }
@@ -40,18 +44,26 @@ function polyToolEnumeratorCore(m: HelmMol, position: number, monomerList: strin
40
44
  * @returns {string[]} List of enumerated molecules in Helm format
41
45
  */
42
46
  function getPtEnumeratorSingle(m: HelmMol, placeholders: PolyToolPlaceholders): HelmMol[] {
43
- const coreResList: HelmMol[][] = Object.entries(placeholders)
44
- .map(([p, monomerList]: [string, string[]]) => polyToolEnumeratorCore(m, parseInt(p), monomerList));
47
+ const coreResList: HelmMol[][] = placeholders
48
+ .map((ph) => polyToolEnumeratorCore(m, ph.position, ph.position, ph.monomers));
45
49
  const resMolList = coreResList.reduce((acc, posList) => acc.concat(posList), []);
46
50
  return resMolList;
47
51
  }
48
52
 
49
53
  function getPtEnumeratorMatrix(m: HelmMol, placeholders: PolyToolPlaceholders): HelmMol[] {
50
54
  let resMolList = [m];
51
- for (const [p, monomerList] of Object.entries(placeholders)) {
52
- const pos: number = parseInt(p);
53
- const posResMolList: HelmMol[][] = resMolList.map((m: HelmMol) => polyToolEnumeratorCore(m, pos, monomerList));
54
- resMolList = posResMolList.reduce((acc, l) => acc.concat(l), []);
55
+ for (const ph of placeholders) {
56
+ const phResMolList: HelmMol[][] = resMolList.map((m: HelmMol) => polyToolEnumeratorCore(m, ph.position, ph.position, ph.monomers));
57
+ resMolList = phResMolList.reduce((acc, l) => acc.concat(l), []);
58
+ }
59
+ return resMolList;
60
+ }
61
+
62
+ function getPtEnumeratorBreadth(m: HelmMol, placeholdersBreadth: PolyToolPlaceholdersBreadth): HelmMol[] {
63
+ let resMolList = [m];
64
+ for (const phb of placeholdersBreadth) {
65
+ const phResMolList: HelmMol[][] = resMolList.map((m: HelmMol) => polyToolEnumeratorCore(m, phb.start, phb.end, phb.monomers));
66
+ resMolList = phResMolList.reduce((acc, l) => acc.concat(l), []);
55
67
  }
56
68
  return resMolList;
57
69
  }
@@ -66,17 +78,25 @@ export function doPolyToolEnumerateHelm(
66
78
  const m = molHandler.m;
67
79
  m.name = id;
68
80
 
69
- let resMolList: HelmMol[];
70
- switch (params.type) {
71
- case PolyToolEnumeratorTypes.Single: {
72
- resMolList = getPtEnumeratorSingle(molHandler.m, params.placeholders);
73
- break;
74
- }
75
- case PolyToolEnumeratorTypes.Matrix: {
76
- resMolList = getPtEnumeratorMatrix(molHandler.m, params.placeholders);
77
- break;
81
+ let resMolList: HelmMol[] = [];
82
+ if (params.placeholders) {
83
+ switch (params.type) {
84
+ case PolyToolEnumeratorTypes.Single: {
85
+ resMolList = getPtEnumeratorSingle(molHandler.m, params.placeholders);
86
+ break;
87
+ }
88
+ case PolyToolEnumeratorTypes.Matrix: {
89
+ resMolList = getPtEnumeratorMatrix(molHandler.m, params.placeholders);
90
+ break;
91
+ }
92
+ }
78
93
  }
94
+
95
+ let resBreadthMolList: HelmMol[] = [];
96
+ if (params.placeholdersBreadth) {
97
+ resBreadthMolList = getPtEnumeratorBreadth(molHandler.m, params.placeholdersBreadth);
79
98
  }
99
+ resMolList = resMolList.concat(resBreadthMolList);
80
100
 
81
101
  if (params.keepOriginal)
82
102
  resMolList = [m, ...resMolList];
@@ -0,0 +1,111 @@
1
+ import * as ui from 'datagrok-api/ui';
2
+ import * as grok from 'datagrok-api/grok';
3
+ import * as DG from 'datagrok-api/dg';
4
+
5
+ import {Unsubscribable} from 'rxjs';
6
+ import {PolyToolPlaceholders, PolyToolPlaceholdersBreadth} from './types';
7
+ import {parseMonomerSymbolList} from './pt-placeholders-input';
8
+
9
+ export class PolyToolPlaceholdersBreadthInput extends DG.JsInputBase<DG.DataFrame> {
10
+ get inputType(): string { return 'Breadth'; }
11
+
12
+ get dataType(): string { return DG.TYPE.DATA_FRAME; }
13
+
14
+ getInput(): HTMLElement { return this.gridHost; }
15
+
16
+ getValue(): DG.DataFrame { return this.grid.dataFrame; }
17
+
18
+ setValue(value: DG.DataFrame): void { this.grid.dataFrame = value; }
19
+
20
+ getStringValue(): string { return this.grid.dataFrame.toCsv(); }
21
+
22
+ setStringValue(str: string): void { this.grid.dataFrame = DG.DataFrame.fromCsv(str); }
23
+
24
+ get placeholdersBreadthValue(): PolyToolPlaceholdersBreadth {
25
+ return dfToPlaceholdersBreadth(this.grid.dataFrame);
26
+ }
27
+
28
+ private readonly gridHost: HTMLDivElement;
29
+ public readonly grid: DG.Grid;
30
+
31
+ private subs: Unsubscribable[] = [];
32
+
33
+ protected constructor(name: string | undefined, grid: DG.Grid, heightRowCount?: number) {
34
+ super();
35
+
36
+ if (name) this.captionLabel.innerText = name;
37
+
38
+ this.gridHost = ui.div([], {
39
+ classes: 'ui-input-editor',
40
+ style: {width: '100%', height: '100%', marginTop: '-8px', marginBottom: '8px', paddingBottom: '4px'},
41
+ });
42
+
43
+ this.grid = grid;
44
+ this.gridHost.append(this.grid.root);
45
+
46
+ if (heightRowCount != null) {
47
+ this.updateGridHeight(heightRowCount + 0.7);
48
+ } else {
49
+ this.updateGridHeight(this.grid.dataFrame.rowCount + 0.6);
50
+ this.subs.push(this.grid.dataFrame.onRowsAdded
51
+ .subscribe(() => { this.updateGridHeight(this.grid.dataFrame.rowCount + 0.6); }));
52
+ }
53
+ this.grid.root.style.width = `100%`;
54
+
55
+ this.subs.push(this.grid.dataFrame.onDataChanged.subscribe(() => {
56
+ this.fireChanged();
57
+ }));
58
+
59
+ this.subs.push(ui.onSizeChanged(this.grid.root).subscribe(() => {
60
+ this.grid.columns.byIndex(3)!.width = this.grid.root.clientWidth - this.grid.horzScroll.root.offsetWidth -
61
+ this.grid.columns.byIndex(0)!.width - this.grid.columns.byIndex(1)!.width - this.grid.columns.byIndex(2)!.width - 10;
62
+ }));
63
+
64
+ this.root.classList.add('ui-input-polytool-pos-grid');
65
+ this.root.append(this.gridHost);
66
+ }
67
+
68
+ detach(): void {
69
+ for (const sub of this.subs) sub.unsubscribe();
70
+ }
71
+
72
+ public static async create(
73
+ name?: string, options?: {}, heightRowCount?: number
74
+ ): Promise<PolyToolPlaceholdersBreadthInput> {
75
+ const df: DG.DataFrame = DG.DataFrame.fromColumns([
76
+ DG.Column.fromType(DG.COLUMN_TYPE.INT, 'Start', 0),
77
+ DG.Column.fromType(DG.COLUMN_TYPE.INT, 'End', 0),
78
+ DG.Column.fromType(DG.COLUMN_TYPE.STRING, 'Monomers', 0),
79
+ ])!;
80
+ const grid = (await df.plot.fromType(DG.VIEWER.GRID, options)) as DG.Grid;
81
+ grid.sort(['Start', 'End']);
82
+ return new PolyToolPlaceholdersBreadthInput(name, grid, heightRowCount);
83
+ }
84
+
85
+ // -- Update view --
86
+
87
+ private updateGridHeight(visibleRowCount: number): void {
88
+ const gridHeight = this.grid.colHeaderHeight + visibleRowCount * this.grid.props.rowHeight + 6 + 2;
89
+ this.grid.root.style.height = `${gridHeight}px`;
90
+ }
91
+
92
+ // -- Handle events --
93
+
94
+ private gridRootOnSizeChanged(): void {
95
+ this.grid.columns.byIndex(3)!.width = this.grid.root.clientWidth - this.grid.horzScroll.root.offsetWidth -
96
+ this.grid.columns.byIndex(0)!.width - this.grid.columns.byIndex(1)!.width - this.grid.columns.byIndex(2)!.width - 10;
97
+ }
98
+ }
99
+
100
+ export function dfToPlaceholdersBreadth(df: DG.DataFrame): PolyToolPlaceholdersBreadth {
101
+ const res: PolyToolPlaceholdersBreadth = [];
102
+ for (let rowI = 0; rowI < df.rowCount; rowI++) {
103
+ const startPos = parseInt(df.get('Start', rowI)) - 1;
104
+ const endPos = parseInt(df.get('End', rowI)) - 1;
105
+ if (!isNaN(startPos) && !isNaN(endPos)) {
106
+ const monomerSymbolList = parseMonomerSymbolList(df.get('Monomers', rowI));
107
+ res.push({start: startPos, end: endPos, monomers: monomerSymbolList});
108
+ }
109
+ }
110
+ return res;
111
+ }
@@ -13,23 +13,15 @@ export class PolyToolPlaceholdersInput extends DG.JsInputBase<DG.DataFrame> {
13
13
 
14
14
  getInput(): HTMLElement { return this.gridHost; }
15
15
 
16
- getValue(): DG.DataFrame {
17
- return this.grid.dataFrame;
18
- }
16
+ getValue(): DG.DataFrame { return this.grid.dataFrame; }
19
17
 
20
- setValue(value: DG.DataFrame): void {
21
- this.grid.dataFrame = value;
22
- }
18
+ setValue(value: DG.DataFrame): void { this.grid.dataFrame = value; }
23
19
 
24
- getStringValue(): string {
25
- return this.grid.dataFrame.toCsv();
26
- }
20
+ getStringValue(): string { return this.grid.dataFrame.toCsv(); }
27
21
 
28
- setStringValue(str: string): void {
29
- this.grid.dataFrame = DG.DataFrame.fromCsv(str);
30
- }
22
+ setStringValue(str: string): void { this.grid.dataFrame = DG.DataFrame.fromCsv(str); }
31
23
 
32
- get placeholdersValue() {
24
+ get placeholdersValue(): PolyToolPlaceholders {
33
25
  return dfToPlaceholders(this.grid.dataFrame);
34
26
  }
35
27
 
@@ -80,7 +72,9 @@ export class PolyToolPlaceholdersInput extends DG.JsInputBase<DG.DataFrame> {
80
72
  public static async create(
81
73
  name?: string, options?: {}, heightRowCount?: number
82
74
  ): Promise<PolyToolPlaceholdersInput> {
83
- const df: DG.DataFrame = DG.DataFrame.fromObjects([{Position: '', Monomers: ''}])!;
75
+ const df: DG.DataFrame = DG.DataFrame.fromColumns([
76
+ DG.Column.fromType(DG.COLUMN_TYPE.INT, 'Position', 0),
77
+ DG.Column.fromType(DG.COLUMN_TYPE.STRING, 'Monomers', 0),])!;
84
78
  const grid = (await df.plot.fromType(DG.VIEWER.GRID, options)) as DG.Grid;
85
79
  grid.sort(['Position']);
86
80
  return new PolyToolPlaceholdersInput(name, grid, heightRowCount);
@@ -102,26 +96,25 @@ export class PolyToolPlaceholdersInput extends DG.JsInputBase<DG.DataFrame> {
102
96
  }
103
97
 
104
98
  export function getPlaceholdersFromText(src: string): PolyToolPlaceholders {
105
- const res: PolyToolPlaceholders = {};
99
+ const res: PolyToolPlaceholders = [];
106
100
  for (const line of src.split('\n')) {
107
101
  const lineM = /^\s*(?<pos>\d+)\s*:\s*(?<monomers>.+)$/.exec(line);
108
102
  if (lineM) {
109
103
  const pos: number = parseInt(lineM.groups!['pos']) - 1;
110
104
  const monomerList: string[] = lineM.groups!['monomers'].split(',').map((m) => m.trim());
111
- if (!(pos in res)) res[pos] = [];
112
- res[pos].push(...monomerList);
105
+ res.push({position: pos, monomers: monomerList});
113
106
  }
114
107
  }
115
108
  return res;
116
109
  }
117
110
 
118
111
  export function dfToPlaceholders(df: DG.DataFrame): PolyToolPlaceholders {
119
- const res: PolyToolPlaceholders = {};
112
+ const res: PolyToolPlaceholders = [];
120
113
  for (let rowI = 0; rowI < df.rowCount; rowI++) {
121
- const pos = parseInt(df.get('Position', rowI));
114
+ const pos = parseInt(df.get('Position', rowI)) - 1;
122
115
  if (!isNaN(pos)) {
123
116
  const monomerSymbolList = parseMonomerSymbolList(df.get('Monomers', rowI));
124
- res[pos - 1] = monomerSymbolList;
117
+ res.push({position: pos, monomers: monomerSymbolList});
125
118
  }
126
119
  }
127
120
  return res;
@@ -5,19 +5,24 @@ import {ActiveFiles} from '@datagrok-libraries/utils/src/settings/active-files-b
5
5
  export const RULES_PATH = 'System:AppData/SequenceTranslator/polytool-rules/';
6
6
  export const RULES_STORAGE_NAME = 'Polytool';
7
7
  export const RULES_TYPE_LINK = 'link';
8
+ export const RULES_TYPE_REACTION = 'reaction';
8
9
  export const RULES_TYPE_HOMODIMER = 'fragmentDuplication';
9
10
  export const RULES_TYPE_HETERODIMER = 'differentFragments';
10
11
 
11
12
  export class RuleInputs extends ActiveFiles {
12
- constructor(path: string, userStorageName: string, ext: string ) {
13
- super(path, userStorageName, ext);
13
+ constructor(
14
+ path: string, userStorageName: string, ext: string,
15
+ options?: { onValueChanged: (value: string[]) => void }
16
+ ) {
17
+ super(path, userStorageName, ext, options);
14
18
  }
15
19
  }
16
20
 
17
21
  export type Rules = {
18
22
  homodimerCode: string | null,
19
23
  heterodimerCode: string | null,
20
- linkRules: RuleLink[]
24
+ linkRules: RuleLink[],
25
+ reactionRules: RuleReaction[]
21
26
  }
22
27
 
23
28
  export type RuleLink = {
@@ -30,10 +35,19 @@ export type RuleLink = {
30
35
  secondLinkingGroup: number
31
36
  }
32
37
 
38
+ export type RuleReaction = {
39
+ code: number,
40
+ firstMonomer: string,
41
+ secondMonomer: string,
42
+ reaction: string,
43
+ name: string
44
+ }
45
+
33
46
  export async function getRules(ruleFiles: string[]): Promise<Rules> {
34
47
  const fileSource = new DG.FileSource(RULES_PATH);
35
48
  const linkRules: RuleLink[] = [];
36
- const rules: Rules = {homodimerCode: null, heterodimerCode: null, linkRules: linkRules};
49
+ const reactionRules: RuleReaction[] = [];
50
+ const rules: Rules = {homodimerCode: null, heterodimerCode: null, linkRules: linkRules, reactionRules: reactionRules};
37
51
 
38
52
  for (let i = 0; i < ruleFiles.length; i++) {
39
53
  const rulesRaw = await fileSource.readAsText(ruleFiles[i].replace(RULES_PATH, ''));
@@ -47,6 +61,12 @@ export async function getRules(ruleFiles: string[]): Promise<Rules> {
47
61
  linkRules.push(rule);
48
62
  break;
49
63
  }
64
+ case RULES_TYPE_REACTION: {
65
+ const rule = ruleSingle[j].monomericSubstitution;
66
+ rule['code'] = ruleSingle[j].code;
67
+ reactionRules.push(rule);
68
+ break;
69
+ }
50
70
  case RULES_TYPE_HOMODIMER: {
51
71
  if (rules.homodimerCode)
52
72
  grok.shell.warning(`PolyTool: homodimer code is duplicated in rules.`);
@@ -0,0 +1,106 @@
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as ui from 'datagrok-api/ui';
3
+ import * as DG from 'datagrok-api/dg';
4
+
5
+ import {Unsubscribable} from 'rxjs';
6
+
7
+ import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
8
+ import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
9
+ import {getUnusedColName} from '@datagrok-libraries/bio/src/monomer-works/utils';
10
+
11
+ import {defaultErrorHandler} from '../utils/err-info';
12
+ import {doPolyToolUnrule} from './pt-unrule';
13
+ import {getRules, RuleInputs, RULES_PATH, RULES_STORAGE_NAME} from './pt-rules';
14
+ import {PT_ERROR_DATAFRAME, PT_UI_DIALOG_UNRULE, PT_UI_RULES_USED} from './const';
15
+
16
+ type PolyToolUnruleSerialized = {
17
+ rules: string[];
18
+ };
19
+
20
+ export async function getPolyToolUnruleDialog(srcCol?: DG.Column<string>): Promise<DG.Dialog> {
21
+ const subs: Unsubscribable[] = [];
22
+ const destroy = () => {
23
+ for (const sub of subs) sub.unsubscribe();
24
+ };
25
+ try {
26
+ let srcColVal: DG.Column<string> | undefined = srcCol;
27
+ if (!srcColVal) {
28
+ const srcColList = grok.shell.t.columns.bySemTypeAll(DG.SEMTYPE.MACROMOLECULE);
29
+ if (!srcColList)
30
+ throw new Error(PT_ERROR_DATAFRAME);
31
+ srcColVal = srcColList[0];
32
+ }
33
+ const srcColInput = ui.input.column('Column', {
34
+ table: srcColVal.dataFrame, value: srcColVal,
35
+ filter: (col: DG.Column) => {
36
+ if (col.semType !== DG.SEMTYPE.MACROMOLECULE) return false;
37
+ const sh = SeqHandler.forColumn(col);
38
+ return sh.notation === NOTATION.HELM;
39
+ }
40
+ });
41
+ let ruleFileList: string[];
42
+ const ruleInputs = new RuleInputs(RULES_PATH, RULES_STORAGE_NAME, '.json', {
43
+ onValueChanged: (value: string[]) => { ruleFileList = value;}
44
+ });
45
+ const rulesHeader = ui.inlineText([PT_UI_RULES_USED]);
46
+ const rulesForm = await ruleInputs.getForm();
47
+
48
+ const div = ui.divV([
49
+ srcColInput,
50
+ rulesHeader,
51
+ rulesForm
52
+ ]);
53
+
54
+ const exec = async (): Promise<void> => {
55
+ try {
56
+ const ruleFileList = await ruleInputs.getActive();
57
+ await polyToolUnrule(srcColInput.value!, ruleFileList);
58
+ } catch (err: any) {
59
+ defaultErrorHandler(err);
60
+ }
61
+ };
62
+
63
+ const dialog = ui.dialog(PT_UI_DIALOG_UNRULE)
64
+ .add(div)
65
+ .onOK(() => { exec(); });
66
+ subs.push(dialog.onClose.subscribe(() => {
67
+ destroy();
68
+ }));
69
+ dialog.history(
70
+ /* getInput */ (): PolyToolUnruleSerialized => {
71
+ return {
72
+ rules: ruleFileList,
73
+ };
74
+ },
75
+ /* applyInput */ (x: PolyToolUnruleSerialized): void => {
76
+ ruleInputs.setActive(ruleFileList);
77
+ });
78
+ return dialog;
79
+ } catch (err: any) {
80
+ destroy(); // on failing to build a dialog
81
+ throw err;
82
+ }
83
+ }
84
+
85
+ export async function polyToolUnrule(
86
+ srcCol: DG.Column<string>, ruleFiles: string[]
87
+ ): Promise<DG.Column> {
88
+ const pi = DG.TaskBarProgressIndicator.create('PolyTool unrule...');
89
+ try {
90
+ const table = srcCol.dataFrame;
91
+ const rules = await getRules(ruleFiles);
92
+ const resHelmList = doPolyToolUnrule(srcCol.toList(), rules);
93
+ const resHelmColName = `harmonized(srcCol.name)`;
94
+ const resHelmCol = DG.Column.fromList(DG.COLUMN_TYPE.STRING, resHelmColName, resHelmList,);
95
+ resHelmCol.semType = DG.SEMTYPE.MACROMOLECULE;
96
+ resHelmCol.meta.units = NOTATION.CUSTOM;
97
+ if (table) {
98
+ resHelmCol.name = getUnusedColName(table, resHelmColName);
99
+ table.columns.add(resHelmCol, true);
100
+ await grok.data.detectSemanticTypes(table);
101
+ }
102
+ return resHelmCol;
103
+ } finally {
104
+ pi.close();
105
+ }
106
+ }
@@ -0,0 +1,37 @@
1
+ import * as grok from 'datagrok-api/grok';
2
+ import * as ui from 'datagrok-api/ui';
3
+ import * as DG from 'datagrok-api/dg';
4
+
5
+ import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
6
+
7
+ import {Chain} from './pt-conversion';
8
+ import {getPolyToolUnruleDialog} from './pt-unrule-dialog';
9
+ import {Rules} from './pt-rules';
10
+
11
+ import {_package} from '../package';
12
+
13
+ export async function polyToolUnruleUI(): Promise<void> {
14
+ let dialog: DG.Dialog;
15
+ try {
16
+ dialog = await getPolyToolUnruleDialog();
17
+ dialog.show();
18
+ } catch (err: any) {
19
+ const [errMsg, errStack] = errInfo(err);
20
+ grok.shell.warning('To run PolyTool Unrule, open a dataframe with Helm');
21
+ _package.logger.error(errMsg, undefined, errStack);
22
+ }
23
+ }
24
+
25
+ /** Returns list of harmonized sequences. Covered with tests. */
26
+ export function doPolyToolUnrule(helms: string[], rules: Rules): string[] {
27
+ const resHrzSeqList = new Array<string>(helms.length);
28
+ for (let i = 0; i < helms.length; ++i) {
29
+ if (!helms[i])
30
+ resHrzSeqList[i] = '';
31
+ else {
32
+ const chain = Chain.fromHelm(helms[i]);
33
+ resHrzSeqList[i] = chain.getNotation(rules);
34
+ }
35
+ }
36
+ return resHrzSeqList;
37
+ }