diff --git a/controllers/gdd_controller.go b/controllers/gdd_controller.go index 7c47374c89e2f279de72791678b3da6a9ce0be8c..182b78d5c2b824178ac73896558d597a48b4da73 100644 --- a/controllers/gdd_controller.go +++ b/controllers/gdd_controller.go @@ -1,49 +1,50 @@ -package controllers - -import ( - "github.com/tgs266/dawn-go-common/common" - "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/models" - "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/services" - - "github.com/gofiber/fiber/v2" -) - -var GetGddValues = services.GetGddValues - -// GetDailyGdd godoc -// @Summary Get gdd values -// @Tags Gdd -// @Description get gdd values -// @Accept json -// @Produce json -// @Success 200 {object} models.GddResponse -// @Failure 400 {object} common.StandardError -// @Param year query int true "Year to get gdd for" -// @Param product query string true "Crop to calculate gdd for" Enums(corn, soybean, sunflower, tomato, sugar_beet, peanut, cotton, potato, wheat, pea, oat, spring_wheat, rice, sorghum) -// @Param latitude query number true "Latitude to search for" -// @Param longitude query number true "Longitude to search for" -// @Param accumulate query boolean true "Accumulate gdd values" -// @Router /gdd/daily [get] -func GetDailyGdd(c *fiber.Ctx) error { - ctx := common.DawnCtx{FiberCtx: c} - request := models.BuildGddRequest(ctx.FiberCtx) - return c.Status(fiber.StatusOK).JSON(GetGddValues(ctx, request)) -} - -// GetNormalGdd godoc -// @Summary Get gdd normals -// @Tags Gdd -// @Description get gdd normals -// @Accept json -// @Produce json -// @Success 200 {object} models.GddResponse -// @Failure 400 {object} common.StandardError -// @Param product query string true "Crop to calculate gdd for" Enums(corn, soybean, sunflower, tomato, sugar_beet, peanut, cotton, potato, wheat, pea, oat, spring_wheat, rice, sorghum) -// @Param latitude query number true "Latitude to search for" -// @Param longitude query number true "Longitude to search for" -// @Param accumulate query boolean true "Accumulate gdd values" -// @Router /gdd/normals [get] -func GetNormalGdd(c *fiber.Ctx) error { - request := models.BuildGddRequest(c) - return c.Status(fiber.StatusOK).JSON(services.GetNormalValues(request)) -} +package controllers + +import ( + "github.com/tgs266/dawn-go-common/common" + "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/models" + "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/services" + + "github.com/gofiber/fiber/v2" +) + +var GetGddValues = services.GetGddValues + +// GetDailyGdd godoc +// @Summary Get gdd values +// @Tags Gdd +// @Description get gdd values +// @Accept json +// @Produce json +// @Success 200 {object} models.GddResponse +// @Failure 400 {object} common.StandardError +// @Param year query int true "Year to get gdd for" +// @Param product query string true "Crop to calculate gdd for" Enums(corn, soybean, sunflower, tomato, sugar_beet, peanut, cotton, potato, wheat, pea, oat, spring_wheat, rice, sorghum) +// @Param latitude query number true "Latitude to search for" +// @Param longitude query number true "Longitude to search for" +// @Param accumulate query boolean true "Accumulate gdd values" +// @Param plantingDate query string true "Plant date, ISO8601 or RFC3339 format" +// @Router /gdd/daily [get] +func GetDailyGdd(c *fiber.Ctx) error { + ctx := common.DawnCtx{FiberCtx: c} + request := models.BuildGddRequest(ctx.FiberCtx) + return c.Status(fiber.StatusOK).JSON(GetGddValues(ctx, request)) +} + +// GetNormalGdd godoc +// @Summary Get gdd normals +// @Tags Gdd +// @Description get gdd normals +// @Accept json +// @Produce json +// @Success 200 {object} models.GddResponse +// @Failure 400 {object} common.StandardError +// @Param product query string true "Crop to calculate gdd for" Enums(corn, soybean, sunflower, tomato, sugar_beet, peanut, cotton, potato, wheat, pea, oat, spring_wheat, rice, sorghum) +// @Param latitude query number true "Latitude to search for" +// @Param longitude query number true "Longitude to search for" +// @Param accumulate query boolean true "Accumulate gdd values" +// @Router /gdd/normals [get] +func GetNormalGdd(c *fiber.Ctx) error { + request := models.BuildGddRequest(c) + return c.Status(fiber.StatusOK).JSON(services.GetNormalValues(request)) +} diff --git a/models/gdd.go b/models/gdd.go index 7d71a9fe0e162b4b128371549e3afdea5d614184..1b4f537f2f0715bc4f98d0b5ac591902266efbec 100644 --- a/models/gdd.go +++ b/models/gdd.go @@ -1,92 +1,100 @@ -package models - -import ( - "strconv" - "time" - - "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/config" - "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/persistence/entities" - - validation "github.com/go-ozzo/ozzo-validation" - "github.com/gofiber/fiber/v2" -) - -type GddResponse struct { - Product string `json:"product"` - ClosestLatitude float64 `json:"closest_latitude"` - ClosestLongitude float64 `json:"closest_longitude"` - GddValues []float64 `json:"gdd_values"` - LastDate time.Time `json:"last_date"` -} - -type GddRequest struct { - Year int `json:"year"` - Product string `json:"product"` - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` - Accumulate bool `json:"accumulate"` -} - -func (r GddRequest) Validate() error { - return validation.ValidateStruct(&r, - validation.Field(&r.Year, validation.Required, validation.Min(1981), validation.Max(time.Now().Year())), - validation.Field(&r.Product, validation.Required, validation.Length(1, 100)), - validation.Field(&r.Latitude, validation.Required, validation.Min(-90.0), validation.Max(90.0)), - validation.Field(&r.Longitude, validation.Required, validation.Min(-180.0), validation.Max(180.0)), - ) -} - -func (r GddRequest) ValidateNoYear() error { - return validation.ValidateStruct(&r, - validation.Field(&r.Product, validation.Required, validation.Length(1, 100)), - validation.Field(&r.Latitude, validation.Required, validation.Min(-90.0), validation.Max(90.0)), - validation.Field(&r.Longitude, validation.Required, validation.Min(-180.0), validation.Max(180.0)), - ) -} - -var BuildGddRequest = func(c *fiber.Ctx) GddRequest { - year, errYear := strconv.Atoi(c.Query("year", "0")) - product := c.Query("product") - latitude, errLat := strconv.ParseFloat(c.Query("latitude"), 64) - longitude, errLon := strconv.ParseFloat(c.Query("longitude"), 64) - accumulate, errBool := strconv.ParseBool(c.Query("accumulate", "false")) - - rNew := GddRequest{ - Year: year, - Product: product, - Latitude: latitude, - Longitude: longitude, - Accumulate: accumulate, - } - - if errYear != nil || errLat != nil || errLon != nil || errBool != nil { - panic(config.BAD_REQUEST) - } - - if rNew.Year != 0 { - if e := rNew.Validate(); e != nil { - panic(config.BAD_REQUEST.AddLogDetails(e.Error())) - } - } else { - if e := rNew.ValidateNoYear(); e != nil { - panic(config.BAD_REQUEST.AddLogDetails(e.Error())) - } - } - return rNew -} - -func (r GddRequest) BuildLocation() entities.Location { - l := entities.Location{ - Type: "Point", - Coordinates: []float64{r.Longitude, r.Latitude}, - } - return l -} - -func BuildLocation(lat float64, lng float64) entities.Location { - l := entities.Location{ - Type: "Point", - Coordinates: []float64{lng, lat}, - } - return l -} +package models + +import ( + "strconv" + "time" + + "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/config" + "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/persistence/entities" + + validation "github.com/go-ozzo/ozzo-validation" + "github.com/gofiber/fiber/v2" +) + +type GddResponse struct { + Product string `json:"product"` + ClosestLatitude float64 `json:"closest_latitude"` + ClosestLongitude float64 `json:"closest_longitude"` + GddValues []float64 `json:"gdd_values"` + LastDate time.Time `json:"last_date"` +} + +type GddRequest struct { + Year int `json:"year"` + Product string `json:"product"` + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + Accumulate bool `json:"accumulate"` + PlantingDate time.Time `json:"plantingDate"` +} + +func (r GddRequest) Validate() error { + return validation.ValidateStruct(&r, + validation.Field(&r.Year, validation.Required, validation.Min(1981), validation.Max(time.Now().Year())), + validation.Field(&r.Product, validation.Required, validation.Length(1, 100)), + validation.Field(&r.Latitude, validation.Required, validation.Min(-90.0), validation.Max(90.0)), + validation.Field(&r.Longitude, validation.Required, validation.Min(-180.0), validation.Max(180.0)), + ) +} + +func (r GddRequest) ValidateNoYear() error { + return validation.ValidateStruct(&r, + validation.Field(&r.Product, validation.Required, validation.Length(1, 100)), + validation.Field(&r.Latitude, validation.Required, validation.Min(-90.0), validation.Max(90.0)), + validation.Field(&r.Longitude, validation.Required, validation.Min(-180.0), validation.Max(180.0)), + ) +} + +var BuildGddRequest = func(c *fiber.Ctx) GddRequest { + year, errYear := strconv.Atoi(c.Query("year", strconv.Itoa(time.Now().Year()))) + product := c.Query("product") + pd := c.Query("plantingDate") + latitude, errLat := strconv.ParseFloat(c.Query("latitude"), 64) + longitude, errLon := strconv.ParseFloat(c.Query("longitude"), 64) + accumulate, errBool := strconv.ParseBool(c.Query("accumulate", "false")) + plantingDate, errDate := time.Parse(time.RFC3339, pd) + + rNew := GddRequest{ + Year: year, + Product: product, + Latitude: latitude, + Longitude: longitude, + Accumulate: accumulate, + PlantingDate: plantingDate, + } + + if errYear != nil || errLat != nil || errLon != nil || errBool != nil { + panic(config.BAD_REQUEST) + } + + if errDate != nil && pd != "" { + panic(config.BAD_REQUEST.PutDetail("reason", "date must be ISO8601 or RFC3339 format")) + } + + if rNew.Year != 0 { + if e := rNew.Validate(); e != nil { + panic(config.BAD_REQUEST.AddLogDetails(e.Error())) + } + } else { + if e := rNew.ValidateNoYear(); e != nil { + panic(config.BAD_REQUEST.AddLogDetails(e.Error())) + } + } + return rNew +} + +func (r GddRequest) BuildLocation() entities.Location { + l := entities.Location{ + Type: "Point", + Coordinates: []float64{r.Longitude, r.Latitude}, + } + return l +} + +func BuildLocation(lat float64, lng float64) entities.Location { + l := entities.Location{ + Type: "Point", + Coordinates: []float64{lng, lat}, + } + return l +} diff --git a/services/gdd_service.go b/services/gdd_service.go index 7b170d52d23f3980429223a8a4f7b51d0dc905d3..c6adc8d4a5a7c751ec3ba557f584dae14ccd4714 100644 --- a/services/gdd_service.go +++ b/services/gdd_service.go @@ -41,6 +41,13 @@ func GetGddValues(ctx common.DawnCtx, request models.GddRequest) models.GddRespo } else { gdds = persistence.GddFindFirstByYearAndLocation(request.Year, request.BuildLocation()) } + + if !request.PlantingDate.IsZero() && request.Year >= time.Now().Year() { + pdInt := request.PlantingDate.YearDay() - 1 + gdds.MaxTemps = gdds.MaxTemps[pdInt:] + gdds.MinTemps = gdds.MinTemps[pdInt:] + } + returnGdds := models.GddResponse{ Product: product.Name, ClosestLatitude: gdds.Location.Coordinates[1],