Add compare handler
This commit is contained in:
parent
ec93617ca8
commit
763c1f77d1
3 changed files with 169 additions and 3 deletions
119
compare.go
Normal file
119
compare.go
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/csv"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleCompare(w http.ResponseWriter, r *http.Request) *appError {
|
||||||
|
// types
|
||||||
|
type Comparisions map[string]map[string]string
|
||||||
|
type ComparisionsJSON [][]string
|
||||||
|
|
||||||
|
// vars
|
||||||
|
mimeType := r.FormValue("mimeType")
|
||||||
|
if mimeType == "" {
|
||||||
|
mimeType = "json"
|
||||||
|
}
|
||||||
|
claims := getClaims(r)
|
||||||
|
var header string
|
||||||
|
var data []byte
|
||||||
|
|
||||||
|
// Get measurements for comparision
|
||||||
|
measService := MeasurementService{}
|
||||||
|
opt := r.URL.Query()
|
||||||
|
opt.Del("mimeType")
|
||||||
|
opt.Del("token")
|
||||||
|
opt.Add("Genus", mux.Vars(r)["genus"])
|
||||||
|
measurementsEntity, appErr := measService.list(&opt, &claims)
|
||||||
|
if appErr != nil {
|
||||||
|
return appErr
|
||||||
|
}
|
||||||
|
measurementsPayload := (measurementsEntity).(*MeasurementsPayload)
|
||||||
|
|
||||||
|
// Assemble matrix
|
||||||
|
characteristic_ids := strings.Split(opt.Get("characteristic_ids"), ",")
|
||||||
|
strain_ids := strings.Split(opt.Get("strain_ids"), ",")
|
||||||
|
|
||||||
|
comparisions := make(Comparisions)
|
||||||
|
for _, characteristic_id := range characteristic_ids {
|
||||||
|
characteristic_id_int, _ := strconv.ParseInt(characteristic_id, 10, 0)
|
||||||
|
values := make(map[string]string)
|
||||||
|
for _, strain_id := range strain_ids {
|
||||||
|
strain_id_int, _ := strconv.ParseInt(strain_id, 10, 0)
|
||||||
|
for _, m := range *measurementsPayload.Measurements {
|
||||||
|
if (m.CharacteristicId == characteristic_id_int) && (m.StrainId == strain_id_int) {
|
||||||
|
values[strain_id] = m.Value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
comparisions[characteristic_id] = values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return, based on mimetype
|
||||||
|
switch mimeType {
|
||||||
|
case "json":
|
||||||
|
header = "application/json"
|
||||||
|
|
||||||
|
comparisionsJSON := make(ComparisionsJSON, 0)
|
||||||
|
for _, characteristic_id := range characteristic_ids {
|
||||||
|
row := []string{characteristic_id}
|
||||||
|
for _, strain_id := range strain_ids {
|
||||||
|
row = append(row, comparisions[characteristic_id][strain_id])
|
||||||
|
}
|
||||||
|
comparisionsJSON = append(comparisionsJSON, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, _ = json.Marshal(comparisionsJSON)
|
||||||
|
case "csv":
|
||||||
|
header = "text/csv"
|
||||||
|
|
||||||
|
// maps to translate ids
|
||||||
|
strains := make(map[string]string)
|
||||||
|
for _, strain := range *measurementsPayload.Strains {
|
||||||
|
strains[fmt.Sprintf("%d", strain.Id)] = strain.StrainName
|
||||||
|
}
|
||||||
|
characteristics := make(map[string]string)
|
||||||
|
for _, characteristic := range *measurementsPayload.Characteristics {
|
||||||
|
characteristics[fmt.Sprintf("%d", characteristic.Id)] = characteristic.CharacteristicName
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
wr := csv.NewWriter(b)
|
||||||
|
|
||||||
|
// Write header row
|
||||||
|
r := []string{""}
|
||||||
|
for _, strain_id := range strain_ids {
|
||||||
|
r = append(r, strains[strain_id])
|
||||||
|
}
|
||||||
|
wr.Write(r)
|
||||||
|
|
||||||
|
// Write data
|
||||||
|
for key, record := range comparisions {
|
||||||
|
r := []string{characteristics[key]}
|
||||||
|
for _, val := range record {
|
||||||
|
r = append(r, val)
|
||||||
|
}
|
||||||
|
wr.Write(r)
|
||||||
|
}
|
||||||
|
wr.Flush()
|
||||||
|
|
||||||
|
data = b.Bytes()
|
||||||
|
|
||||||
|
w.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="compare-%d.csv"`, int32(time.Now().Unix())))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap it up
|
||||||
|
w.Header().Set("Content-Type", header)
|
||||||
|
w.Write(data)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -92,6 +92,8 @@ func Handler() http.Handler {
|
||||||
s.Handle("/users/verify/{Nonce}", errorHandler(handleUserVerify)).Methods("GET")
|
s.Handle("/users/verify/{Nonce}", errorHandler(handleUserVerify)).Methods("GET")
|
||||||
s.Handle("/users/lockout", errorHandler(handleUserLockout)).Methods("POST")
|
s.Handle("/users/lockout", errorHandler(handleUserLockout)).Methods("POST")
|
||||||
|
|
||||||
|
s.Handle("/compare", j.Secure(errorHandler(handleCompare), verifyClaims)).Methods("GET")
|
||||||
|
|
||||||
type r struct {
|
type r struct {
|
||||||
f errorHandler
|
f errorHandler
|
||||||
m string
|
m string
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
@ -62,6 +63,19 @@ type Measurement struct {
|
||||||
CanEdit bool `db:"-" json:"canEdit"`
|
CanEdit bool `db:"-" json:"canEdit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Measurement) Value() string {
|
||||||
|
if m.TextMeasurementType.Valid {
|
||||||
|
return m.TextMeasurementType.String
|
||||||
|
}
|
||||||
|
if m.TxtValue.Valid {
|
||||||
|
return m.TxtValue.String
|
||||||
|
}
|
||||||
|
if m.NumValue.Valid {
|
||||||
|
return fmt.Sprintf("%f", m.NumValue.Float64)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type Measurements []*Measurement
|
type Measurements []*Measurement
|
||||||
|
|
||||||
type MeasurementMeta struct {
|
type MeasurementMeta struct {
|
||||||
|
@ -72,9 +86,10 @@ type MeasurementPayload struct {
|
||||||
Measurement *Measurement `json:"measurement"`
|
Measurement *Measurement `json:"measurement"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add related models
|
|
||||||
type MeasurementsPayload struct {
|
type MeasurementsPayload struct {
|
||||||
Measurements *Measurements `json:"measurements"`
|
Strains *Strains `json:"strains"`
|
||||||
|
Characteristics *Characteristics `json:"characteristics"`
|
||||||
|
Measurements *Measurements `json:"measurements"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MeasurementPayload) marshal() ([]byte, error) {
|
func (m *MeasurementPayload) marshal() ([]byte, error) {
|
||||||
|
@ -105,8 +120,30 @@ func (m MeasurementService) list(val *url.Values, claims *Claims) (entity, *appE
|
||||||
return nil, newJSONError(err, http.StatusInternalServerError)
|
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char_opts, err := characteristicOptsFromMeasurements(opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
characteristics, err := listCharacteristics(*char_opts, claims)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
strain_opts, err := strainOptsFromMeasurements(opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
strains, err := listStrains(*strain_opts, claims)
|
||||||
|
if err != nil {
|
||||||
|
return nil, newJSONError(err, http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
payload := MeasurementsPayload{
|
payload := MeasurementsPayload{
|
||||||
Measurements: measurements,
|
Characteristics: characteristics,
|
||||||
|
Strains: strains,
|
||||||
|
Measurements: measurements,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &payload, nil
|
return &payload, nil
|
||||||
|
@ -212,3 +249,11 @@ func getMeasurement(id int64, genus string, claims *Claims) (*Measurement, error
|
||||||
|
|
||||||
return &measurement, nil
|
return &measurement, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func characteristicOptsFromMeasurements(opt MeasurementListOptions) (*ListOptions, error) {
|
||||||
|
return &ListOptions{Genus: opt.Genus, Ids: opt.Characteristics}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func strainOptsFromMeasurements(opt MeasurementListOptions) (*ListOptions, error) {
|
||||||
|
return &ListOptions{Genus: opt.Genus, Ids: opt.Strains}, nil
|
||||||
|
}
|
||||||
|
|
Reference in a new issue