@datagrok/sequence-translator 0.0.8 → 0.0.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/jest.config.js ADDED
@@ -0,0 +1,33 @@
1
+ module.exports = {
2
+ "roots": [
3
+ "<rootDir>/src"
4
+ ],
5
+ "testMatch": [
6
+ "**/__jest__/**/*.test.+(ts|tsx)"
7
+ ],
8
+ moduleFileExtensions: [
9
+ 'ts',
10
+ 'js',
11
+ ],
12
+ "transform": {
13
+ "^.+\\.(ts|tsx)$": "ts-jest"
14
+ },
15
+ transformIgnorePatterns: ['^.+\\.js$'],
16
+ globals: {
17
+ 'ts-jest': {
18
+ 'tsconfig': {
19
+ "target": "es6",
20
+ "module": "es2020",
21
+ }
22
+ }
23
+ },
24
+ reporters: [
25
+ "default",
26
+ [
27
+ "./node_modules/jest-html-reporter",
28
+ {
29
+ "includeConsoleLog": true
30
+ }
31
+ ]
32
+ ]
33
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/sequence-translator",
3
- "friendlyName": "SequenceTranslator",
4
- "version": "0.0.8",
3
+ "friendlyName": "Sequence Translator",
4
+ "version": "0.0.9",
5
5
  "description": "",
6
6
  "dependencies": {
7
7
  "@datagrok-libraries/utils": "^0.1.0",
@@ -23,7 +23,10 @@
23
23
  "debug-sequencetranslator-public": "grok publish public --rebuild",
24
24
  "release-sequencetranslator-public": "grok publish public --rebuild --release",
25
25
  "debug-sequencetranslator-local": "grok publish local --rebuild",
26
- "release-sequencetranslator-local": "grok publish local --rebuild --release"
26
+ "release-sequencetranslator-local": "grok publish local --rebuild --release",
27
+ "test": "set HOST=dev && jest",
28
+ "test-dev": "set HOST=dev && jest",
29
+ "test-local": "set HOST=localhost && jest"
27
30
  },
28
31
  "devDependencies": {
29
32
  "@typescript-eslint/eslint-plugin": "^4.29.1",
@@ -32,6 +35,11 @@
32
35
  "eslint": "^7.32.0",
33
36
  "eslint-config-google": "^0.14.0",
34
37
  "webpack": "^5.31.0",
35
- "webpack-cli": "^4.6.0"
38
+ "webpack-cli": "^4.6.0",
39
+ "@types/jest": "latest",
40
+ "jest": "latest",
41
+ "jest-html-reporter": "latest",
42
+ "puppeteer": "^13.1.2",
43
+ "ts-jest": "^27.1.2"
36
44
  }
37
45
  }
package/setup.cmd CHANGED
@@ -8,4 +8,4 @@ call npm link datagrok-api
8
8
  cd ../../packages/SequenceTranslator
9
9
  call npm install
10
10
  call npm link datagrok-api @datagrok-libraries/utils
11
- webpack
11
+ webpack
package/src/ICDs.ts ADDED
@@ -0,0 +1,3 @@
1
+ export const ICDS = `DISPLAY
2
+ NO DATA
3
+ `;
package/src/IDPs.ts ADDED
@@ -0,0 +1,3 @@
1
+ export const IDPS = `DISPLAY
2
+ NO DATA
3
+ `;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import * as utils from './test-node';
6
+ import puppeteer from 'puppeteer';
7
+
8
+ const P_START_TIMEOUT: number = 100000;
9
+ let browser: puppeteer.Browser;
10
+ let page: puppeteer.Page;
11
+
12
+ beforeAll(async () => {
13
+ let out = await utils.getBrowserPage(puppeteer);
14
+ browser = out.browser;
15
+ page = out.page;
16
+ }, P_START_TIMEOUT);
17
+
18
+ afterAll(async () => {
19
+ await browser.close();
20
+ });
21
+
22
+ it('TEST', async () => {
23
+ const target_package:string = process.env.TARGET_PACKAGE ?? 'SequenceTranslator';
24
+ console.log(`Testing ${target_package} package`);
25
+
26
+ //console.log(require('root-require')('package.json').version);
27
+ let r = await page.evaluate((target_package):Promise<object> => {
28
+ return new Promise<object>((resolve, reject) => {
29
+ (<any>window).grok.functions.eval(target_package + ':test()').then((df: any) => {
30
+ let cStatus = df.columns.byName('success');
31
+ let cMessage = df.columns.byName('result');
32
+ let cCat = df.columns.byName('category');
33
+ let cName = df.columns.byName('name');
34
+ 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`;
39
+ failed = true;
40
+ }
41
+ resolve({report, failed});
42
+ }).catch((e: any) => reject(e));
43
+ });
44
+ }, target_package);
45
+ // @ts-ignore
46
+ console.log(r.report);
47
+ // @ts-ignore
48
+ expect(r.failed).toBe(false);
49
+ }, 100000);
@@ -0,0 +1,96 @@
1
+ import * as path from "path";
2
+ import * as os from "os";
3
+ import * as fs from "fs";
4
+ // @ts-ignore
5
+ import * as yaml from 'js-yaml';
6
+ const fetch = require('node-fetch');
7
+
8
+ export async function getToken(url: string, key: string) {
9
+ let response = await fetch(`${url}/users/login/dev/${key}`, {method: 'POST'});
10
+ let json = await response.json();
11
+ if (json.isSuccess == true)
12
+ return json.token;
13
+ else
14
+ throw 'Unable to login to server. Check your dev key';
15
+ }
16
+
17
+ export async function getWebUrl(url: string, token: string) {
18
+ let response = await fetch(`${url}/admin/plugins/admin/settings`, {headers: {Authorization: token}});
19
+ let json = await response.json();
20
+ return json.settings.webRoot;
21
+ }
22
+
23
+ const grokDir = path.join(os.homedir(), '.grok');
24
+ const confPath = path.join(grokDir, 'config.yaml');
25
+
26
+ function mapURL(conf: Config): Indexable {
27
+ let urls: Indexable = {};
28
+ for (let server in conf.servers) {
29
+ urls[conf['servers'][server]['url']] = conf['servers'][server];
30
+ }
31
+ return urls;
32
+ }
33
+
34
+ export function getDevKey(hostKey: string): {url: string, key: string} {
35
+ let config = yaml.load(fs.readFileSync(confPath, 'utf8')) as any;
36
+ let host = hostKey == '' ? config.default : hostKey;
37
+ host = host.trim();
38
+ let urls = mapURL(config);
39
+ let key = '';
40
+ let url = '';
41
+ try {
42
+ let url = new URL(host).href;
43
+ if (url.endsWith('/')) url = url.slice(0, -1);
44
+ if (url in urls) key = config['servers'][urls[url]]['key'];
45
+ } catch (error) {
46
+ if (config['servers'][host] == null)
47
+ throw `Unknown server alias. Please add it to ${confPath}`;
48
+ url = config['servers'][host]['url'];
49
+ key = config['servers'][host]['key'];
50
+ }
51
+ return {url, key};
52
+ }
53
+
54
+ export async function getBrowserPage(puppeteer: any): Promise<{browser: any, page: any}> {
55
+ let url:string = process.env.HOST ?? '';
56
+ let cfg = getDevKey(url);
57
+ url = cfg.url;
58
+
59
+ let key = cfg.key;
60
+ let token = await getToken(url, key);
61
+ url = await getWebUrl(url, token);
62
+ console.log(`Using web root: ${url}`);
63
+
64
+ let browser = await puppeteer.launch({
65
+ args: ['--disable-dev-shm-usage', '--disable-features=site-per-process'],
66
+ ignoreHTTPSErrors: true,
67
+ });
68
+
69
+ let page = await browser.newPage();
70
+ await page.goto(`${url}/oauth/`);
71
+ await page.setCookie({name: 'auth', value: token});
72
+ await page.evaluate((token: any) => {
73
+ window.localStorage.setItem('auth', token);
74
+ }, token);
75
+ await page.goto(url);
76
+ try {
77
+ await page.waitForSelector('.grok-preloader');
78
+ await page.waitForFunction(() => document.querySelector('.grok-preloader') == null, {timeout: 100000});
79
+ } catch (error) {
80
+ throw error;
81
+ }
82
+ return {browser, page};
83
+ }
84
+
85
+
86
+ interface Config {
87
+ servers: {
88
+ [alias: string]: {
89
+ url: string,
90
+ key: string
91
+ }
92
+ },
93
+ default: string,
94
+ }
95
+
96
+ interface Indexable { [key: string]: any }
@@ -1,8 +1,9 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
- import {runTests} from '@datagrok-libraries/utils/src/test';
2
+ import {runTests, tests} from '@datagrok-libraries/utils/src/test';
3
3
  import './tests/smiles-tests';
4
4
 
5
5
  export const _package = new DG.Package();
6
+ export {tests}
6
7
 
7
8
  //name: test
8
9
  //output: dataframe result
package/src/package.ts CHANGED
@@ -6,9 +6,14 @@ import * as OCL from 'openchemlib/full.js';
6
6
  import $ from 'cash-dom';
7
7
  import {defineAxolabsPattern} from './defineAxolabsPattern';
8
8
  import {saveSenseAntiSense} from './structures-works/save-sense-antisense';
9
- import {sequenceToSmiles} from './structures-works/from-monomers';
9
+ import {sequenceToSmiles, sequenceToMolV3000} from './structures-works/from-monomers';
10
10
  import {convertSequence, undefinedInputSequence} from './structures-works/sequence-codes-tools';
11
+ import {map, COL_NAMES, MODIFICATIONS} from './structures-works/map';
11
12
  import {SALTS_CSV} from './salts';
13
+ import {USERS_CSV} from './users';
14
+ import {ICDS} from './ICDs';
15
+ import {SOURCES} from './sources';
16
+ import {IDPS} from './IDPs';
12
17
 
13
18
  export const _package = new DG.Package();
14
19
 
@@ -72,12 +77,8 @@ export function sequenceTranslator(): void {
72
77
  });
73
78
  switchInput.enabled = true;
74
79
  } else {
75
- asoGapmersGrid.onCellPrepare(function(gc) {
76
- gc.style.backColor = 0xFFFFFFFF;
77
- });
78
- omeAndFluoroGrid.onCellPrepare(function(gc) {
79
- gc.style.backColor = 0xFFFFFFFF;
80
- });
80
+ asoGapmersGrid.onCellPrepare(function(gc) {gc.style.backColor = 0xFFFFFFFF;});
81
+ omeAndFluoroGrid.onCellPrepare(function(gc) {gc.style.backColor = 0xFFFFFFFF;});
81
82
  }
82
83
 
83
84
  outputTableDiv.append(
@@ -92,18 +93,18 @@ export function sequenceTranslator(): void {
92
93
  const canvas = ui.canvas(300, 170);
93
94
  canvas.addEventListener('click', () => {
94
95
  const canv = ui.canvas($(window).width(), $(window).height());
95
- const smiles = sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''));
96
+ const mol = sequenceToMolV3000(inputSequenceField.value.replace(/\s/g, ''), false, true);
96
97
  // @ts-ignore
97
- OCL.StructureView.drawMolecule(canv, OCL.Molecule.fromSmiles(smiles), {suppressChiralText: true});
98
+ OCL.StructureView.drawMolecule(canv, OCL.Molecule.fromMolfile(mol), {suppressChiralText: true});
98
99
  ui.dialog('Molecule: ' + inputSequenceField.value)
99
100
  .add(canv)
100
101
  .showModal(true);
101
102
  });
102
103
  $(canvas).on('mouseover', () => $(canvas).css('cursor', 'zoom-in'));
103
104
  $(canvas).on('mouseout', () => $(canvas).css('cursor', 'default'));
104
- const smiles = sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''));
105
+ const mol = sequenceToMolV3000(inputSequenceField.value.replace(/\s/g, ''), false, true);
105
106
  // @ts-ignore
106
- OCL.StructureView.drawMolecule(canvas, OCL.Molecule.fromSmiles(smiles), {suppressChiralText: true});
107
+ OCL.StructureView.drawMolecule(canvas, OCL.Molecule.fromMolfile(mol), {suppressChiralText: true});
107
108
  moleculeSvgDiv.append(canvas);
108
109
  } else
109
110
  moleculeSvgDiv.innerHTML = '';
@@ -216,8 +217,7 @@ export function sequenceTranslator(): void {
216
217
 
217
218
  const topPanel = [
218
219
  ui.iconFA('download', () => {
219
- const smiles = sequenceToSmiles(inputSequenceField.value.replace(/\s/g, ''));
220
- const result = `${OCL.Molecule.fromSmiles(smiles).toMolfile()}\n`;
220
+ const result = sequenceToMolV3000(inputSequenceField.value.replace(/\s/g, ''));
221
221
  const element = document.createElement('a');
222
222
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
223
223
  element.setAttribute('download', inputSequenceField.value.replace(/\s/g, '') + '.mol');
@@ -250,21 +250,23 @@ export function sequenceTranslator(): void {
250
250
  async function saveTableAsSdFile(table: DG.DataFrame) {
251
251
  if (!table.columns.contains('Compound Name')) {
252
252
  grok.shell.warning(
253
- 'File saved without columns \'Compound Name\', \'Compound Components\', \'Cpd MW\', \'Salt mass\', \'Batch MW\'');
253
+ 'File saved without columns \'' +
254
+ [COL_NAMES.COMPOUND_NAME, COL_NAMES.COMPOUND_COMMENTS, COL_NAMES.CPD_MW,
255
+ COL_NAMES.SALT_MASS, COL_NAMES.BATCH_MW].join('\', \''),
256
+ );
254
257
  }
255
- const structureColumn = table.columns.byName('Sequence');
258
+ const structureColumn = table.col(COL_NAMES.SEQUENCE)!;
259
+ const typeColumn = table.col(COL_NAMES.TYPE)!;
256
260
  let result = '';
257
261
  for (let i = 0; i < table.rowCount; i++) {
258
- try {
259
- const smiles = sequenceToSmiles(structureColumn.get(i));
260
- const mol = OCL.Molecule.fromSmiles(smiles);
261
- result += `\n${mol.toMolfile()}\n`;
262
- for (const col of table.columns)
262
+ result += (typeColumn.get(i) == 'SS') ?
263
+ sequenceToMolV3000(structureColumn.get(i), false, true) + '\n' + `> <Sequence>\nSense Strand\n\n` :
264
+ sequenceToMolV3000(structureColumn.get(i), true, true) + '\n' + `> <Sequence>\nAnti Sense\n\n`;
265
+ for (const col of table.columns) {
266
+ if (col.name != COL_NAMES.SEQUENCE)
263
267
  result += `> <${col.name}>\n${col.get(i)}\n\n`;
264
- result += '$$$$';
265
- } catch (error) {
266
- console.error(error);
267
268
  }
269
+ result += '$$$$\n\n';
268
270
  }
269
271
  const element = document.createElement('a');
270
272
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
@@ -272,83 +274,112 @@ async function saveTableAsSdFile(table: DG.DataFrame) {
272
274
  element.click();
273
275
  }
274
276
 
277
+ const weightsObj: {[code: string]: number} = {};
278
+ for (const synthesizer of Object.keys(map)) {
279
+ for (const technology of Object.keys(map[synthesizer])) {
280
+ for (const code of Object.keys(map[synthesizer][technology]))
281
+ weightsObj[code] = map[synthesizer][technology][code].weight;
282
+ }
283
+ }
284
+ for (const [key, value] of Object.entries(MODIFICATIONS))
285
+ weightsObj[key] = value.molecularWeight;
286
+
287
+ function sortByStringLengthInDescendingOrder(array: string[]): string[] {
288
+ return array.sort(function(a, b) {return b.length - a.length;});
289
+ }
290
+
291
+ function stringifyItems(items: string[]): string {
292
+ return '["' + items.join('", "') + '"]';
293
+ }
294
+
295
+ function molecularWeight(sequence: string, weightsObj: {[index: string]: number}): number {
296
+ const codes = sortByStringLengthInDescendingOrder(Object.keys(weightsObj)).concat(Object.keys(MODIFICATIONS));
297
+ let weight = 0;
298
+ let i = 0;
299
+ while (i < sequence.length) {
300
+ const matchedCode = codes.find((s) => s == sequence.slice(i, i + s.length))!;
301
+ weight += weightsObj[sequence.slice(i, i + matchedCode.length)];
302
+ i += matchedCode!.length;
303
+ }
304
+ return weight - 61.97;
305
+ }
306
+
275
307
  //tags: autostart
276
308
  export function autostartOligoSdFileSubscription() {
277
309
  grok.events.onViewAdded.subscribe((v: any) => {
278
- if (v.type == 'TableView' && v.dataFrame.columns.contains('Type'))
310
+ if (v.type == 'TableView' && v.dataFrame.columns.contains(COL_NAMES.TYPE))
279
311
  oligoSdFile(v.dataFrame);
280
312
  });
281
313
  }
282
314
 
283
315
  export function oligoSdFile(table: DG.DataFrame) {
284
316
  const saltsDf = DG.DataFrame.fromCsv(SALTS_CSV);
317
+ const usersDf = DG.DataFrame.fromCsv(USERS_CSV);
318
+ const sourcesDf = DG.DataFrame.fromCsv(SOURCES);
319
+ const icdsDf = DG.DataFrame.fromCsv(ICDS);
320
+ const idpsDf = DG.DataFrame.fromCsv(IDPS);
321
+
285
322
  function addColumns(t: DG.DataFrame, saltsDf: DG.DataFrame) {
286
- if (t.columns.contains('Compound Name'))
323
+ if (t.columns.contains(COL_NAMES.COMPOUND_NAME))
287
324
  return grok.shell.error('Columns already exist!');
288
325
 
289
- table.col('Source')?.init('Johnson and Johnson Pharma');
290
- table.col('ICD')?.init('No Contract');
326
+ const sequence = t.col(COL_NAMES.SEQUENCE)!;
327
+ const equivalents = t.col(COL_NAMES.EQUIVALENTS)!;
291
328
 
292
- const sequence = t.col('Sequence')!;
293
- const salt = t.col('Salt')!;
294
- const equivalents = t.col('Equivalents')!;
295
-
296
- t.columns.addNewString('Compound Name').init((i: number) => sequence.get(i));
297
- t.columns.addNewString('Compound Comments').init((i: number) => (i > 0 && i % 2 == 0) ?
329
+ t.columns.addNewString(COL_NAMES.COMPOUND_NAME).init((i: number) => sequence.get(i));
330
+ t.columns.addNewString(COL_NAMES.COMPOUND_COMMENTS).init((i: number) => (i > 0 && i % 2 == 0) ?
298
331
  sequence.getString(i) + '; duplex of SS: ' + sequence.getString(i - 2) + ' and AS: ' + sequence.getString(i - 1) :
299
332
  sequence.getString(i),
300
333
  );
301
- const chargeCol = saltsDf.col('CHARGE')!.toList();
302
- const saltNames = saltsDf.col('DISPLAY')!.toList();
303
- const molWeight = saltsDf.col('MOLWEIGHT')!.toList();
304
- t.columns.addNewFloat('Cpd MW').init((i: number) => ((i + 1) % 3 == 0) ? DG.FLOAT_NULL : molWeight[i]);
305
- t.columns.addNewFloat('Salt mass').init((i: number) => {
306
- const v = chargeCol[saltNames.indexOf(salt.get(i))];
307
- const n = (v == null) ? 0 : chargeCol[saltNames.indexOf(salt.get(i))];
308
- return n * equivalents.get(i);
334
+ t.columns.addNewFloat(COL_NAMES.CPD_MW)
335
+ .init((i: number) => molecularWeight(sequence.get(i), weightsObj));
336
+ const mwCol = t.col(COL_NAMES.CPD_MW)!;
337
+ t.columns.addNewFloat(COL_NAMES.SALT_MASS).init((i: number) => {
338
+ const mw = mwCol.get(i);
339
+ return mw * equivalents.get(i);
309
340
  });
310
- t.columns.addNewCalculated('Batch MW', '${Cpd MW} + ${Salt mass}', DG.COLUMN_TYPE.FLOAT, false);
341
+ t.columns.addNewCalculated(COL_NAMES.BATCH_MW,
342
+ '${' + COL_NAMES.CPD_MW + '} + ${' + COL_NAMES.SALT_MASS + '}', DG.COLUMN_TYPE.FLOAT, false,
343
+ );
311
344
 
312
345
  addColumnsPressed = true;
313
346
  return newDf = t;
314
347
  }
315
348
 
316
- const columnsOrder = ['Chemistry', 'Number', 'Type', 'Chemistry Name', 'Internal compound ID',
317
- 'IDP', 'Sequence', 'Compound Name', 'Compound Comments', 'Salt', 'Equivalents', 'Purity', 'Cpd MW', 'Salt mass',
318
- 'Batch MW', 'Source', 'ICD', 'Owner'];
319
349
  let newDf: DG.DataFrame;
320
350
  let addColumnsPressed = false;
321
351
 
322
352
  const d = ui.div([
323
353
  ui.icons.edit(() => {
324
354
  d.innerHTML = '';
355
+ if (table.col(COL_NAMES.IDP)!.type != DG.COLUMN_TYPE.STRING)
356
+ table.changeColumnType(COL_NAMES.IDP, DG.COLUMN_TYPE.STRING);
325
357
  d.append(
326
- ui.link('Add Columns', async () => {
327
- await addColumns(table, saltsDf);
328
- grok.shell.tableView(table.name).grid.columns.setOrder(columnsOrder);
329
- }, 'Add columns: Compound Name, Compound Components, Cpd MW, Salt mass, Batch MW', ''),
358
+ ui.link('Add Columns', () => {
359
+ addColumns(table, saltsDf);
360
+ grok.shell.tableView(table.name).grid.columns.setOrder(Object.values(COL_NAMES));
361
+ }, 'Add columns: \'' + [COL_NAMES.COMPOUND_NAME, COL_NAMES.COMPOUND_COMMENTS, COL_NAMES.CPD_MW,
362
+ COL_NAMES.SALT_MASS, COL_NAMES.BATCH_MW].join('\', \''), ''),
330
363
  ui.button('Save SD file', () => saveTableAsSdFile(addColumnsPressed ? newDf : table)),
331
364
  );
332
- const view = grok.shell.getTableView(table.name);
333
- const typeCol = view.grid.col('Type')!;
334
- const saltCol = view.grid.col('Salt')!;
335
- saltCol.cellType = 'html';
336
- typeCol.cellType = 'html';
337
- view.grid.onCellPrepare(function(gc: DG.GridCell) {
338
- if (gc.isTableCell) {
339
- if (gc.gridColumn.name == 'Type')
340
- gc.style.element = ui.choiceInput('', gc.cell.value, ['AS', 'SS', 'Duplex']).root;
341
- else if (gc.gridColumn.name == 'Salt') {
342
- gc.style.element = ui.choiceInput('', gc.cell.value, saltsDf.columns.byIndex(1).toList(), () => {
343
- view.dataFrame.col('Salt')!.set(gc.gridRow, '');
344
- }).root;
345
- }
346
- }
347
- });
348
365
 
349
- table.onDataChanged.subscribe((_) => {
350
- if (table.currentCol.name == 'IDP' && typeof table.currentCell.value != 'number')
351
- grok.shell.error('Value should be numeric');
366
+ const view = grok.shell.getTableView(table.name)!;
367
+
368
+ view.table!.col(COL_NAMES.TYPE)!.setTag(DG.TAGS.CHOICES, '["AS", "SS", "Duplex"]');
369
+ view.table!.col(COL_NAMES.OWNER)!.setTag(DG.TAGS.CHOICES, stringifyItems(usersDf.columns.byIndex(0).toList()));
370
+ view.table!.col(COL_NAMES.SALT)!.setTag(DG.TAGS.CHOICES, stringifyItems(saltsDf.columns.byIndex(1).toList()));
371
+ view.table!.col(COL_NAMES.SOURCE)!.setTag(DG.TAGS.CHOICES, stringifyItems(sourcesDf.columns.byIndex(0).toList()));
372
+ view.table!.col(COL_NAMES.ICD)!.setTag(DG.TAGS.CHOICES, stringifyItems(icdsDf.columns.byIndex(0).toList()));
373
+ view.table!.col(COL_NAMES.IDP)!.setTag(DG.TAGS.CHOICES, stringifyItems(idpsDf.columns.byIndex(0).toList()));
374
+
375
+ grok.events.onContextMenu.subscribe((args) => {
376
+ if ([COL_NAMES.TYPE, COL_NAMES.OWNER, COL_NAMES.SALT, COL_NAMES.SOURCE, COL_NAMES.ICD, COL_NAMES.IDP]
377
+ .includes(args.args.context.table.currentCol.name)) {
378
+ args.args.menu.item('Fill Column With Value', () => {
379
+ const v = args.args.context.table.currentCell.value;
380
+ args.args.context.table.currentCell.column.init(v);
381
+ });
382
+ }
352
383
  });
353
384
  }),
354
385
  ]);
package/src/sources.ts ADDED
@@ -0,0 +1,3 @@
1
+ export const SOURCES = `DISPLAY
2
+ NO DATA
3
+ `;
@@ -1,5 +1,42 @@
1
1
  import {map, stadardPhosphateLinkSmiles, SYNTHESIZERS, TECHNOLOGIES, MODIFICATIONS} from './map';
2
2
  import {isValidSequence} from './sequence-codes-tools';
3
+ import {getNucleotidesMol} from './mol-transformations';
4
+
5
+ export function sequenceToMolV3000(sequence: string, inverted: boolean = false, oclRender: boolean = false): string {
6
+ const obj = getObjectWithCodesAndSmiles(sequence);
7
+ let codes = sortByStringLengthInDescendingOrder(Object.keys(obj));
8
+ let i = 0;
9
+ const smilesCodes:string[] = [];
10
+ const codesList = [];
11
+ const links = ['s', 'ps', '*'];
12
+ const includesStandardLinkAlready = ['e', 'h', /*'g',*/ 'f', 'i', 'l', 'k', 'j'];
13
+ const dropdowns = Object.keys(MODIFICATIONS);
14
+ codes = codes.concat(dropdowns);
15
+ while (i < sequence.length) {
16
+ const code = codes.find((s: string) => s == sequence.slice(i, i + s.length))!;
17
+ i += code.length;
18
+ inverted ? codesList.unshift(code) : codesList.push(code);
19
+ }
20
+ for (let i = 0; i < codesList.length; i++) {
21
+ if (dropdowns.includes(codesList[i])) {
22
+ smilesCodes.push((i >= codesList.length / 2) ?
23
+ MODIFICATIONS[codesList[i]].right : MODIFICATIONS[codesList[i]].left);
24
+ smilesCodes.push(stadardPhosphateLinkSmiles);
25
+ } else {
26
+ if (links.includes(codesList[i]) ||
27
+ includesStandardLinkAlready.includes(codesList[i]) ||
28
+ (i < codesList.length - 1 && links.includes(codesList[i + 1]))
29
+ )
30
+ smilesCodes.push(obj[codesList[i]]);
31
+ else {
32
+ smilesCodes.push(obj[codesList[i]]);
33
+ smilesCodes.push(stadardPhosphateLinkSmiles);
34
+ }
35
+ }
36
+ }
37
+
38
+ return getNucleotidesMol(smilesCodes, oclRender);
39
+ }
3
40
 
4
41
  export function sequenceToSmiles(sequence: string, inverted: boolean = false): string {
5
42
  const obj = getObjectWithCodesAndSmiles(sequence);
@@ -18,15 +55,9 @@ export function sequenceToSmiles(sequence: string, inverted: boolean = false): s
18
55
  }
19
56
  for (let i = 0; i < codesList.length; i++) {
20
57
  if (dropdowns.includes(codesList[i])) {
21
- if (i == codesList.length -1 || (i < codesList.length - 1 && links.includes(codesList[i + 1]))) {
22
- smiles += (i >= codesList.length / 2) ?
23
- MODIFICATIONS[codesList[i]].right:
24
- MODIFICATIONS[codesList[i]].left;
25
- } else if (i < codesList.length - 1) {
26
- smiles += (i >= codesList.length / 2) ?
27
- MODIFICATIONS[codesList[i]].right + stadardPhosphateLinkSmiles:
28
- MODIFICATIONS[codesList[i]].left + stadardPhosphateLinkSmiles;
29
- }
58
+ smiles += (i >= codesList.length / 2) ?
59
+ MODIFICATIONS[codesList[i]].right + stadardPhosphateLinkSmiles:
60
+ MODIFICATIONS[codesList[i]].left + stadardPhosphateLinkSmiles;
30
61
  } else {
31
62
  if (links.includes(codesList[i]) ||
32
63
  includesStandardLinkAlready.includes(codesList[i]) ||
@@ -11,14 +11,36 @@ export const TECHNOLOGIES = {
11
11
  ASO_GAPMERS: 'For ASO Gapmers',
12
12
  SI_RNA: 'For 2\'-OMe and 2\'-F modified siRNA',
13
13
  };
14
+ export const COL_NAMES = {
15
+ CHEMISTRY: 'Chemistry',
16
+ NUMBER: 'Number',
17
+ TYPE: 'Type',
18
+ CHEMISTRY_NAME: 'Chemistry Name',
19
+ INTERNAL_COMPOUND_ID: 'Internal compound ID',
20
+ IDP: 'IDP',
21
+ SEQUENCE: 'Sequence',
22
+ COMPOUND_NAME: 'Compound Name',
23
+ COMPOUND_COMMENTS: 'Compound Comments',
24
+ SALT: 'Salt',
25
+ EQUIVALENTS: 'Equivalents',
26
+ PURITY: 'Purity',
27
+ CPD_MW: 'Cpd MW',
28
+ SALT_MASS: 'Salt mass',
29
+ BATCH_MW: 'Batch MW',
30
+ SOURCE: 'Source',
31
+ ICD: 'ICD',
32
+ OWNER: 'Owner',
33
+ };
14
34
  // interface CODES {
15
35
  // }
16
- export const MODIFICATIONS: {[index: string]: {left: string, right: string}} = {
36
+ export const MODIFICATIONS: {[index: string]: {molecularWeight: number, left: string, right: string}} = {
17
37
  '(invabasic)': {
38
+ molecularWeight: 118.13,
18
39
  left: 'O[C@@H]1C[C@@H]O[C@H]1CO',
19
40
  right: 'O[C@@H]1C[C@@H]O[C@H]1CO',
20
41
  },
21
42
  '(GalNAc-2-JNJ)': {
43
+ molecularWeight: 1273.3,
22
44
  left: 'C(COCCC(=O)NCCCNC(=O)CCCCOC2OC(CO)C(O)C(O)C2NC(=O)C)' +
23
45
  '(COCCC(=O)NCCCNC(=O)CCCCOC2OC(CO)C(O)C(O)C2NC(=O)C)' +
24
46
  '(COCCC(=O)NCCCNC(=O)CCCCOC2OC(CO)C(O)C(O)C2NC(=O)C)NC(=O)CCCC(=O)NCC(O)CO',
@@ -49,7 +71,7 @@ export const map: {[synthesizer: string]:
49
71
  'name': 'Guanine',
50
72
  'weight': 329.21,
51
73
  'normalized': 'dG',
52
- 'SMILES': 'OC[C@H]1O[C@@H](N2C3N=C(N)NC(=O)C=3N=C2)C)[C@@H]1O',
74
+ 'SMILES': 'OC[C@H]1O[C@@H](N2C3N=C(N)NC(=O)C=3N=C2)C[C@@H]1O',
53
75
  },
54
76
  'C': {
55
77
  'name': 'Cytosine',
@@ -75,7 +97,7 @@ export const map: {[synthesizer: string]:
75
97
  'name': 'Guanine',
76
98
  'weight': 329.21,
77
99
  'normalized': 'dG',
78
- 'SMILES': 'OC[C@H]1O[C@@H](N2C3N=C(N)NC(=O)C=3N=C2)C)[C@@H]1O',
100
+ 'SMILES': 'OC[C@H]1O[C@@H](N2C3N=C(N)NC(=O)C=3N=C2)C[C@@H]1O',
79
101
  },
80
102
  'C': {
81
103
  'name': 'Cytosine',
@@ -139,7 +161,7 @@ export const map: {[synthesizer: string]:
139
161
  'name': 'Guanine',
140
162
  'weight': 329.21,
141
163
  'normalized': 'dG',
142
- 'SMILES': 'OC[C@H]1O[C@@H](N2C3N=C(N)NC(=O)C=3N=C2)C)[C@@H]1O',
164
+ 'SMILES': 'OC[C@H]1O[C@@H](N2C3N=C(N)NC(=O)C=3N=C2)C[C@@H]1O',
143
165
  },
144
166
  'T': {
145
167
  'name': 'Tyrosine',
@@ -341,13 +363,13 @@ export const map: {[synthesizer: string]:
341
363
  'name': 'Guanine',
342
364
  'weight': 329.21,
343
365
  'normalized': 'dG',
344
- 'SMILES': 'OC[C@H]1O[C@@H](N2C3N=C(N)NC(=O)C=3N=C2)C)[C@@H]1O',
366
+ 'SMILES': 'OC[C@H]1O[C@@H](N2C3N=C(N)NC(=O)C=3N=C2)C[C@@H]1O',
345
367
  },
346
368
  'dG': {
347
369
  'name': 'Guanine',
348
370
  'weight': 329.21,
349
371
  'normalized': 'dG',
350
- 'SMILES': 'OC[C@H]1O[C@@H](N2C3N=C(N)NC(=O)C=3N=C2)C)[C@@H]1O',
372
+ 'SMILES': 'OC[C@H]1O[C@@H](N2C3N=C(N)NC(=O)C=3N=C2)C[C@@H]1O',
351
373
  },
352
374
  'T': {
353
375
  'name': 'Tyrosine',