diff --git a/persistence/repositories/gdd.go b/persistence/repositories/gdd.go index bd358373299d6c8eb6c2b8e6eeaaa72f648b3991..b7ff7934503c9ca51c9ed87d25ff897a341bbbf0 100644 --- a/persistence/repositories/gdd.go +++ b/persistence/repositories/gdd.go @@ -13,9 +13,16 @@ import ( ) type GddRepository interface { + // FindCurrentGddByLocation finds the current GDD value for a given location. FindCurrentGddByLocation(ctx common.DawnCtx, location entities.Location) entities.Gdd + + // FindGddByLocationAndYear finds the GDD value for a given location and year. FindGddByLocationAndYear(ctx common.DawnCtx, year int, location entities.Location) entities.Gdd + + // FindGddsOverNormalsRangeByLocation finds the GDD values for a given location over a range of years. FindGddsOverNormalsRangeByLocation(ctx common.DawnCtx, location entities.Location) []entities.Gdd + + // FindAnalogYearByLocation finds the analog year for a given location. FindAnalogYearByLocation(ctx common.DawnCtx, location entities.Location) models.AnalogResponse } @@ -23,116 +30,103 @@ type gddRepositoryImpl struct { session *common.DBSession } +// NewGddRepository creates a new GddRepository instance. func NewGddRepository(session *common.DBSession) *gddRepositoryImpl { return &gddRepositoryImpl{ session: session, } } +// buildLocationRequest builds a BSON filter for a given location and year. +// If the year is nil, only the location filter will be included. func buildLocationRequest(location entities.Location, year *int) bson.M { - var filter bson.M - if year != nil { - filter = bson.M{ - "location": bson.M{ - "$nearSphere": bson.M{ - "$geometry": location, - "$maxDistance": 50000, - }, + filter := bson.M{ + "location": bson.M{ + "$nearSphere": bson.M{ + "$geometry": location, + "$maxDistance": 50000, }, - "year": *year, - } - } else { - filter = bson.M{ - "location": bson.M{ - "$nearSphere": bson.M{ - "$geometry": location, - "$maxDistance": 50000, - }, - }, - } + }, } + + if year != nil { + filter["year"] = *year + } + return filter } func buildLocationRequestLarger(location entities.Location, year *int) bson.M { - var filter bson.M - if year != nil { - filter = bson.M{ - "location": bson.M{ - "$nearSphere": bson.M{ - "$geometry": location, - "$maxDistance": 100000, - }, - }, - "year": *year, - } - } else { - filter = bson.M{ - "location": bson.M{ - "$nearSphere": bson.M{ - "$geometry": location, - "$maxDistance": 100000, - }, + filter := bson.M{ + "location": bson.M{ + "$nearSphere": bson.M{ + "$geometry": location, + "$maxDistance": 100000, }, - } + }, } + + if year != nil { + filter["year"] = *year + } + return filter } +// FindCurrentGddByLocation finds the current Gdd based on the given location. func (g *gddRepositoryImpl) FindCurrentGddByLocation(ctx common.DawnCtx, location entities.Location) entities.Gdd { coll := g.session.Collection("gdd_current") filter := buildLocationRequest(location, nil) var gdd entities.Gdd - err := coll.FindOne(ctx.FiberCtx.Context(), filter).Decode(&gdd) - - if errs.Is(err, mongo.ErrNoDocuments) { - panic(errors.NewNotFound(err).PutDetail("reason", "gdd_current")) - } else if err != nil { + if err := coll.FindOne(ctx.FiberCtx.Context(), filter).Decode(&gdd); err != nil { + if errs.Is(err, mongo.ErrNoDocuments) { + panic(errors.NewNotFound(err).PutDetail("reason", "gdd_current")) + } panic(errors.NewInternal(err)) } - return gdd } +// FindGddByLocationAndYear finds the Gdd based on the given location and year. func (g *gddRepositoryImpl) FindGddByLocationAndYear(ctx common.DawnCtx, year int, location entities.Location) entities.Gdd { coll := g.session.Collection("gdd") filter := buildLocationRequest(location, &year) var gdds entities.Gdd - err := coll.FindOne(ctx.FiberCtx.Context(), filter).Decode(&gdds) - - if errs.Is(err, mongo.ErrNoDocuments) { - panic(errors.NewNotFound(err).PutDetail("reason", "gdd")) - } else if err != nil { + if err := coll.FindOne(ctx.FiberCtx.Context(), filter).Decode(&gdds); err != nil { + if errs.Is(err, mongo.ErrNoDocuments) { + panic(errors.NewNotFound(err).PutDetail("reason", "gdd")) + } panic(errors.NewInternal(err)) } - return gdds } +// FindGddsOverNormalsRangeByLocation returns a slice of Gdd entities for the given location +// within the year range from 1991 to 2020. It panics if an error occurs or if no Gdd entities are found. func (g *gddRepositoryImpl) FindGddsOverNormalsRangeByLocation(ctx common.DawnCtx, location entities.Location) []entities.Gdd { coll := g.session.Collection("gdd") filter := buildLocationRequest(location, nil) filter["year"] = bson.M{"$gte": 1991, "$lte": 2020} - var n []entities.Gdd cursor, err := coll.Find(ctx.FiberCtx.Context(), filter, options.Find().SetLimit(30)) if err != nil { panic(errors.NewInternal(err).PutDetail("reason", "gdd")) } - if err = cursor.All(ctx.FiberCtx.Context(), &n); err != nil { + var gdds []entities.Gdd + if err = cursor.All(ctx.FiberCtx.Context(), &gdds); err != nil { panic(errors.NewInternal(err)) } - if len(n) == 0 { + if len(gdds) == 0 { panic(errors.NewNotFound(nil).PutDetail("reason", "gdd")) } - return n + return gdds } func (g *gddRepositoryImpl) FindAnalogYearByLocation(ctx common.DawnCtx, location entities.Location) models.AnalogResponse { diff --git a/services/gdd_service.go b/services/gdd_service.go index 1de99f5c42529b34ce5cb1fc8ef25651caca4647..1a4ecba64162bcd8a7d7b0f4c0cf30bcbf34e9dc 100644 --- a/services/gdd_service.go +++ b/services/gdd_service.go @@ -59,14 +59,14 @@ func GetNormalValues(ctx common.DawnCtx, request models.GddRequest, repeatYears sliceDateInt = 0 } + row := make([]float64, len(gs)) for i := sliceDateInt; i < len(gs[0].MinTemps)*repeatYears; i++ { //; i++ { - row := []float64{} idx := i if i >= len(gs[0].MinTemps) { idx -= len(gs[0].MinTemps) } for j := 0; j < len(gs); j++ { - row = append(row, utils.CalculateSingleGdd(gs[j].MinTemps[idx], gs[j].MaxTemps[idx], product)) + row[j] = utils.CalculateSingleGdd(gs[j].MinTemps[idx], gs[j].MaxTemps[idx], product) if request.Accumulate && i > sliceDateInt { row[j] += rows[len(rows)-1][j] } diff --git a/utils/gdd_calculations.go b/utils/gdd_calculations.go index a649a085396e45a386c430054ee39f87c43858a1..971c13480acee07aede4476fb5ea4c5def0b1497 100644 --- a/utils/gdd_calculations.go +++ b/utils/gdd_calculations.go @@ -4,33 +4,28 @@ import ( "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/models/enums" ) -func CalculateSingleGdd(minTemp float64, maxTemp float64, product enums.Product) float64 { +// CalculateSingleGdd calculates the Growing Degree Days (GDD) value for a given temperature and product. +func CalculateSingleGdd(minTemp, maxTemp float64, product enums.Product) float64 { + // If the product is corn, clip the minimum and maximum temperature values to a range of 50-86. if product.Name == "CORN" { minTemp = ClipFloat(minTemp, 50, 86) maxTemp = ClipFloat(maxTemp, 50, 86) } + + // Calculate the GDD mean := (maxTemp + minTemp) / 2.0 value := mean - product.BaseTemp + + // If the product is corn, return the value without further processing if product.Name == "CORN" { return value } + + // Clip the value to a minimum of 0 value = ClipMinFloat(value, 0) return value } -func CalculateNormalGddValues(base []float64, product enums.Product, accumulate bool) []float64 { - var returnList []float64 - for i := 0; i < len(base); i++ { - value := base[i] - product.BaseTemp - value = ClipMinFloat(value, 0) - if accumulate && i > 0 { - value += returnList[len(returnList)-1] - } - returnList = append(returnList, value) - } - return returnList -} - func CalculateGddValues(minTemps []float64, maxTemps []float64, product enums.Product, accumulate bool) []float64 { var returnList []float64 for i := 0; i < len(minTemps); i++ { diff --git a/utils/gdd_calculations_test.go b/utils/gdd_calculations_test.go index 1c06303bffd2d7e8886c0b9044f35ba49824869a..1257d62c7b96934565c6efb49412330fd0d3711e 100644 --- a/utils/gdd_calculations_test.go +++ b/utils/gdd_calculations_test.go @@ -71,28 +71,3 @@ func Test_CalculateGddValues_Accumulated(t *testing.T) { assert.Equal(t, expectedGdd, CalculateGddValues(minTempsArr[:], maxTempsArr[:], enums.ProductType.WHEAT, true)) } - -func Test_CalculateNormalGddValues(t *testing.T) { - var expectedGdd []float64 - for i := 0; i < 4; i++ { - val := normalBaseArr[i] - enums.ProductType.PEANUT.BaseTemp - val = ClipMinFloat(val, 0) - expectedGdd = append(expectedGdd, val) - } - - assert.Equal(t, expectedGdd, CalculateNormalGddValues(normalBaseArr[:], enums.ProductType.PEANUT, false)) -} - -func Test_CalculateNormalGddValues_Accumulated(t *testing.T) { - var expectedGdd []float64 - for i := 0; i < 4; i++ { - val := normalBaseArr[i] - enums.ProductType.PEANUT.BaseTemp - val = ClipMinFloat(val, 0) - if i > 0 { - val += expectedGdd[len(expectedGdd)-1] - } - expectedGdd = append(expectedGdd, val) - } - - assert.Equal(t, expectedGdd, CalculateNormalGddValues(normalBaseArr[:], enums.ProductType.PEANUT, true)) -}