@datagrok/helm 2.1.7 → 2.1.9

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,12 +1,12 @@
1
1
  {
2
2
  "name": "@datagrok/helm",
3
3
  "friendlyName": "Helm",
4
- "version": "2.1.7",
4
+ "version": "2.1.9",
5
5
  "author": {
6
6
  "name": "Oleksandra Serhiienko",
7
7
  "email": "oserhiienko@datagrok.ai"
8
8
  },
9
- "description": "Provides support for HELM notation (importing, detecting, rendering, conversion). [Learn more](https://github.com/datagrok-ai/public/blob/master/packages/Helm/README.md)",
9
+ "description": "Provides support for HELM notation (importing, detecting, rendering, conversion).",
10
10
  "dependencies": {
11
11
  "@datagrok-libraries/bio": "^5.22.1",
12
12
  "@datagrok-libraries/utils": "^1.19.1",
@@ -37,27 +37,27 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
37
37
  } else if (argsX > maxLengthWordsSum[mid + 1]) {
38
38
  left = mid + 1;
39
39
  }
40
- if (left == right) {
40
+ if (left == right)
41
41
  found = true;
42
- }
43
42
  }
44
43
  }
45
44
  left = (argsX >= maxLengthWordsSum[left]) ? left + 1 : left;
46
45
  const monomers = findMonomers(gridCell.cell.value);
47
- let s: string = gridCell.cell.value ?? '';
48
- let subParts: string[] = parseHelm(s);
49
- let allParts: string[] = getParts(subParts, s);
50
- let tooltipMessage: HTMLElement[] = [];
46
+ const s: string = gridCell.cell.value ?? '';
47
+ const subParts: string[] = parseHelm(s);
48
+ const allParts: string[] = getParts(subParts, s);
49
+ const tooltipMessage: HTMLElement[] = [];
51
50
  for (let i = 0; i < allParts.length; ++i) {
52
- if (monomers.has(allParts[i]))
51
+ if (monomers.has(allParts[i])) {
53
52
  tooltipMessage[i] = ui.divV([
54
53
  ui.divText(`Monomer ${allParts[i]} not found.`),
55
54
  ui.divText('Open the Context Panel, then expand Manage Libraries')
56
55
  ]);
56
+ }
57
57
  }
58
- (((tooltipMessage[left]?.childNodes.length ?? 0) > 0))
59
- ? ui.tooltip.show(ui.div(tooltipMessage[left]), e.x + 16, e.y + 16)
60
- : ui.tooltip.hide();
58
+ (((tooltipMessage[left]?.childNodes.length ?? 0) > 0)) ?
59
+ ui.tooltip.show(ui.div(tooltipMessage[left]), e.x + 16, e.y + 16) :
60
+ ui.tooltip.hide();
61
61
  } catch (err: any) {
62
62
  const errMsg: string = errorToConsole(err);
63
63
  console.error('Helm: HelmCellRenderer.onMouseMove() error:\n' + errMsg);
@@ -71,9 +71,9 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
71
71
  const undefinedColor = 'rgb(100,100,100)';
72
72
  const grayColor = '#808080';
73
73
  const monomers = findMonomers(gridCell.cell.value);
74
- let s: string = gridCell.cell.value ?? '';
75
- let subParts: string[] = parseHelm(s);
76
- if (monomers.size == 0) {
74
+ const s: string = gridCell.cell.value ?? '';
75
+ const subParts: string[] = parseHelm(s);
76
+ if (monomers.size == 0 && grid) {
77
77
  const host = ui.div([], {style: {width: `${w}px`, height: `${h}px`}});
78
78
  host.setAttribute('dataformat', 'helm');
79
79
  host.setAttribute('data', gridCell.cell.value);
@@ -81,8 +81,13 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
81
81
  //@ts-ignore
82
82
  const canvas = new JSDraw2.Editor(host, {width: w, height: h, skin: 'w8', viewonly: true});
83
83
  return;
84
- }
85
- if (monomers.size > 0) {
84
+ } else {
85
+ if (!grid) {
86
+ const r = window.devicePixelRatio;
87
+ h = 28;
88
+ g.canvas.height = h*r;
89
+ g.canvas.style.height = `${h}px`;
90
+ }
86
91
  w = grid ? Math.min(grid.canvas.width - x, w) : g.canvas.width - x;
87
92
  g.save();
88
93
  g.beginPath();
@@ -91,20 +96,20 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
91
96
  g.font = '12px monospace';
92
97
  g.textBaseline = 'top';
93
98
  let x1 = x;
94
- let maxLengthWords: any = {};
95
- let maxLengthWordSum: any = {};
96
- let allParts: string[] = getParts(subParts, s);
99
+ const maxLengthWords: any = {};
100
+ const maxLengthWordSum: any = {};
101
+ const allParts: string[] = getParts(subParts, s);
97
102
  for (let i = 0; i < allParts.length; ++i) {
98
103
  maxLengthWords[i] = allParts[i].length * 7;
99
- let color = monomers.has(allParts[i]) ? 'red' : grayColor;
104
+ const color = monomers.has(allParts[i]) ? 'red' : grayColor;
100
105
  g.fillStyle = undefinedColor;
101
106
  x1 = printLeftOrCentered(x1, y, w, h, g, allParts[i], color, 0, true, 1.0);
102
107
  }
103
108
 
104
109
  maxLengthWordSum[0] = maxLengthWords[0];
105
- for (let i = 1; i < allParts.length; i++) {
110
+ for (let i = 1; i < allParts.length; i++)
106
111
  maxLengthWordSum[i] = maxLengthWordSum[i - 1] + maxLengthWords[i];
107
- }
112
+
108
113
  gridCell.cell.column.temp = {
109
114
  'helm-sum-maxLengthWords': maxLengthWordSum,
110
115
  'helm-maxLengthWords': maxLengthWords
@@ -1,4 +1,4 @@
1
- import {after, before, category, delay, expect, test} from '@datagrok-libraries/utils/src/test';
1
+ import {after, before, category, delay, expect, expectArray, test} from '@datagrok-libraries/utils/src/test';
2
2
  //import {findMonomers, helmToFasta, helmToPeptide, helmToRNA, initHelm} from '../package';
3
3
  import {_package} from '../package-test';
4
4
  import {parseHelm} from '../utils';
@@ -18,7 +18,9 @@ category('Helm', () => {
18
18
  // expect(await helmToRNA('RNA1{R(U)P.R(T)P.R(G)P.R(C)P.R(A)}$$$$'), 'UTGCA');
19
19
  // expect(await helmToRNA('RNA1{P.R(U).P.R(T)}$$$$'), 'UT');
20
20
  // // eslint-disable-next-line max-len
21
- // expect(await helmToRNA('RNA1{R(U)P.R(T)P}|RNA2{P.R(A)P.R(A)}$RNA1,RNA2,2:pair-6:pair|RNA1,RNA2,5:pair-3:pair$$$'), 'UT AA');
21
+ // expect(
22
+ // await helmToRNA('RNA1{R(U)P.R(T)P}|RNA2{P.R(A)P.R(A)}$RNA1,RNA2,2:pair-6:pair|RNA1,RNA2,5:pair-3:pair$$$'), 'UT AA'
23
+ // );
22
24
  // });
23
25
  //
24
26
  // test('helmToPeptide', async () => {
@@ -46,7 +48,32 @@ category('Helm', () => {
46
48
  // expect(col.tags[DG.TAGS.UNITS], 'HELM');
47
49
  // });
48
50
 
49
- test('parseHelm', async() => {
51
+ const complexMonomers = [
52
+ // tests parsing of complex monomemer in square brackets like [L-hArg(Et,Et)]
53
+ 'PEPTIDE1{[L-hArg(Et,Et)].E.F.G}|PEPTIDE2{C.E}$PEPTIDE1,PEPTIDE2,2:R3-1:R1$$$V2.0',
54
+ // tests parsing of complex monomer and one as a side chain as well
55
+ 'PEPTIDE1{[L-hArg(Et,Et)]([L-hArg(Et,Et)]).E.F.G}$$$$',
56
+ // tests parsing of complex monomer and one as a side chain as well
57
+ 'RNA1{[dhp]([m1A])p.r(T)p.r(T)p.r(G)p}$$$$V2.0',
58
+ // tests complex monomer as side chain
59
+ 'RNA1{r([mo6pur])p.r(T)p.r(T)p.r(G)p}$$$$V2.0',
60
+ // test complex monomer, side chain and linker
61
+ 'RNA1{[afhna]([mo6pur])[36SS].r(T)p}$$$$V2.0'
62
+ ];
63
+
64
+ const complexMonomersExpected = [
65
+ ['L-hArg(Et,Et)', 'E', 'F', 'G', 'C', 'E'],
66
+ ['L-hArg(Et,Et)', 'L-hArg(Et,Et)', 'E', 'F', 'G'],
67
+ ['dhp', 'm1A', 'p', 'r', 'T', 'p', 'r', 'T', 'p', 'r', 'G', 'p'],
68
+ ['r', 'mo6pur', 'p', 'r', 'T', 'p', 'r', 'T', 'p', 'r', 'G', 'p'],
69
+ ['afhna', 'mo6pur', '36SS', 'r', 'T', 'p']
70
+ ];
71
+
72
+ const complexTestNames = [
73
+ 'comples-monomer', 'complex-monomer+side-chain', 'complex-monomer+side-chain-RNA',
74
+ 'complex-side-chain', 'complex+side-chain+linker'
75
+ ];
76
+ test('parseHelm', async () => {
50
77
  const expectedResults = [
51
78
  ['meI', 'hHis', 'Aca', 'N', 'T', 'dK', 'Thr_PO3H2', 'D-Tyr_Et', 'Aze', 'dV', 'E', 'Phe_4Me'],
52
79
  ['A', 'C', 'T', 'G', 'W', 'E', 'Q'],
@@ -55,14 +82,24 @@ category('Helm', () => {
55
82
  ];
56
83
  const helmStrings = [
57
84
  'PEPTIDE1{meI.hHis.Aca.N.T.dK.Thr_PO3H2.Aca.D-Tyr_Et.Aze.dV.E.N.dV.Phe_4Me}$$$',
85
+ // eslint-disable-next-line max-len
58
86
  'PEPTIDE1{A.C.T.G.C.T.W.G.T.W.E.C.W.C.Q.W}|PEPTIDE2{A.C.T.G.C.T.W.G.T.W.E.Q}$PEPTIDE1,PEPTIDE1,5:R3-14:R3|PEPTIDE2,PEPTIDE1,2:R3-12:R3$$$',
59
87
  'RNA1{R(U)P.R(T)P}|RNA2{P.R(A)P.R(A)}$RNA1,RNA2,2:pair-6:pair|RNA1,RNA2,5:pair-3:pair$$$',
60
88
  'PEPTIDE1{A.*.G.C}$$$$V2.0'
61
89
  ];
62
90
  for (let idx = 0; idx < helmStrings.length; ++idx) {
63
- let monomerSet = new Set(parseHelm(helmStrings[idx]));
64
- let monomerArray = Array.from(monomerSet);
91
+ const monomerSet = new Set(parseHelm(helmStrings[idx]));
92
+ const monomerArray = Array.from(monomerSet);
65
93
  expect(JSON.stringify(monomerArray), JSON.stringify(expectedResults[idx]));
66
94
  }
67
95
  });
96
+ complexMonomers.forEach((helm, i) => {
97
+ test(complexTestNames[i], async () => {
98
+ const monomersArray = parseHelm(helm);
99
+ monomersArray.sort();
100
+ const expected = complexMonomersExpected[i];
101
+ expected.sort();
102
+ expectArray(monomersArray, expected);
103
+ });
104
+ });
68
105
  });
package/src/utils.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
- import { RGROUP_CAP_GROUP_NAME, RGROUP_CAP_GROUP_SMILES, jsonSdfMonomerLibDict, MONOMER_SYMBOL, RGROUP_ALTER_ID, RGROUPS, RGROUP_LABEL, SDF_MONOMER_NAME } from "./constants";
3
2
 
4
3
  export function getParts(subParts: string[], s: string): string[] {
5
- let j = 0;
6
- let allParts: string[] = [];
4
+ const j = 0;
5
+ const allParts: string[] = [];
7
6
  for (let k = 0; k < subParts.length; ++k) {
8
- let indexOfMonomer = s.indexOf(subParts[k]);
9
- let helmBeforeMonomer = s.slice(j, indexOfMonomer);
7
+ const indexOfMonomer = s.indexOf(subParts[k]);
8
+ const helmBeforeMonomer = s.slice(j, indexOfMonomer);
10
9
  allParts.push(helmBeforeMonomer);
11
10
  allParts.push(subParts[k]);
12
11
  s = s.substring(indexOfMonomer + subParts[k].length);
@@ -16,32 +15,44 @@ export function getParts(subParts: string[], s: string): string[] {
16
15
  }
17
16
 
18
17
  export function parseHelm(s: string) {
19
- var sections = split(s, '$');
18
+ const sections = split(s, '$');
20
19
  s = sections[0];
21
- var monomers = [];
20
+ const monomers = [];
22
21
  //@ts-ignore
23
22
  if (!scil.Utils.isNullOrEmpty(s)) {
24
- var seqs = split(s, '|');
25
- for (var i = 0; i < seqs.length; ++i) {
26
- var e = detachAnnotation(seqs[i]);
23
+ const seqs = split(s, '|');
24
+ for (let i = 0; i < seqs.length; ++i) {
25
+ const e = detachAnnotation(seqs[i]);
27
26
  s = e.str;
28
27
 
29
- var p = s.indexOf('{');
28
+ let p = s.indexOf('{');
30
29
 
31
30
  s = s.substring(p + 1);
32
31
  p = s.indexOf('}');
33
32
  s = s.substring(0, p);
34
33
 
35
- var ss = split(s, '.');
36
- for (var monomer of ss) {
37
- if (monomer.includes('(') && monomer.includes(')')) {
38
- var elements = monomer.replace(/[()]/g, '').split('');
39
- for (var el of elements) {
40
- monomers.push(el);
41
- }
42
- } else if (monomer.includes('[') && monomer.includes(']')) {
43
- var element = monomer.match(/(?<=\[).*?(?=\])/g, '');
44
- monomers.push(element[0]);
34
+ const ss = split(s, '.');
35
+ for (const monomer of ss) {
36
+ if (!monomer || monomer === '') continue;
37
+ if (monomer.startsWith('[') && monomer.includes(']')) {
38
+ const element = monomer.substring(1, monomer.indexOf(']'));
39
+ monomers.push(element);
40
+ const residue = monomer.substring(monomer.indexOf(']') + 1);
41
+ ss.push(residue);
42
+ } else if (monomer.includes('[') && monomer.endsWith(']')) {
43
+ const element = monomer.substring(monomer.lastIndexOf('[') + 1, monomer.length - 1);
44
+ monomers.push(element);
45
+ const residue = monomer.substring(0, monomer.lastIndexOf('['));
46
+ ss.push(residue);
47
+ } else if (monomer.includes('(') && monomer.includes(')')) {
48
+ // here we only want to split the string at first '(' and last ')'
49
+ // because entries like [L-hArg(Et,Et)]([L-hArg(Et,Et)]) where L-hArg(Et,Et) is a single monomer
50
+ const firstPiece = monomer.substring(0, monomer.indexOf('('));
51
+ const thirdPiece = monomer.substring(monomer.lastIndexOf(')') + 1);
52
+ const secondPiece = monomer.substring(firstPiece.length + 1, monomer.length - thirdPiece.length - 1);
53
+ const elements =[firstPiece, secondPiece, thirdPiece];
54
+ for (const el of elements)
55
+ ss.push(el);
45
56
  } else {
46
57
  monomers.push(monomer);
47
58
  }
@@ -50,32 +61,33 @@ export function parseHelm(s: string) {
50
61
  }
51
62
  return monomers;
52
63
  }
53
-
64
+ /* this function returns names of monomers that are NOT in the monomer library */
54
65
  export function findMonomers(helmString: string) {
55
66
  //@ts-ignore
56
67
  const types = Object.keys(org.helm.webeditor.monomerTypeList());
57
68
  const monomers: any = [];
58
- const monomer_names: any = [];
59
- for (var i = 0; i < types.length; i++) {
69
+ const monomerNames: any = [];
70
+ for (let i = 0; i < types.length; i++) {
60
71
  //@ts-ignore
72
+ // eslint-disable-next-line new-cap
61
73
  monomers.push(new scil.helm.Monomers.getMonomerSet(types[i]));
62
- Object.keys(monomers[i]).forEach(k => {
63
- monomer_names.push(monomers[i][k].id);
74
+ Object.keys(monomers[i]).forEach((k) => {
75
+ monomerNames.push(monomers[i][k].id);
64
76
  });
65
77
  }
66
- const split_string = parseHelm(helmString);
67
- return new Set(split_string.filter(val => !monomer_names.includes(val)));
78
+ const splitString = parseHelm(helmString);
79
+ return new Set(splitString.filter((val) => !monomerNames.includes(val)));
68
80
  }
69
81
 
70
82
  function split(s: string, sep: string) {
71
- var ret = [];
72
- var frag = '';
73
- var parentheses = 0;
74
- var bracket = 0;
75
- var braces = 0;
76
- var quote = 0;
77
- for (var i = 0; i < s.length; ++i) {
78
- var c = s.substring(i, i + 1);
83
+ const ret = [];
84
+ let frag = '';
85
+ let parentheses = 0;
86
+ let bracket = 0;
87
+ let braces = 0;
88
+ let quote = 0;
89
+ for (let i = 0; i < s.length; ++i) {
90
+ let c = s.substring(i, i + 1);
79
91
  if (c == sep && bracket == 0 && parentheses == 0 && braces == 0 && quote == 0) {
80
92
  ret.push(frag);
81
93
  frag = '';
@@ -84,7 +96,7 @@ function split(s: string, sep: string) {
84
96
  if (quote > 0) {
85
97
  if (c == '\\' && i + 1 < s.length) {
86
98
  ++i;
87
- var c2 = s.substring(i, i + 1);
99
+ const c2 = s.substring(i, i + 1);
88
100
  frag += c2;
89
101
  c += c2;
90
102
  }
@@ -92,18 +104,19 @@ function split(s: string, sep: string) {
92
104
  if (c == '\"') {
93
105
  if (!(i > 0 && s.substring(i - 1, i) == '\\'))
94
106
  quote = quote == 0 ? 1 : 0;
95
- } else if (c == '[')
107
+ } else if (c == '[') {
96
108
  ++bracket;
97
- else if (c == ']')
109
+ } else if (c == ']') {
98
110
  --bracket;
99
- else if (c == '(')
111
+ } else if (c == '(') {
100
112
  ++parentheses;
101
- else if (c == ')')
113
+ } else if (c == ')') {
102
114
  --parentheses;
103
- else if (c == '{')
115
+ } else if (c == '{') {
104
116
  ++braces;
105
- else if (c == '}')
117
+ } else if (c == '}') {
106
118
  --braces;
119
+ }
107
120
  }
108
121
  }
109
122
  ret.push(frag);
@@ -111,19 +124,19 @@ function split(s: string, sep: string) {
111
124
  }
112
125
 
113
126
  function detachAnnotation(s: string) {
114
- var ret = _detachAppendix(s, '\"');
127
+ const ret = _detachAppendix(s, '\"');
115
128
  if (ret.tag != null)
116
129
  return ret;
117
130
 
118
- var r = _detachAppendix(s, '\'');
131
+ const r = _detachAppendix(s, '\'');
119
132
  return {tag: ret.tag, repeat: r.tag, str: r.str};
120
133
  }
121
134
 
122
135
  function _detachAppendix(s: string, c: string) {
123
- var tag = null;
136
+ let tag = null;
124
137
  //@ts-ignore
125
138
  if (scil.Utils.endswith(s, c)) {
126
- var p = s.length - 1;
139
+ let p = s.length - 1;
127
140
  while (p > 0) {
128
141
  p = s.lastIndexOf(c, p - 1);
129
142
  if (p <= 0 || s.substring(p - 1, p) != '\\')
package/tsconfig.json CHANGED
@@ -6,7 +6,7 @@
6
6
  // "incremental": true, /* Enable incremental compilation */
7
7
  "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
8
8
  "module": "es2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
9
- "lib": ["es2020", "dom", "ES2021.String"], /* Specify library files to be included in the compilation. */
9
+ "lib": ["ES2022", "dom"], /* Specify library files to be included in the compilation. */
10
10
  "allowJs": true, /* Allow javascript files to be compiled. */
11
11
  // "checkJs": true, /* Report errors in .js files. */
12
12
  // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */