Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
gdd.go 4.92 KiB
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"
	"gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/utils"

	validation "github.com/go-ozzo/ozzo-validation"
	"github.com/gofiber/fiber/v2"
	"github.com/tgs266/dawn-go-common/errors"
)

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 {
	Product      string    `json:"product"`
	Latitude     float64   `json:"latitude"`
	Longitude    float64   `json:"longitude"`
	Accumulate   bool      `json:"accumulate"`
	PlantingDate time.Time `json:"plantingDate"`
}

type NormalsGddRequest struct {
	Product      string    `json:"product"`
	Latitude     float64   `json:"latitude"`
	Longitude    float64   `json:"longitude"`
	Accumulate   bool      `json:"accumulate"`
	PlantingDate time.Time `json:"plantingDate"`
	RepeatYears  int       `json:"repeatYears"`
}

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", utils.GetFirstOfTheYear().Format(time.RFC3339))
	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)

	if errYear != nil || errLat != nil || errLon != nil || errBool != nil {
		panic(config.BAD_REQUEST)
	}
	if errDate != nil {
		panic(errors.NewBadRequest(nil).PutDetail("reason", "no planting date provided"))
	}

	if errDate != nil && pd != "" {
		panic(errors.NewBadRequest(nil).PutDetail("reason", "date must be ISO8601 or RFC3339 format"))
	}

	// provided an override year
	// used mostly for doing analog years where we dont provide a date
	// if the years are different and the planting date year is this year, we override
	if year != plantingDate.Year() && plantingDate.Year() == time.Now().Year() {
		plantingDate = utils.GetFirstOfTheYearForYear(year)
	}

	rNew := GddRequest{
		Product:      product,
		Latitude:     latitude,
		Longitude:    longitude,
		Accumulate:   accumulate,
		PlantingDate: plantingDate,
	}

	if e := rNew.Validate(); e != nil {
		panic(errors.NewBadRequest(e).AddLogDetails(e.Error()))
	}
	return rNew
}

var BuildNormalsGddRequest = func(c *fiber.Ctx) (GddRequest, int) {
	gddReq := BuildGddRequest(c)
	repeatYears, errYears := strconv.Atoi(c.Query("repeatYears", "1"))

	if errYears != nil {
		panic(errors.NewBadRequest(nil).PutDetail("reason", "repeat years must be an integer"))
	}

	if repeatYears < 1 || repeatYears > 3 {
		panic(errors.NewBadRequest(nil).PutDetail("reason", "repeat years must be between 1 and 3 inclusive"))
	}

	return gddReq, repeatYears

}

var BuildYearlessGddRequest = func(c *fiber.Ctx) GddRequest {
	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"))

	if errLat != nil || errLon != nil || errBool != nil {
		panic(config.BAD_REQUEST)
	}

	rNew := GddRequest{
		Product:    product,
		Latitude:   latitude,
		Longitude:  longitude,
		Accumulate: accumulate,
	}

	if e := rNew.ValidateNoYear(); e != nil {
		panic(errors.NewBadRequest(e).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
}