@datagrok/sequence-translator 1.0.5 → 1.0.7

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/sequence-translator",
3
3
  "friendlyName": "Sequence Translator",
4
- "version": "1.0.5",
4
+ "version": "1.0.7",
5
5
  "author": {
6
6
  "name": "Vadym Kovadlo",
7
7
  "email": "vkovadlo@datagrok.ai"
@@ -15,7 +15,7 @@
15
15
  "dependencies": {
16
16
  "@datagrok-libraries/utils": "^0.1.0",
17
17
  "@types/react": "^18.0.15",
18
- "datagrok-api": "^1.1.7",
18
+ "datagrok-api": "^1.6.0",
19
19
  "datagrok-tools": "^4.1.2",
20
20
  "npm": "^8.11.0",
21
21
  "openchemlib": "6.0.1",
@@ -33,7 +33,7 @@
33
33
  "release-sequencetranslator-public": "grok publish public --release",
34
34
  "debug-sequencetranslator-local": "grok publish local",
35
35
  "release-sequencetranslator-local": "grok publish local --release",
36
- "test": "set HOST=dev && jest",
36
+ "test": "jest",
37
37
  "test-dev": "set HOST=dev && jest",
38
38
  "test-local": "set HOST=localhost && jest"
39
39
  },
@@ -54,7 +54,11 @@
54
54
  "puppeteer": "^13.7.0",
55
55
  "ts-jest": "^27.0.0",
56
56
  "webpack": "^5.31.0",
57
- "webpack-cli": "^4.6.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"
58
62
  },
59
63
  "category": "Bioinformatics"
60
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
 
@@ -42,19 +42,20 @@ it('TEST', async () => {
42
42
  let r = await page.evaluate((targetPackage):Promise<object> => {
43
43
  return new Promise<object>((resolve, reject) => {
44
44
  (<any>window).grok.functions.eval(targetPackage + ':test()').then((df: any) => {
45
- let cStatus = df.columns.byName('success');
46
- let cMessage = df.columns.byName('result');
47
- let cCat = df.columns.byName('category');
48
- let cName = df.columns.byName('name');
45
+ const cStatus = df.columns.byName('success');
46
+ const cMessage = df.columns.byName('result');
47
+ const cCat = df.columns.byName('category');
48
+ const cName = df.columns.byName('name');
49
+ const cTime = df.columns.byName('ms');
49
50
  let failed = false;
50
51
  let passReport = '';
51
52
  let failReport = '';
52
53
  for (let i = 0; i < df.rowCount; i++) {
53
54
  if (cStatus.get(i)) {
54
- passReport += `Test result : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
55
+ passReport += `Test result : Success : ${cTime.get(i)} : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
55
56
  } else {
56
57
  failed = true;
57
- failReport += `Test result : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
58
+ failReport += `Test result : Failed : ${cTime.get(i)} : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
58
59
  }
59
60
  }
60
61
  resolve({failReport, passReport, failed});
@@ -65,4 +66,4 @@ it('TEST', async () => {
65
66
  console.log(r.passReport);
66
67
  // @ts-ignore
67
68
  expect(r.failed).checkOutput(false, r.failReport);
68
- }, 100000);
69
+ }, 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
  }
@@ -4,7 +4,7 @@ import * as DG from 'datagrok-api/dg';
4
4
  import {siRnaAxolabsToGcrs, gcrsToNucleotides, asoGapmersBioSpringToGcrs, gcrsToMermade12,
5
5
  } from '../structures-works/converters';
6
6
  import {map, COL_NAMES, MODIFICATIONS} from '../structures-works/map';
7
- import {getFormat} from '../structures-works/sequence-codes-tools';
7
+ import {isValidSequence} from '../structures-works/sequence-codes-tools';
8
8
  import {sequenceToMolV3000} from '../structures-works/from-monomers';
9
9
 
10
10
  import {SALTS_CSV} from '../salts';
@@ -13,16 +13,6 @@ import {ICDS} from '../ICDs';
13
13
  import {SOURCES} from '../sources';
14
14
  import {IDPS} from '../IDPs';
15
15
 
16
- const weightsObj: {[code: string]: number} = {};
17
- for (const synthesizer of Object.keys(map)) {
18
- for (const technology of Object.keys(map[synthesizer])) {
19
- for (const code of Object.keys(map[synthesizer][technology]))
20
- weightsObj[code] ?? map[synthesizer][technology][code].weight;
21
- }
22
- }
23
- for (const [key, value] of Object.entries(MODIFICATIONS))
24
- weightsObj[key] = value.molecularWeight;
25
-
26
16
 
27
17
  function sortByStringLengthInDescendingOrder(array: string[]): string[] {
28
18
  return array.sort(function(a, b) {return b.length - a.length;});
@@ -56,15 +46,15 @@ async function saveTableAsSdFile(table: DG.DataFrame) {
56
46
  const typeColumn = table.getCol(COL_NAMES.TYPE);
57
47
  let result = '';
58
48
  for (let i = 0; i < table.rowCount; i++) {
59
- const format = getFormat(structureColumn.get(i))!;
49
+ const format = 'Janssen GCRS Codes'; //getFormat(structureColumn.get(i))!;
60
50
  result += (typeColumn.get(i) == 'SS') ?
61
- sequenceToMolV3000(structureColumn.get(i), false, true, format) + '\n' + `> <Sequence>\nSense Strand\n\n` :
62
- sequenceToMolV3000(structureColumn.get(i), true, true, format) + '\n' + `> <Sequence>\nAnti Sense\n\n`;
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`;
63
53
  for (const col of table.columns) {
64
54
  if (col.name != COL_NAMES.SEQUENCE)
65
- result += `> <${col.name}>\n${col.get(i)}\n\n`;
55
+ result += `> <${col.name}>\n${col.get(i)}\n\n`;
66
56
  }
67
- result += '$$$$\n\n';
57
+ result += '$$$$\n';
68
58
  }
69
59
  const element = document.createElement('a');
70
60
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result));
@@ -78,14 +68,16 @@ export function autostartOligoSdFileSubscription() {
78
68
  if (v.dataFrame.columns.contains(COL_NAMES.TYPE))
79
69
  oligoSdFile(v.dataFrame);
80
70
  grok.events.onContextMenu.subscribe((args) => {
81
- const seqCol = args.args.context.table.currentCol;
82
- if (DG.Detector.sampleCategories(seqCol, (s) => /^[fsACGUacgu]{6,}$/.test(s))) {
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))) {
83
74
  args.args.menu.item('Convert Axolabs to GCRS', () => {
84
75
  args.args.context.table.columns.addNewString(seqCol.name + ' to GCRS').init((i: number) => {
85
76
  return siRnaAxolabsToGcrs(seqCol.get(i));
86
77
  });
87
- });
88
- } else if (DG.Detector.sampleCategories(seqCol, (s) => /^[fmpsACGU]{6,}$/.test(s)) ||
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)) ||
89
81
  DG.Detector.sampleCategories(seqCol, (s) => /^(?=.*moe)(?=.*5mC)(?=.*ps){6,}/.test(s))) {
90
82
  args.args.menu.item('Convert GCRS to raw', () => {
91
83
  args.args.context.table.columns.addNewString(seqCol.name + ' to raw').init((i: number) => {
@@ -96,15 +88,17 @@ export function autostartOligoSdFileSubscription() {
96
88
  args.args.context.table.columns.addNewString(seqCol.name + ' to MM12').init((i: number) => {
97
89
  return gcrsToMermade12(seqCol.get(i));
98
90
  });
99
- });
100
- } else if (DG.Detector.sampleCategories(seqCol, (s) => /^[*56789ATGC]{6,}$/.test(s))) {
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))) {
101
94
  args.args.menu.item('Convert Biospring to GCRS', () => {
102
95
  const seqCol = args.args.context.table.currentCol;
103
96
  args.args.context.table.columns.addNewString(seqCol.name + ' to GCRS').init((i: number) => {
104
97
  return asoGapmersBioSpringToGcrs(seqCol.get(i));
105
98
  });
106
- });
107
- } else if (DG.Detector.sampleCategories(seqCol, (s) => /^[*1-8]{6,}$/.test(s))) {
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))) {
108
102
  args.args.menu.item('Convert Biospring to GCRS', () => {
109
103
  args.args.context.table.columns.addNewString(seqCol.name + ' to GCRS').init((i: number) => {
110
104
  return siRnaAxolabsToGcrs(seqCol.get(i));
@@ -123,29 +117,54 @@ export function oligoSdFile(table: DG.DataFrame) {
123
117
  const icdsDf = DG.DataFrame.fromCsv(ICDS);
124
118
  const idpsDf = DG.DataFrame.fromCsv(IDPS);
125
119
 
126
- function addColumns(t: DG.DataFrame, saltsDf: DG.DataFrame) {
120
+ async function addColumns(t: DG.DataFrame, saltsDf: DG.DataFrame) {
127
121
  if (t.columns.contains(COL_NAMES.COMPOUND_NAME))
128
122
  return grok.shell.error('Columns already exist');
129
123
 
130
- const sequence = t.getCol(COL_NAMES.SEQUENCE);
131
- const salt = t.getCol(COL_NAMES.SALT);
132
- const equivalents = t.getCol(COL_NAMES.EQUIVALENTS);
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
+ for (let i = t.rowCount - 1; i > -1; i--) {
129
+ if (sequenceCol.get(i) == '')
130
+ t.rows.removeAt(i, 1, false);
131
+ }
132
+
133
+ t.columns.addNewString(COL_NAMES.COMPOUND_NAME).init((i: number) => sequenceCol.get(i));
133
134
 
134
- t.columns.addNewString(COL_NAMES.COMPOUND_NAME).init((i: number) => sequence.get(i));
135
135
  t.columns.addNewString(COL_NAMES.COMPOUND_COMMENTS).init((i: number) => (i > 0 && i % 2 == 0) ?
136
- sequence.getString(i) + '; duplex of SS: ' + sequence.getString(i - 2) + ' and AS: ' + sequence.getString(i - 1) :
137
- sequence.getString(i),
136
+ sequenceCol.get(i) + '; duplex of SS: ' + sequenceCol.get(i - 2) + ' and AS: ' + sequenceCol.get(i - 1) :
137
+ sequenceCol.get(i),
138
138
  );
139
139
  const molWeightCol = saltsDf.getCol('MOLWEIGHT');
140
140
  const saltNamesList = saltsDf.getCol('DISPLAY').toList();
141
- t.columns.addNewFloat(COL_NAMES.CPD_MW)
142
- .init((i: number) => molecularWeight(sequence.get(i), weightsObj));
141
+ const weightsObj: {[code: string]: number} = {};
142
+ for (const synthesizer of Object.keys(map)) {
143
+ for (const technology of Object.keys(map[synthesizer])) {
144
+ for (const code of Object.keys(map[synthesizer][technology]))
145
+ weightsObj[code] = map[synthesizer][technology][code].weight!;
146
+ }
147
+ }
148
+ for (const [key, value] of Object.entries(MODIFICATIONS))
149
+ weightsObj[key] = value.molecularWeight;
150
+
151
+ t.columns.addNewFloat(COL_NAMES.CPD_MW).init((i: number) => {
152
+ return (isValidSequence(sequenceCol.get(i), null).indexOfFirstNotValidChar == -1) ?
153
+ molecularWeight(sequenceCol.get(i), weightsObj) :
154
+ DG.FLOAT_NULL;
155
+ });
156
+
143
157
  t.columns.addNewFloat(COL_NAMES.SALT_MASS).init((i: number) => {
144
- const saltRowIndex = saltNamesList.indexOf(salt.get(i));
145
- const mw = molWeightCol.get(saltRowIndex);
146
- return mw * equivalents.get(i);
158
+ const saltRowIndex = saltNamesList.indexOf(saltCol.get(i));
159
+ return (saltRowIndex == -1) ? DG.FLOAT_NULL : molWeightCol.get(saltRowIndex) * equivalentsCol.get(i);
160
+ });
161
+
162
+ t.columns.addNewFloat(COL_NAMES.SALT_MOL_WEIGHT).init((i: number) => {
163
+ const saltRowIndex = saltNamesList.indexOf(saltCol.get(i));
164
+ return (saltRowIndex == -1) ? DG.FLOAT_NULL : molWeightCol.get(saltRowIndex);
147
165
  });
148
- t.columns.addNewCalculated(COL_NAMES.BATCH_MW,
166
+
167
+ await t.columns.addNewCalculated(COL_NAMES.BATCH_MW,
149
168
  '${' + COL_NAMES.CPD_MW + '} + ${' + COL_NAMES.SALT_MASS + '}', DG.COLUMN_TYPE.FLOAT, false,
150
169
  );
151
170
 
@@ -162,11 +181,12 @@ export function oligoSdFile(table: DG.DataFrame) {
162
181
  if (table.getCol(COL_NAMES.IDP).type != DG.COLUMN_TYPE.STRING)
163
182
  table.changeColumnType(COL_NAMES.IDP, DG.COLUMN_TYPE.STRING);
164
183
  d.append(
165
- ui.link('Add Columns', () => {
166
- addColumns(table, saltsDf);
167
- grok.shell.tableView(table.name).grid.columns.setOrder(Object.values(COL_NAMES));
184
+ ui.link('Add Columns', async () => {
185
+ await addColumns(table, saltsDf);
186
+ view.grid.columns.setOrder(Object.values(COL_NAMES));
168
187
  }, 'Add columns: \'' + [COL_NAMES.COMPOUND_NAME, COL_NAMES.COMPOUND_COMMENTS, COL_NAMES.CPD_MW,
169
- COL_NAMES.SALT_MASS, COL_NAMES.BATCH_MW].join('\', \''), ''),
188
+ COL_NAMES.SALT_MASS, COL_NAMES.BATCH_MW].join('\', \''), '',
189
+ ),
170
190
  ui.button('Save SD file', () => saveTableAsSdFile(addColumnsPressed ? newDf : table)),
171
191
  );
172
192
 
@@ -361,6 +361,11 @@ export function defineAxolabsPattern() {
361
361
  })
362
362
  .show();
363
363
  }
364
+ if (col.get(0) != ssLength.value) {
365
+ const d = ui.dialog('Length was updated by value to from imported file');
366
+ d.add(ui.divText('Latest modifications may not take effect during translation'))
367
+ .onOK(() => grok.shell.info('Lengths changed')).show();
368
+ }
364
369
  return allLengthsAreTheSame;
365
370
  }
366
371
 
@@ -513,7 +518,9 @@ export function defineAxolabsPattern() {
513
518
  let asPtoLinkages = Array(defaultSequenceLength).fill(ui.boolInput('', defaultPto));
514
519
 
515
520
  const ssLength = ui.intInput('SS Length', defaultSequenceLength, () => updateUiForNewSequenceLength());
521
+ ssLength.setTooltip('Length of sense strand, including overhangs');
516
522
  const asLength = ui.intInput('AS Length', defaultSequenceLength, () => updateUiForNewSequenceLength());
523
+ asLength.setTooltip('Length of sense strand, including overhangs');
517
524
  const asLengthDiv = ui.div([asLength.root]);
518
525
 
519
526
  function validateSsColumn(colName: string): void {
@@ -605,7 +612,9 @@ export function defineAxolabsPattern() {
605
612
  });
606
613
 
607
614
  const firstSsPto = ui.boolInput('First SS PTO', fullyPto.value!, () => updateSvgScheme());
615
+ firstSsPto.setTooltip('ps linkage before first nucleotide of sense strand');
608
616
  const firstAsPto = ui.boolInput('First AS PTO', fullyPto.value!, () => updateSvgScheme());
617
+ firstAsPto.setTooltip('ps linkage before first nucleotide of antisense strand');
609
618
  firstAsPtoDiv.append(firstAsPto.root);
610
619
 
611
620
  const createAsStrand = ui.boolInput('Create AS Strand', true, (v: boolean) => {
@@ -617,6 +626,7 @@ export function defineAxolabsPattern() {
617
626
  firstAsPtoDiv.hidden = (!v);
618
627
  updateSvgScheme();
619
628
  });
629
+ createAsStrand.setTooltip('Create antisense strand sections on SVG and table to the right');
620
630
 
621
631
  const saveAs = ui.textInput('Save As', 'Pattern Name', () => updateSvgScheme());
622
632
  saveAs.setTooltip('Name Of New Pattern');
@@ -4,7 +4,6 @@ import * as DG from 'datagrok-api/dg';
4
4
  import {convertSequence, undefinedInputSequence, isValidSequence} from '../structures-works/sequence-codes-tools';
5
5
  import {map, MODIFICATIONS} from '../structures-works/map';
6
6
  import {sequenceToSmiles, sequenceToMolV3000} from '../structures-works/from-monomers';
7
-
8
7
  import $ from 'cash-dom';
9
8
 
10
9
  const defaultInput = 'fAmCmGmAmCpsmU';
@@ -12,7 +11,7 @@ const sequenceWasCopied = 'Copied';
12
11
  const tooltipSequence = 'Copy sequence';
13
12
 
14
13
  export function mainView() {
15
- function updateTableAndMolecule(sequence: string, inputFormat: string, isSet: boolean): void {
14
+ function updateTableAndMolecule(sequence: string, inputFormat: string): void {
16
15
  moleculeSvgDiv.innerHTML = '';
17
16
  outputTableDiv.innerHTML = '';
18
17
  const pi = DG.TaskBarProgressIndicator.create('Rendering table and molecule...');
@@ -20,9 +19,8 @@ export function mainView() {
20
19
  try {
21
20
  sequence = sequence.replace(/\s/g, '');
22
21
  const output = isValidSequence(sequence, null);
23
- if (isSet)
24
- output.synthesizer = [inputFormat];
25
- inputFormatChoiceInput.value = output.synthesizer![0];
22
+ output.synthesizer = [inputFormat];
23
+ // inputFormatChoiceInput.value = output.synthesizer![0];
26
24
  const outputSequenceObj = convertSequence(sequence, output);
27
25
  const tableRows = [];
28
26
 
@@ -101,14 +99,15 @@ export function mainView() {
101
99
  }
102
100
  }
103
101
 
104
- const inputFormatChoiceInput = ui.choiceInput(
105
- 'Input format: ', 'Janssen GCRS Codes', Object.keys(map), (format: string) => {
106
- updateTableAndMolecule(inputSequenceField.value.replace(/\s/g, ''), format, true);
107
- });
102
+ const inputFormatChoiceInput = ui.choiceInput('Input format: ', 'Janssen GCRS Codes', Object.keys(map));
103
+ inputFormatChoiceInput.onInput((format: string) => {
104
+ updateTableAndMolecule(inputSequenceField.value.replace(/\s/g, ''), format);
105
+ });
108
106
  const moleculeSvgDiv = ui.block([]);
109
107
  const outputTableDiv = ui.div([]);
110
- const inputSequenceField = ui.textInput('', defaultInput, (sequence: string) => updateTableAndMolecule(sequence,
111
- inputFormatChoiceInput.value!, false));
108
+ const inputSequenceField = ui.textInput('', defaultInput, (sequence: string) => {
109
+ updateTableAndMolecule(sequence, inputFormatChoiceInput.value!);
110
+ });
112
111
 
113
112
  const asoDf = DG.DataFrame.fromObjects([
114
113
  {'Name': '2\'MOE-5Me-rU', 'BioSpring': '5', 'Janssen GCRS': 'moeT'},
@@ -151,7 +150,7 @@ export function mainView() {
151
150
  DG.Column.fromStrings('Name', Object.keys(MODIFICATIONS)),
152
151
  ])!, {showRowHeader: false, showCellTooltip: false},
153
152
  );
154
- updateTableAndMolecule(defaultInput, inputFormatChoiceInput.value!, true);
153
+ updateTableAndMolecule(defaultInput, inputFormatChoiceInput.value!);
155
154
 
156
155
  const codesTablesDiv = ui.splitV([
157
156
  ui.box(ui.h2('ASO Gapmers'), {style: {maxHeight: '40px'}}),
@@ -1,6 +1,6 @@
1
1
  import {lcmsToGcrs} from './map';
2
2
  import * as DG from 'datagrok-api/dg';
3
-
3
+ import {delimiter} from './map';
4
4
  //name: gcrsToLcms
5
5
  //input: string nucleotides {semType: GCRS}
6
6
  //output: string result {semType: LCMS}
@@ -10,15 +10,24 @@ export function gcrsToLcms(sequence: string): string {
10
10
  const arr2 = df.getCol('LCMS').toList();
11
11
  const obj: {[i: string]: string} = {};
12
12
  arr1.forEach((element, index) => obj[element] = arr2[index]);
13
- for (let i = 0; i < arr1.length; i++) {
14
- arr1[i] = arr1[i].replace('(', '\\(');
15
- arr1[i] = arr1[i].replace(')', '\\)');
13
+ obj[delimiter] = delimiter;
14
+ // for (let i = 0; i < arr1.length; i++) {
15
+ // arr1[i] = arr1[i].replace('(', '\\(');
16
+ // arr1[i] = arr1[i].replace(')', '\\)');
17
+ // }
18
+ // const regExp = new RegExp('(' + arr1.join('|') + ')', 'g');
19
+ // let r1 = sequence.replace(regExp, function(code) {return obj[code];});
20
+ const codes = arr1.concat(delimiter).sort(function(a, b) {return b.length - a.length;});
21
+ let i = 0;
22
+ let r1 = '';
23
+ while (i < sequence.length) {
24
+ const matchedCode = codes.find((c) => c == sequence.slice(i, i + c.length));
25
+ r1 += obj[sequence.slice(i, i + matchedCode.length)];
26
+ i += matchedCode.length;
16
27
  }
17
- const regExp = new RegExp('(' + arr1.join('|') + ')', 'g');
18
- let r1 = sequence.replace(regExp, function(code) {return obj[code];});
19
- r1 = r1.replace('//', '/');
20
- r1 = r1.replace('//', '/');
21
- return r1.replace('//', '/');
28
+ while (r1.indexOf('//') != -1)
29
+ r1 = r1.replace('//', '/');
30
+ return r1;
22
31
  }
23
32
 
24
33
  //name: asoGapmersNucleotidesToBioSpring
@@ -22,7 +22,8 @@ export function sequenceToMolV3000(sequence: string, inverted: boolean = false,
22
22
  if (dropdowns.includes(codesList[i])) {
23
23
  smilesCodes.push((i >= codesList.length / 2) ?
24
24
  MODIFICATIONS[codesList[i]].right : MODIFICATIONS[codesList[i]].left);
25
- smilesCodes.push(stadardPhosphateLinkSmiles);
25
+ if (!(i < codesList.length - 1 && links.includes(codesList[i + 1])))
26
+ smilesCodes.push(stadardPhosphateLinkSmiles);
26
27
  } else {
27
28
  if (links.includes(codesList[i]) ||
28
29
  includesStandardLinkAlready.includes(codesList[i]) ||
@@ -36,7 +37,7 @@ export function sequenceToMolV3000(sequence: string, inverted: boolean = false,
36
37
  }
37
38
  }
38
39
 
39
- return getNucleotidesMol(smilesCodes, oclRender);
40
+ return getNucleotidesMol(smilesCodes);
40
41
  }
41
42
 
42
43
  export function sequenceToSmiles(sequence: string, inverted: boolean = false, format: string): string {
@@ -30,6 +30,7 @@ export const COL_NAMES = {
30
30
  EQUIVALENTS: 'Equivalents',
31
31
  PURITY: 'Purity',
32
32
  CPD_MW: 'Cpd MW',
33
+ SALT_MOL_WEIGHT: 'Salt MW',
33
34
  SALT_MASS: 'Salt mass',
34
35
  BATCH_MW: 'Batch MW',
35
36
  SOURCE: 'Source',
@@ -340,6 +341,12 @@ export const map: {[synthesizer: string]:
340
341
  'normalized': '',
341
342
  'SMILES': 'OP(=O)(S)O',
342
343
  },
344
+ 's': {
345
+ 'name': 'ps linkage',
346
+ 'weight': 16.07,
347
+ 'normalized': '',
348
+ 'SMILES': 'OP(=O)(S)O',
349
+ },
343
350
  'A': {
344
351
  'name': 'Adenine',
345
352
  'weight': 313.21,
@@ -78,7 +78,7 @@ M V30 END COLLECTION
78
78
  M V30 END CTAB
79
79
  M END`;
80
80
 
81
- export function getNucleotidesMol(smilesCodes: string[], oclRender: boolean = false) {
81
+ export function getNucleotidesMol(smilesCodes: string[]) {
82
82
  const molBlocks: string[] = [];
83
83
 
84
84
  for (let i = 0; i < smilesCodes.length - 1; i++) {
@@ -88,10 +88,10 @@ export function getNucleotidesMol(smilesCodes: string[], oclRender: boolean = fa
88
88
  molBlocks.push(rotateNucleotidesV3000(smilesCodes[i]));
89
89
  }
90
90
 
91
- return linkV3000(molBlocks, false, oclRender);
91
+ return linkV3000(molBlocks, false);
92
92
  }
93
93
 
94
- export function linkV3000(molBlocks: string[], twoChains: boolean = false, oclRender: boolean = false) {
94
+ export function linkV3000(molBlocks: string[], twoChains: boolean = false, useChirality: boolean = true) {
95
95
  let macroMolBlock = '\nDatagrok macromolecule handler\n\n';
96
96
  macroMolBlock += ' 0 0 0 0 0 0 999 V3000\n';
97
97
  macroMolBlock += 'M V30 BEGIN CTAB\n';
@@ -213,14 +213,14 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, oclRe
213
213
  const entries = 4;
214
214
  const collNumber = Math.ceil(collection.length / entries);
215
215
 
216
- if (oclRender) {
217
- collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length;
216
+ //if (oclRender) {
217
+ // collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length;
218
218
 
219
- for (let j = 0; j < collection.length; j++)
220
- collectionBlock += ' ' + collection[j];
219
+ // for (let j = 0; j < collection.length; j++)
220
+ // collectionBlock += ' ' + collection[j];
221
221
 
222
- collectionBlock += ')\n';
223
- } else {
222
+ // collectionBlock += ')\n';
223
+ //} else {
224
224
  collectionBlock += 'M V30 MDLV30/STEABS ATOMS=(' + collection.length + ' -\n';
225
225
  for (let i = 0; i < collNumber; i++) {
226
226
  collectionBlock += 'M V30 ';
@@ -231,7 +231,7 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, oclRe
231
231
  collection[entries*i + j] + ' ';
232
232
  }
233
233
  }
234
- }
234
+ //}
235
235
 
236
236
  //generate file
237
237
  twoChains? natom : natom++;
@@ -242,13 +242,15 @@ export function linkV3000(molBlocks: string[], twoChains: boolean = false, oclRe
242
242
  macroMolBlock += 'M V30 BEGIN BOND\n';
243
243
  macroMolBlock += bondBlock;
244
244
  macroMolBlock += 'M V30 END BOND\n';
245
- //macroMolBlock += 'M V30 BEGIN COLLECTION\n';
246
- //macroMolBlock += collectionBlock;
247
- //macroMolBlock += 'M V30 END COLLECTION\n';
248
- macroMolBlock += 'M V30 END CTAB\n';
249
- macroMolBlock += 'M END\n';
245
+ if(useChirality){
246
+ macroMolBlock += 'M V30 BEGIN COLLECTION\n';
247
+ macroMolBlock += collectionBlock;
248
+ macroMolBlock += 'M V30 END COLLECTION\n';
249
+ } else
250
+ macroMolBlock = macroMolBlock.replace(/ CFG=\d/g, ' ');
250
251
 
251
- macroMolBlock = macroMolBlock.replaceAll('CFG=1', '').replaceAll('CFG=2', '').replaceAll('CFG=3', '').replaceAll('CFG=4', '');
252
+ macroMolBlock += 'M V30 END CTAB\n';
253
+ macroMolBlock += 'M END';
252
254
 
253
255
  return macroMolBlock;
254
256
  }
@@ -3,20 +3,20 @@ import {sequenceToMolV3000} from '../structures-works/from-monomers';
3
3
  import {linkV3000} from '../structures-works/mol-transformations';
4
4
  import {getFormat} from '../structures-works/sequence-codes-tools';
5
5
 
6
- export function saveSdf(as: string, ss: string, oneEntity: boolean, fit3dx: boolean) {
6
+ export function saveSdf(as: string, ss: string, oneEntity: boolean, useChirality: boolean, invertSS: boolean, invertAS: boolean) {
7
7
  const formatAs = getFormat(as);
8
8
  const formatSs = getFormat(ss);
9
- const molSS = sequenceToMolV3000(ss, false, false, formatSs!);
10
- const molAS = sequenceToMolV3000(as, true, false, formatAs!);
9
+ const molSS = sequenceToMolV3000(ss, invertSS, false, formatSs!);
10
+ const molAS = sequenceToMolV3000(as, invertAS, false, formatAs!);
11
11
  let result: string;
12
12
  if (oneEntity)
13
- result = linkV3000([molSS, molAS], true, !fit3dx) + '\n\n$$$$\n';
13
+ result = linkV3000([molSS, molAS], true, useChirality) + '\n$$$$\n';
14
14
  else {
15
15
  result =
16
16
  molSS + '\n' +
17
- `> <Sequence>\nSense Strand\n\n$$$$\n` +
17
+ `> <Sequence>\nSense Strand\n$$$$\n` +
18
18
  molAS + '\n' +
19
- `> <Sequence>\nAnti Sense\n\n$$$$\n`;
19
+ `> <Sequence>\nAnti Sense\n$$$$\n`;
20
20
  }
21
21
 
22
22
  const element = document.createElement('a');
@@ -27,22 +27,35 @@ export function saveSdf(as: string, ss: string, oneEntity: boolean, fit3dx: bool
27
27
 
28
28
  export function saveSenseAntiSense() {
29
29
  const moleculeSvgDiv = ui.block([]);
30
- const ssInput = ui.textInput('Sense Strand 5\' ->3\'', '');
31
- const asInput = ui.textInput('Anti Sense 3\' ->5\'', '');
30
+ const ssInput = ui.textInput('Sense Strand', '');
31
+ const asInput = ui.textInput('Anti Sense', '');
32
+ const straight = '5\' ->3\'';
33
+ const inverse = '3\' ->5\'';
34
+ let ssInverse = false;
35
+ let asInverse = false;
36
+
37
+ const changeSense = ui.choiceInput('SS direction', straight, [straight, inverse]);
38
+ changeSense.onChanged(() => {ssInverse = changeSense.value == inverse;});
39
+ const changeAntiSense = ui.choiceInput('AS direction', straight, [straight, inverse]);
40
+ changeAntiSense.onChanged(() => {asInverse = changeAntiSense.value == inverse;});
41
+
32
42
  const saveOption = ui.switchInput('Save as one entity', true);
33
- const save3dx = ui.switchInput('Save 3dx', true);
43
+ const chirality = ui.switchInput('Use chiral', true);
34
44
  const saveBtn = ui.button('Save SDF', () =>
35
- saveSdf(asInput.value, ssInput.value, saveOption.value, save3dx.value));
45
+ saveSdf(asInput.value, ssInput.value, saveOption.value, chirality.value, ssInverse, asInverse));
36
46
 
37
47
  const saveSection = ui.panel([
38
48
  ui.div([
39
49
  ui.div([
40
50
  ui.divH([ui.h1('Inputs')]),
41
51
  ui.divV([
42
- ui.div([ssInput.root]),
43
- ui.div([asInput.root]),
52
+ ssInput,
53
+ asInput,
54
+ ui.div([changeSense], {style: {width: '40'}}),
55
+ changeSense,
56
+ changeAntiSense,
44
57
  saveOption,
45
- save3dx,
58
+ chirality,
46
59
  ui.buttonsInput([saveBtn]),
47
60
  ], 'ui-form'),
48
61
  ], 'ui-form'),