@datagrok/sequence-translator 1.0.15 → 1.0.17
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/.eslintrc.json +16 -3
- package/detectors.js +0 -28
- package/dist/package-test.js +4372 -3957
- package/dist/package.js +3875 -3460
- package/package.json +4 -2
- package/setup-unlink-clean.sh +21 -0
- package/src/apps/oligo-sd-file-app.ts +58 -0
- package/src/autostart/calculations.ts +11 -8
- package/src/autostart/constants.ts +0 -12
- package/src/autostart/registration.ts +194 -157
- package/src/{axolabs/define-pattern.ts → axolabs-tab/axolabs-tab.ts} +2 -2
- package/src/axolabs-tab/define-pattern.ts +874 -0
- package/src/{axolabs → axolabs-tab}/draw-svg.ts +1 -1
- package/src/{axolabs → axolabs-tab}/helpers.ts +2 -2
- package/src/{autostart → hardcode-to-be-eliminated}/ICDs.ts +0 -0
- package/src/{autostart → hardcode-to-be-eliminated}/IDPs.ts +0 -0
- package/src/{structures-works → hardcode-to-be-eliminated}/const.ts +0 -0
- package/src/{axolabs → hardcode-to-be-eliminated}/constants.ts +0 -0
- package/src/{structures-works → hardcode-to-be-eliminated}/converters.ts +1 -1
- package/src/{structures-works → hardcode-to-be-eliminated}/map.ts +2 -2
- package/src/{autostart → hardcode-to-be-eliminated}/salts.ts +0 -0
- package/src/{autostart → hardcode-to-be-eliminated}/sources.ts +0 -0
- package/src/{autostart → hardcode-to-be-eliminated}/users.ts +0 -0
- package/src/{main/main-view.ts → main-tab/main-tab.ts} +28 -80
- package/src/package.ts +77 -13
- package/src/sdf-tab/sdf-tab.ts +163 -0
- package/src/{structures-works → sdf-tab}/sequence-codes-tools.ts +8 -5
- package/src/tests/smiles-tests.ts +2 -2
- package/src/utils/const.ts +0 -0
- package/src/{helpers.ts → utils/helpers.ts} +3 -3
- package/src/utils/parse.ts +27 -0
- package/src/utils/sdf-add-columns.ts +118 -0
- package/src/utils/sdf-save-table.ts +56 -0
- package/src/utils/structures-works/draw-molecule.ts +84 -0
- package/src/{structures-works → utils/structures-works}/from-monomers.ts +15 -16
- package/src/{structures-works → utils/structures-works}/mol-transformations.ts +34 -52
- package/{test-SequenceTranslator-91c83d8913ff-f94596bc.html → test-SequenceTranslator-6288c2fbe346-695b7b55.html} +10 -10
- package/src/structures-works/save-sense-antisense.ts +0 -91
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.
|
|
4
|
+
"version": "1.0.17",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Alexey Choposky",
|
|
7
7
|
"email": "achopovsky@datagrok.ai"
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
"@datagrok-libraries/utils": "^1.17.2",
|
|
17
17
|
"@types/react": "^18.0.15",
|
|
18
18
|
"@datagrok-libraries/bio": "^5.11.1",
|
|
19
|
-
"
|
|
19
|
+
"@deck.gl/core": "8.8.12",
|
|
20
|
+
"@luma.gl/core": "8.5.17",
|
|
21
|
+
"datagrok-api": "^1.8.2",
|
|
20
22
|
"datagrok-tools": "^4.1.2",
|
|
21
23
|
"npm": "^8.11.0",
|
|
22
24
|
"openchemlib": "6.0.1",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
package_dir=$(pwd)
|
|
3
|
+
|
|
4
|
+
GREEN='\e[0;32m'
|
|
5
|
+
NO_COLOR='\e[0m'
|
|
6
|
+
|
|
7
|
+
dirs=(
|
|
8
|
+
"../../js-api/"
|
|
9
|
+
"../../libraries/utils/"
|
|
10
|
+
"../../libraries/bio/"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
npm uninstall --location=global datagrok-api @datagrok-libraries/utils @datagrok-libraries/ml @datagrok-libraries/bio
|
|
14
|
+
|
|
15
|
+
for dir in ${dirs[@]}; do
|
|
16
|
+
cd $package_dir
|
|
17
|
+
cd $dir
|
|
18
|
+
echo -e $GREEN Removing node_modules and dist in $(pwd) $NO_COLOR
|
|
19
|
+
rm -rf node_modules dist
|
|
20
|
+
# rm package-lock.json
|
|
21
|
+
done
|
|
@@ -0,0 +1,58 @@
|
|
|
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 {_package} from '../package';
|
|
5
|
+
import {FileSource} from 'datagrok-api/dg';
|
|
6
|
+
|
|
7
|
+
export class OligoSdFileApp {
|
|
8
|
+
df!: DG.DataFrame;
|
|
9
|
+
|
|
10
|
+
constructor() {}
|
|
11
|
+
|
|
12
|
+
async init(srcDf?: DG.DataFrame): Promise<void> {
|
|
13
|
+
let dataDf: DG.DataFrame;
|
|
14
|
+
if (srcDf) {
|
|
15
|
+
dataDf = srcDf;
|
|
16
|
+
} else {
|
|
17
|
+
const dfFn: string = 'System:AppData/SequenceTranslator/test input_Nov28_Duplex_dimer.xlsx';
|
|
18
|
+
dataDf = await this.loadData(dfFn);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
this.setData(dataDf);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async loadData(dfFn: string): Promise<DG.DataFrame> {
|
|
25
|
+
//
|
|
26
|
+
const dataDf: DG.DataFrame = await grok.data.files.openTable(dfFn);
|
|
27
|
+
return dataDf;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async setData(df: DG.DataFrame): Promise<void> {
|
|
31
|
+
if (this.viewed) {
|
|
32
|
+
await this.destroyView();
|
|
33
|
+
this.viewed = false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.df = df;
|
|
37
|
+
|
|
38
|
+
if (!this.viewed) {
|
|
39
|
+
await this.buildView();
|
|
40
|
+
this.viewed = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private viewed: boolean = false;
|
|
45
|
+
private tView?: DG.TableView;
|
|
46
|
+
|
|
47
|
+
async destroyView(): Promise<void> {
|
|
48
|
+
this.tView!.close();
|
|
49
|
+
delete this.tView;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async buildView(): Promise<void> {
|
|
53
|
+
console.debug('SequenceTranslator: OligoSdFileApp.buildView() ');
|
|
54
|
+
|
|
55
|
+
this.tView = grok.shell.addTableView(this.df);
|
|
56
|
+
this.tView.path = this.tView.basePath = 'func/SequenceTranslator.oligoSdFileApp';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
|
|
3
|
-
import {
|
|
2
|
+
|
|
3
|
+
import {sortByStringLengthInDescendingOrder} from '../utils/helpers';
|
|
4
|
+
import {MODIFICATIONS} from '../hardcode-to-be-eliminated/map';
|
|
4
5
|
|
|
5
6
|
export function saltMass(
|
|
6
|
-
saltNames: string[],
|
|
7
|
+
saltNames: string[], saltsMolWeightList: number[], equivalentsCol: DG.Column, i: number, saltCol: DG.Column
|
|
7
8
|
): number {
|
|
8
9
|
const saltRowIndex = saltNames.indexOf(saltCol.get(i));
|
|
9
10
|
return (
|
|
10
|
-
saltRowIndex == -1 ||
|
|
11
|
+
saltRowIndex == -1 || saltsMolWeightList[saltRowIndex] == DG.FLOAT_NULL || equivalentsCol.get(i) == DG.INT_NULL
|
|
11
12
|
) ?
|
|
12
13
|
DG.FLOAT_NULL :
|
|
13
|
-
|
|
14
|
+
saltsMolWeightList[saltRowIndex] * equivalentsCol.get(i);
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export function saltMolWeigth(
|
|
17
|
+
export function saltMolWeigth(
|
|
18
|
+
saltNamesList: string[], saltCol: DG.Column, saltsMolWeightList: number[], i: number
|
|
19
|
+
): number {
|
|
17
20
|
const saltRowIndex = saltNamesList.indexOf(saltCol.get(i));
|
|
18
|
-
return (saltRowIndex == -1) ? DG.FLOAT_NULL :
|
|
21
|
+
return (saltRowIndex == -1) ? DG.FLOAT_NULL : saltsMolWeightList[saltRowIndex];
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
export function batchMolWeight(compoundMolWeightCol: DG.Column, saltMassCol: DG.Column, i: number): number {
|
|
@@ -24,7 +27,7 @@ export function batchMolWeight(compoundMolWeightCol: DG.Column, saltMassCol: DG.
|
|
|
24
27
|
compoundMolWeightCol.get(i) + saltMassCol.get(i);
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
export function molecularWeight(sequence: string, weightsObj: {[index: string]: number}): number {
|
|
30
|
+
export function molecularWeight(sequence: string, weightsObj: { [index: string]: number }): number {
|
|
28
31
|
const codes = sortByStringLengthInDescendingOrder(Object.keys(weightsObj)).concat(Object.keys(MODIFICATIONS));
|
|
29
32
|
let weight = 0;
|
|
30
33
|
let i = 0;
|
|
@@ -6,18 +6,6 @@ export const SEQUENCE_TYPES = {
|
|
|
6
6
|
DIMER: 'Dimer',
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
export const CELL_STRUCTURE = {
|
|
10
|
-
DUPLEX: {
|
|
11
|
-
BEFORE_SS: 'SS ',
|
|
12
|
-
BEFORE_AS: '\r\nAS ',
|
|
13
|
-
},
|
|
14
|
-
TRIPLEX_OR_DIMER: {
|
|
15
|
-
BEFORE_SS: 'SS ',
|
|
16
|
-
BEFORE_AS1: '\r\nAS1 ',
|
|
17
|
-
BEFORE_AS2: '\r\nAS2 ',
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
|
|
21
9
|
export const COL_NAMES = {
|
|
22
10
|
CHEMISTRY: 'Chemistry',
|
|
23
11
|
NUMBER: 'Number',
|
|
@@ -1,125 +1,220 @@
|
|
|
1
1
|
import * as grok from 'datagrok-api/grok';
|
|
2
2
|
import * as ui from 'datagrok-api/ui';
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import {
|
|
5
|
+
siRnaBioSpringToGcrs, siRnaAxolabsToGcrs, gcrsToNucleotides, asoGapmersBioSpringToGcrs, gcrsToMermade12,
|
|
6
|
+
siRnaNucleotidesToGcrs
|
|
7
|
+
} from '../hardcode-to-be-eliminated/converters';
|
|
8
|
+
import {weightsObj, SYNTHESIZERS} from '../hardcode-to-be-eliminated/map';
|
|
9
|
+
import {SEQUENCE_TYPES, COL_NAMES, GENERATED_COL_NAMES} from './constants';
|
|
8
10
|
import {saltMass, saltMolWeigth, molecularWeight, batchMolWeight} from './calculations';
|
|
9
|
-
import {isValidSequence} from '../
|
|
10
|
-
import {sequenceToMolV3000} from '../structures-works/from-monomers';
|
|
11
|
-
import {linkStrandsV3000} from '../structures-works/mol-transformations';
|
|
12
|
-
import {stringify, download, removeEmptyRows, differenceOfTwoArrays} from '../helpers';
|
|
11
|
+
import {isValidSequence} from '../sdf-tab/sequence-codes-tools';
|
|
12
|
+
import {sequenceToMolV3000} from '../utils/structures-works/from-monomers';
|
|
13
|
+
import {linkStrandsV3000} from '../utils/structures-works/mol-transformations';
|
|
14
|
+
import {stringify, download, removeEmptyRows, differenceOfTwoArrays} from '../utils/helpers';
|
|
13
15
|
|
|
14
|
-
import {SALTS_CSV} from '
|
|
15
|
-
import {USERS_CSV} from '
|
|
16
|
-
import {ICDS} from '
|
|
17
|
-
import {SOURCES} from '
|
|
18
|
-
import {IDPS} from '
|
|
16
|
+
import {SALTS_CSV} from '../hardcode-to-be-eliminated/salts';
|
|
17
|
+
import {USERS_CSV} from '../hardcode-to-be-eliminated/users';
|
|
18
|
+
import {ICDS} from '../hardcode-to-be-eliminated/ICDs';
|
|
19
|
+
import {SOURCES} from '../hardcode-to-be-eliminated/sources';
|
|
20
|
+
import {IDPS} from '../hardcode-to-be-eliminated/IDPs';
|
|
19
21
|
|
|
22
|
+
import {sdfAddColumns} from '../utils/sdf-add-columns';
|
|
23
|
+
import {sdfSaveTable} from '../utils/sdf-save-table';
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
const enum PREFIXES {
|
|
26
|
+
AS = 'AS',
|
|
27
|
+
SS = 'SS',
|
|
28
|
+
AS1 = 'AS1',
|
|
29
|
+
AS2 = 'AS2'
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.split(CELL_STRUCTURE.TRIPLEX_OR_DIMER.BEFORE_AS2);
|
|
34
|
-
return {SS: arr1[0], AS1: arr2[0], AS2: arr2[1]};
|
|
32
|
+
const enum SEQ_TYPE {
|
|
33
|
+
AS = 'AS',
|
|
34
|
+
SS = 'SS',
|
|
35
|
+
DUPLEX = 'Duplex',
|
|
36
|
+
DIMER = 'Dimer',
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
/** Computable categories of sequence types */
|
|
40
|
+
const enum SEQ_TYPE_CATEGORY {
|
|
41
|
+
AS_OR_SS,
|
|
42
|
+
DUPLEX,
|
|
43
|
+
DIMER,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Map between types and their categories inferrable from 'Sequence' column */
|
|
47
|
+
const typeCategoryMap = {
|
|
48
|
+
[SEQ_TYPE.AS]: SEQ_TYPE_CATEGORY.AS_OR_SS,
|
|
49
|
+
[SEQ_TYPE.SS]: SEQ_TYPE_CATEGORY.AS_OR_SS,
|
|
50
|
+
[SEQ_TYPE.DIMER]: SEQ_TYPE_CATEGORY.DIMER,
|
|
51
|
+
[SEQ_TYPE.DUPLEX]: SEQ_TYPE_CATEGORY.DUPLEX,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** Style used for cells in 'Type' column */
|
|
55
|
+
const typeColCellStyle = {
|
|
56
|
+
'display': 'flex',
|
|
57
|
+
'justify-content': 'center',
|
|
58
|
+
'align-items': 'center',
|
|
59
|
+
'text-color': 'var(--grey-5)', // --grey-6 does not match other cells
|
|
60
|
+
'width': '100%',
|
|
61
|
+
'height': '100%',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const pinkBackground = {
|
|
65
|
+
'background-color': '#ff8080',
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** Style used for a cell with invalid value */
|
|
69
|
+
const typeColErrorStyle = Object.assign({}, pinkBackground, typeColCellStyle);
|
|
70
|
+
|
|
71
|
+
export function sdfHandleErrorUI(msgPrefix: string, df: DG.DataFrame, rowI: number, err: any) {
|
|
72
|
+
const errStr: string = err.toString();
|
|
73
|
+
const errMsg: string = msgPrefix + `row #${rowI + 1}, name: '${df.get('Chemistry Name', rowI)}', ` +
|
|
74
|
+
`type: ${df.get('Type', rowI)} error: ${errStr}.`;
|
|
75
|
+
grok.shell.warning(errMsg);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Determine the category of the value specified in 'Types' column */
|
|
79
|
+
function getActualTypeClass(actualType: string): SEQ_TYPE_CATEGORY {
|
|
80
|
+
if (Object.keys(typeCategoryMap).includes(actualType))
|
|
81
|
+
return typeCategoryMap[actualType as SEQ_TYPE];
|
|
82
|
+
else
|
|
83
|
+
throw new Error('Some types in \'Types\' column are invalid ');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isASorSS(splittedLines: string[][]): boolean {
|
|
87
|
+
return splittedLines.length === 1 && splittedLines[0].length === 1;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Check whether the number of lines and prefixes in the 'Sequence' string
|
|
91
|
+
* are valid */
|
|
92
|
+
function verifyPrefixes(splittedLines: string[][], allowedPrefixes: Set<PREFIXES>, allowedLength: number): boolean {
|
|
93
|
+
const lengthCriterion = splittedLines.length === allowedLength;
|
|
94
|
+
let prefixCriterion = true;
|
|
95
|
+
for (const line of splittedLines) {
|
|
96
|
+
const prefix = line[0];
|
|
97
|
+
prefixCriterion &&= (allowedPrefixes.has(prefix as PREFIXES));
|
|
41
98
|
}
|
|
99
|
+
return lengthCriterion && prefixCriterion;
|
|
100
|
+
}
|
|
42
101
|
|
|
43
|
-
|
|
44
|
-
const
|
|
102
|
+
function isDuplex(splittedLines: string[][]): boolean {
|
|
103
|
+
const allowedPrefixes = new Set([PREFIXES.SS, PREFIXES.AS]);
|
|
104
|
+
return verifyPrefixes(splittedLines, allowedPrefixes, 2);
|
|
105
|
+
}
|
|
45
106
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
result += `${sequenceToMolV3000(sequenceCol.get(i), false, true, format)}\n> <Sequence>\nSense Strand\n\n`;
|
|
51
|
-
else if (typeCol.get(i) == SEQUENCE_TYPES.ANTISENSE_STRAND)
|
|
52
|
-
result += `${sequenceToMolV3000(sequenceCol.get(i), true, true, format)}\n> <Sequence>\nAnti Sense\n\n`;
|
|
53
|
-
else if (typeCol.get(i) == SEQUENCE_TYPES.DUPLEX) {
|
|
54
|
-
const obj = parseStrandsFromDuplexCell(sequenceCol.get(i));
|
|
55
|
-
const as = `${sequenceToMolV3000(obj.AS, true, true, format)}\n> <Sequence>\nAnti Sense\n\n`;
|
|
56
|
-
const ss = `${sequenceToMolV3000(obj.SS, false, true, format)}\n> <Sequence>\nSense Strand\n\n`;
|
|
57
|
-
result += `${linkStrandsV3000({senseStrands: [ss], antiStrands: [as]}, true)}\n\n`;
|
|
58
|
-
} else if ([SEQUENCE_TYPES.TRIPLEX, SEQUENCE_TYPES.DIMER].includes(typeCol.get(i))) {
|
|
59
|
-
const obj = parseStrandsFromTriplexOrDimerCell(sequenceCol.get(i));
|
|
60
|
-
const as1 = `${sequenceToMolV3000(obj.AS1, true, true, format)}\n> <Sequence>\nAnti Sense\n\n`;
|
|
61
|
-
const as2 = `${sequenceToMolV3000(obj.AS2, true, true, format)}\n> <Sequence>\nAnti Sense\n\n`;
|
|
62
|
-
const ss = `${sequenceToMolV3000(obj.SS, false, true, format)}\n> <Sequence>\nSense Strand\n\n`;
|
|
63
|
-
result += `${linkStrandsV3000({senseStrands: [ss], antiStrands: [as1, as2]}, true)}\n\n`;
|
|
64
|
-
}
|
|
107
|
+
function isDimer(splittedLines: string[][]): boolean {
|
|
108
|
+
const allowedPrefixes = new Set([PREFIXES.SS, PREFIXES.AS1, PREFIXES.AS2]);
|
|
109
|
+
return verifyPrefixes(splittedLines, allowedPrefixes, 3);
|
|
110
|
+
}
|
|
65
111
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
112
|
+
function inferTypeClassFromSequence(seq: string): SEQ_TYPE_CATEGORY {
|
|
113
|
+
const lines = seq.split('\n');
|
|
114
|
+
const splittedLines = [];
|
|
115
|
+
for (const line of lines)
|
|
116
|
+
splittedLines.push(line.split(' '));
|
|
117
|
+
if (isASorSS(splittedLines))
|
|
118
|
+
return SEQ_TYPE_CATEGORY.AS_OR_SS;
|
|
119
|
+
else if (isDuplex(splittedLines))
|
|
120
|
+
return SEQ_TYPE_CATEGORY.DUPLEX;
|
|
121
|
+
else if (isDimer(splittedLines))
|
|
122
|
+
return SEQ_TYPE_CATEGORY.DIMER;
|
|
123
|
+
else
|
|
124
|
+
throw new Error('Some cells in \'Sequence\' column have wrong formatting');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Compare type specified in 'Type' column to that computed from 'Sequence' column */
|
|
128
|
+
function validateType(actualType: string, seq: string): boolean {
|
|
129
|
+
if (actualType === '' && seq === '')
|
|
130
|
+
return true;
|
|
131
|
+
else
|
|
132
|
+
return getActualTypeClass(actualType) === inferTypeClassFromSequence(seq);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function oligoSdFileGrid(view: DG.TableView): void {
|
|
136
|
+
const typeColName = 'Type';
|
|
137
|
+
const seqColName = 'Sequence';
|
|
138
|
+
const grid = view.grid;
|
|
139
|
+
const df = view.dataFrame;
|
|
140
|
+
const typeCol = df.getCol(typeColName);
|
|
141
|
+
grid.columns.byName(typeColName)!.cellType = 'html';
|
|
142
|
+
const seqCol = df.getCol(seqColName);
|
|
143
|
+
grid.onCellPrepare((gridCell: DG.GridCell) => {
|
|
144
|
+
if (gridCell.isTableCell && gridCell.gridColumn.column!.name === typeColName) {
|
|
145
|
+
let isValidType = false;
|
|
146
|
+
let formattingError = false;
|
|
147
|
+
try {
|
|
148
|
+
isValidType = validateType(gridCell.cell.value, seqCol.get(gridCell.tableRow!.idx));
|
|
149
|
+
} catch {
|
|
150
|
+
formattingError = true;
|
|
151
|
+
}
|
|
152
|
+
const el = ui.div(
|
|
153
|
+
gridCell.cell.value, isValidType ? {style: typeColCellStyle} : {style: typeColErrorStyle}
|
|
154
|
+
);
|
|
155
|
+
gridCell.style.element = el;
|
|
156
|
+
const msg = formattingError ? 'Sequence pattern or Type value has wrong formatting' :
|
|
157
|
+
'Input in Type column doesn\'t match the Sequence pattern';
|
|
158
|
+
if (!isValidType)
|
|
159
|
+
ui.tooltip.bind(el, msg);
|
|
69
160
|
}
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
download(`${table.name}.sdf`, encodeURIComponent(result));
|
|
161
|
+
});
|
|
73
162
|
}
|
|
74
163
|
|
|
75
164
|
export function autostartOligoSdFileSubscription() {
|
|
76
165
|
grok.events.onViewAdded.subscribe((v: any) => {
|
|
77
|
-
if (v.type
|
|
78
|
-
if (v.dataFrame.columns.contains(COL_NAMES.TYPE))
|
|
166
|
+
if (v.type === DG.VIEW_TYPE.TABLE_VIEW) {
|
|
167
|
+
if (v.dataFrame.columns.contains(COL_NAMES.TYPE)) {
|
|
168
|
+
oligoSdFileGrid(v);
|
|
79
169
|
oligoSdFile(v.dataFrame);
|
|
170
|
+
}
|
|
80
171
|
|
|
81
172
|
// Should be removed after fixing bug https://github.com/datagrok-ai/public/issues/808
|
|
82
173
|
grok.events.onContextMenu.subscribe((args) => {
|
|
83
|
-
|
|
174
|
+
if (!(args.args.context instanceof DG.Grid)) return;
|
|
175
|
+
const grid: DG.Grid = args.args.context as DG.Grid;
|
|
176
|
+
const menu: DG.Menu = args.args.menu;
|
|
177
|
+
|
|
178
|
+
const seqCol = grid.table.currentCol; // /^[fsACGUacgu]{6,}$/
|
|
84
179
|
if (DG.Detector.sampleCategories(seqCol,
|
|
85
180
|
(s) => /(\(invabasic\)|\(GalNAc-2-JNJ\)|A|U|G|C){6,}$/.test(s))) {
|
|
86
|
-
|
|
87
|
-
|
|
181
|
+
menu.item('Convert raw nucleotides to GCRS', () => {
|
|
182
|
+
grid.table.columns.addNewString(seqCol.name + ' to GCRS').init((i: number) => {
|
|
88
183
|
return siRnaNucleotidesToGcrs(seqCol.get(i));
|
|
89
184
|
});
|
|
90
185
|
});
|
|
91
186
|
} else if (DG.Detector.sampleCategories(seqCol,
|
|
92
187
|
(s) => /(\(invabasic\)|\(GalNAc-2-JNJ\)|f|s|A|C|G|U|a|c|g|u){6,}$/.test(s))) {
|
|
93
|
-
|
|
94
|
-
|
|
188
|
+
menu.item('Convert Axolabs to GCRS', () => {
|
|
189
|
+
grid.table.columns.addNewString(seqCol.name + ' to GCRS').init((i: number) => {
|
|
95
190
|
return siRnaAxolabsToGcrs(seqCol.get(i));
|
|
96
191
|
});
|
|
97
192
|
}); // /^[fmpsACGU]{6,}$/
|
|
98
193
|
} else if (DG.Detector.sampleCategories(seqCol,
|
|
99
194
|
(s) => /(\(invabasic\)|\(GalNAc-2-JNJ\)|f|m|ps|A|C|G|U){6,}$/.test(s)) ||
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
195
|
+
DG.Detector.sampleCategories(seqCol, (s) => /^(?=.*moe)(?=.*5mC)(?=.*ps){6,}/.test(s))) {
|
|
196
|
+
menu.item('Convert GCRS to raw', () => {
|
|
197
|
+
grid.table.columns.addNewString(seqCol.name + ' to raw').init((i: number) => {
|
|
103
198
|
return gcrsToNucleotides(seqCol.get(i));
|
|
104
199
|
});
|
|
105
200
|
});
|
|
106
|
-
|
|
107
|
-
|
|
201
|
+
menu.item('Convert GCRS to MM12', () => {
|
|
202
|
+
grid.table.columns.addNewString(seqCol.name + ' to MM12').init((i: number) => {
|
|
108
203
|
return gcrsToMermade12(seqCol.get(i));
|
|
109
204
|
});
|
|
110
205
|
}); // /^[*56789ATGC]{6,}$/
|
|
111
206
|
} else if (DG.Detector.sampleCategories(seqCol,
|
|
112
207
|
(s) => /(\(invabasic\)|\(GalNAc-2-JNJ\)|\*|5|6|7|8|9|A|T|G|C){6,}$/.test(s))) {
|
|
113
|
-
|
|
114
|
-
const seqCol =
|
|
115
|
-
|
|
208
|
+
menu.item('Convert Biospring to GCRS', () => {
|
|
209
|
+
const seqCol = grid.table.currentCol;
|
|
210
|
+
grid.table.columns.addNewString(seqCol.name + ' to GCRS').init((i: number) => {
|
|
116
211
|
return asoGapmersBioSpringToGcrs(seqCol.get(i));
|
|
117
212
|
});
|
|
118
213
|
}); // /^[*1-8]{6,}$/
|
|
119
214
|
} else if (DG.Detector.sampleCategories(seqCol,
|
|
120
215
|
(s) => /(\(invabasic\)|\(GalNAc-2-JNJ\)|\*|1|2|3|4|5|6|7|8){6,}$/.test(s))) {
|
|
121
|
-
|
|
122
|
-
|
|
216
|
+
menu.item('Convert Biospring to GCRS', () => {
|
|
217
|
+
grid.table.columns.addNewString(seqCol.name + ' to GCRS').init((i: number) => {
|
|
123
218
|
return siRnaBioSpringToGcrs(seqCol.get(i));
|
|
124
219
|
});
|
|
125
220
|
});
|
|
@@ -136,85 +231,13 @@ export function oligoSdFile(table: DG.DataFrame) {
|
|
|
136
231
|
const icdsDf = DG.DataFrame.fromCsv(ICDS);
|
|
137
232
|
const idpsDf = DG.DataFrame.fromCsv(IDPS);
|
|
138
233
|
|
|
139
|
-
const sequenceCol = table.getCol(COL_NAMES.SEQUENCE);
|
|
140
234
|
const saltCol = table.getCol(COL_NAMES.SALT);
|
|
141
235
|
const equivalentsCol = table.getCol(COL_NAMES.EQUIVALENTS);
|
|
142
|
-
const typeCol = table.getCol(COL_NAMES.TYPE);
|
|
143
|
-
const chemistryNameCol = table.getCol(COL_NAMES.CHEMISTRY_NAME);
|
|
144
|
-
|
|
145
|
-
const molWeightCol = saltsDf.getCol('MOLWEIGHT');
|
|
146
|
-
const saltNamesList = saltsDf.getCol('DISPLAY').toList();
|
|
147
|
-
|
|
148
|
-
let newDf: DG.DataFrame;
|
|
149
|
-
let addColumnsPressed = false;
|
|
150
|
-
|
|
151
|
-
function addColumns(t: DG.DataFrame) {
|
|
152
|
-
if (GENERATED_COL_NAMES.some((colName) => t.columns.contains(colName)))
|
|
153
|
-
return grok.shell.error('Columns already exist');
|
|
154
|
-
|
|
155
|
-
t = removeEmptyRows(t, sequenceCol);
|
|
156
|
-
|
|
157
|
-
t.columns.addNewString(COL_NAMES.COMPOUND_NAME).init((i: number) => {
|
|
158
|
-
return ([SEQUENCE_TYPES.DUPLEX, SEQUENCE_TYPES.DIMER, SEQUENCE_TYPES.TRIPLEX].includes(typeCol.get(i))) ?
|
|
159
|
-
chemistryNameCol.get(i) :
|
|
160
|
-
sequenceCol.get(i);
|
|
161
|
-
});
|
|
162
236
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return sequenceCol.get(i);
|
|
166
|
-
else if (typeCol.get(i) == SEQUENCE_TYPES.DUPLEX) {
|
|
167
|
-
const obj = parseStrandsFromDuplexCell(sequenceCol.get(i));
|
|
168
|
-
return `${chemistryNameCol.get(i)}; duplex of SS: ${obj.SS} and AS: ${obj.AS}`;
|
|
169
|
-
} else if ([SEQUENCE_TYPES.DIMER, SEQUENCE_TYPES.TRIPLEX].includes(typeCol.get(i))) {
|
|
170
|
-
const obj = parseStrandsFromTriplexOrDimerCell(sequenceCol.get(i));
|
|
171
|
-
return `${chemistryNameCol.get(i)}; duplex of SS: ${obj.SS} and AS1: ${obj.AS1} and AS2: ${obj.AS2}`;
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
t.columns.addNewFloat(COL_NAMES.COMPOUND_MOL_WEIGHT).init((i: number) => {
|
|
176
|
-
if ([SEQUENCE_TYPES.SENSE_STRAND, SEQUENCE_TYPES.ANTISENSE_STRAND].includes(typeCol.get(i))) {
|
|
177
|
-
return (isValidSequence(sequenceCol.get(i), null).indexOfFirstNotValidChar == -1) ?
|
|
178
|
-
molecularWeight(sequenceCol.get(i), weightsObj) :
|
|
179
|
-
DG.FLOAT_NULL;
|
|
180
|
-
} else if (typeCol.get(i) == SEQUENCE_TYPES.DUPLEX) {
|
|
181
|
-
const obj = parseStrandsFromDuplexCell(sequenceCol.get(i));
|
|
182
|
-
return (Object.values(obj).every((seq) => isValidSequence(seq, null).indexOfFirstNotValidChar == -1)) ?
|
|
183
|
-
molecularWeight(obj.SS, weightsObj) + molecularWeight(obj.AS, weightsObj) :
|
|
184
|
-
DG.FLOAT_NULL;
|
|
185
|
-
} else if ([SEQUENCE_TYPES.DIMER, SEQUENCE_TYPES.TRIPLEX].includes(typeCol.get(i))) {
|
|
186
|
-
const obj = parseStrandsFromTriplexOrDimerCell(sequenceCol.get(i));
|
|
187
|
-
return (Object.values(obj).every((seq) => isValidSequence(seq, null).indexOfFirstNotValidChar == -1)) ?
|
|
188
|
-
molecularWeight(obj.SS, weightsObj) + molecularWeight(obj.AS1, weightsObj) +
|
|
189
|
-
molecularWeight(obj.AS2, weightsObj) :
|
|
190
|
-
DG.FLOAT_NULL;
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
t.columns.addNewFloat(COL_NAMES.SALT_MASS).init((i: number) =>
|
|
195
|
-
saltMass(saltNamesList, molWeightCol, equivalentsCol, i, saltCol));
|
|
196
|
-
|
|
197
|
-
t.columns.addNewFloat(COL_NAMES.SALT_MOL_WEIGHT).init((i: number) =>
|
|
198
|
-
saltMolWeigth(saltNamesList, saltCol, molWeightCol, i));
|
|
237
|
+
const saltsMolWeightList: number[] = saltsDf.getCol('MOLWEIGHT').toList();
|
|
238
|
+
const saltNamesList: string[] = saltsDf.getCol('DISPLAY').toList();
|
|
199
239
|
|
|
200
|
-
|
|
201
|
-
const saltMassCol = t.getCol(COL_NAMES.SALT_MASS);
|
|
202
|
-
t.columns.addNewFloat(COL_NAMES.BATCH_MOL_WEIGHT).init((i: number) =>
|
|
203
|
-
batchMolWeight(compoundMolWeightCol, saltMassCol, i));
|
|
204
|
-
|
|
205
|
-
grok.shell.getTableView(table.name).grid.columns.setOrder(Object.values(COL_NAMES));
|
|
206
|
-
addColumnsPressed = true;
|
|
207
|
-
return newDf = t;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function updateCalculatedColumns(t: DG.DataFrame, i: number): void {
|
|
211
|
-
const smValue = saltMass(saltNamesList, molWeightCol, equivalentsCol, i, saltCol);
|
|
212
|
-
t.getCol(COL_NAMES.SALT_MASS).set(i, smValue, false);
|
|
213
|
-
const smwValue = saltMolWeigth(saltNamesList, saltCol, molWeightCol, i);
|
|
214
|
-
t.getCol(COL_NAMES.SALT_MOL_WEIGHT).set(i, smwValue, false);
|
|
215
|
-
const bmw = batchMolWeight(t.getCol(COL_NAMES.COMPOUND_MOL_WEIGHT), t.getCol(COL_NAMES.SALT_MASS), i);
|
|
216
|
-
t.getCol(COL_NAMES.BATCH_MOL_WEIGHT).set(i, bmw, false);
|
|
217
|
-
}
|
|
240
|
+
let newDf: DG.DataFrame | undefined = undefined;
|
|
218
241
|
|
|
219
242
|
const d = ui.div([
|
|
220
243
|
ui.icons.edit(() => {
|
|
@@ -223,9 +246,19 @@ export function oligoSdFile(table: DG.DataFrame) {
|
|
|
223
246
|
table.changeColumnType(COL_NAMES.IDP, DG.COLUMN_TYPE.STRING);
|
|
224
247
|
d.append(
|
|
225
248
|
ui.divH([
|
|
226
|
-
ui.
|
|
227
|
-
|
|
228
|
-
|
|
249
|
+
ui.button('Add columns',
|
|
250
|
+
() => {
|
|
251
|
+
newDf = sdfAddColumns(table, saltNamesList, saltsMolWeightList,
|
|
252
|
+
(rowI, err) => { sdfHandleErrorUI('Error on ', table, rowI, err); });
|
|
253
|
+
grok.shell.getTableView(newDf.name).grid.columns.setOrder(Object.values(COL_NAMES));
|
|
254
|
+
},
|
|
255
|
+
`Add columns: '${GENERATED_COL_NAMES.join(`', '`)}'`),
|
|
256
|
+
ui.bigButton('Save SDF', () => {
|
|
257
|
+
const df: DG.DataFrame = newDf ?? table;
|
|
258
|
+
sdfSaveTable(df,
|
|
259
|
+
(rowI, err) => { sdfHandleErrorUI('Skip ', df, rowI, err); });
|
|
260
|
+
}, 'Save SD file'),
|
|
261
|
+
])
|
|
229
262
|
);
|
|
230
263
|
|
|
231
264
|
const view = grok.shell.getTableView(table.name);
|
|
@@ -238,11 +271,15 @@ export function oligoSdFile(table: DG.DataFrame) {
|
|
|
238
271
|
view.dataFrame.getCol(COL_NAMES.IDP).setTag(DG.TAGS.CHOICES, stringify(idpsDf.columns.byIndex(0).toList()));
|
|
239
272
|
|
|
240
273
|
grok.events.onContextMenu.subscribe((args) => {
|
|
274
|
+
if (!(args.args.context instanceof DG.Grid)) return;
|
|
275
|
+
const grid: DG.Grid = args.args.context as DG.Grid;
|
|
276
|
+
const menu: DG.Menu = args.args.menu;
|
|
277
|
+
|
|
241
278
|
if ([COL_NAMES.TYPE, COL_NAMES.OWNER, COL_NAMES.SALT, COL_NAMES.SOURCE, COL_NAMES.ICD, COL_NAMES.IDP]
|
|
242
|
-
.includes(
|
|
243
|
-
|
|
244
|
-
const v =
|
|
245
|
-
|
|
279
|
+
.includes(grid.table.currentCol.name)) {
|
|
280
|
+
menu.item('Fill Column With Value', () => {
|
|
281
|
+
const v = grid.table.currentCell.value;
|
|
282
|
+
grid.table.currentCell.column.init(v);
|
|
246
283
|
for (let i = 0; i < view.dataFrame.rowCount; i++)
|
|
247
284
|
updateCalculatedColumns(view.dataFrame, i);
|
|
248
285
|
});
|
|
@@ -256,9 +293,9 @@ export function oligoSdFile(table: DG.DataFrame) {
|
|
|
256
293
|
});
|
|
257
294
|
|
|
258
295
|
function updateCalculatedColumns(t: DG.DataFrame, i: number): void {
|
|
259
|
-
const smValue = saltMass(saltNamesList,
|
|
296
|
+
const smValue = saltMass(saltNamesList, saltsMolWeightList, equivalentsCol, i, saltCol);
|
|
260
297
|
t.getCol(COL_NAMES.SALT_MASS).set(i, smValue, false);
|
|
261
|
-
const smwValue = saltMolWeigth(saltNamesList, saltCol,
|
|
298
|
+
const smwValue = saltMolWeigth(saltNamesList, saltCol, saltsMolWeightList, i);
|
|
262
299
|
t.getCol(COL_NAMES.SALT_MOL_WEIGHT).set(i, smwValue, false);
|
|
263
300
|
const bmw = batchMolWeight(t.getCol(COL_NAMES.COMPOUND_MOL_WEIGHT), t.getCol(COL_NAMES.SALT_MASS), i);
|
|
264
301
|
t.getCol(COL_NAMES.BATCH_MOL_WEIGHT).set(i, bmw, false);
|
|
@@ -6,7 +6,7 @@ import * as svg from 'save-svg-as-png';
|
|
|
6
6
|
import $ from 'cash-dom';
|
|
7
7
|
|
|
8
8
|
import {drawAxolabsPattern} from './draw-svg';
|
|
9
|
-
import {AXOLABS_MAP} from '
|
|
9
|
+
import {AXOLABS_MAP} from '../hardcode-to-be-eliminated/constants';
|
|
10
10
|
import {isOverhang} from './helpers';
|
|
11
11
|
|
|
12
12
|
const baseChoices: string[] = Object.keys(AXOLABS_MAP);
|
|
@@ -114,7 +114,7 @@ function addColumnWithTranslatedSequences(
|
|
|
114
114
|
});
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
export function
|
|
117
|
+
export function getAxolabsTab() {
|
|
118
118
|
const enumerateModifications = [defaultBase];
|
|
119
119
|
let maximalSsLength = defaultSequenceLength;
|
|
120
120
|
let maximalAsLength = defaultSequenceLength;
|