@datagrok/bio 2.11.39 → 2.11.41

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.
@@ -0,0 +1 @@
1
+ code,monomer1,monomer2,modification1,modification2,R1,R2
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "Leonid Stolbov",
6
6
  "email": "lstolbov@datagrok.ai"
7
7
  },
8
- "version": "2.11.39",
8
+ "version": "2.11.41",
9
9
  "description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
10
10
  "repository": {
11
11
  "type": "git",
package/src/package.ts CHANGED
@@ -59,7 +59,7 @@ import {BioPackage, BioPackageProperties} from './package-types';
59
59
  import {getCompositionAnalysisWidget} from './widgets/composition-analysis-widget';
60
60
  import {MacromoleculeColumnWidget} from './utils/macromolecule-column-widget';
61
61
  import {addCopyMenuUI} from './utils/context-menu';
62
- import {getPolyToolDialog} from './utils/poly-tool/ui';
62
+ import {PolyTool} from './utils/poly-tool/ui';
63
63
  import {PolyToolCsvLibHandler} from './utils/poly-tool/csv-to-json-monomer-lib-converter';
64
64
  import {_setPeptideColumn} from './utils/poly-tool/utils';
65
65
  import {getRegionDo} from './utils/get-region';
@@ -689,10 +689,11 @@ export function convertDialog() {
689
689
  //top-menu: Bio | Convert | PolyTool
690
690
  //name: polyTool
691
691
  //description: Perform cyclization of polymers
692
- export function polyTool(): void {
692
+ export async function polyTool(): Promise<void> {
693
+ const polytool = new PolyTool();
693
694
  let dialog: DG.Dialog;
694
695
  try {
695
- dialog = getPolyToolDialog();
696
+ dialog = await polytool.getPolyToolDialog();
696
697
  dialog.show();
697
698
  } catch (err: any) {
698
699
  grok.shell.warning('To run PolyTool, open a dataframe with macromolecules');
@@ -1,11 +1,11 @@
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
- import * as ui from 'datagrok-api/ui';
4
3
  import * as DG from 'datagrok-api/dg';
4
+ import * as OCL from 'openchemlib/full';
5
5
 
6
6
  import {MolfileHandler} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler';
7
7
  import {MolfileHandlerBase} from '@datagrok-libraries/chem-meta/src/parsing-utils/molfile-handler-base';
8
- import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
8
+ import {RDModule, RDMol} from '@datagrok-libraries/chem-meta/src/rdkit-api';
9
9
  import {HELM_POLYMER_TYPE, HELM_RGROUP_FIELDS} from '@datagrok-libraries/bio/src/utils/const';
10
10
  import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
11
11
 
@@ -51,10 +51,10 @@ export async function helm2mol(df: DG.DataFrame, helmCol: DG.Column<string>): Pr
51
51
 
52
52
  /** Translate HELM column into molfile column and append to the dataframe */
53
53
  export async function getMolColumnFromHelm(
54
- df: DG.DataFrame, helmCol: DG.Column<string>
54
+ df: DG.DataFrame, helmCol: DG.Column<string>, chiralityEngine?: boolean
55
55
  ): Promise<DG.Column<string>> {
56
56
  const converter = new HelmToMolfileConverter(helmCol, df);
57
- const molCol = await converter.convertToRdKitBeautifiedMolfileColumn();
57
+ const molCol = await converter.convertToRdKitBeautifiedMolfileColumn(chiralityEngine);
58
58
  molCol.semType = DG.SEMTYPE.MOLECULE;
59
59
  return molCol;
60
60
  }
@@ -89,7 +89,28 @@ export class HelmToMolfileConverter {
89
89
  return smiles;
90
90
  }
91
91
 
92
- async convertToRdKitBeautifiedMolfileColumn(): Promise<DG.Column<string>> {
92
+ async getMolV3000ViaOCL(beautifiedMols: (RDMol | null)[], columnName: string) {
93
+ const beautifiedMolV2000 = beautifiedMols.map((mol) => {
94
+ if (mol === null)
95
+ return '';
96
+ const molBlock = mol.get_molblock();
97
+ mol!.delete();
98
+ return molBlock;
99
+ });
100
+ const molv3000Arr = new Array<string>(beautifiedMolV2000.length);
101
+ const chiralityPb = DG.TaskBarProgressIndicator.create(`Handling chirality...`);
102
+ for (let i = 0; i < beautifiedMolV2000.length; i++) {
103
+ const oclMolecule = OCL.Molecule.fromMolfile(beautifiedMolV2000[i]);
104
+ const molV3000 = oclMolecule.toMolfileV3();
105
+ molv3000Arr[i] = molV3000.replace('STERAC1', 'STEABS');
106
+ const progress = i/beautifiedMolV2000.length*100;
107
+ chiralityPb.update(progress, `${progress?.toFixed(2)}% of molecules completed`);
108
+ }
109
+ chiralityPb.close();
110
+ return DG.Column.fromStrings(columnName, molv3000Arr);
111
+ }
112
+
113
+ async convertToRdKitBeautifiedMolfileColumn(chiralityEngine?: boolean): Promise<DG.Column<string>> {
93
114
  const smiles = await this.getSmilesList();
94
115
  const rdKitModule: RDModule = await grok.functions.call('Chem:getRdKitModule');
95
116
  const beautifiedMols = smiles.map((item) =>{
@@ -103,13 +124,16 @@ export class HelmToMolfileConverter {
103
124
  return mol;
104
125
  });
105
126
  const columnName = this.df.columns.getUnusedName(`molfile(${this.helmColumn.name})`);
127
+
128
+ if (chiralityEngine)
129
+ return await this.getMolV3000ViaOCL(beautifiedMols, columnName);
106
130
  return DG.Column.fromStrings(columnName, beautifiedMols.map((mol) => {
107
131
  if (mol === null)
108
132
  return '';
109
133
  const molBlock = mol.get_v3Kmolblock();
110
134
  mol!.delete();
111
135
  return molBlock;
112
- }));
136
+ }));
113
137
  }
114
138
 
115
139
  async convertToMolfileV2KColumn(): Promise<DG.Column<string>> {
@@ -4,12 +4,14 @@ import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
6
6
  import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
7
- import {_package} from '../../package';
7
+ import {ALIGNMENT, ALPHABET} from '@datagrok-libraries/bio/src/utils/macromolecule';
8
+
8
9
  import {HELM_WRAPPER} from './const';
9
10
  import {getMolColumnFromHelm} from '../helm-to-molfile';
10
- import {ALIGNMENT, ALPHABET} from '@datagrok-libraries/bio/src/utils/macromolecule';
11
11
 
12
- const RULE_PATH = 'System:AppData/Bio/polytool-rules/';
12
+ export const RULES_PATH = 'System:AppData/Bio/polytool-rules/';
13
+ export const RULES_STORAGE_NAME = 'Polytool';
14
+
13
15
 
14
16
  type ConnectionData = {
15
17
  allPos1: number[],
@@ -28,7 +30,7 @@ type Rule = {
28
30
  secondR: number
29
31
  }
30
32
 
31
- function addCommonTags(col: DG.Column):void {
33
+ function addCommonTags(col: DG.Column): void {
32
34
  col.setTag('quality', DG.SEMTYPE.MACROMOLECULE);
33
35
  col.setTag('aligned', ALIGNMENT.SEQ);
34
36
  col.setTag('alphabet', ALPHABET.PT);
@@ -54,7 +56,7 @@ class TransformationCommon {
54
56
  protected getLinkedPositions(helm: string, rules: Rule[]): [number, number][] {
55
57
  const seq = helm.replace(HELM_WRAPPER.LEFT, '').replace(HELM_WRAPPER.RIGHT, '');
56
58
  const monomers = seq.split('.').map((m) => { return m.replace('[', '').replace(']', ''); });
57
- const result:[number, number][] = new Array<[number, number]>(rules.length);
59
+ const result: [number, number][] = new Array<[number, number]>(rules.length);
58
60
 
59
61
  for (let i = 0; i < rules.length; i++) {
60
62
  let firstFound = false;
@@ -109,27 +111,31 @@ class TransformationCommon {
109
111
  return result;
110
112
  }
111
113
 
112
- protected getRules(rulesTable: DG.DataFrame): Rule[] {
113
- const ruleCount = rulesTable.rowCount;
114
+ protected getRules(rulesTables: DG.DataFrame[]): Rule[] {
115
+ const ruleCount = rulesTables.map((df) => df.rowCount).reduce((a, b) => a + b);
114
116
  const rules: Rule[] = new Array<Rule>(ruleCount);
115
- const codeCol = rulesTable.columns.byName('code');
116
- const monomer1Col = rulesTable.columns.byName('monomer1');
117
- const monomer2Col = rulesTable.columns.byName('monomer2');
118
- const modification1Col = rulesTable.columns.byName('modification1');
119
- const modification2Col = rulesTable.columns.byName('modification2');
120
- const r1Col = rulesTable.columns.byName('R1');
121
- const r2Col = rulesTable.columns.byName('R2');
122
117
 
123
- for (let i = 0; i < ruleCount; i++) {
124
- rules[i] = {
125
- code: codeCol.get(i),
126
- firstMonomer: monomer1Col.get(i),
127
- secondMonomer: monomer2Col.get(i),
128
- firstModification: modification1Col.get(i),
129
- secondModification: modification2Col.get(i),
130
- firstR: r1Col.get(i),
131
- secondR: r2Col.get(i),
132
- };
118
+ let counter = 0;
119
+ for (let i = 0; i < rulesTables.length; i++) {
120
+ const codeCol = rulesTables[i].columns.byName('code');
121
+ const monomer1Col = rulesTables[i].columns.byName('monomer1');
122
+ const monomer2Col = rulesTables[i].columns.byName('monomer2');
123
+ const modification1Col = rulesTables[i].columns.byName('modification1');
124
+ const modification2Col = rulesTables[i].columns.byName('modification2');
125
+ const r1Col = rulesTables[i].columns.byName('R1');
126
+ const r2Col = rulesTables[i].columns.byName('R2');
127
+
128
+ for (let j = 0; j < rulesTables[i].rowCount; j++, counter++) {
129
+ rules[counter] = {
130
+ code: codeCol.get(j),
131
+ firstMonomer: monomer1Col.get(j),
132
+ secondMonomer: monomer2Col.get(j),
133
+ firstModification: modification1Col.get(j),
134
+ secondModification: modification2Col.get(j),
135
+ firstR: r1Col.get(j),
136
+ secondR: r2Col.get(j),
137
+ };
138
+ }
133
139
  }
134
140
 
135
141
  return rules;
@@ -163,7 +169,7 @@ class TransformationCommon {
163
169
  allAttaches2.push(rules[i].secondR);
164
170
 
165
171
  helm = HELM_WRAPPER.LEFT;
166
- for (let i = 0; i < monomers.length; i ++) {
172
+ for (let i = 0; i < monomers.length; i++) {
167
173
  if (i != monomers.length - 1)
168
174
  helm = helm + monomers[i] + '.';
169
175
  else
@@ -178,8 +184,8 @@ class TransformationCommon {
178
184
  return cycledHelm;
179
185
  }
180
186
 
181
- transform(rulesTable: DG.DataFrame): string[] {
182
- const rules = this.getRules(rulesTable);
187
+ transform(rulesTables: DG.DataFrame[]): string[] {
188
+ const rules = this.getRules(rulesTables);
183
189
  const resultList = this.helmColumn.toList().map((helm: string) => {
184
190
  if (this.hasTerminals(helm))
185
191
  return this.getTransformedHelm(helm, rules);
@@ -212,37 +218,32 @@ function getHelmCycle(helm: string, source: ConnectionData): string {
212
218
 
213
219
  cycled += '$$$';
214
220
  return cycled;
215
- // return helm.replace(HELM_WRAPPER.RIGHT,
216
- // `}$PEPTIDE1,PEPTIDE1,${
217
- // source.monomerPosition
218
- // }:R${
219
- // source.attachmentPoint
220
- // }-${
221
- // target.monomerPosition
222
- // }:R${
223
- // target.attachmentPoint
224
- // }${'$'.repeat(6)}`
225
- // );
226
221
  }
227
222
 
228
223
  export async function addTransformedColumn(
229
- molColumn: DG.Column<string>, addHelm: boolean
224
+ molColumn: DG.Column<string>, addHelm: boolean, ruleFiles: string[], chiralityEngine?: boolean
230
225
  ): Promise<void> {
231
226
  const df = molColumn.dataFrame;
232
227
  const uh = UnitsHandler.getOrCreate(molColumn);
233
228
  const sourceHelmCol = uh.convert(NOTATION.HELM);
234
229
  const pt = PolymerTransformation.getInstance(sourceHelmCol);
235
- const fileSource = new DG.FileSource(RULE_PATH);
236
- const rulesRaw = await fileSource.readAsText('rules.csv');
237
- const rulesTable = DG.DataFrame.fromCsv(rulesRaw);
238
- const targetList = pt.transform(rulesTable);
230
+ const fileSource = new DG.FileSource(RULES_PATH);
231
+
232
+ const rulesRawFrames: DG.DataFrame[] = new Array<DG.DataFrame>(ruleFiles.length);
233
+
234
+ for (let i = 0; i < ruleFiles.length; i++) {
235
+ const rulesRaw = await fileSource.readAsText(ruleFiles[i].replace(RULES_PATH, ''));
236
+ rulesRawFrames[i] = DG.DataFrame.fromCsv(rulesRaw);
237
+ }
238
+
239
+ const targetList = pt.transform(rulesRawFrames);
239
240
  const helmColName = df.columns.getUnusedName('transformed(' + molColumn.name + ')');
240
241
  const targetHelmCol = DG.Column.fromList('string', helmColName, targetList);
241
242
 
242
243
  addCommonTags(targetHelmCol);
243
244
  targetHelmCol.setTag('units', NOTATION.HELM);
244
245
 
245
- const molCol = await getMolColumnFromHelm(df, targetHelmCol);
246
+ const molCol = await getMolColumnFromHelm(df, targetHelmCol, chiralityEngine);
246
247
  molCol.name = df.columns.getUnusedName('molfile(' + molColumn.name + ')');
247
248
 
248
249
  if (addHelm) {
@@ -3,60 +3,146 @@ 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 {HELM_POLYMER_TYPE} from '@datagrok-libraries/bio/src/utils/const';
7
- import {MonomerLibManager} from '../monomer-lib/lib-manager';
8
- import {ALL_MONOMERS, CYCLIZATION_TYPE, TRANSFORMATION_TYPE} from './const';
9
6
  import {addTransformedColumn} from './transformation';
10
- import * as rxjs from 'rxjs';
11
- //import {MetaData} from './types';
12
-
13
- const RULE_PATH = 'System:AppData/Bio/polytool-rules/';
14
-
15
- export function getPolyToolDialog(): DG.Dialog {
16
- //const monomerLib = MonomerLibManager.instance.getBioLib();
17
- const targetColumns = grok.shell.t.columns.bySemTypeAll(DG.SEMTYPE.MACROMOLECULE);
18
- if (!targetColumns)
19
- throw new Error('No dataframe with macromolecule columns open');
20
-
21
- const targetColumnInput = ui.columnInput(
22
- 'Column', grok.shell.t, targetColumns[0], null,
23
- {filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE}
24
- );
25
-
26
- const generateHelmChoiceInput = ui.boolInput('Get HELM', true);
27
- ui.tooltip.bind(generateHelmChoiceInput.root, 'Add HELM column');
28
-
29
- // let rulesTable: DG.DataFrame = DG.DataFrame.create();
30
-
31
- const ruleFileInput = ui.button('ADD RULES', () => {
32
- DG.Utils.openFile({
33
- accept: '.csv',
34
- open: async (selectedFile) => {
35
- const content = await selectedFile.text();
36
- // rulesTable = DG.DataFrame.fromCsv(content);
37
- await grok.dapi.files.writeAsText(RULE_PATH + `${selectedFile.name}`, content);
38
- //console.log(df.toCsv());
39
- },
40
- });
7
+ import {RULES_PATH, RULES_STORAGE_NAME} from './transformation';
8
+
9
+ export type UserRuleSettings = {
10
+ included: string[],
11
+ notIncluded: string[],
12
+ }
13
+
14
+ async function getAllAvailableRuleFiles(): Promise<string[]> {
15
+ const list = await grok.dapi.files.list(RULES_PATH);
16
+ const paths = list.map((fileInfo) => {
17
+ return fileInfo.fullPath;
41
18
  });
42
19
 
43
- const div = ui.div([
44
- targetColumnInput,
45
- generateHelmChoiceInput,
46
- ruleFileInput
47
- ]);
48
-
49
- const dialog = ui.dialog('Poly Tool')
50
- .add(div)
51
- .onOK(async () => {
52
- const molCol = targetColumnInput.value;
53
- if (!molCol) {
54
- grok.shell.warning('No marcomolecule column chosen!');
55
- return;
56
- }
57
- addTransformedColumn(molCol!, generateHelmChoiceInput.value!);
20
+ return paths;
21
+ }
22
+
23
+ async function getUserRulesSettings(): Promise<UserRuleSettings> {
24
+ const resStr: string = await grok.dapi.userDataStorage.getValue(RULES_STORAGE_NAME, 'Settings', true);
25
+ const res = resStr ? JSON.parse(resStr) : {included: [], enotIncludedxplicit: []};
26
+
27
+ res.included = res.included instanceof Array ? res.included : [];
28
+ res.notIncluded = res.notIncluded instanceof Array ? res.notIncluded : [];
29
+
30
+ return res!;
31
+ }
32
+
33
+ async function setUserLibSettings(value: UserRuleSettings): Promise<void> {
34
+ await grok.dapi.userDataStorage.postValue(RULES_STORAGE_NAME, 'Settings', JSON.stringify(value), true);
35
+ }
36
+
37
+ export class PolyTool {
38
+ ruleFiles: string[];
39
+ userRuleSettings: UserRuleSettings;
40
+ ruleFilesInputs: HTMLDivElement;// DG.InputBase<boolean | null>[];
41
+ dialog: DG.Dialog;
42
+
43
+ constructor() {
44
+ this.ruleFiles = [];
45
+ this.userRuleSettings = {included: [], notIncluded: []};
46
+ }
47
+
48
+ private updateRulesSelectionStatus(ruleFileName: string, isSelected: boolean): void {
49
+ const isRuleFileSelected = this.userRuleSettings.included.includes(ruleFileName);
50
+
51
+ if (!isRuleFileSelected && isSelected) {
52
+ this.userRuleSettings.included.push(ruleFileName);
53
+ this.userRuleSettings.included = this.userRuleSettings.included.sort();
54
+
55
+ const index = this.userRuleSettings.notIncluded.indexOf(ruleFileName);
56
+ if (index > -1)
57
+ this.userRuleSettings.notIncluded.splice(index, 1);
58
+ } else {
59
+ const index = this.userRuleSettings.included.indexOf(ruleFileName);
60
+ if (index > -1)
61
+ this.userRuleSettings.included.splice(index, 1);
62
+
63
+ this.userRuleSettings.notIncluded.push(ruleFileName);
64
+ this.userRuleSettings.notIncluded = this.userRuleSettings.notIncluded.sort();
58
65
  }
66
+
67
+ setUserLibSettings(this.userRuleSettings);
68
+ }
69
+
70
+ private getAddButton(): HTMLButtonElement {
71
+ return ui.button('ADD RULES', () => {
72
+ DG.Utils.openFile({
73
+ accept: '.csv',
74
+ open: async (selectedFile) => {
75
+ const content = await selectedFile.text();
76
+ await grok.dapi.files.writeAsText(RULES_PATH + `${selectedFile.name}`, content);
77
+ this.updateRulesSelectionStatus(selectedFile.name, false);
78
+ const cb = ui.boolInput(
79
+ selectedFile.name,
80
+ false,
81
+ (isSelected: boolean) => this.updateRulesSelectionStatus(RULES_PATH + `${selectedFile.name}`, isSelected)
82
+ );
83
+ this.ruleFilesInputs.append(cb.root);
84
+ },
85
+ });
86
+ });
87
+ }
88
+
89
+ private async getRuleFilesBlock(): Promise<DG.InputBase<boolean | null>[]> {
90
+ this.ruleFiles = await getAllAvailableRuleFiles();
91
+ this.userRuleSettings = await getUserRulesSettings();
92
+ const cBoxes: DG.InputBase<boolean | null>[] = [];
93
+
94
+ for (let i = 0; i < this.ruleFiles.length; i++) {
95
+ const ruleFileName = this.ruleFiles[i];
96
+ const isRuleFileSelected = this.userRuleSettings.included.includes(ruleFileName);
97
+ const cb = ui.boolInput(
98
+ ruleFileName.replace(RULES_PATH, ''),
99
+ isRuleFileSelected,
100
+ (isSelected: boolean) => this.updateRulesSelectionStatus(ruleFileName, isSelected)
101
+ );
102
+
103
+ cBoxes.push(cb);
104
+ }
105
+ return cBoxes;
106
+ }
107
+
108
+ async getPolyToolDialog(): Promise<DG.Dialog> {
109
+ const targetColumns = grok.shell.t.columns.bySemTypeAll(DG.SEMTYPE.MACROMOLECULE);
110
+ if (!targetColumns)
111
+ throw new Error('No dataframe with macromolecule columns open');
112
+
113
+ const targetColumnInput = ui.columnInput(
114
+ 'Column', grok.shell.t, targetColumns[0], null,
115
+ {filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE}
59
116
  );
60
117
 
61
- return dialog;
118
+ const generateHelmChoiceInput = ui.boolInput('Get HELM', true);
119
+ ui.tooltip.bind(generateHelmChoiceInput.root, 'Add HELM column');
120
+
121
+ const addButton = this.getAddButton();
122
+ this.ruleFilesInputs = ui.div(await this.getRuleFilesBlock());
123
+ //const rulesFiles = ui.div(this.ruleFilesInputs);
124
+ const chiralityEngineInput = ui.boolInput('Chirality engine', false);
125
+
126
+ const div = ui.div([
127
+ targetColumnInput,
128
+ generateHelmChoiceInput,
129
+ chiralityEngineInput,
130
+ 'Rules used',
131
+ this.ruleFilesInputs,
132
+ addButton
133
+ ]);
134
+
135
+ this.dialog = ui.dialog('Poly Tool')
136
+ .add(div)
137
+ .onOK(async () => {
138
+ const molCol = targetColumnInput.value;
139
+ if (!molCol) {
140
+ grok.shell.warning('No marcomolecule column chosen!');
141
+ return;
142
+ }
143
+ addTransformedColumn(molCol!, generateHelmChoiceInput.value!, this.userRuleSettings.included, chiralityEngineInput.value!);
144
+ });
145
+
146
+ return this.dialog;
147
+ }
62
148
  }