From 2295a6e84c52d0335a40f266e9e7ae23da47c1f0 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 17 Sep 2015 10:42:56 -0700 Subject: [PATCH] Update measurements --- handlers.go | 1 + measurements.go | 106 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 101 insertions(+), 6 deletions(-) diff --git a/handlers.go b/handlers.go index eee446a..73dfd21 100644 --- a/handlers.go +++ b/handlers.go @@ -120,6 +120,7 @@ func Handler() http.Handler { r{handleUpdater(characteristicService), "PUT", "/characteristics/{Id:.+}"}, r{handleLister(measurementService), "GET", "/measurements"}, r{handleGetter(measurementService), "GET", "/measurements/{Id:.+}"}, + r{handleUpdater(measurementService), "PUT", "/measurements/{Id:.+}"}, } for _, route := range routes { diff --git a/measurements.go b/measurements.go index 9c208ab..b2be185 100644 --- a/measurements.go +++ b/measurements.go @@ -34,17 +34,17 @@ func (m *MeasurementBase) PreUpdate(e modl.SqlExecutor) error { type MeasurementService struct{} -// There are three types of supported measurements: fixed-test, free-text, -// & numerical. The table has a constraint that will allow one or the other -// for a particular combination of strain & characteristic, but not both. +// There are three types of supported measurements: fixed-text, free-text, +// & numerical. The table has a constraint that will allow at most one +// for a particular combination of strain & characteristic. // MeasurementBase is what the DB expects to see for inserts/updates type MeasurementBase struct { Id int64 `json:"id,omitempty"` StrainId int64 `db:"strain_id" json:"strain"` CharacteristicId int64 `db:"characteristic_id" json:"characteristic"` TextMeasurementTypeId NullInt64 `db:"text_measurement_type_id" json:"-"` - TxtValue NullString `db:"txt_value" json:"txtValue"` - NumValue NullFloat64 `db:"num_value" json:"numValue"` + TxtValue NullString `db:"txt_value" json:"-"` + NumValue NullFloat64 `db:"num_value" json:"-"` ConfidenceInterval NullFloat64 `db:"confidence_interval" json:"confidenceInterval"` UnitTypeId NullInt64 `db:"unit_type_id" json:"-"` Notes NullString `db:"notes" json:"notes"` @@ -57,12 +57,58 @@ type MeasurementBase struct { type Measurement struct { *MeasurementBase - TextMeasurementType NullString `db:"text_measurement_type_name" json:"textMeasurementType"` + TextMeasurementType NullString `db:"text_measurement_type_name" json:"-"` UnitType NullString `db:"unit_type_name" json:"unitType"` TestMethod NullString `db:"test_method_name" json:"testMethod"` CanEdit bool `db:"-" json:"canEdit"` } +type FakeMeasurement Measurement + +func (m *Measurement) MarshalJSON() ([]byte, error) { + fm := FakeMeasurement(*m) + return json.Marshal(struct { + *FakeMeasurement + Value string `json:"value"` + }{ + FakeMeasurement: &fm, + Value: m.Value(), + }) +} + +func (m *Measurement) UnmarshalJSON(b []byte) error { + var measurement struct { + FakeMeasurement + Value interface{} `json:"value"` + } + if err := json.Unmarshal(b, &measurement); err != nil { + return err + } + + switch v := measurement.Value.(type) { + case string: + // Test if actually a lookup + id, err := getTextMeasurementTypeId(v) + if err != nil { + if err == sql.ErrNoRows { + measurement.TxtValue = NullString{sql.NullString{String: v, Valid: true}} + } else { + return err + } + } else { + measurement.TextMeasurementTypeId = NullInt64{sql.NullInt64{Int64: id, Valid: true}} + } + case int64: + measurement.NumValue = NullFloat64{sql.NullFloat64{Float64: float64(v), Valid: true}} + case float64: + measurement.NumValue = NullFloat64{sql.NullFloat64{Float64: v, Valid: true}} + } + + *m = Measurement(measurement.FakeMeasurement) + + return nil +} + func (m *Measurement) Value() string { if m.TextMeasurementType.Valid { return m.TextMeasurementType.String @@ -100,6 +146,12 @@ func (m *MeasurementsPayload) marshal() ([]byte, error) { return json.Marshal(m) } +func (s MeasurementService) unmarshal(b []byte) (entity, error) { + var mj MeasurementPayload + err := json.Unmarshal(b, &mj) + return &mj, err +} + type MeasurementListOptions struct { ListOptions Strains []int64 `schema:"strain_ids"` @@ -162,6 +214,38 @@ func (m MeasurementService) get(id int64, genus string, claims *Claims) (entity, return &payload, nil } +func (s MeasurementService) update(id int64, e *entity, genus string, claims *Claims) *appError { + payload := (*e).(*MeasurementPayload) + payload.Measurement.UpdatedBy = claims.Sub + payload.Measurement.Id = id + + if payload.Measurement.TextMeasurementType.Valid { + id, err := getTextMeasurementTypeId(payload.Measurement.TextMeasurementType.String) + if err != nil { + return newJSONError(err, http.StatusInternalServerError) + } + payload.Measurement.TextMeasurementTypeId.Int64 = id + payload.Measurement.TextMeasurementTypeId.Valid = true + } + + count, err := DBH.Update(payload.Measurement.MeasurementBase) + if err != nil { + return newJSONError(err, http.StatusInternalServerError) + } + if count != 1 { + return newJSONError(ErrStrainNotUpdated, http.StatusBadRequest) + } + + measurement, err := getMeasurement(id, genus, claims) + if err != nil { + return newJSONError(err, http.StatusInternalServerError) + } + + payload.Measurement = measurement + + return nil +} + func listMeasurements(opt MeasurementListOptions, claims *Claims) (*Measurements, error) { var vals []interface{} @@ -257,3 +341,13 @@ func characteristicOptsFromMeasurements(opt MeasurementListOptions) (*ListOption func strainOptsFromMeasurements(opt MeasurementListOptions) (*ListOptions, error) { return &ListOptions{Genus: opt.Genus, Ids: opt.Strains}, nil } + +func getTextMeasurementTypeId(val string) (int64, error) { + var id int64 + q := `SELECT id FROM text_measurement_types WHERE text_measurement_name=$1;` + + if err := DBH.SelectOne(&id, q, val); err != nil { + return 0, err + } + return id, nil +}