@datagrok/sequence-translator 1.0.13 → 1.0.15

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,10 +1,10 @@
1
1
  {
2
2
  "name": "@datagrok/sequence-translator",
3
3
  "friendlyName": "Sequence Translator",
4
- "version": "1.0.13",
4
+ "version": "1.0.15",
5
5
  "author": {
6
- "name": "Vadym Kovadlo",
7
- "email": "vkovadlo@datagrok.ai"
6
+ "name": "Alexey Choposky",
7
+ "email": "achopovsky@datagrok.ai"
8
8
  },
9
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).",
10
10
  "repository": {
@@ -13,8 +13,9 @@
13
13
  "directory": "packages/SequenceTranslator"
14
14
  },
15
15
  "dependencies": {
16
- "@datagrok-libraries/utils": "^1.15.5",
16
+ "@datagrok-libraries/utils": "^1.17.2",
17
17
  "@types/react": "^18.0.15",
18
+ "@datagrok-libraries/bio": "^5.11.1",
18
19
  "datagrok-api": "^1.7.2",
19
20
  "datagrok-tools": "^4.1.2",
20
21
  "npm": "^8.11.0",
@@ -23,24 +24,6 @@
23
24
  "ts-loader": "^9.3.1",
24
25
  "typescript": "^4.7.4"
25
26
  },
26
- "scripts": {
27
- "link-api": "npm link datagrok-api",
28
- "debug-sequencetranslator": "grok publish",
29
- "release-sequencetranslator": "grok publish localhost --release",
30
- "build-sequencetranslator": "webpack",
31
- "build": "webpack",
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",
37
- "test-dev": "set HOST=dev && jest",
38
- "test-local": "set HOST=localhost && jest"
39
- },
40
- "sources": [
41
- "css/style.css",
42
- "vendors/openchemlib-full.js"
43
- ],
44
27
  "devDependencies": {
45
28
  "@types/jest": "^27.0.0",
46
29
  "@types/jquery": "^3.5.14",
@@ -60,5 +43,27 @@
60
43
  "@types/node-fetch": "^2.6.2",
61
44
  "node-fetch": "^2.6.7"
62
45
  },
46
+ "grokDependencies": {
47
+ "@datagrok/chem": "1.3.32"
48
+ },
49
+ "scripts": {
50
+ "link-api": "npm link datagrok-api",
51
+ "link-all": "npm link datagrok-api @datagrok-libraries/utils @datagrok-libraries/bio",
52
+ "debug-sequencetranslator": "grok publish",
53
+ "release-sequencetranslator": "grok publish localhost --release",
54
+ "build-sequencetranslator": "webpack",
55
+ "build": "webpack",
56
+ "debug-sequencetranslator-public": "grok publish public",
57
+ "release-sequencetranslator-public": "grok publish public --release",
58
+ "debug-sequencetranslator-local": "grok publish local",
59
+ "release-sequencetranslator-local": "grok publish local --release",
60
+ "test": "jest",
61
+ "test-dev": "set HOST=dev && jest",
62
+ "test-local": "set HOST=localhost && jest"
63
+ },
64
+ "sources": [
65
+ "css/style.css",
66
+ "vendors/openchemlib-full.js"
67
+ ],
63
68
  "category": "Bioinformatics"
64
69
  }
@@ -0,0 +1,178 @@
1
+ from io import TextIOWrapper
2
+
3
+ from rdkit import Chem
4
+
5
+ import orjson
6
+
7
+ import click
8
+
9
+ from click_default_group import DefaultGroup
10
+ from rdkit.Chem.rdchem import Mol
11
+
12
+
13
+ def molAddCollection(mol: Mol, name: str, title: str = None) -> str:
14
+ """
15
+ Get and postprocess (atom's CFG, title, e.t.c.) molblock
16
+ :param mol: Mol molecule structure / object
17
+ :param name: Monomer name to add to molblock title
18
+ :param title: title to replace in Chem.MolToMolBlock() string output
19
+ :return: molblock string
20
+ """
21
+ res: str = Chem.MolToMolBlock(mol, forceV3000=True) # MolToMolFile
22
+
23
+ mb_line_list: list[str] = res.split('\n')
24
+ if title:
25
+ mb_line_list[1] = title
26
+
27
+ if name and name not in mb_line_list[1]:
28
+ mb_line_list[1] += '|' + name
29
+
30
+ end_bond_idx: int = mb_line_list.index('M V30 END BOND')
31
+ chirality = [atom.GetChiralTag() for atom in mol.GetAtoms()]
32
+ begin_atom_idx = mb_line_list.index('M V30 BEGIN ATOM')
33
+ end_atom_idx = mb_line_list.index('M V30 END ATOM')
34
+ for atom_idx in range(1, end_atom_idx - begin_atom_idx):
35
+ line_idx = begin_atom_idx + atom_idx
36
+ atom_ch = chirality[atom_idx - 1]
37
+ if atom_ch != Chem.rdchem.CHI_UNSPECIFIED:
38
+ mb_line_list[line_idx] += " CFG={0}".format(int(atom_ch))
39
+
40
+ steabs: list[int] = [i + 1 for (i, ch) in enumerate(chirality) if ch != Chem.rdchem.CHI_UNSPECIFIED]
41
+ if len(steabs) > 0:
42
+ steabs_str: str = "M V30 MDLV30/STEABS ATOMS=({count} {list})" \
43
+ .format(count=len(steabs), list=' '.join([str(idx) for idx in steabs]))
44
+
45
+ mb_line_list = mb_line_list[:(end_bond_idx + 1)] + \
46
+ ["M V30 BEGIN COLLECTION", steabs_str, "M V30 END COLLECTION"] + \
47
+ mb_line_list[(end_bond_idx + 1):]
48
+
49
+ return '\n'.join(mb_line_list)
50
+
51
+
52
+ def molfile2molfile(src_mol: str, name: str) -> str:
53
+ mol: Mol = Chem.MolFromMolBlock(src_mol)
54
+ src_mf_lines = src_mol.split('\n')
55
+ title = src_mf_lines[1]
56
+ return molAddCollection(mol, name, title=title)
57
+
58
+
59
+ def smiles2molfile(smiles: str, name: str) -> str:
60
+ mol: Mol = Chem.MolFromSmiles(smiles)
61
+ return molAddCollection(mol, name)
62
+
63
+
64
+ CodesType = dict[str, dict[str, list[str]]]
65
+
66
+
67
+ class Monomer:
68
+ def __init__(self,
69
+ symbol: str, name: str, molfile: str, smiles: str,
70
+ codes: CodesType):
71
+ self.monomerType = 'Backbone'
72
+ self.smiles = smiles
73
+ self.name = name
74
+ self.author = 'SequenceTranslator'
75
+ self.molfile = molfile2molfile(molfile, name) if molfile else smiles2molfile(smiles, name)
76
+ self.naturalAnalog = ''
77
+ self.rgroups = [
78
+ {
79
+ "capGroupSmiles": "O[*:1]",
80
+ "alternateId": "R1-OH",
81
+ "capGroupName": "OH",
82
+ "label": "R1"
83
+ },
84
+ {
85
+ "capGroupSmiles": "O[*:2]",
86
+ "alternateId": "R2-OH",
87
+ "capGroupName": "OH",
88
+ "label": "R2"
89
+ }]
90
+ self.createDate = None
91
+ self.id = 0
92
+ self.polymerType = 'RNA'
93
+ self.symbol = symbol
94
+ self.codes: CodesType = codes
95
+
96
+ @staticmethod
97
+ def from_json(src_json: {}):
98
+ obj = Monomer(src_json['symbol'], src_json['name'],
99
+ src_json['molfile'], src_json['smiles'],
100
+ src_json['codes'])
101
+ return obj
102
+
103
+ def to_json(self):
104
+ return {
105
+ 'monomerType': self.monomerType,
106
+ 'smiles': self.smiles,
107
+ 'name': self.name,
108
+ 'author': self.author,
109
+ 'molfile': self.molfile,
110
+ 'naturalAnalog': self.naturalAnalog,
111
+ 'rgroups': self.rgroups,
112
+ 'createDate': self.createDate,
113
+ 'id': self.id,
114
+ 'polymerType': self.polymerType,
115
+ 'symbol': self.symbol,
116
+ 'codes': self.codes,
117
+ }
118
+
119
+
120
+ def codes2monomers(codes_json: {}) -> dict[str, Monomer]:
121
+ monomers_res: dict[str, Monomer] = {}
122
+ for (codes_src, src_dict) in codes_json.items():
123
+ for (codes_type, monomers_dict) in src_dict.items():
124
+ for (codes_code, monomer_json) in monomers_dict.items():
125
+ monomer_name = monomer_json['name']
126
+ if monomer_name not in monomers_res:
127
+ symbol = monomer_json['name']
128
+ name = monomer_json['name']
129
+ smiles = monomer_json['SMILES']
130
+ monomers_res[monomer_name] = Monomer(symbol, name, None, smiles, {})
131
+ codes = monomers_res[monomer_name].codes
132
+ if codes_src not in codes:
133
+ codes[codes_src] = {}
134
+ if codes_type not in codes[codes_src]:
135
+ codes[codes_src][codes_type] = [];
136
+ codes[codes_src][codes_type].append(codes_code)
137
+ return monomers_res
138
+
139
+
140
+ @click.group(cls=DefaultGroup, default='main')
141
+ def cli():
142
+ pass
143
+
144
+
145
+ @cli.command()
146
+ @click.pass_context
147
+ @click.option('--initial', 'initial_f',
148
+ help='Initial monomers source file.',
149
+ type=click.File('r', 'utf-8'))
150
+ @click.option('--lib', 'lib_f',
151
+ help='Output library (HELM format) file.',
152
+ type=click.File('wb', 'utf-8'))
153
+ @click.option('--add', 'add_f_list', multiple=True,
154
+ help='Additional libraries to build.',
155
+ type=click.File('r', 'utf-8'))
156
+ def main(ctx, initial_f: TextIOWrapper, lib_f: TextIOWrapper, add_f_list: list[TextIOWrapper]):
157
+ initial_json_str = initial_f.read()
158
+
159
+ initial_json = orjson.loads(initial_json_str)
160
+
161
+ monomers: dict[str, Monomer] = codes2monomers(initial_json)
162
+
163
+ for add_f in add_f_list:
164
+ add_json_str = add_f.read()
165
+ add_json = orjson.loads(add_json_str)
166
+ for add_m in add_json:
167
+ m = Monomer.from_json(add_m)
168
+ monomers[m.name] = m
169
+
170
+ add_json = [m.to_json() for m in monomers.values()]
171
+
172
+ lib_json_txt = orjson.dumps(add_json, option=orjson.OPT_INDENT_2)
173
+ lib_f.write(lib_json_txt)
174
+ k = 11
175
+
176
+
177
+ if __name__ == '__main__':
178
+ cli()
@@ -0,0 +1,14 @@
1
+ set package_dir=%cd%
2
+
3
+ set dirs=^
4
+ \..\..\js-api\ ^
5
+ \..\..\libraries\utils\ ^
6
+ \..\..\libraries\bio\ ^
7
+ \
8
+
9
+ call npm uninstall -g datagrok-api @datagrok-libraries/utils @datagrok-libraries/bio
10
+
11
+ for %%p in (%dirs%) do cd %package_dir%\%%p & rmdir /s /q node_modules
12
+ for %%p in (%dirs%) do cd %package_dir%\%%p & rmdir /s /q dist
13
+
14
+ rem for %%p in (%dirs%) do cd %package_dir%\%%p & del "package-lock.json"
package/setup.cmd CHANGED
@@ -1,11 +1,14 @@
1
- cd ../../js-api
2
- call npm install
3
- call npm link
4
- cd ../libraries/utils
5
- call npm install
6
- call npm link
7
- call npm link datagrok-api
8
- cd ../../packages/SequenceTranslator
9
- call npm install
10
- call npm link datagrok-api @datagrok-libraries/utils
11
- webpack
1
+ call setup-unlink-clean.cmd
2
+
3
+ set package_dir=%cd%
4
+
5
+ set dirs=^
6
+ \..\..\js-api\ ^
7
+ \..\..\libraries\utils\ ^
8
+ \..\..\libraries\bio\ ^
9
+ \
10
+
11
+ for %%p in (%dirs%) do cd %package_dir%\%%p & call npm install
12
+ for %%p in (%dirs%) do cd %package_dir%\%%p & call npm link
13
+ for %%p in (%dirs%) do cd %package_dir%\%%p & call npm run link-all
14
+ for %%p in (%dirs%) do cd %package_dir%\%%p & call npm run build
package/setup.sh ADDED
@@ -0,0 +1,37 @@
1
+ #!/bin/bash
2
+
3
+ ./setup-unlink-clean.sh
4
+
5
+ GREEN='\e[0;32m'
6
+ NO_COLOR='\e[0m'
7
+
8
+ package_dir=$(pwd)
9
+
10
+ dirs=(
11
+ "../../js-api/"
12
+ "../../libraries/utils/"
13
+ "../../libraries/bio/"
14
+ )
15
+
16
+ for dir in ${dirs[@]}; do
17
+ cd $package_dir
18
+ cd $dir
19
+ echo -e $GREEN npm install in $(pwd) $NO_COLOR
20
+ npm install
21
+ echo -e $GREEN npm link in $(pwd) $NO_COLOR
22
+ npm link
23
+ done
24
+
25
+ for dir in ${dirs[@]}; do
26
+ cd $package_dir
27
+ cd $dir
28
+ if [ $dir != "../../js-api/" ]; then
29
+ echo -e $GREEN npm link-all in $(pwd) $NO_COLOR
30
+ npm run link-all
31
+ fi
32
+ echo -e $GREEN npm run build in$(pwd) $NO_COLOR
33
+ npm run build || exit
34
+ done
35
+
36
+ cd $package_dir
37
+ npm run link-all
@@ -6,6 +6,18 @@ 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
+
9
21
  export const COL_NAMES = {
10
22
  CHEMISTRY: 'Chemistry',
11
23
  NUMBER: 'Number',
@@ -4,7 +4,7 @@ import * as DG from 'datagrok-api/dg';
4
4
  import {siRnaBioSpringToGcrs, siRnaAxolabsToGcrs, gcrsToNucleotides, asoGapmersBioSpringToGcrs, gcrsToMermade12,
5
5
  siRnaNucleotidesToGcrs} from '../structures-works/converters';
6
6
  import {weightsObj, SYNTHESIZERS} from '../structures-works/map';
7
- import {SEQUENCE_TYPES, COL_NAMES, GENERATED_COL_NAMES} from './constants';
7
+ import {SEQUENCE_TYPES, COL_NAMES, GENERATED_COL_NAMES, CELL_STRUCTURE} from './constants';
8
8
  import {saltMass, saltMolWeigth, molecularWeight, batchMolWeight} from './calculations';
9
9
  import {isValidSequence} from '../structures-works/sequence-codes-tools';
10
10
  import {sequenceToMolV3000} from '../structures-works/from-monomers';
@@ -19,13 +19,18 @@ import {IDPS} from './IDPs';
19
19
 
20
20
 
21
21
  function parseStrandsFromDuplexCell(s: string): {SS: string, AS: string} {
22
- const arr = s.slice(3).split('\r\nAS ');
22
+ const arr = s
23
+ .slice(CELL_STRUCTURE.DUPLEX.BEFORE_SS.length)
24
+ .split(CELL_STRUCTURE.DUPLEX.BEFORE_AS);
23
25
  return {SS: arr[0], AS: arr[1]};
24
26
  }
25
27
 
26
28
  function parseStrandsFromTriplexOrDimerCell(s: string): {SS: string, AS1: string, AS2: string} {
27
- const arr1 = s.slice(3).split('\r\nAS1 ');
28
- const arr2 = arr1[1].split('\r\nAS2 ');
29
+ const arr1 = s
30
+ .slice(CELL_STRUCTURE.TRIPLEX_OR_DIMER.BEFORE_SS.length)
31
+ .split(CELL_STRUCTURE.TRIPLEX_OR_DIMER.BEFORE_AS1);
32
+ const arr2 = arr1[1]
33
+ .split(CELL_STRUCTURE.TRIPLEX_OR_DIMER.BEFORE_AS2);
29
34
  return {SS: arr1[0], AS1: arr2[0], AS2: arr2[1]};
30
35
  }
31
36
 
@@ -249,6 +254,15 @@ export function oligoSdFile(table: DG.DataFrame) {
249
254
  if ([COL_NAMES.SALT, COL_NAMES.EQUIVALENTS, COL_NAMES.SALT_MOL_WEIGHT].includes(colName))
250
255
  updateCalculatedColumns(view.dataFrame, view.dataFrame.currentRowIdx);
251
256
  });
257
+
258
+ function updateCalculatedColumns(t: DG.DataFrame, i: number): void {
259
+ const smValue = saltMass(saltNamesList, molWeightCol, equivalentsCol, i, saltCol);
260
+ t.getCol(COL_NAMES.SALT_MASS).set(i, smValue, false);
261
+ const smwValue = saltMolWeigth(saltNamesList, saltCol, molWeightCol, i);
262
+ t.getCol(COL_NAMES.SALT_MOL_WEIGHT).set(i, smwValue, false);
263
+ const bmw = batchMolWeight(t.getCol(COL_NAMES.COMPOUND_MOL_WEIGHT), t.getCol(COL_NAMES.SALT_MASS), i);
264
+ t.getCol(COL_NAMES.BATCH_MOL_WEIGHT).set(i, bmw, false);
265
+ }
252
266
  }),
253
267
  ]);
254
268
  grok.shell.v.setRibbonPanels([[d]]);
@@ -1,12 +1,12 @@
1
- const rnaColor = 'rgb(255,230,153)';
2
- const invAbasicColor = 'rgb(203,119,211)';
3
- export const axolabsMap:
1
+ const RNA_COLOR = 'rgb(255,230,153)';
2
+ const INVABASIC_COLOR = 'rgb(203,119,211)';
3
+ export const AXOLABS_MAP:
4
4
  {[index: string]: {fullName: string, symbols: [string, string, string, string], color: string}} =
5
5
  {
6
6
  'RNA': {
7
7
  fullName: 'RNA nucleotides',
8
8
  symbols: ['A', 'C', 'G', 'U'],
9
- color: rnaColor,
9
+ color: RNA_COLOR,
10
10
  },
11
11
  'DNA': {
12
12
  fullName: 'DNA nucleotides',
@@ -44,24 +44,24 @@ export const axolabsMap:
44
44
  color: 'rgb(255,192,0)',
45
45
  },
46
46
  'A': {
47
- fullName: 'Adenine',
47
+ fullName: 'Adenosine',
48
48
  symbols: ['a', 'a', 'a', 'a'],
49
- color: rnaColor,
49
+ color: RNA_COLOR,
50
50
  },
51
51
  'C': {
52
- fullName: 'Cytosine',
52
+ fullName: 'Cytidine',
53
53
  symbols: ['c', 'c', 'c', 'c'],
54
- color: rnaColor,
54
+ color: RNA_COLOR,
55
55
  },
56
56
  'G': {
57
- fullName: 'Guanine',
57
+ fullName: 'Guanosine',
58
58
  symbols: ['g', 'g', 'g', 'g'],
59
- color: rnaColor,
59
+ color: RNA_COLOR,
60
60
  },
61
61
  'U': {
62
- fullName: 'Uracil',
62
+ fullName: 'Uridine',
63
63
  symbols: ['u', 'u', 'u', 'u'],
64
- color: rnaColor,
64
+ color: RNA_COLOR,
65
65
  },
66
66
  'X-New': {
67
67
  fullName: '',
@@ -81,7 +81,7 @@ export const axolabsMap:
81
81
  'InvAbasic': {
82
82
  fullName: 'Inverted abasic capped',
83
83
  symbols: ['(invabasic)', '(invabasic)', '(invabasic)', '(invabasic)'],
84
- color: invAbasicColor,
84
+ color: INVABASIC_COLOR,
85
85
  },
86
86
  "5\"-vinylps": {
87
87
  fullName: '5\'-vinylphosphonate-2\'-OMe-uridine',
@@ -91,7 +91,7 @@ export const axolabsMap:
91
91
  'InvAbasic(o)': {
92
92
  fullName: 'Inverted abasic capped (overhang)',
93
93
  symbols: ['(invabasic)', '(invabasic)', '(invabasic)', '(invabasic)'],
94
- color: invAbasicColor,
94
+ color: INVABASIC_COLOR,
95
95
  },
96
96
  "2\"-OMe-U(o)": {
97
97
  fullName: 'Nucleotide Uridine with 2\'O-Methyl protection (overhang)',
@@ -6,9 +6,10 @@ 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 {axolabsMap} from './constants';
9
+ import {AXOLABS_MAP} from './constants';
10
+ import {isOverhang} from './helpers';
10
11
 
11
- const baseChoices: string[] = Object.keys(axolabsMap);
12
+ const baseChoices: string[] = Object.keys(AXOLABS_MAP);
12
13
  const defaultBase: string = baseChoices[0];
13
14
  const defaultPto: boolean = true;
14
15
  const defaultSequenceLength: number = 23;
@@ -17,7 +18,7 @@ const userStorageKey: string = 'SequenceTranslator';
17
18
  const exampleMinWidth: string = '400px';
18
19
 
19
20
  function generateExample(sequenceLength: number, sequenceBasis: string): string {
20
- const uniqueSymbols = axolabsMap[sequenceBasis].symbols.join('');
21
+ const uniqueSymbols = AXOLABS_MAP[sequenceBasis].symbols.join('');
21
22
  return uniqueSymbols.repeat(Math.floor(sequenceLength / 4)) + uniqueSymbols.slice(0, sequenceLength % 4);
22
23
  }
23
24
 
@@ -64,12 +65,12 @@ function translateSequence(
64
65
  let i: number = -1;
65
66
  let mainSequence = sequence.replace(/[AUGC]/g, function(x: string) {
66
67
  i++;
67
- const indexOfSymbol = axolabsMap['RNA']['symbols'].indexOf(x);
68
- let symbol = axolabsMap[bases[i].value]['symbols'][indexOfSymbol];
69
- if (bases[i].value.slice(-3) == '(o)') {
70
- if (i < sequence.length / 2 && bases[i + 1].value.slice(-3) != '(o)')
68
+ const indexOfSymbol = AXOLABS_MAP['RNA']['symbols'].indexOf(x);
69
+ let symbol = AXOLABS_MAP[bases[i].value]['symbols'][indexOfSymbol];
70
+ if (isOverhang(bases[i].value)) {
71
+ if (i < sequence.length / 2 && !isOverhang(bases[i + 1].value))
71
72
  symbol = symbol + x + 'f';
72
- else if (i > sequence.length / 2 && bases[i - 1].value.slice(-3) != '(o)')
73
+ else if (i > sequence.length / 2 && !isOverhang(bases[i - 1].value))
73
74
  symbol = x + 'f' + symbol;
74
75
  }
75
76
  return (ptoLinkages[i].value) ? symbol + 's' : symbol;
@@ -150,12 +151,12 @@ export function defineAxolabsPattern() {
150
151
  updateSvgScheme();
151
152
  updateOutputExamples();
152
153
  });
153
- if (asBases[i].value.slice(-3) != '(o)')
154
+ if (!isOverhang(asBases[i].value))
154
155
  nucleotideCounter++;
155
156
 
156
157
  asModificationItems.append(
157
158
  ui.divH([
158
- ui.div([ui.label(asBases[i].value.slice(-3) == '(o)' ? '' : String(nucleotideCounter))],
159
+ ui.div([ui.label(isOverhang(asBases[i].value) ? '' : String(nucleotideCounter))],
159
160
  {style: {width: '20px'}})!,
160
161
  ui.block75([asBases[i]])!,
161
162
  ui.div([asPtoLinkages[i]])!,
@@ -196,12 +197,12 @@ export function defineAxolabsPattern() {
196
197
  updateSvgScheme();
197
198
  updateOutputExamples();
198
199
  });
199
- if (ssBases[i].value.slice(-3) != '(o)')
200
+ if (!isOverhang(ssBases[i].value))
200
201
  nucleotideCounter++;
201
202
 
202
203
  ssModificationItems.append(
203
204
  ui.divH([
204
- ui.div([ui.label(ssBases[i].value.slice(-3) == '(o)' ? '' : String(nucleotideCounter))],
205
+ ui.div([ui.label(isOverhang(ssBases[i].value) ? '' : String(nucleotideCounter))],
205
206
  {style: {width: '20px'}})!,
206
207
  ui.block75([ssBases[i]])!,
207
208
  ui.div([ssPtoLinkages[i]])!,