@datagrok/helm 2.1.16 → 2.1.18

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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/helm",
3
3
  "friendlyName": "Helm",
4
- "version": "2.1.16",
4
+ "version": "2.1.18",
5
5
  "author": {
6
6
  "name": "Oleksandra Serhiienko",
7
7
  "email": "oserhiienko@datagrok.ai"
@@ -15,7 +15,7 @@
15
15
  "helm/JSDraw/Pistoia.HELM-uncompressed.js"
16
16
  ],
17
17
  "dependencies": {
18
- "@datagrok-libraries/bio": "^5.33.2",
18
+ "@datagrok-libraries/bio": "^5.38.0",
19
19
  "@datagrok-libraries/utils": "^4.0.17",
20
20
  "cash-dom": "^8.1.1",
21
21
  "datagrok-api": "^1.10.2",
@@ -60,11 +60,11 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
60
60
  if (argsX >= sumLengths[mid] && argsX <= sumLengths[mid + 1]) {
61
61
  left = mid;
62
62
  found = true;
63
- } else if (argsX < sumLengths[mid]) {
63
+ } else if (argsX < sumLengths[mid])
64
64
  right = mid - 1;
65
- } else if (argsX > sumLengths[mid + 1]) {
65
+ else if (argsX > sumLengths[mid + 1])
66
66
  left = mid + 1;
67
- }
67
+
68
68
  if (left == right)
69
69
  found = true;
70
70
 
@@ -73,7 +73,9 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
73
73
  }
74
74
  left = (argsX >= sumLengths[left]) ? left : left - 1; // correct left to between sumLengths
75
75
 
76
- const seq: string = gridCell.cell.value;
76
+ const seq: string = !gridCell.cell.value ? '' : gridCell.cell.value
77
+ .replaceAll(helmGapStartRe, '{').replaceAll(helmGapIntRe, '.').replaceAll(helmGapEndRe, '}')
78
+ .replace('{*}', '{}');
77
79
  const monomerList = parseHelm(seq);
78
80
  const monomers = new Set<string>(monomerList);
79
81
  const missedMonomers = findMonomers(monomerList);
@@ -121,15 +123,19 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
121
123
  const monomerColor: string = '#404040';
122
124
  const frameColor: string = '#C0C0C0';
123
125
 
124
- const seq = gridCell.cell.value;
126
+ const seq: string = !gridCell.cell.value ? '' : gridCell.cell.value
127
+ .replaceAll(helmGapStartRe, '{').replaceAll(helmGapIntRe, '.').replaceAll(helmGapEndRe, '}')
128
+ .replace('{*}', '{}');
125
129
  const monomerList = parseHelm(seq);
126
130
  const monomers: Set<string> = new Set<string>(monomerList);
127
131
  const missedMonomers: Set<string> = findMonomers(monomerList);
132
+ const helmPlacer = HelmMonomerPlacer.getOrCreate(tableCol);
128
133
 
129
134
  if (missedMonomers.size == 0) {
135
+ helmPlacer.skipCell(gridCell.tableRowIndex!);
130
136
  const host = ui.div([], {style: {width: `${w}px`, height: `${h}px`}});
131
137
  host.setAttribute('dataformat', 'helm');
132
- host.setAttribute('data', gridCell.cell.value);
138
+ host.setAttribute('data', seq /* gaps skipped */);
133
139
  gridCell.element = host;
134
140
  //@ts-ignore
135
141
  const canvas = new JSDraw2.Editor(host, {width: w, height: h, skin: 'w8', viewonly: true});
@@ -152,7 +158,6 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
152
158
  g.transform(1, 0, 0, 1, x, y);
153
159
  g.font = '12px monospace';
154
160
  g.textBaseline = 'top';
155
- const helmPlacer = HelmMonomerPlacer.getOrCreate(tableCol);
156
161
  const [allParts, lengths, sumLengths] = helmPlacer.getCellAllPartsLengths(gridCell.tableRowIndex!);
157
162
 
158
163
  for (let i = 0; i < allParts.length; ++i) {
@@ -25,13 +25,23 @@ export class HelmMonomerPlacer {
25
25
  constructor(public readonly col: DG.Column<string>) {
26
26
  this.col.dataFrame.onDataChanged.subscribe();
27
27
  this.monomerLib = getMonomerLib();
28
+ this.monomerLib.onChanged.subscribe(this.monomerLibOnChanged.bind(this));
29
+ }
30
+
31
+ public skipCell(rowIdx: number): void {
32
+ if (this._allPartsList === null)
33
+ this._allPartsList = new Array<string[] | null>(this.col.length).fill(null);
34
+ if (this._lengthsList === null)
35
+ this._lengthsList = new Array<number[] | null>(this.col.length).fill(null);
36
+
37
+ this._allPartsList[rowIdx] = [];
38
+ this._lengthsList[rowIdx] = [];
28
39
  }
29
40
 
30
41
  /** @param rowIdx Row index of the table {@link DG.DataFrame}, HelmMonomerPlacer is {@link DG.Column} based */
31
42
  public getCellAllPartsLengths(rowIdx: number): [string[], number[], number[]] {
32
43
  if (this._allPartsList === null)
33
44
  this._allPartsList = new Array<string[] | null>(this.col.length).fill(null);
34
-
35
45
  if (this._lengthsList === null)
36
46
  this._lengthsList = new Array<number[] | null>(this.col.length).fill(null);
37
47
 
@@ -45,12 +55,17 @@ export class HelmMonomerPlacer {
45
55
  }
46
56
 
47
57
  private getCellMonomerLengthsForSeq(rowIdx: number): [string[], number[]] {
48
- const allParts: string[] = this._allPartsList![rowIdx] = this.getAllParts(rowIdx);
49
- const lengths: number[] = this._lengthsList![rowIdx] = new Array<number>(allParts.length);
50
-
51
- for (const [part, partI] of wu.enumerate(allParts)) {
52
- const partWidth: number = part.length * this.monomerCharWidth;
53
- lengths[partI] = partWidth;
58
+ let allParts: string[] | null = this._allPartsList![rowIdx];
59
+ if (allParts === null)
60
+ allParts = this._allPartsList![rowIdx] = this.getAllParts(rowIdx);
61
+
62
+ let lengths: number[] | null = this._lengthsList![rowIdx];
63
+ if (lengths === null) {
64
+ lengths = this._lengthsList![rowIdx] = new Array<number>(allParts.length);
65
+ for (const [part, partI] of wu.enumerate(allParts)) {
66
+ const partWidth: number = part.length * this.monomerCharWidth;
67
+ lengths[partI] = partWidth;
68
+ }
54
69
  }
55
70
 
56
71
  return [allParts, lengths];
@@ -71,6 +86,14 @@ export class HelmMonomerPlacer {
71
86
  return res;
72
87
  }
73
88
 
89
+ // -- Handle events --
90
+
91
+ private monomerLibOnChanged(_value: any): void {
92
+ this._lengthsList = null;
93
+ this._allPartsList = null;
94
+ // TODO: Invalidate all grids of this.col.dataFrame
95
+ }
96
+
74
97
  public static getOrCreate(col: DG.Column<string>): HelmMonomerPlacer {
75
98
  if (!(Temps.helmMonomerPlacer in col.temp)) col.temp[Temps.helmMonomerPlacer] = new HelmMonomerPlacer(col);
76
99
  return col.temp[Temps.helmMonomerPlacer];
package/src/package.ts CHANGED
@@ -7,16 +7,20 @@ import {WebEditorMonomer, RGROUP_CAP_GROUP_NAME, RGROUP_LABEL, SMILES} from './c
7
7
  import {HelmWebEditor} from './helm-web-editor';
8
8
  import {HelmCellRenderer} from './cell-renderer';
9
9
  import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types';
10
- import {NotationConverter} from '@datagrok-libraries/bio/src/utils/notation-converter';
10
+ import {GapSymbols, UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
11
11
  import {findMonomers, parseHelm} from './utils';
12
12
  import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
13
13
  import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
14
14
 
15
- export const _package = new DG.Package();
16
15
  let monomerLib: IMonomerLib | null = null;
17
16
 
17
+ export const _package = new DG.Package();
18
+
18
19
  //tags: init
19
20
  export async function initHelm(): Promise<void> {
21
+ //@ts-ignore
22
+ org.helm.webeditor.kCaseSensitive = true; // GROK-13880
23
+
20
24
  return Promise.all([
21
25
  new Promise((resolve, reject) => {
22
26
  // @ts-ignore
@@ -58,6 +62,7 @@ export function getMonomerLib(): IMonomerLib {
58
62
  return monomerLib!;
59
63
  }
60
64
 
65
+ /** Fills org.helm.webeditor.Monomers dictionary for WebEditor */
61
66
  function rewriteLibraries() {
62
67
  // @ts-ignore
63
68
  org.helm.webeditor.Monomers.clear();
@@ -87,9 +92,8 @@ function rewriteLibraries() {
87
92
  } else if (monomer[SMILES] != null) {
88
93
  webEditorMonomer.rs = Object.keys(getRS(monomer[SMILES].toString())).length;
89
94
  webEditorMonomer.at = getRS(monomer[SMILES].toString());
90
- } else {
95
+ } else
91
96
  isBroken = true;
92
- }
93
97
 
94
98
  if (!isBroken) {
95
99
  // @ts-ignore
@@ -116,8 +120,13 @@ function checkMonomersAndOpenWebEditor(cell?: DG.Cell, value?: string, units?: s
116
120
  const cellValue: string = !!cell && units === undefined ? cell.value : value;
117
121
  const monomerList: string[] = parseHelm(cellValue);
118
122
  const monomers = findMonomers(monomerList);
119
- if (monomers.size == 0) { webEditor(cell, value, units); } else {
120
- grok.shell.warning(`Monomers ${Array.from(monomers).join(', ')} are absent! <br/>` +
123
+ if (monomers.size === 0)
124
+ webEditor(cell, value, units);
125
+ else if (monomers.size === 1 && monomers.has(GapSymbols[NOTATION.HELM]))
126
+ grok.shell.warning(`WebEditor doesn't support Helm with gaps '${GapSymbols[NOTATION.HELM]}'.`);
127
+ else {
128
+ grok.shell.warning(
129
+ `Monomers ${Array.from(monomers).map((m) => `'${m}'`).join(', ')} are absent! <br/>` +
121
130
  `Please, upload the monomer library! <br/>` +
122
131
  `<a href="https://datagrok.ai/help/domains/bio/macromolecules" target="_blank">Learn more</a>`);
123
132
  }
@@ -138,12 +147,13 @@ export function editMoleculeCell(cell: DG.GridCell): void {
138
147
  export function openEditor(mol: string): void {
139
148
  const df = grok.shell.tv.grid.dataFrame;
140
149
  const col = df.columns.bySemType('Macromolecule')!;
150
+ const colUh = UnitsHandler.getOrCreate(col);
141
151
  const colUnits = col.getTag(DG.TAGS.UNITS);
142
152
  if (colUnits === NOTATION.HELM)
143
153
  checkMonomersAndOpenWebEditor(df.currentCell, undefined, undefined);
144
- const converter = new NotationConverter(col);
145
- const resStr = converter.convertStringToHelm(mol, '/');
146
- checkMonomersAndOpenWebEditor(df.currentCell, resStr, col.getTag(DG.TAGS.UNITS));
154
+ const convert = colUh.getConverter(NOTATION.HELM);
155
+ const helmMol = convert(mol);
156
+ checkMonomersAndOpenWebEditor(df.currentCell, helmMol, col.getTag(DG.TAGS.UNITS));
147
157
  }
148
158
 
149
159
  //name: Properties
@@ -175,7 +185,8 @@ export async function propertiesPanel(helmString: string) {
175
185
  function webEditor(cell?: DG.Cell, value?: string, units?: string) {
176
186
  const view = ui.div();
177
187
  const df = grok.shell.tv.grid.dataFrame;
178
- const converter = new NotationConverter(df.columns.bySemType('Macromolecule')!);
188
+ const col = df.columns.bySemType('Macromolecule')!;
189
+ const uh = UnitsHandler.getOrCreate(col);
179
190
  // @ts-ignore
180
191
  org.helm.webeditor.MolViewer.molscale = 0.8;
181
192
  // @ts-ignore
@@ -214,10 +225,10 @@ function webEditor(cell?: DG.Cell, value?: string, units?: string) {
214
225
  const helmValue = app.canvas.getHelm(true).replace(/<\/span>/g, '')
215
226
  .replace(/<span style='background:#bbf;'>/g, '');
216
227
  if (!!cell) {
217
- if (units === undefined) {
228
+ if (units === undefined)
218
229
  cell.value = helmValue;
219
- } else {
220
- const convertedRes = converter.convertHelmToFastaSeparator(helmValue, units!);
230
+ else {
231
+ const convertedRes = uh.convertHelmToFastaSeparator(helmValue, units!);
221
232
  cell.value = convertedRes;
222
233
  }
223
234
  }
package/src/utils.ts CHANGED
@@ -94,9 +94,8 @@ export function parseHelm(s: string): string[] {
94
94
  // return new Set(helmPartList.filter((val) => !monomerNameList.includes(val)));
95
95
  // }
96
96
 
97
- /** Searches monomers of {@link helmString} for missed. */
97
+ /** Searches monomers of helmString for missed. */
98
98
  export function findMonomers(monomerSymbolList: string[]): Set<string> {
99
- //@ts-ignore
100
99
  const types = Object.keys(org.helm.webeditor.monomerTypeList());
101
100
  const monomers: any = [];
102
101
  const monomerNames: any = [];