@datagrok/bio 2.10.7 → 2.10.8

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.
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "Leonid Stolbov",
6
6
  "email": "lstolbov@datagrok.ai"
7
7
  },
8
- "version": "2.10.7",
8
+ "version": "2.10.8",
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",
@@ -14,8 +14,25 @@ const LEFT_HELM_WRAPPER = 'PEPTIDE1{';
14
14
  const RIGHT_HELM_WRAPPER = '}$$$$';
15
15
  const ALL_MONOMERS = '<All>';
16
16
 
17
+ type MetaData = {
18
+ leftTerminal: string,
19
+ rightTerminal: string,
20
+ transformationType: TRANSFORMATION_TYPE,
21
+ cyclizationType: CYCLIZATION_TYPE,
22
+ }
23
+
24
+ type ConnectionData = {
25
+ monomerPosition: number,
26
+ attachmentPoint: number,
27
+ }
28
+
29
+ const enum TRANSFORMATION_TYPE {
30
+ CYCLIZATION = 'Cyclization',
31
+ }
32
+
17
33
  const enum CYCLIZATION_TYPE {
18
34
  NO = 'N-O',
35
+ NCys = 'N-Cys',
19
36
  R3 = 'R3-R3',
20
37
  }
21
38
 
@@ -32,72 +49,147 @@ export function _setPeptideColumn(col: DG.Column): void {
32
49
  // col.setTag('cell.renderer', 'sequence');
33
50
  }
34
51
 
35
- async function enumerator(
36
- molColumn: DG.Column, cyclizationType: CYCLIZATION_TYPE, leftTerminal: string, rightTerminal: string
37
- ): Promise<void> {
38
- function hasR3Terminals(helm: string, leftTerminal: string, rightTerminal: string): boolean {
39
- if (leftTerminal === ALL_MONOMERS || rightTerminal === ALL_MONOMERS)
52
+ abstract class TransformationBase {
53
+ constructor(helmColumn: DG.Column<string>, meta: MetaData) {
54
+ this.helmColumn = helmColumn;
55
+ this.leftTerminal = meta.leftTerminal;
56
+ this.rightTerminal = meta.rightTerminal;
57
+ }
58
+
59
+ protected helmColumn: DG.Column<string>;
60
+ protected leftTerminal?: string;
61
+ protected rightTerminal?: string;
62
+
63
+ protected abstract hasTerminals(helm: string): boolean;
64
+ protected abstract getTransformedHelm(helm: string): string;
65
+ protected abstract getLinkedPositions(helm: string): [number, number];
66
+
67
+ transform(): string[] {
68
+ const resultList = this.helmColumn.toList().map((helm: string) => {
69
+ if (this.hasTerminals(helm))
70
+ return this.getTransformedHelm(helm);
71
+ return helm;
72
+ });
73
+ return resultList;
74
+ }
75
+ }
76
+
77
+ class TransformationNCys extends TransformationBase {
78
+ constructor(helmColumn: DG.Column<string>, meta: MetaData) {
79
+ super(helmColumn, meta);
80
+ }
81
+
82
+ protected hasTerminals(helm: string): boolean {
83
+ if (! helm.includes(this.rightTerminal + RIGHT_HELM_WRAPPER))
84
+ return false;
85
+ if (this.leftTerminal === ALL_MONOMERS)
40
86
  return true;
41
- const positions = getLinkedR3Positions(helm);
42
- return positions.every((el) => el > 0);
87
+ return helm.includes(LEFT_HELM_WRAPPER + this.leftTerminal);
43
88
  }
44
89
 
45
- function hasNOTerminals(helm: string, leftTerminal: string, rightTerminal: string): boolean {
46
- if (leftTerminal === ALL_MONOMERS || rightTerminal === ALL_MONOMERS)
90
+ protected getLinkedPositions(helm: string): [number, number] {
91
+ return [1, getNumberOfMonomers(helm)];
92
+ }
93
+
94
+ protected getTransformedHelm(helm: string): string {
95
+ const positions = this.getLinkedPositions(helm);
96
+ const source = {monomerPosition: positions[0], attachmentPoint: 1};
97
+ const target = {monomerPosition: positions[1], attachmentPoint: 3};
98
+ return getHelmCycle(helm, source, target);
99
+ }
100
+ }
101
+
102
+ class TransformationNO extends TransformationBase {
103
+ constructor(helmColumn: DG.Column<string>, meta: MetaData) {
104
+ super(helmColumn, meta);
105
+ }
106
+
107
+ protected hasTerminals(helm: string): boolean {
108
+ if (this.leftTerminal === ALL_MONOMERS || this.rightTerminal === ALL_MONOMERS)
47
109
  return true;
48
- return helm.includes(LEFT_HELM_WRAPPER + leftTerminal) && helm.includes(rightTerminal + RIGHT_HELM_WRAPPER);
110
+ return helm.includes(LEFT_HELM_WRAPPER + this.leftTerminal) &&
111
+ helm.includes(this.rightTerminal + RIGHT_HELM_WRAPPER);
49
112
  }
50
113
 
51
- function applyModification(helm: string): string {
52
- if (cyclizationType === CYCLIZATION_TYPE.R3)
53
- return applyR3Modification(helm);
54
- else
55
- return applyNOModification(helm);
114
+ protected getLinkedPositions(helm: string): [number, number] {
115
+ return [1, getNumberOfMonomers(helm)];
56
116
  }
57
117
 
58
- function applyNOModification(helm: string): string {
59
- if (hasNOTerminals(helm, leftTerminal, rightTerminal))
60
- return getNOCycle(helm, getLinkedNOPositions(helm));
61
- return helm;
118
+ protected getTransformedHelm(helm: string): string {
119
+ const positions = this.getLinkedPositions(helm);
120
+ const source = {monomerPosition: positions[0], attachmentPoint: 1};
121
+ const target = {monomerPosition: positions[1], attachmentPoint: 2};
122
+ return getHelmCycle(helm, source, target);
62
123
  }
124
+ }
63
125
 
64
- function applyR3Modification(helm: string): string {
65
- if (hasR3Terminals(helm, leftTerminal, rightTerminal))
66
- return getR3Cycle(helm, getLinkedR3Positions(helm));
67
- return helm;
126
+ class TransformationR3 extends TransformationBase {
127
+ constructor(helmColumn: DG.Column<string>, meta: MetaData) {
128
+ super(helmColumn, meta);
68
129
  }
69
130
 
70
- function getLinkedR3Positions(helm: string): [number, number] {
131
+ protected hasTerminals(helm: string): boolean {
132
+ if (this.leftTerminal === ALL_MONOMERS || this.rightTerminal === ALL_MONOMERS)
133
+ return true;
134
+ const positions = this.getLinkedPositions(helm);
135
+ return positions.every((el) => el > 0);
136
+ }
137
+
138
+ protected getLinkedPositions(helm: string): [number, number] {
71
139
  const seq = helm.replace(LEFT_HELM_WRAPPER, '').replace(RIGHT_HELM_WRAPPER, '');
72
140
  const monomers = seq.split('.');
73
- const start = monomers.findIndex((el) => el === leftTerminal);
74
- const end = monomers.findIndex((el, idx) => el === rightTerminal && idx > start);
141
+ const start = monomers.findIndex((el) => el === this.leftTerminal);
142
+ const end = monomers.findIndex((el, idx) => el === this.rightTerminal && idx > start);
75
143
  return [start + 1, end + 1];
76
144
  }
77
145
 
78
- function getLinkedNOPositions(helm: string): [number, number] {
79
- const seq = helm.replace(LEFT_HELM_WRAPPER, '').replace(RIGHT_HELM_WRAPPER, '');
80
- const lastMonomerNumber = seq.split('.').length;
81
- return [1, lastMonomerNumber];
146
+ protected getTransformedHelm(helm: string): string {
147
+ const positions = this.getLinkedPositions(helm);
148
+ const source = {monomerPosition: positions[0], attachmentPoint: 3};
149
+ const target = {monomerPosition: positions[1], attachmentPoint: 3};
150
+ return getHelmCycle(helm, source, target);
82
151
  }
152
+ }
83
153
 
84
- function getR3Cycle(helm: string, position: [number, number]): string {
85
- const result = helm.replace(RIGHT_HELM_WRAPPER,
86
- `}$PEPTIDE1,PEPTIDE1,${position[0]}:R3-${position[1]}:R3${'$'.repeat(6)}`);
87
- return result;
88
- }
154
+ class PolymerTransformation {
155
+ private constructor() {}
89
156
 
90
- function getNOCycle(helm: string, position: [number, number]): string {
91
- const result = helm.replace(RIGHT_HELM_WRAPPER,
92
- `}$PEPTIDE1,PEPTIDE1,${position[1]}:R2-${position[0]}:R1${'$'.repeat(6)}`);
93
- return result;
157
+ static getInstance(molColumn: DG.Column<string>, meta: MetaData): TransformationBase {
158
+ const cyclizationType = meta.cyclizationType;
159
+ return (cyclizationType === CYCLIZATION_TYPE.R3) ? new TransformationR3(molColumn, meta) :
160
+ (cyclizationType === CYCLIZATION_TYPE.NCys) ? new TransformationNCys(molColumn, meta) :
161
+ new TransformationNO(molColumn, meta);
94
162
  }
163
+ }
95
164
 
165
+ function getNumberOfMonomers(helm: string): number {
166
+ const seq = helm.replace(LEFT_HELM_WRAPPER, '').replace(RIGHT_HELM_WRAPPER, '');
167
+ return seq.split('.').length;
168
+ }
169
+
170
+ function getHelmCycle(helm: string, source: ConnectionData, target: ConnectionData): string {
171
+ return helm.replace(RIGHT_HELM_WRAPPER,
172
+ `}$PEPTIDE1,PEPTIDE1,${
173
+ source.monomerPosition
174
+ }:R${
175
+ source.attachmentPoint
176
+ }-${
177
+ target.monomerPosition
178
+ }:R${
179
+ target.attachmentPoint
180
+ }${'$'.repeat(6)}`
181
+ );
182
+ }
183
+
184
+ async function addTransformedColumn(
185
+ molColumn: DG.Column<string>, meta: MetaData
186
+ ): Promise<void> {
96
187
  const df = molColumn.dataFrame;
97
188
  const uh = UnitsHandler.getOrCreate(molColumn);
98
189
  const sourceHelmCol = uh.convert(NOTATION.HELM);
99
- const targetList = sourceHelmCol.toList().map((helm) => applyModification(helm));
100
- const colName = df.columns.getUnusedName('Cyclization(' + molColumn.name + ')');
190
+ const pt = PolymerTransformation.getInstance(sourceHelmCol, meta);
191
+ const targetList = pt.transform();
192
+ const colName = df.columns.getUnusedName(`${meta.transformationType}(` + molColumn.name + ')');
101
193
  const targetHelmCol = DG.Column.fromList('string', colName, targetList);
102
194
 
103
195
  addCommonTags(targetHelmCol);
@@ -109,50 +201,91 @@ async function enumerator(
109
201
  }
110
202
 
111
203
  export function _getEnumeratorWidget(molColumn: DG.Column): DG.Widget {
112
- function updateMonomerList(): void {
113
- console.log('hi from update:');
114
- if (cyclizationTypeChoice.value === cyclizationTypes[0]) {
115
- console.log('hi from first branch:');
116
- monomerList = [ALL_MONOMERS].concat(
204
+ function getMonomerList(cyclizationType: CYCLIZATION_TYPE): string[] {
205
+ if (cyclizationType === cyclizationTypes[0]) {
206
+ return [ALL_MONOMERS].concat(
117
207
  monomerLib.getMonomerSymbolsByType(HELM_POLYMER_TYPE.PEPTIDE)
118
208
  );
119
- } else if (cyclizationTypeChoice.value === cyclizationTypes[1]) {
120
- monomerList = [ALL_MONOMERS].concat(
209
+ }
210
+ if (cyclizationType === cyclizationTypes[1]) {
211
+ return [ALL_MONOMERS].concat(
121
212
  monomerLib.getMonomerSymbolsByRGroup(3, HELM_POLYMER_TYPE.PEPTIDE)
122
213
  );
123
- console.log('hi from second branch:');
124
214
  }
125
- leftTerminalChoice = ui.choiceInput('R1:', monomerList[0], monomerList);
126
- rightTerminalChoice = ui.choiceInput('R2:', monomerList[0], monomerList);
215
+ return ['C'];
216
+ }
217
+
218
+ function updateMonomerList(): void {
219
+ if (cyclizationTypeChoice.value === CYCLIZATION_TYPE.NCys) {
220
+ monomerList1 = getMonomerList(CYCLIZATION_TYPE.NO);
221
+ monomerList2 = getMonomerList(CYCLIZATION_TYPE.NCys);
222
+ } else {
223
+ monomerList1 = getMonomerList(cyclizationTypeChoice.value as CYCLIZATION_TYPE);
224
+ monomerList2 = [...monomerList1];
225
+ }
226
+
227
+ leftTerminalChoice = ui.choiceInput(
228
+ 'R1:', monomerList1[0], monomerList1, () => { onRGroupValueChange.next(); }
229
+ );
230
+ rightTerminalChoice = ui.choiceInput('R2:', monomerList2[0], monomerList2, () => { onRGroupValueChange.next(); });
231
+ onRGroupValueChange.next();
127
232
  ui.empty(terminalControls);
128
233
  [leftTerminalChoice, rightTerminalChoice].forEach((el) => { terminalControls.appendChild(el.root); });
129
234
  }
130
235
 
131
236
  const onCyclizationChoice = new rxjs.Subject<string>();
132
- onCyclizationChoice.subscribe(() => updateMonomerList());
237
+ const onRGroupValueChange = new rxjs.Subject<string>();
238
+ onCyclizationChoice.subscribe(() => {
239
+ meta.cyclizationType = cyclizationTypeChoice.value!;
240
+ updateMonomerList();
241
+ });
242
+ onRGroupValueChange.subscribe(() => {
243
+ meta.rightTerminal = rightTerminalChoice.value!;
244
+ meta.leftTerminal = leftTerminalChoice.value!;
245
+ });
133
246
 
134
- const modifications = ['Cyclization'];
135
- const modificationChoice = ui.choiceInput('Modification', modifications[0], modifications);
136
247
 
137
- const cyclizationTypes = [CYCLIZATION_TYPE.NO, CYCLIZATION_TYPE.R3];
248
+ const meta = {} as MetaData;
249
+ const transformations = [TRANSFORMATION_TYPE.CYCLIZATION];
250
+ const transformationChoice = ui.choiceInput(
251
+ 'Modification', transformations[0], transformations, () => meta.transformationType = transformationChoice.value!
252
+ );
253
+
254
+ const cyclizationTypes = [CYCLIZATION_TYPE.NO, CYCLIZATION_TYPE.R3, CYCLIZATION_TYPE.NCys];
138
255
  const cyclizationTypeChoice = ui.choiceInput(
139
256
  'Type', cyclizationTypes[0], cyclizationTypes, () => { onCyclizationChoice.next(); }
140
257
  );
141
258
 
142
259
  const monomerLib = MonomerLibHelper.instance.getBioLib();
143
- let monomerList: string[] = [];
144
- let leftTerminalChoice = ui.choiceInput('R1:', monomerList[0], monomerList);
145
- let rightTerminalChoice = ui.choiceInput('R2:', monomerList[0], monomerList);
260
+ let monomerList1: string[] = [];
261
+ let monomerList2: string[] = [];
262
+ let leftTerminalChoice = ui.choiceInput(
263
+ 'R1:', monomerList1[0], monomerList1, () => {
264
+ meta.leftTerminal = leftTerminalChoice.value!;
265
+ }
266
+ );
267
+ let rightTerminalChoice = ui.choiceInput('R2:', monomerList2[0], monomerList2, () => {
268
+ meta.rightTerminal = rightTerminalChoice.value!;
269
+ });
146
270
  const terminalControls = ui.divV([leftTerminalChoice.root, rightTerminalChoice.root]);
147
271
 
272
+ function updateMeta() {
273
+ meta.cyclizationType = cyclizationTypeChoice.value!;
274
+ meta.leftTerminal = leftTerminalChoice.value!;
275
+ meta.rightTerminal = rightTerminalChoice.value!;
276
+ meta.transformationType = transformationChoice.value!;
277
+ }
278
+
148
279
  updateMonomerList();
149
280
 
281
+ updateMeta();
282
+
150
283
  const btn = ui.bigButton('Run', async () =>
151
- enumerator(molColumn, cyclizationTypeChoice.value!, leftTerminalChoice.value!, rightTerminalChoice.value!)
284
+ addTransformedColumn(molColumn, meta)
152
285
  );
153
286
 
154
287
  const div = ui.div([
155
- modificationChoice,
288
+ transformationChoice,
156
289
  cyclizationTypeChoice,
157
290
  terminalControls,
158
291
  btn
@@ -158,16 +158,10 @@ export class MonomerLib implements IMonomerLib {
158
158
  monomers = monomers.filter((monomer) => {
159
159
  if (!monomer?.rgroups)
160
160
  return false;
161
- console.log('monomer symbol:', monomer.symbol);
162
161
  let criterion = monomer?.rgroups.length >= rGroupNumber;
163
- console.log(`has rGroupNumber ${rGroupNumber}`, criterion);
164
162
  const molfileHandler = MolfileHandler.getInstance(monomer.molfile);
165
- console.log(molfileHandler.atomTypes);
166
163
  const rGroupIndices = findAllIndices(molfileHandler.atomTypes, 'R#');
167
- console.log('rGroup indices', rGroupIndices);
168
- console.log(molfileHandler.pairsOfBondedAtoms);
169
164
  criterion &&= true;
170
- console.log('criterion', criterion);
171
165
  return criterion;
172
166
  });
173
167
  return monomers.map((monomer) => monomer?.symbol!);