201 lines
4.5 KiB
Go
201 lines
4.5 KiB
Go
// utils/calculations.go
|
|
package utils
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
)
|
|
|
|
// Observation struct with json.Number for flexible value handling
|
|
type Observation struct {
|
|
ObsKey map[string]string `json:"ObsKey"`
|
|
ObsValue struct {
|
|
Value json.Number `json:"Value"`
|
|
} `json:"ObsValue"`
|
|
}
|
|
|
|
type Request struct {
|
|
Formula string `json:"formula"`
|
|
RecordCount string `json:"record_count"`
|
|
RecodeIndex string `json:"record_index"`
|
|
RecordKey string `json:"record_key"`
|
|
NumberFormat string `json:"number_format"`
|
|
Conversion string `json:"conversion"`
|
|
}
|
|
|
|
func Calculate(data []Observation, req Request) (map[string]string, error) {
|
|
// Sort the data in descending order based on TimePeriod
|
|
sort.Slice(data, func(i, j int) bool {
|
|
return data[i].ObsKey["TIME_PERIOD"] > data[j].ObsKey["TIME_PERIOD"]
|
|
})
|
|
|
|
var selectedData []Observation
|
|
if req.RecodeIndex != "" {
|
|
// Convert RecodeIndex to an integer
|
|
index, err := strconv.Atoi(req.RecodeIndex)
|
|
if err != nil || index < 0 || index >= len(data) {
|
|
return nil, errors.New("invalid record index")
|
|
}
|
|
// Select only the requested index record
|
|
selectedData = []Observation{data[index]}
|
|
|
|
} else {
|
|
// Determine the number of records to use
|
|
n := len(data) // array leangth
|
|
if req.RecordCount != "all" {
|
|
var err error
|
|
n, err = strconv.Atoi(req.RecordCount)
|
|
if err != nil || n > len(data) {
|
|
return nil, errors.New("invalid record count")
|
|
}
|
|
}
|
|
selectedData = data[:n]
|
|
}
|
|
|
|
var result float64
|
|
switch req.Formula {
|
|
case "none":
|
|
result = none(selectedData)
|
|
case "total":
|
|
result = sum(selectedData)
|
|
case "average":
|
|
result = average(selectedData)
|
|
case "highest":
|
|
result = highest(selectedData)
|
|
case "lowest":
|
|
result = lowest(selectedData)
|
|
case "different":
|
|
result = difference(selectedData)
|
|
default:
|
|
return nil, errors.New("invalid formula")
|
|
}
|
|
|
|
convertedValue := applyConversion(result, req.Conversion, req.NumberFormat)
|
|
displayValue := extractRecordKeyValue(selectedData, req.RecordKey, req.Formula, result)
|
|
|
|
return map[string]string{
|
|
"value": convertedValue,
|
|
"display_value": displayValue,
|
|
}, nil
|
|
}
|
|
|
|
// Helper function to convert json.Number to float64 safely
|
|
func getFloatValue(num json.Number) float64 {
|
|
val, err := num.Float64()
|
|
if err != nil {
|
|
fmt.Println("Error converting value:", err)
|
|
return 0
|
|
}
|
|
return val
|
|
}
|
|
|
|
func none(data []Observation) float64 {
|
|
total := 0.0
|
|
for _, obs := range data {
|
|
total += getFloatValue(obs.ObsValue.Value)
|
|
}
|
|
return total
|
|
}
|
|
|
|
func sum(data []Observation) float64 {
|
|
total := 0.0
|
|
for _, obs := range data {
|
|
total += getFloatValue(obs.ObsValue.Value)
|
|
}
|
|
return total
|
|
}
|
|
|
|
func average(data []Observation) float64 {
|
|
if len(data) == 0 {
|
|
return 0
|
|
}
|
|
return sum(data) / float64(len(data))
|
|
}
|
|
|
|
func highest(data []Observation) float64 {
|
|
max := getFloatValue(data[0].ObsValue.Value)
|
|
for _, obs := range data {
|
|
val := getFloatValue(obs.ObsValue.Value)
|
|
if val > max {
|
|
max = val
|
|
}
|
|
}
|
|
return max
|
|
}
|
|
|
|
func lowest(data []Observation) float64 {
|
|
min := getFloatValue(data[0].ObsValue.Value)
|
|
for _, obs := range data {
|
|
val := getFloatValue(obs.ObsValue.Value)
|
|
if val < min {
|
|
min = val
|
|
}
|
|
}
|
|
return min
|
|
}
|
|
|
|
func difference(data []Observation) float64 {
|
|
if len(data) < 2 {
|
|
return 0
|
|
}
|
|
latest := getFloatValue(data[0].ObsValue.Value)
|
|
previous := getFloatValue(data[1].ObsValue.Value)
|
|
if previous == 0 {
|
|
return 0
|
|
}
|
|
return ((latest - previous) / previous) * 100
|
|
}
|
|
|
|
func applyConversion(value float64, conversion string, numberFormat string) string {
|
|
|
|
if numberFormat == "M" {
|
|
value *= 1000000
|
|
} else if numberFormat == "T" {
|
|
value *= 1000000000000
|
|
} else if numberFormat == "K" {
|
|
value *= 1000
|
|
}
|
|
|
|
switch conversion {
|
|
case "M":
|
|
return fmt.Sprintf("%.2fM", value/1000000)
|
|
case "T":
|
|
return fmt.Sprintf("%.2fT", value/1000000000000)
|
|
case "K":
|
|
return fmt.Sprintf("%.2fK", value/1000)
|
|
case "%":
|
|
return fmt.Sprintf("%.2f%%", value)
|
|
default:
|
|
return fmt.Sprintf("%.2f", value)
|
|
}
|
|
}
|
|
|
|
func extractRecordKeyValue(data []Observation, key string, formula string, targetValue float64) string {
|
|
if len(data) == 0 {
|
|
return ""
|
|
}
|
|
|
|
switch formula {
|
|
case "highest", "lowest", "none":
|
|
for _, obs := range data {
|
|
value, err := obs.ObsValue.Value.Float64()
|
|
if err == nil && value == targetValue {
|
|
if val, exists := obs.ObsKey[key]; exists {
|
|
return val
|
|
}
|
|
}
|
|
}
|
|
case "total", "average", "different":
|
|
if val1, exists1 := data[0].ObsKey[key]; exists1 {
|
|
if val2, exists2 := data[len(data)-1].ObsKey[key]; exists2 {
|
|
return val1 + " - " + val2
|
|
}
|
|
return val1
|
|
}
|
|
}
|
|
return ""
|
|
}
|