diff --git a/chart/.helmignore b/chart/.helmignore deleted file mode 100644 index 0e8a0eb36f4ca2c939201c0d54b5d82a1ea34778..0000000000000000000000000000000000000000 --- a/chart/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/chart/Chart.yaml b/chart/Chart.yaml deleted file mode 100644 index 4acf55a2d44bae7a4ff6157f1b1646c4725a823d..0000000000000000000000000000000000000000 --- a/chart/Chart.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v2 -name: dawn-gdd -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "0.1.0" diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl deleted file mode 100644 index 57ac2c921b4bc3ac122010da99311443b6595f3d..0000000000000000000000000000000000000000 --- a/chart/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "dawn-gateway.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "dawn-gateway.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "dawn-gateway.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "dawn-gateway.labels" -}} -helm.sh/chart: {{ include "dawn-gateway.chart" . }} -{{ include "dawn-gateway.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "dawn-gateway.selectorLabels" -}} -app.kubernetes.io/name: {{ include "dawn-gateway.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "dawn-gateway.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "dawn-gateway.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/chart/templates/configMap.yaml b/chart/templates/configMap.yaml deleted file mode 100644 index fb0c1d2ba5a5142b4cb4f2b21eb29ea2d5dea469..0000000000000000000000000000000000000000 --- a/chart/templates/configMap.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: dawn-gdd-conf - namespace: {{ .Release.Namespace }} -data: - conf: | - app: - name: {{ .Values.conf.app.name }} - logLevel: {{ .Values.conf.app.logLevel }} - logType: {{ .Values.conf.app.logType }} - swagger: {{ .Values.conf.app.swagger }} - auth: {{ .Values.conf.app.auth }} - swagger-host-url: {{ .Values.conf.app.swaggerHostUrl }} - api-version: {{ .Values.conf.app.apiVersion }} - - server: - host: {{ .Values.conf.server.host }} - port: {{ .Values.conf.server.port }} - context-path: {{ .Values.conf.server.contextPath }} - - db: - uri: {{ .Values.conf.db.uri }} - database: {{ .Values.conf.db.database }} - \ No newline at end of file diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml deleted file mode 100644 index 940317e0f2d2e0a9a9d15159431ee0cd015cafbc..0000000000000000000000000000000000000000 --- a/chart/templates/deployment.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: apps/v1 # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1 -kind: Deployment -metadata: - name: dawn-gdd - namespace: {{ .Release.Namespace }} - labels: - version: {{ .Chart.AppVersion }} -spec: - selector: - matchLabels: - service: dawn-gdd - env: {{ .Release.Namespace }} - replicas: 1 - template: - metadata: - labels: - service: dawn-gdd - env: {{ .Release.Namespace }} - version: {{ .Chart.AppVersion }} - annotations: - checksum/config: {{ include (print $.Template.BasePath "/configMap.yaml") . | sha256sum }} - spec: - containers: - - name: dawn-gdd - image: docker.registry.dawn.int:5000/dawn/dawn-gdd:{{.Chart.AppVersion}} - resources: - requests: - cpu: {{.Values.resources.requests.cpu}} - memory: {{.Values.resources.requests.memory}} - volumeMounts: - - name: conf - mountPath: "/root/config/conf" - subPath: conf - readOnly: true - args: - - --config - - conf - ports: - - containerPort: {{.Values.port}} - volumes: - - name: conf - configMap: - name: dawn-gdd-conf diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml deleted file mode 100644 index af3b224540c79d4d587395d0015ce81f02460c6c..0000000000000000000000000000000000000000 --- a/chart/templates/service.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: dawn-gdd - namespace: {{ .Release.Namespace }} - labels: - service: dawn-gdd - env: {{ .Release.Namespace }} -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: {{.Values.port}} - selector: - service: dawn-gdd - env: {{ .Release.Namespace }} \ No newline at end of file diff --git a/chart/values.yaml b/chart/values.yaml deleted file mode 100644 index 069af186c43979595bee83a47a763a1d24e85387..0000000000000000000000000000000000000000 --- a/chart/values.yaml +++ /dev/null @@ -1,25 +0,0 @@ -port: 5000 -resources: - requests: - cpu: 100m - memory: 100Mi - -# default for staging -conf: - app: - name: gdd-service - logType: json - logLevel: DEBUG - swagger: true - auth: false - swaggerHostUrl: "localhost:5000" - apiVersion: 1 - - server: - host: "localhost" - port: 5000 - contextPath: "/api/weather" - - db: - uri: "mongodb://deployment-internal-mongo.deployment-internal.svc.cluster.local:27017/" - database: "weather-service" diff --git a/services/forecast_service.go b/services/forecast_service.go index d83bebba1f99b84c9e325b2f06e06699e1a43609..800af411a96b66888576869c179ad191c28de2cd 100644 --- a/services/forecast_service.go +++ b/services/forecast_service.go @@ -1,249 +1,251 @@ -package services - -import ( - "math" - "sort" - "time" - - "github.com/montanaflynn/stats" - "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/models/enums" - "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/persistence" - "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/persistence/entities" - "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/utils" -) - -func GetStageYearData(ctx common.DawnCtx, request models.GddRequest, comparison int) models.StageData { - product := enums.GetProductFromString(request.Product) - gddData := persistence.CurrentGddFindFirstByYearAndLocation(ctx, request.BuildLocation()) - gdds := utils.CalculateGddValues(gddData.MinTemps, gddData.MaxTemps, product, false) - request.Year = gddData.AnalogYear - - var gs []entities.Gdd - norms := persistence.GetLastNormalsYearly(request.BuildLocation()) - - if comparison == -1 { - gs = norms - } else { - gs = []entities.Gdd{persistence.GddFindFirstByYearAndLocation(comparison, request.BuildLocation())} - } - var normalMeanNonAcc []float64 - comparisonRows := [][]float64{} - - for i := 0; i < len(gs[0].MinTemps); i++ { - rowComp := []float64{} - rowNormal := []float64{} - for j := 0; j < len(gs); j++ { - rowComp = append(rowComp, utils.CalculateSingleGdd(gs[j].MinTemps[i], gs[j].MaxTemps[i], product)) - } - for j := 0; j < len(norms); j++ { - rowNormal = append(rowNormal, utils.CalculateSingleGdd(norms[j].MinTemps[i], norms[j].MaxTemps[i], product)) - } - comparisonRows = append(comparisonRows, rowComp) - normMeanNoAccValue, _ := stats.Mean(rowNormal) - normalMeanNonAcc = append(normalMeanNonAcc, normMeanNoAccValue) - } - - allCfs := persistence.CfsFindByLocationMultiple(request.BuildLocation(), 4) - // cfsMeans := persistence.CfsFindAllByLocation(request.BuildLocation()) - - gddArr := [][]float64{} - for i, c := range allCfs { - gddArr = append(gddArr, gdds) - cfsGddData := utils.CalculateGddValues(c.MinTemps, c.MaxTemps, product, false) // not accumulated - // anomaly adjustment function - // cfsGddData := utils.CalculateGddValuesCfsNormed(c.MinTemps, c.MaxTemps, product, cfsMeans.MinTemps, cfsMeans.MaxTemps, normalMeanNonAcc) // not accumulated - gddArr[i] = append(gddArr[i], cfsGddData...) - if len(gddArr[i]) > len(normalMeanNonAcc) { - gddArr[i] = gddArr[i][:len(normalMeanNonAcc)] - } else { - gddArr[i] = append(gddArr[i], normalMeanNonAcc[len(gddArr[i]):]...) - } - } - // none of this data is accumulated - returnData := models.StageData{ - AllGdds: gddArr, - ComparisonAll: comparisonRows, - } - return returnData -} - -func CalculateStages(ctx common.DawnCtx, request models.StageRequest) map[string]models.Bins { - gddReq := models.GddRequest{ - Year: request.PlantDate.Year(), - Latitude: request.Latitude, - Longitude: request.Longitude, - Accumulate: false, - Product: "CORN", - } - fyData := GetStageYearData(ctx, gddReq, request.Comparison) - - start := request.PlantDate.YearDay() - year := request.PlantDate.Year() - if year%4 == 0 && year%100 != 0 || year%400 == 0 { - start -= 1 - } - - state := map[string]models.StageStateInner{} - stageMatches := models.BuildStageMatches(request.Mode, request.Value, start, fyData, request) - - accs := make([]float64, len(fyData.AllGdds)) - accs2 := make([]float64, len(fyData.ComparisonAll[0])) - accNormal := 0.0 - for i := start; i < len(fyData.AllGdds[0]); i++ { - - for r, v := range fyData.AllGdds { - accs[r] += v[i] - } - for j := 0; j < len(fyData.ComparisonAll[0]); j++ { - accs2[j] += fyData.ComparisonAll[i][j] - } - - normal, _ := stats.Mean(accs2) - - accNormal = normal - - for stage, stageVal := range stageMatches { - dists := make([]float64, len(fyData.AllGdds)) - for r, v := range accs { - dists[r] = math.Abs(stageVal - v) - } - if val, ok := state[stage]; !ok { - - state[stage] = models.StageStateInner{ - Dists: dists, - Hists: make([]int, len(fyData.AllGdds)), - NormalMeanDist: 1000000, - NormalMeanIdx: 0, - } - } else { - normalMeanDist := math.Abs(stageVal - accNormal) - - if normalMeanDist < val.NormalMeanDist { - val.NormalMeanDist = normalMeanDist - val.NormalMeanIdx = i - } - - for r := range accs { - if dists[r] < val.Dists[r] { - val.Hists[r] = i - val.Dists[r] = dists[r] - } - } - state[stage] = val - } - } - - } - ret := BinStageMatches(state, year, start, request.PlantDate) - return ret - -} - -func AvgDiff(data []int) float64 { - sort.Ints(data) - sum := 0.0 - c := 0 - for i := 0; i < len(data)-1; i++ { - diff := math.Abs(float64(data[i] - data[i+1])) - sum += diff - c += 1 - } - return sum / float64(c) -} -func Min(data []int) int { - sort.Ints(data) - return data[0] -} - -func BinStageMatches(stageState map[string]models.StageStateInner, year int, start int, plantDate time.Time) map[string]models.Bins { - response := map[string]models.Bins{} - alpha := 1.0 - add := 0 - if year%4 == 0 && year%100 != 0 || year%400 == 0 { - add -= 1 - } - - for state, stateVal := range stageState { - // min := stateVal.Normal95thIdx - min := Min(stateVal.Hists) - stepSize := int(math.Ceil(AvgDiff(stateVal.Hists)) + 1) // add 1 to increase range (cheating a little) and for uncertainty - arr := []float64{} - idxs := []int{} - base := min - total := 0 - for i := 0; i < 5; i++ { - count := 0.0 - for _, h := range stateVal.Hists { - if base <= h && h < base+stepSize { - count += 1 - total += 1 - } - } - idxs = append(idxs, base) - arr = append(arr, count) - base += stepSize - } - inner := models.Bins{} - inner.Bins = []models.Bin{} - for i := 0; i < 5; i++ { - idx := idxs[i] + add - date := plantDate.AddDate(0, 0, idx-start) - val := arr[i] - smoothedVal := (val + alpha) / (float64(total) + 5*alpha) // modified version of laplace smoothing to remove 0% - inner.Bins = append(inner.Bins, models.Bin{ - Date: date, - Value: smoothedVal, - }) - } - inner.ComparisonMean = plantDate.AddDate(0, 0, stateVal.NormalMeanIdx-start) - inner.Count = total - response[state] = inner - } - return response -} - -func ForecastFirstLastFreeze(ctx common.DawnCtx, request models.FreezingForecastRequest) models.FreezingForecastResponse { - lastFreezeIdx := 0 - firstFreezeIdx := 0 - - baseData := persistence.CurrentGddFindFirstByYearAndLocation(ctx, models.BuildLocation(request.Latitude, request.Longitude)) - cfsData := persistence.CfsFindAllByLocation(models.BuildLocation(request.Latitude, request.Longitude)) - normalsData := persistence.NormalsFindFirstByYearAndLocation(models.BuildLocation(request.Latitude, request.Longitude)) - - cfsData.MinTemps = append(baseData.MinTemps, cfsData.MinTemps...) - - if len(cfsData.MinTemps) < len(normalsData.MinTemps) { - smallerNormalRegion := normalsData.MinTemps[len(cfsData.MinTemps):] - cfsData.MinTemps = append(cfsData.MinTemps, smallerNormalRegion...) - } - - startDate := time.Date(time.Now().Year(), time.January, 1, 0, 0, 0, 0, time.UTC) - - firstHalfFirstDate := time.Date(time.Now().Year(), time.January, 1, 0, 0, 0, 0, time.UTC) - firstHalfLastDate := time.Date(time.Now().Year(), time.July, 31, 0, 0, 0, 0, time.UTC) - - lastHalfFirstDate := time.Date(time.Now().Year(), time.August, 1, 0, 0, 0, 0, time.UTC) - lastHalfLastDate := time.Date(time.Now().Year(), time.December, 31, 0, 0, 0, 0, time.UTC) - - for i := 0; i < len(cfsData.MinTemps); i++ { - currentDate := startDate.AddDate(0, 0, i) - if cfsData.MinTemps[i] <= request.FreezingTemp && currentDate.After(firstHalfFirstDate) && currentDate.Before(firstHalfLastDate) { - lastFreezeIdx = i - } - if cfsData.MinTemps[i] <= request.FreezingTemp && currentDate.After(lastHalfFirstDate) && currentDate.Before(lastHalfLastDate) && firstFreezeIdx == 0 { - firstFreezeIdx = i - break - } - } - - lastFreezeDate := startDate.AddDate(0, 0, lastFreezeIdx) - firstFreezeDate := startDate.AddDate(0, 0, firstFreezeIdx) - - return models.FreezingForecastResponse{ - LastFreeze: []time.Time{lastFreezeDate}, - FirstFreeze: []time.Time{firstFreezeDate}, - } - -} +package services + +import ( + "math" + "sort" + "time" + + "github.com/montanaflynn/stats" + "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/models/enums" + "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/persistence" + "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/persistence/entities" + "gitlab.cs.umd.edu/dawn/go-backend/dawn-gdd/utils" +) + +func GetStageYearData(ctx common.DawnCtx, request models.GddRequest, comparison int) models.StageData { + product := enums.GetProductFromString(request.Product) + gddData := persistence.CurrentGddFindFirstByYearAndLocation(ctx, request.BuildLocation()) + gdds := utils.CalculateGddValues(gddData.MinTemps, gddData.MaxTemps, product, false) + request.Year = gddData.AnalogYear + + var gs []entities.Gdd + norms := persistence.GetLastNormalsYearly(request.BuildLocation()) + + if comparison == -1 { + gs = norms + } else { + gs = []entities.Gdd{persistence.GddFindFirstByYearAndLocation(comparison, request.BuildLocation())} + } + var normalMeanNonAcc []float64 + comparisonRows := [][]float64{} + + for i := 0; i < len(gs[0].MinTemps); i++ { + rowComp := []float64{} + rowNormal := []float64{} + for j := 0; j < len(gs); j++ { + rowComp = append(rowComp, utils.CalculateSingleGdd(gs[j].MinTemps[i], gs[j].MaxTemps[i], product)) + } + for j := 0; j < len(norms); j++ { + rowNormal = append(rowNormal, utils.CalculateSingleGdd(norms[j].MinTemps[i], norms[j].MaxTemps[i], product)) + } + comparisonRows = append(comparisonRows, rowComp) + normMeanNoAccValue, _ := stats.Mean(rowNormal) + normalMeanNonAcc = append(normalMeanNonAcc, normMeanNoAccValue) + } + + allCfs := persistence.CfsFindByLocationMultiple(request.BuildLocation(), 4) + // cfsMeans := persistence.CfsFindAllByLocation(request.BuildLocation()) + + gddArr := [][]float64{} + for i, c := range allCfs { + gddArr = append(gddArr, gdds) + cfsGddData := utils.CalculateGddValues(c.MinTemps, c.MaxTemps, product, false) // not accumulated + // anomaly adjustment function + // cfsGddData := utils.CalculateGddValuesCfsNormed(c.MinTemps, c.MaxTemps, product, cfsMeans.MinTemps, cfsMeans.MaxTemps, normalMeanNonAcc) // not accumulated + gddArr[i] = append(gddArr[i], cfsGddData...) + if len(gddArr[i]) > len(normalMeanNonAcc) { + gddArr[i] = gddArr[i][:len(normalMeanNonAcc)] + } else { + gddArr[i] = append(gddArr[i], normalMeanNonAcc[len(gddArr[i]):]...) + } + } + // none of this data is accumulated + returnData := models.StageData{ + AllGdds: gddArr, + ComparisonAll: comparisonRows, + } + return returnData +} + +func CalculateStages(ctx common.DawnCtx, request models.StageRequest) map[string]models.Bins { + gddReq := models.GddRequest{ + Year: request.PlantDate.Year(), + Latitude: request.Latitude, + Longitude: request.Longitude, + Accumulate: false, + Product: "CORN", + } + fyData := GetStageYearData(ctx, gddReq, request.Comparison) + + start := request.PlantDate.YearDay() + year := request.PlantDate.Year() + if year%4 == 0 && year%100 != 0 || year%400 == 0 { + start -= 1 + } + + state := map[string]models.StageStateInner{} + stageMatches := models.BuildStageMatches(request.Mode, request.Value, start, fyData, request) + + accs := make([]float64, len(fyData.AllGdds)) + accs2 := make([]float64, len(fyData.ComparisonAll[0])) + accNormal := 0.0 + for i := start; i < len(fyData.AllGdds[0]); i++ { + + for r, v := range fyData.AllGdds { + accs[r] += v[i] + } + for j := 0; j < len(fyData.ComparisonAll[0]); j++ { + accs2[j] += fyData.ComparisonAll[i][j] + } + + normal, _ := stats.Mean(accs2) + + accNormal = normal + + for stage, stageVal := range stageMatches { + dists := make([]float64, len(fyData.AllGdds)) + for r, v := range accs { + dists[r] = math.Abs(stageVal - v) + } + if val, ok := state[stage]; !ok { + + state[stage] = models.StageStateInner{ + Dists: dists, + Hists: make([]int, len(fyData.AllGdds)), + NormalMeanDist: 1000000, + NormalMeanIdx: 0, + } + } else { + normalMeanDist := math.Abs(stageVal - accNormal) + + if normalMeanDist < val.NormalMeanDist { + val.NormalMeanDist = normalMeanDist + val.NormalMeanIdx = i + } + + for r := range accs { + if dists[r] < val.Dists[r] { + val.Hists[r] = i + val.Dists[r] = dists[r] + } + } + state[stage] = val + } + } + + } + ret := BinStageMatches(state, year, start, request.PlantDate) + return ret + +} + +func AvgDiff(data []int) float64 { + sort.Ints(data) + sum := 0.0 + c := 0 + for i := 0; i < len(data)-1; i++ { + diff := math.Abs(float64(data[i] - data[i+1])) + sum += diff + c += 1 + } + return sum / float64(c) +} +func Min(data []int) int { + sort.Ints(data) + return data[0] +} + +func BinStageMatches(stageState map[string]models.StageStateInner, year int, start int, plantDate time.Time) map[string]models.Bins { + response := map[string]models.Bins{} + alpha := 1.0 + add := 0 + if year%4 == 0 && year%100 != 0 || year%400 == 0 { + add -= 1 + } + + binCount := 3 + + for state, stateVal := range stageState { + // min := stateVal.Normal95thIdx + min := Min(stateVal.Hists) + stepSize := int(math.Ceil(AvgDiff(stateVal.Hists)) + 1) // add 1 to increase range (cheating a little) and for uncertainty + arr := []float64{} + idxs := []int{} + base := min + total := 0 + for i := 0; i < binCount; i++ { + count := 0.0 + for _, h := range stateVal.Hists { + if base <= h && h < base+stepSize { + count += 1 + total += 1 + } + } + idxs = append(idxs, base) + arr = append(arr, count) + base += stepSize + } + inner := models.Bins{} + inner.Bins = []models.Bin{} + for i := 0; i < binCount; i++ { + idx := idxs[i] + add + date := plantDate.AddDate(0, 0, idx-start) + val := arr[i] + smoothedVal := (val + alpha) / (float64(total) + float64(binCount)*alpha) // modified version of laplace smoothing to remove 0% + inner.Bins = append(inner.Bins, models.Bin{ + Date: date, + Value: smoothedVal, + }) + } + inner.ComparisonMean = plantDate.AddDate(0, 0, stateVal.NormalMeanIdx-start) + inner.Count = total + response[state] = inner + } + return response +} + +func ForecastFirstLastFreeze(ctx common.DawnCtx, request models.FreezingForecastRequest) models.FreezingForecastResponse { + lastFreezeIdx := 0 + firstFreezeIdx := 0 + + baseData := persistence.CurrentGddFindFirstByYearAndLocation(ctx, models.BuildLocation(request.Latitude, request.Longitude)) + cfsData := persistence.CfsFindAllByLocation(models.BuildLocation(request.Latitude, request.Longitude)) + normalsData := persistence.NormalsFindFirstByYearAndLocation(models.BuildLocation(request.Latitude, request.Longitude)) + + cfsData.MinTemps = append(baseData.MinTemps, cfsData.MinTemps...) + + if len(cfsData.MinTemps) < len(normalsData.MinTemps) { + smallerNormalRegion := normalsData.MinTemps[len(cfsData.MinTemps):] + cfsData.MinTemps = append(cfsData.MinTemps, smallerNormalRegion...) + } + + startDate := time.Date(time.Now().Year(), time.January, 1, 0, 0, 0, 0, time.UTC) + + firstHalfFirstDate := time.Date(time.Now().Year(), time.January, 1, 0, 0, 0, 0, time.UTC) + firstHalfLastDate := time.Date(time.Now().Year(), time.July, 31, 0, 0, 0, 0, time.UTC) + + lastHalfFirstDate := time.Date(time.Now().Year(), time.August, 1, 0, 0, 0, 0, time.UTC) + lastHalfLastDate := time.Date(time.Now().Year(), time.December, 31, 0, 0, 0, 0, time.UTC) + + for i := 0; i < len(cfsData.MinTemps); i++ { + currentDate := startDate.AddDate(0, 0, i) + if cfsData.MinTemps[i] <= request.FreezingTemp && currentDate.After(firstHalfFirstDate) && currentDate.Before(firstHalfLastDate) { + lastFreezeIdx = i + } + if cfsData.MinTemps[i] <= request.FreezingTemp && currentDate.After(lastHalfFirstDate) && currentDate.Before(lastHalfLastDate) && firstFreezeIdx == 0 { + firstFreezeIdx = i + break + } + } + + lastFreezeDate := startDate.AddDate(0, 0, lastFreezeIdx) + firstFreezeDate := startDate.AddDate(0, 0, firstFreezeIdx) + + return models.FreezingForecastResponse{ + LastFreeze: []time.Time{lastFreezeDate}, + FirstFreeze: []time.Time{firstFreezeDate}, + } + +} diff --git a/spec.yaml b/spec.yaml deleted file mode 100644 index 8517e4b8dc73940f79a61d42aedb42d801ab8495..0000000000000000000000000000000000000000 --- a/spec.yaml +++ /dev/null @@ -1,8 +0,0 @@ -kind: product -name: dawn-gdd ---- -kind: release -product-name: dawn-gdd -product-version: 0.1.0 -release-channel: DEV -helm-chart: dawn/dawn-gdd \ No newline at end of file