forked from bog2n/science-cup
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
219 lines (193 loc) · 4.86 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package main
import (
"compress/gzip"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"mime"
"net/http"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"github.com/bog2n/science-cup/aminoacids"
"github.com/bog2n/science-cup/frontend"
"github.com/bog2n/science-cup/image"
"github.com/bog2n/science-cup/ribosome"
)
// Config variables
var (
bindAddress string
port uint
noBrowser bool
bindString string
maxUploadSize int64
)
type Response struct {
Ok bool `json:"ok"`
Proteins []Data `json:"proteins"`
}
type Data struct {
Protein string `json:"protein"`
Mass float64 `json:"mass"`
HydroIndex float64 `json:"hindex"`
Isoelectric float64 `json:"isopoint"`
PH float64 `json:"ph"`
Polarity float64 `json:"polarity"`
}
// Used to move data from goroutine
type result struct {
data []Data
err error
}
// Handles opening browser on startup
func openBrowser(url string) {
var err error
switch runtime.GOOS {
case "linux":
err = exec.Command("xdg-open", url).Start()
case "windows":
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
case "darwin":
err = exec.Command("open", url).Start()
default:
err = fmt.Errorf("unsupported platform")
}
if err != nil {
log.Fatal(err)
}
}
// Used to return error
func handleDataError(w http.ResponseWriter) {
w.Header().Set("Content-type", "application/json")
b, _ := json.Marshal(Response{Ok: false, Proteins: []Data{}})
w.Write(b)
}
// Data processing function
func processData(genome string, c chan result) {
out := new(result)
prot, err := ribosome.GetAminoAcids(genome)
if err != nil {
out.err = err
c <- *out
return
}
for i := range prot {
out.data = append(out.data, Data{
Protein: prot[i],
Mass: aminoacids.CalculateMass(prot[i]),
HydroIndex: aminoacids.CalculateHydroIndex(prot[i]),
Isoelectric: aminoacids.CalculatePI(prot[i]),
PH: aminoacids.CalculatePH(prot[i]),
Polarity: aminoacids.CalculatePolarity(prot[i]),
})
}
c <- *out
}
// Handles finding all proteins in RNA/DNA sequence
func handleData(w http.ResponseWriter, r *http.Request) {
var genome string
var out Response
out.Ok = true
genome = r.FormValue("genome")
// String not found, try to fetch file
if genome == "" {
if err := r.ParseMultipartForm(maxUploadSize << 20); err != nil {
log.Print("Error while parsing form data ", err)
handleDataError(w)
return
}
file, _, err := r.FormFile("file")
if err != nil {
log.Print("Error while getting the file ", err)
handleDataError(w)
return
}
defer file.Close()
b, err := ioutil.ReadAll(file)
if err != nil {
log.Print("Error while fetching the file ", err)
handleDataError(w)
return
}
genome = string(b)
}
res := make(chan result)
// Start processing data for 3 offsets
for i := 0; i < 3; i++ {
go processData(genome[i:], res)
}
// Receive data from goroutines
for i := 0; i < 3; i++ {
d := <-res
out.Proteins = append(out.Proteins, d.data...)
// Unknown codon, clear everything
if d.err == ribosome.UnknownCodonError {
out.Ok = false
out.Proteins = []Data{}
log.Print("Error while processing data ", d.err)
break
}
}
// No proteins where found return false
if len(out.Proteins) == 0 {
out.Ok = false
out.Proteins = []Data{}
}
// Send data
b, _ := json.Marshal(out)
w.Header().Set("Content-type", "application/json")
w.Write(b)
}
// Handles /image endpoint for generating images
func handleImage(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-type", "image/svg+xml")
// Gzip if necessary
if strings.Contains(r.Header.Get("Accept-encoding"), "gzip") {
g := gzip.NewWriter(w)
defer g.Close()
w.Header().Set("Content-encoding", "gzip")
image.DrawProtein(r.URL.Path[len("/image/"):], g)
} else {
image.DrawProtein(r.URL.Path[len("/image/"):], w)
}
}
// I guess this is actual frontend
func handleMain(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
if path == "/" {
path = "/index.html"
}
path = "out" + path
b, err := frontend.Content.ReadFile(path)
if err != nil {
w.WriteHeader(404)
w.Write(frontend.NotFound)
return
}
ext := filepath.Ext(path)
m := mime.TypeByExtension(ext)
w.Header().Set("Content-type", m)
w.Write(b)
}
func init() {
// Parse cli flags
flag.StringVar(&bindAddress, "b", "127.0.0.1", "Address to listen on")
flag.UintVar(&port, "p", 8080, "Port to listen on")
flag.BoolVar(&noBrowser, "nobrowser", false, "Do not open browser on startup")
flag.Int64Var(&maxUploadSize, "max", 128, "Max upload size in MB")
flag.Parse()
bindString = bindAddress + ":" + strconv.Itoa(int(port))
}
func main() {
if !noBrowser {
openBrowser("http://" + bindString)
}
http.HandleFunc("/", handleMain)
http.HandleFunc("/api/data", handleData)
http.HandleFunc("/api/image/", handleImage)
log.Fatal(http.ListenAndServe(bindString, nil))
}