diff --git a/compare.go b/compare.go new file mode 100644 index 0000000..3e26302 --- /dev/null +++ b/compare.go @@ -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 +} diff --git a/handlers.go b/handlers.go index 57fa72a..4268e0b 100644 --- a/handlers.go +++ b/handlers.go @@ -92,6 +92,8 @@ func Handler() http.Handler { s.Handle("/users/verify/{Nonce}", errorHandler(handleUserVerify)).Methods("GET") s.Handle("/users/lockout", errorHandler(handleUserLockout)).Methods("POST") + s.Handle("/compare", j.Secure(errorHandler(handleCompare), verifyClaims)).Methods("GET") + type r struct { f errorHandler m string diff --git a/measurements.go b/measurements.go index 4ff82bc..4385f38 100644 --- a/measurements.go +++ b/measurements.go @@ -4,6 +4,7 @@ import ( "database/sql" "encoding/json" "errors" + "fmt" "net/http" "net/url" @@ -62,6 +63,19 @@ type Measurement struct { 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 MeasurementMeta struct { @@ -72,9 +86,10 @@ type MeasurementPayload struct { Measurement *Measurement `json:"measurement"` } -// TODO: Add related models 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) { @@ -105,8 +120,30 @@ func (m MeasurementService) list(val *url.Values, claims *Claims) (entity, *appE 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{ - Measurements: measurements, + Characteristics: characteristics, + Strains: strains, + Measurements: measurements, } return &payload, nil @@ -212,3 +249,11 @@ func getMeasurement(id int64, genus string, claims *Claims) (*Measurement, error 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 +}