├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── manifest.json
└── index.html
├── src
├── setupTests.js
├── App.test.js
├── reportWebVitals.js
├── index.css
├── index.js
├── App.css
├── logo.svg
└── App.js
├── README.md
├── .gitignore
├── package.json
└── LICENSE
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theosanderson/Codon2Nucleotide/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theosanderson/Codon2Nucleotide/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/theosanderson/Codon2Nucleotide/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Codon2Nucleotide
2 |
3 | ## http://codon2nucleotide.theo.io/
4 |
5 |
6 | ## Development
7 |
8 | This tool was originally developed by Theo Sanderson. [Zach Hensel](https://www.itqb.unl.pt/research/biology/single-molecule-microbiology/zach-hensel-lab) provided additional functionality for conversion from coordinates in ORF1ab to coordinates in indidual NSP proteins.
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | font-size:1em;
9 | color:#333
10 | }
11 |
12 | code {
13 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
14 | monospace;
15 | }
16 |
17 | input{width:5em}
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codon2nucleotide",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "4.0.3",
12 | "web-vitals": "^1.0.1"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": [
22 | "react-app",
23 | "react-app/jest"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Theo Sanderson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .bolded{font-weight: bold;}
6 | .App-logo {
7 | height: 40vmin;
8 | pointer-events: none;
9 | }
10 |
11 | @media (prefers-reduced-motion: no-preference) {
12 | .App-logo {
13 | animation: App-logo-spin infinite 20s linear;
14 | }
15 | }
16 |
17 | .App-header {
18 | background-color: #282c34;
19 | min-height: 100vh;
20 | display: flex;
21 | flex-direction: column;
22 | align-items: center;
23 | justify-content: center;
24 | font-size: calc(10px + 2vmin);
25 | color: white;
26 | }
27 |
28 | .App-link {
29 | color: #61dafb;
30 | }
31 |
32 | @keyframes App-logo-spin {
33 | from {
34 | transform: rotate(0deg);
35 | }
36 | to {
37 | transform: rotate(360deg);
38 | }
39 | }
40 |
41 | .flex-container {
42 | display: flex;
43 | max-width: 800px;
44 | margin: 0 auto;
45 | }
46 |
47 | .flex-child {
48 | flex: 1;
49 | border: 2px solid #eee;
50 | padding: 10px;
51 | padding-bottom: 50px;
52 | margin-top: 20px;
53 | padding-top: 0px;
54 | }
55 |
56 | .flex-child:first-child {
57 | margin-right: 20px;
58 | }
59 |
60 | .flex-child:nth-child(2) {
61 | margin-right: 20px;
62 | }
63 |
64 | * {
65 | font-size: 18px;
66 | }
67 | h3 {
68 | font-size: 24px;
69 | }
70 | input {
71 | padding: 3px;
72 | width: 150px;
73 | }
74 | select {
75 | padding: 3px;
76 | }
77 |
78 | h1 {
79 | font-size: 30px;
80 | }
81 | .to_sec {
82 | min-width: 5em;
83 | display: inline-block;
84 | }
85 |
86 | button {
87 | font-size: 40%;
88 | /* remove all style and format as link*/
89 | border: none;
90 | background: none;
91 | cursor: pointer;
92 | padding: 0;
93 | font-size: inherit;
94 | color: rgb(167, 167, 167);
95 | text-decoration: underline;
96 | }
97 |
98 | .margined{
99 | margin-top:50px;
100 | }
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
36 | Codon2Nucleotide
37 |
38 |
39 |
40 |
41 |
42 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import './App.css';
4 |
5 | const genes = {
6 | 'ORF1a': [266, 13467],
7 | 'ORF1b': [13468, 21556],
8 | 'S': [21563, 25385],
9 | 'ORF3a': [25393, 26221],
10 | 'E': [26245, 26473],
11 | 'M': [26523, 27192],
12 | 'ORF6': [27202, 27388],
13 | 'ORF7a': [27394, 27760],
14 | 'ORF7b': [27756, 27888],
15 | 'ORF8': [27894, 28260],
16 | 'N': [28274, 29534],
17 | 'ORF10': [29558, 29675]
18 | }
19 |
20 | // const nsps = { // from uniprot P0DTD1 orf1ab numbering of nsps
21 | // 'nsp1': [1, 180],
22 | // 'nsp2': [181, 818],
23 | // 'nsp3': [819, 2763],
24 | // 'nsp4': [2764, 3263],
25 | // 'nsp5 (Mpro)': [3264, 3569],
26 | // 'nsp6': [3570, 3859],
27 | // 'nsp7': [3860, 3942],
28 | // 'nsp8': [3943, 4140],
29 | // 'nsp9': [4141, 4253],
30 | // 'nsp10': [4254, 4392],
31 | // 'nsp12 (RdRp)': [4393, 5324],
32 | // 'nsp13': [5325, 5925],
33 | // 'nsp14': [5926, 6452],
34 | // 'nsp15': [6453, 6798],
35 | // 'nsp16': [6799, 7096]
36 | // }
37 |
38 | const nsps = { // from uniprot P0DTD1 orf1ab numbering of nsps
39 | 'nsp1': 1,
40 | 'nsp2': 181,
41 | 'nsp3': 819,
42 | 'nsp4': 2764,
43 | 'nsp5 (Mpro)': 3264,
44 | 'nsp6': 3570,
45 | 'nsp7': 3860,
46 | 'nsp8': 3943,
47 | 'nsp9': 4141,
48 | 'nsp10': 4254,
49 | 'nsp12 (RdRp)': 4393,
50 | 'nsp13': 5325,
51 | 'nsp14': 5926,
52 | 'nsp15': 6453,
53 | 'nsp16': 6799
54 | }
55 |
56 | function App() {
57 |
58 | let [codon, setCodon] = useState(484);
59 | let [nucleotide, setNucleotide] = useState(null);
60 | let [gene, setGene] = useState("S")
61 | let [nsp, setNsp] = useState("nsp6")
62 | let [themode,setthemode] = useState("nucleotide")
63 | let end_nucleotide = ""
64 |
65 | if (nucleotide == null) {
66 | const start_nucleotide = genes[gene][0] + (codon - 1) * 3
67 | end_nucleotide = start_nucleotide + 2
68 | nucleotide = start_nucleotide
69 | }
70 |
71 | if (codon == null) {
72 | const matches = Object.keys(genes).filter(x => genes[x][0] <= nucleotide & genes[x][1] >= nucleotide)
73 | if (matches.length === 1) {
74 | console.log(matches)
75 |
76 | gene = matches[0]
77 | const the_start = genes[gene][0]
78 | codon = Math.floor((nucleotide - the_start) / 3) + 1
79 | }
80 | else {
81 | codon = "!!"
82 | }
83 | }
84 |
85 | let [orf1aorb, setOrf1aOrb] = useState(null);
86 | let [orf1aorbcodon, setORF1aorBCodon] = useState(null);
87 | let [nspCodon, setNspCodon] = useState(null);
88 | let [combinedCodon,setCombinedCodon] = useState(3646);
89 |
90 | if (combinedCodon == null) {
91 | if (orf1aorbcodon == null) {
92 |
93 | combinedCodon = nsps[nsp] + nspCodon - 1;
94 |
95 | if(combinedCodon > 4401) {
96 | orf1aorbcodon = nsps[nsp]+nspCodon-1-4401;
97 | orf1aorb = "ORF1b";
98 | }
99 | else {
100 | orf1aorbcodon = nsps[nsp]+nspCodon-1;
101 | orf1aorb = "ORF1a";
102 | }
103 |
104 | }
105 | combinedCodon = orf1aorbcodon + (orf1aorb === "ORF1b" ? 4401 : 0)
106 | }
107 | else{
108 | if(combinedCodon > 4401){
109 | orf1aorbcodon = combinedCodon - 4401
110 | orf1aorb = "ORF1b"
111 | }
112 | else{
113 | orf1aorb = "ORF1a"
114 | orf1aorbcodon = combinedCodon
115 |
116 | }
117 | }
118 |
119 | if (nspCodon == null) {
120 |
121 | nsp="nsp1"
122 | nspCodon=combinedCodon - nsps[nsp] + 1
123 |
124 | for (const [key, value] of Object.entries(nsps)) {
125 | if (combinedCodon > value && combinedCodon - value < combinedCodon - nsps[nsp]) {
126 | nsp = key;
127 | nspCodon = combinedCodon - value + 1;
128 | }
129 | }
130 | }
131 |
132 | if (themode === "nucleotide") {
133 | if( ["ORF1a","ORF1b"].includes(gene)){
134 | if (orf1aorb !== gene){
135 | setOrf1aOrb(gene)
136 | setCombinedCodon(null)
137 | console.log("b")
138 | }
139 | if(orf1aorbcodon !== codon && !(isNaN(codon)&& isNaN(orf1aorb))) {
140 | console.log("c",codon,orf1aorbcodon)
141 |
142 | setORF1aorBCodon(codon)
143 | setCombinedCodon(null)
144 | }
145 | }
146 |
147 | }
148 | else{
149 | if(gene!==orf1aorb){
150 | console.log("d")
151 | setGene(orf1aorb)
152 | setNucleotide(null)
153 | }
154 | if (codon !== orf1aorbcodon && !(isNaN(codon)&& isNaN(orf1aorbcodon))){
155 | console.log("e")
156 | setCodon(orf1aorbcodon)
157 | setNucleotide(null)
158 | }
159 |
160 | }
161 |
162 | return (
163 |
164 |
Codon2Nucelotide
165 | {themode==="nucleotide"&&
166 | <>
167 |
Convert from SARS-CoV-2 codon position to genomic coordinates or vice versa
168 |
>
171 | }
172 |
173 | {themode==="orf1ab"&&
174 | <>
175 |
176 |
179 |
Convert between ORF1a/b, ORF1ab, and polyprotein nsps
180 | >
181 | }
182 |
183 |
184 |
185 |
186 | {themode==="nucleotide"&&
187 |
188 |
189 |
190 |
191 |
Codon
192 |
Gene:
197 |
198 | Codon:
{ setCodon(parseInt(e.target.value)); setNucleotide(null) }}>
199 |
200 |
201 |
202 |
203 |
Nucleotide
204 | Nucleotide:
{ setNucleotide(parseInt(e.target.value)); setCodon(null); }}>
205 |
{end_nucleotide && " to " + end_nucleotide}
206 |
207 |
208 |
209 |
210 | }
211 |
212 | {themode==="orf1ab"&&
213 |
214 |
215 |
216 |
217 |
ORF1a or B
218 |
Gene:
223 |
224 | Codon:
{ setORF1aorBCodon(parseInt(e.target.value)); setOrf1aOrb(orf1aorb); setCombinedCodon(null); setNspCodon(null)}}>
225 |
226 |
227 |
228 |
229 |
230 |
ORF1ab
231 |
232 | Codon: { setCombinedCodon(parseInt(e.target.value)); setORF1aorBCodon(null); setNspCodon(null)}}>
233 |
234 |
235 |
236 |
237 |
238 |
nsp
239 |
240 |
Gene:
245 |
246 | Codon:
{ setNspCodon(parseInt(e.target.value)); setNsp(nsp); setCombinedCodon(null); setORF1aorBCodon(null)}}>
247 |
248 |
249 |
250 |
251 | }
252 |
253 |
254 |
GitHub
255 |
256 |
257 | );
258 | }
259 |
260 | export default App;
261 |
--------------------------------------------------------------------------------