package analize import ( "errors" "fmt" "math" "net/http" "strconv" "strings" "control/control_case" "github.com/che4web/go4rest" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type AnalizeController struct { *go4rest.ViewSet[Analize] db *gorm.DB } func NewAnalizeController(db *gorm.DB) *AnalizeController { return &AnalizeController{ ViewSet: go4rest.NewViewSet[Analize](db), db: db, } } type AnalizeSeriesPoint struct { CaseID uint `json:"case_id"` CaseName string `json:"case_name"` X float64 `json:"x"` Omega float64 `json:"omega"` PsiMax float64 `json:"psi_max"` } type AnalizeSeriesGroup struct { GroupValue float64 `json:"group_value"` GroupLabel string `json:"group_label"` Points []AnalizeSeriesPoint `json:"points"` } type AnalizeSeriesResponse struct { Parameter string `json:"parameter"` Label string `json:"label"` GroupParameter string `json:"group_parameter,omitempty"` GroupLabel string `json:"group_label,omitempty"` Points []AnalizeSeriesPoint `json:"points,omitempty"` Groups []AnalizeSeriesGroup `json:"groups,omitempty"` } var analizeParameterColumns = map[string]struct { Column string Label string }{ "Rel": {Column: "rel", Label: "Rel"}, "RelC": {Column: "rel_c", Label: "RelC"}, "Le": {Column: "le", Label: "Le"}, "Pr": {Column: "sc", Label: "Sc"}, "Sc": {Column: "sc", Label: "Sc"}, "Pe": {Column: "pe", Label: "Pe"}, "Ma": {Column: "ma", Label: "Ma"}, "Time": {Column: "time", Label: "Time"}, } func (c *AnalizeController) Series(ctx *gin.Context) { param := ctx.DefaultQuery("parameter", "Rel") selected, ok := analizeParameterColumns[param] if !ok { ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid parameter"}) return } groupParam := ctx.Query("group_parameter") groupSelected, groupOK := analizeParameterColumns[groupParam] if groupParam != "" && !groupOK { ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid group parameter"}) return } filterParam := ctx.Query("filter_parameter") filterSelected, filterOK := analizeParameterColumns[filterParam] filterMin := ctx.Query("filter_min") filterMax := ctx.Query("filter_max") type row struct { CaseID uint `gorm:"column:case_id"` CaseName string `gorm:"column:case_name"` GroupVal float64 `gorm:"column:group_val"` X float64 `gorm:"column:x"` Omega float64 `gorm:"column:omega"` PsiMax float64 `gorm:"column:psi_max"` } groupExpr := groupExpr(groupParam, groupSelected.Column) query := fmt.Sprintf(` SELECT a.case_id, a.case_name, %s AS group_val, p.%s AS x, a.omega, a.psi_max FROM analizes a JOIN control_cases c ON c.id = a.case_id JOIN params p ON p.id = c.params_id `, groupExpr, selected.Column) where := make([]string, 0, 2) args := make([]any, 0, 2) if filterParam != "" { if !filterOK { ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid filter parameter"}) return } if filterMin != "" { where = append(where, fmt.Sprintf("p.%s >= ?", filterSelected.Column)) args = append(args, filterMin) } if filterMax != "" { where = append(where, fmt.Sprintf("p.%s <= ?", filterSelected.Column)) args = append(args, filterMax) } } if len(where) > 0 { query += " WHERE " + strings.Join(where, " AND ") } if groupParam != "" { query += fmt.Sprintf(" ORDER BY ROUND(p.%s, 6), p.%s, a.id", groupSelected.Column, selected.Column) } else { query += fmt.Sprintf(" ORDER BY p.%s, a.id", selected.Column) } var rows []row if err := c.db.Raw(query, args...).Scan(&rows).Error; err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } points := make([]AnalizeSeriesPoint, 0, len(rows)) groups := make([]AnalizeSeriesGroup, 0) if groupParam == "" { for _, r := range rows { points = append(points, AnalizeSeriesPoint{ CaseID: r.CaseID, CaseName: r.CaseName, X: r.X, Omega: r.Omega, PsiMax: r.PsiMax, }) } } else { groupIndex := map[string]int{} for _, r := range rows { groupValue := round6(r.GroupVal) key := fmt.Sprintf("%.6f", groupValue) idx, ok := groupIndex[key] if !ok { idx = len(groups) groupIndex[key] = idx groups = append(groups, AnalizeSeriesGroup{ GroupValue: groupValue, GroupLabel: fmt.Sprintf("%s=%.6f", groupSelected.Label, groupValue), Points: []AnalizeSeriesPoint{}, }) } groups[idx].Points = append(groups[idx].Points, AnalizeSeriesPoint{ CaseID: r.CaseID, CaseName: r.CaseName, X: r.X, Omega: r.Omega, PsiMax: r.PsiMax, }) } } ctx.JSON(http.StatusOK, AnalizeSeriesResponse{ Parameter: param, Label: selected.Label, GroupParameter: groupParam, GroupLabel: groupSelected.Label, Points: points, Groups: groups, }) } func groupExpr(groupParam, groupColumn string) string { if groupParam == "" { return "0" } return fmt.Sprintf("ROUND(p.%s, 6)", groupColumn) } func round6(v float64) float64 { return math.Round(v*1e6) / 1e6 } func (c *AnalizeController) Recalculate(ctx *gin.Context) { id, err := strconv.ParseUint(ctx.Param("id"), 10, 32) if err != nil { ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"}) return } var item Analize if err := c.db.First(&item, id).Error; err != nil { ctx.JSON(http.StatusNotFound, gin.H{"error": "record not found"}) return } var controlCase control_case.ControlCase if err := c.db.First(&controlCase, item.CaseID).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { ctx.JSON(http.StatusNotFound, gin.H{"error": "control case not found"}) } else { ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } return } csvPath := controlCase.FooCSVPath() updated, err := AnalyzeCSV(c.db, item.CaseID, item.CaseName, csvPath) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } ctx.JSON(http.StatusOK, updated) } func (c *AnalizeController) RecalculateControlCaseAnalysis(ctx *gin.Context) { id, err := strconv.ParseUint(ctx.Param("id"), 10, 32) if err != nil { ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"}) return } var controlCase control_case.ControlCase if err := c.db.First(&controlCase, id).Error; err != nil { ctx.JSON(http.StatusNotFound, gin.H{"error": "record not found"}) return } result, err := AnalyzeCSV(c.db, controlCase.ID, controlCase.Name, controlCase.FooCSVPath()) if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } ctx.JSON(http.StatusOK, result) }