@datagrok/sequence-translator 1.0.3 → 1.0.6

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,11 @@
1
1
  {
2
2
  "name": "@datagrok/sequence-translator",
3
3
  "friendlyName": "Sequence Translator",
4
- "version": "1.0.3",
4
+ "version": "1.0.6",
5
+ "author": {
6
+ "name": "Vadym Kovadlo",
7
+ "email": "vkovadlo@datagrok.ai"
8
+ },
5
9
  "description": "SequenceTranslator is a [package](https://datagrok.ai/help/develop/develop#packages) for the [Datagrok](https://datagrok.ai) platform, used to translate [oligonucleotide](https://en.wikipedia.org/wiki/Oligonucleotide) sequences between [different representations](https://github.com/datagrok-ai/public/tree/master/packages/SequenceTranslator#sequence-representations).",
6
10
  "repository": {
7
11
  "type": "git",
@@ -10,46 +14,51 @@
10
14
  },
11
15
  "dependencies": {
12
16
  "@datagrok-libraries/utils": "^0.1.0",
13
- "@types/react": "latest",
14
- "datagrok-api": "^1.1.7",
17
+ "@types/react": "^18.0.15",
18
+ "datagrok-api": "^1.6.0",
15
19
  "datagrok-tools": "^4.1.2",
16
20
  "npm": "^8.11.0",
21
+ "openchemlib": "6.0.1",
17
22
  "save-svg-as-png": "^1.4.17",
18
- "ts-loader": "latest",
19
- "typescript": "latest",
20
- "openchemlib": "6.0.1"
23
+ "ts-loader": "^9.3.1",
24
+ "typescript": "^4.7.4"
21
25
  },
22
26
  "scripts": {
23
27
  "link-api": "npm link datagrok-api",
24
- "debug-sequencetranslator": "grok publish --rebuild",
25
- "release-sequencetranslator": "grok publish --rebuild --release",
28
+ "debug-sequencetranslator": "grok publish",
29
+ "release-sequencetranslator": "grok publish localhost --release",
26
30
  "build-sequencetranslator": "webpack",
27
31
  "build": "webpack",
28
- "debug-sequencetranslator-public": "grok publish public --rebuild",
29
- "release-sequencetranslator-public": "grok publish public --rebuild --release",
30
- "debug-sequencetranslator-local": "grok publish local --rebuild",
31
- "release-sequencetranslator-local": "grok publish local --rebuild --release",
32
- "test": "set HOST=dev && jest",
32
+ "debug-sequencetranslator-public": "grok publish public",
33
+ "release-sequencetranslator-public": "grok publish public --release",
34
+ "debug-sequencetranslator-local": "grok publish local",
35
+ "release-sequencetranslator-local": "grok publish local --release",
36
+ "test": "jest",
33
37
  "test-dev": "set HOST=dev && jest",
34
38
  "test-local": "set HOST=localhost && jest"
35
39
  },
36
40
  "sources": [
37
41
  "css/style.css",
38
- "common/openchemlib-full.js"
42
+ "vendors/openchemlib-full.js"
39
43
  ],
40
44
  "devDependencies": {
45
+ "@types/jest": "^27.0.0",
46
+ "@types/jquery": "^3.5.14",
41
47
  "@typescript-eslint/eslint-plugin": "^4.29.1",
42
48
  "@typescript-eslint/parser": "^4.29.1",
43
49
  "cash-dom": "^8.1.0",
44
50
  "eslint": "^7.32.0",
45
51
  "eslint-config-google": "^0.14.0",
46
- "webpack": "^5.31.0",
47
- "webpack-cli": "^4.6.0",
48
- "jest-html-reporter": "^3.5.0",
49
52
  "jest": "^27.0.0",
50
- "@types/jest": "^27.0.0",
53
+ "jest-html-reporter": "^3.5.0",
54
+ "puppeteer": "^13.7.0",
51
55
  "ts-jest": "^27.0.0",
52
- "puppeteer": "^13.7.0"
56
+ "webpack": "^5.31.0",
57
+ "webpack-cli": "^4.6.0",
58
+ "@types/js-yaml": "^4.0.5",
59
+ "js-yaml": "^4.1.0",
60
+ "@types/node-fetch": "^2.6.2",
61
+ "node-fetch": "^2.6.7"
53
62
  },
54
63
  "category": "Bioinformatics"
55
64
  }
@@ -5,7 +5,7 @@
5
5
  import * as utils from './test-node';
6
6
  import puppeteer from 'puppeteer';
7
7
 
8
- const P_START_TIMEOUT: number = 100000;
8
+ const P_START_TIMEOUT: number = 3600000;
9
9
  let browser: puppeteer.Browser;
10
10
  let page: puppeteer.Page;
11
11
 
@@ -16,34 +16,53 @@ beforeAll(async () => {
16
16
  }, P_START_TIMEOUT);
17
17
 
18
18
  afterAll(async () => {
19
- await browser.close();
19
+ await browser?.close();
20
+ });
21
+
22
+ expect.extend({
23
+ checkOutput(received, expected, context) {
24
+ if (received === expected) {
25
+ return {
26
+ message: () => context,
27
+ pass: true
28
+ };
29
+ } else {
30
+ return {
31
+ message: () => context,
32
+ pass: false
33
+ };
34
+ }
35
+ }
20
36
  });
21
37
 
22
38
  it('TEST', async () => {
23
- const target_package:string = process.env.TARGET_PACKAGE ?? 'SequenceTranslator';
24
- console.log(`Testing ${target_package} package`);
39
+ const targetPackage:string = process.env.TARGET_PACKAGE ?? 'SequenceTranslator';
40
+ console.log(`Testing ${targetPackage} package`);
25
41
 
26
- //console.log(require('root-require')('package.json').version);
27
- let r = await page.evaluate((target_package):Promise<object> => {
42
+ let r = await page.evaluate((targetPackage):Promise<object> => {
28
43
  return new Promise<object>((resolve, reject) => {
29
- (<any>window).grok.functions.eval(target_package + ':test()').then((df: any) => {
44
+ (<any>window).grok.functions.eval(targetPackage + ':test()').then((df: any) => {
30
45
  let cStatus = df.columns.byName('success');
31
46
  let cMessage = df.columns.byName('result');
32
47
  let cCat = df.columns.byName('category');
33
48
  let cName = df.columns.byName('name');
34
49
  let failed = false;
35
- let report = '';
36
- for (let i = 0; i < df.rowCount; i++)
37
- if (!cStatus.get(i)) {
38
- report += `${cCat.get(i)}.${cName.get(i)}: ${cMessage.get(i)}\n`;
50
+ let passReport = '';
51
+ let failReport = '';
52
+ for (let i = 0; i < df.rowCount; i++) {
53
+ if (cStatus.get(i)) {
54
+ passReport += `Test result : Success : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
55
+ } else {
39
56
  failed = true;
57
+ failReport += `Test result : Failed : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
40
58
  }
41
- resolve({report, failed});
59
+ }
60
+ resolve({failReport, passReport, failed});
42
61
  }).catch((e: any) => reject(e));
43
62
  });
44
- }, target_package);
63
+ }, targetPackage);
45
64
  // @ts-ignore
46
- console.log(r.report);
65
+ console.log(r.passReport);
47
66
  // @ts-ignore
48
- expect(r.failed).toBe(false);
49
- }, 100000);
67
+ expect(r.failed).checkOutput(false, r.failReport);
68
+ }, 3600000);
@@ -67,6 +67,7 @@ export async function getBrowserPage(puppeteer: any): Promise<{browser: any, pag
67
67
  });
68
68
 
69
69
  let page = await browser.newPage();
70
+ await page.setDefaultNavigationTimeout(0);
70
71
  await page.goto(`${url}/oauth/`);
71
72
  await page.setCookie({name: 'auth', value: token});
72
73
  await page.evaluate((token: any) => {
@@ -74,8 +75,8 @@ export async function getBrowserPage(puppeteer: any): Promise<{browser: any, pag
74
75
  }, token);
75
76
  await page.goto(url);
76
77
  try {
77
- await page.waitForSelector('.grok-preloader');
78
- await page.waitForFunction(() => document.querySelector('.grok-preloader') == null, {timeout: 100000});
78
+ await page.waitForSelector('.grok-preloader', { timeout: 1800000 });
79
+ await page.waitForFunction(() => document.querySelector('.grok-preloader') == null, {timeout: 3600000});
79
80
  } catch (error) {
80
81
  throw error;
81
82
  }
@@ -0,0 +1,206 @@
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
+ import {siRnaAxolabsToGcrs, gcrsToNucleotides, asoGapmersBioSpringToGcrs, gcrsToMermade12,
5
+ } from '../structures-works/converters';
6
+ import {map, COL_NAMES, MODIFICATIONS} from '../structures-works/map';
7
+ import {getFormat, isValidSequence} from '../structures-works/sequence-codes-tools';
8
+ import {sequenceToMolV3000} from '../structures-works/from-monomers';
9
+
10
+ import {SALTS_CSV} from '../salts';
11
+ import {USERS_CSV} from '../users';
12
+ import {ICDS} from '../ICDs';
13
+ import {SOURCES} from '../sources';
14
+ import {IDPS} from '../IDPs';
15
+
16
+
17
+ function sortByStringLengthInDescendingOrder(array: string[]): string[] {
18
+ return array.sort(function(a, b) {return b.length - a.length;});
19
+ }
20
+
21
+ function stringify(items: string[]): string {
22
+ return '["' + items.join('", "') + '"]';
23
+ }
24
+
25
+ function molecularWeight(sequence: string, weightsObj: {[index: string]: number}): number {
26
+ const codes = sortByStringLengthInDescendingOrder(Object.keys(weightsObj)).concat(Object.keys(MODIFICATIONS));
27
+ let weight = 0;
28
+ let i = 0;
29
+ while (i < sequence.length) {
30
+ const matchedCode = codes.find((s) => s == sequence.slice(i, i + s.length))!;
31
+ weight += weightsObj[sequence.slice(i, i + matchedCode.length)];
32
+ i += matchedCode.length;
33
+ }
34
+ return weight - 61.97;
35
+ }
36
+
37
+ async function saveTableAsSdFile(table: DG.DataFrame) {
38
+ if (!table.columns.contains('Compound Name')) {
39
+ grok.shell.warning(
40
+ 'File saved without columns \'' +
41
+ [COL_NAMES.COMPOUND_NAME, COL_NAMES.COMPOUND_COMMENTS, COL_NAMES.CPD_MW,
42
+ COL_NAMES.SALT_MASS, COL_NAMES.BATCH_MW].join('\', \''),
43
+ );
44
+ }
45
+ const structureColumn = table.getCol(COL_NAMES.SEQUENCE);
46
+ const typeColumn = table.getCol(COL_NAMES.TYPE);
47
+ let result = '';
48
+ for (let i = 0; i < table.rowCount; i++) {
49
+ const format = 'Janssen GCRS Codes'; //getFormat(structureColumn.get(i))!;
50
+ result += (typeColumn.get(i) == 'SS') ?
51
+ sequenceToMolV3000(structureColumn.get(i), false, true, format) + '\n' + `> <Sequence>\nSense Strand\n\n` :
52
+ sequenceToMolV3000(structureColumn.get(i), true, true, format) + '\n' + `> <Sequence>\nAnti Sense\n\n`;
53
+ for (const col of table.columns) {
54
+ if (col.name != COL_NAMES.SEQUENCE)
55
+ result += `> <${col.name}>\n${col.get(i)}\n\n`;
56
+ }
57
+ result += '$$$$\n\n';
58
+ }
59
+ const element = document.createElement('a');
60
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
61
+ element.setAttribute('download', table.name + '.sdf');
62
+ element.click();
63
+ }
64
+
65
+ export function autostartOligoSdFileSubscription() {
66
+ grok.events.onViewAdded.subscribe((v: any) => {
67
+ if (v.type == 'TableView') {
68
+ if (v.dataFrame.columns.contains(COL_NAMES.TYPE))
69
+ oligoSdFile(v.dataFrame);
70
+ grok.events.onContextMenu.subscribe((args) => {
71
+ const seqCol = args.args.context.table.currentCol; // /^[fsACGUacgu]{6,}$/
72
+ if (DG.Detector.sampleCategories(seqCol,
73
+ (s) => /(\(invabasic\)|\(GalNAc-2-JNJ\)|f|s|A|C|G|U|a|c|g|u){6,}$/.test(s))) {
74
+ args.args.menu.item('Convert Axolabs to GCRS', () => {
75
+ args.args.context.table.columns.addNewString(seqCol.name + ' to GCRS').init((i: number) => {
76
+ return siRnaAxolabsToGcrs(seqCol.get(i));
77
+ });
78
+ }); // /^[fmpsACGU]{6,}$/
79
+ } else if (DG.Detector.sampleCategories(seqCol,
80
+ (s) => /(\(invabasic\)|\(GalNAc-2-JNJ\)|f|m|ps|A|C|G|U){6,}$/.test(s)) ||
81
+ DG.Detector.sampleCategories(seqCol, (s) => /^(?=.*moe)(?=.*5mC)(?=.*ps){6,}/.test(s))) {
82
+ args.args.menu.item('Convert GCRS to raw', () => {
83
+ args.args.context.table.columns.addNewString(seqCol.name + ' to raw').init((i: number) => {
84
+ return gcrsToNucleotides(seqCol.get(i));
85
+ });
86
+ });
87
+ args.args.menu.item('Convert GCRS to MM12', () => {
88
+ args.args.context.table.columns.addNewString(seqCol.name + ' to MM12').init((i: number) => {
89
+ return gcrsToMermade12(seqCol.get(i));
90
+ });
91
+ }); // /^[*56789ATGC]{6,}$/
92
+ } else if (DG.Detector.sampleCategories(seqCol,
93
+ (s) => /(\(invabasic\)|\(GalNAc-2-JNJ\)|\*|5|6|7|8|9|A|T|G|C){6,}$/.test(s))) {
94
+ args.args.menu.item('Convert Biospring to GCRS', () => {
95
+ const seqCol = args.args.context.table.currentCol;
96
+ args.args.context.table.columns.addNewString(seqCol.name + ' to GCRS').init((i: number) => {
97
+ return asoGapmersBioSpringToGcrs(seqCol.get(i));
98
+ });
99
+ }); // /^[*1-8]{6,}$/
100
+ } else if (DG.Detector.sampleCategories(seqCol,
101
+ (s) => /(\(invabasic\)|\(GalNAc-2-JNJ\)|\*|1|2|3|4|5|6|7|8){6,}$/.test(s))) {
102
+ args.args.menu.item('Convert Biospring to GCRS', () => {
103
+ args.args.context.table.columns.addNewString(seqCol.name + ' to GCRS').init((i: number) => {
104
+ return siRnaAxolabsToGcrs(seqCol.get(i));
105
+ });
106
+ });
107
+ }
108
+ });
109
+ }
110
+ });
111
+ }
112
+
113
+ export function oligoSdFile(table: DG.DataFrame) {
114
+ const saltsDf = DG.DataFrame.fromCsv(SALTS_CSV);
115
+ const usersDf = DG.DataFrame.fromCsv(USERS_CSV);
116
+ const sourcesDf = DG.DataFrame.fromCsv(SOURCES);
117
+ const icdsDf = DG.DataFrame.fromCsv(ICDS);
118
+ const idpsDf = DG.DataFrame.fromCsv(IDPS);
119
+
120
+ async function addColumns(t: DG.DataFrame, saltsDf: DG.DataFrame) {
121
+ if (t.columns.contains(COL_NAMES.COMPOUND_NAME))
122
+ return grok.shell.error('Columns already exist');
123
+
124
+ const sequenceCol = t.getCol(COL_NAMES.SEQUENCE);
125
+ const saltCol = t.getCol(COL_NAMES.SALT);
126
+ const equivalentsCol = t.getCol(COL_NAMES.EQUIVALENTS);
127
+
128
+ t.columns.addNewString(COL_NAMES.COMPOUND_NAME).init((i: number) => sequenceCol.get(i));
129
+
130
+ t.columns.addNewString(COL_NAMES.COMPOUND_COMMENTS).init((i: number) => (i > 0 && i % 2 == 0) ?
131
+ sequenceCol.get(i) + '; duplex of SS: ' + sequenceCol.get(i - 2) + ' and AS: ' + sequenceCol.get(i - 1) :
132
+ sequenceCol.get(i),
133
+ );
134
+ const molWeightCol = saltsDf.getCol('MOLWEIGHT');
135
+ const saltNamesList = saltsDf.getCol('DISPLAY').toList();
136
+ const weightsObj: {[code: string]: number} = {};
137
+ for (const synthesizer of Object.keys(map)) {
138
+ for (const technology of Object.keys(map[synthesizer])) {
139
+ for (const code of Object.keys(map[synthesizer][technology]))
140
+ weightsObj[code] = map[synthesizer][technology][code].weight!;
141
+ }
142
+ }
143
+ for (const [key, value] of Object.entries(MODIFICATIONS))
144
+ weightsObj[key] = value.molecularWeight;
145
+
146
+ t.columns.addNewFloat(COL_NAMES.CPD_MW).init((i: number) => {
147
+ return (isValidSequence(sequenceCol.get(i), null).indexOfFirstNotValidChar == -1) ?
148
+ molecularWeight(sequenceCol.get(i), weightsObj) :
149
+ DG.FLOAT_NULL;
150
+ });
151
+
152
+ t.columns.addNewFloat(COL_NAMES.SALT_MASS).init((i: number) => {
153
+ const saltRowIndex = saltNamesList.indexOf(saltCol.get(i));
154
+ return (saltRowIndex == -1) ?
155
+ DG.FLOAT_NULL :
156
+ molWeightCol.get(saltRowIndex) * equivalentsCol.get(i);
157
+ });
158
+
159
+ await t.columns.addNewCalculated(COL_NAMES.BATCH_MW,
160
+ '${' + COL_NAMES.CPD_MW + '} + ${' + COL_NAMES.SALT_MASS + '}', DG.COLUMN_TYPE.FLOAT, false,
161
+ );
162
+
163
+ addColumnsPressed = true;
164
+ return newDf = t;
165
+ }
166
+
167
+ let newDf: DG.DataFrame;
168
+ let addColumnsPressed = false;
169
+
170
+ const d = ui.div([
171
+ ui.icons.edit(() => {
172
+ d.innerHTML = '';
173
+ if (table.getCol(COL_NAMES.IDP).type != DG.COLUMN_TYPE.STRING)
174
+ table.changeColumnType(COL_NAMES.IDP, DG.COLUMN_TYPE.STRING);
175
+ d.append(
176
+ ui.link('Add Columns', async () => {
177
+ await addColumns(table, saltsDf);
178
+ view.grid.columns.setOrder(Object.values(COL_NAMES));
179
+ }, 'Add columns: \'' + [COL_NAMES.COMPOUND_NAME, COL_NAMES.COMPOUND_COMMENTS, COL_NAMES.CPD_MW,
180
+ COL_NAMES.SALT_MASS, COL_NAMES.BATCH_MW].join('\', \''), '',
181
+ ),
182
+ ui.button('Save SD file', () => saveTableAsSdFile(addColumnsPressed ? newDf : table)),
183
+ );
184
+
185
+ const view = grok.shell.getTableView(table.name);
186
+
187
+ view.dataFrame.getCol(COL_NAMES.TYPE).setTag(DG.TAGS.CHOICES, '["AS", "SS", "Duplex"]');
188
+ view.dataFrame.getCol(COL_NAMES.OWNER).setTag(DG.TAGS.CHOICES, stringify(usersDf.columns.byIndex(0).toList()));
189
+ view.dataFrame.getCol(COL_NAMES.SALT).setTag(DG.TAGS.CHOICES, stringify(saltsDf.columns.byIndex(0).toList()));
190
+ view.dataFrame.getCol(COL_NAMES.SOURCE).setTag(DG.TAGS.CHOICES, stringify(sourcesDf.columns.byIndex(0).toList()));
191
+ view.dataFrame.getCol(COL_NAMES.ICD).setTag(DG.TAGS.CHOICES, stringify(icdsDf.columns.byIndex(0).toList()));
192
+ view.dataFrame.getCol(COL_NAMES.IDP).setTag(DG.TAGS.CHOICES, stringify(idpsDf.columns.byIndex(0).toList()));
193
+
194
+ grok.events.onContextMenu.subscribe((args) => {
195
+ if ([COL_NAMES.TYPE, COL_NAMES.OWNER, COL_NAMES.SALT, COL_NAMES.SOURCE, COL_NAMES.ICD, COL_NAMES.IDP]
196
+ .includes(args.args.context.table.currentCol.name)) {
197
+ args.args.menu.item('Fill Column With Value', () => {
198
+ const v = args.args.context.table.currentCell.value;
199
+ args.args.context.table.currentCell.column.init(v);
200
+ });
201
+ }
202
+ });
203
+ }),
204
+ ]);
205
+ grok.shell.v.setRibbonPanels([[d]]);
206
+ }
@@ -94,7 +94,7 @@ export const axolabsMap:
94
94
  color: invAbasicColor,
95
95
  },
96
96
  '2\'-OMe-U(o)': {
97
- fullName: 'Nucleotide Uridine with 2O-Methyl protection (overhang)',
97
+ fullName: 'Nucleotide Uridine with 2\'O-Methyl protection (overhang)',
98
98
  symbols: ['mU', 'mU', 'mU', 'mU'],
99
99
  color: 'rgb(65,233,80)',
100
100
  },