From 2e01287a71f53a3ccb0dbf84d844225756fb7bfa Mon Sep 17 00:00:00 2001 From: matze Date: Thu, 24 Apr 2025 15:48:21 +0200 Subject: [PATCH] change log api to reflect data split into base and extension --- wagfarm-api/logs/base/model.go | 26 +----- wagfarm-api/logs/base/route.go | 2 +- wagfarm-api/logs/cropprotection/model.go | 4 +- wagfarm-api/logs/cropprotection/route.go | 4 +- wagfarm-api/logs/fertilizing/model.go | 4 +- wagfarm-api/logs/fertilizing/route.go | 4 +- wagfarm-api/logs/generic/controller.go | 112 +++++++++++++++++++++++ wagfarm-api/logs/generic/model.go | 37 ++++++++ wagfarm-api/logs/generic/route.go | 15 +++ wagfarm-api/logs/harvest/model.go | 4 +- wagfarm-api/logs/harvest/route.go | 4 +- wagfarm-api/logs/seeding/model.go | 4 +- wagfarm-api/logs/seeding/route.go | 4 +- 13 files changed, 182 insertions(+), 42 deletions(-) create mode 100644 wagfarm-api/logs/generic/controller.go create mode 100644 wagfarm-api/logs/generic/model.go create mode 100644 wagfarm-api/logs/generic/route.go diff --git a/wagfarm-api/logs/base/model.go b/wagfarm-api/logs/base/model.go index 86093ba..762a786 100644 --- a/wagfarm-api/logs/base/model.go +++ b/wagfarm-api/logs/base/model.go @@ -2,12 +2,10 @@ package base import ( "time" - "wagfarm-api/assets/machine" "wagfarm-api/database" "wagfarm-api/taxonomy/visibility" "github.com/shopspring/decimal" - "gorm.io/gorm" ) type BaseLog struct { @@ -23,26 +21,4 @@ type BaseLog struct { } func (l BaseLog) GetID() uint { return l.ID } - -type ExtendedLog struct { - ID uint `gorm:"primaryKey"` - BaseLogID uint `gorm:"not null"` - BaseLog BaseLog `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"` -} - -func (l ExtendedLog) GetID() uint { return l.ID } -func (l *ExtendedLog) UpdateCost(db *gorm.DB, newCost decimal.Decimal) (err error) { - err = db.Model(&BaseLog{}). - Where("id = ?", l.BaseLogID). - Update("cost", newCost).Error - return -} - -type FieldWorkLog struct { - ExtendedLog - WorkedAreaPercentage float64 `gorm:"not null"` - MachineID uint `gorm:"not null"` - Machine machine.Machine `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"` -} - -func RegisterModels() { database.RegisterModel(&BaseLog{}) } +func RegisterModels() { database.RegisterModel(&BaseLog{}) } diff --git a/wagfarm-api/logs/base/route.go b/wagfarm-api/logs/base/route.go index 4e5e57c..2c75e11 100644 --- a/wagfarm-api/logs/base/route.go +++ b/wagfarm-api/logs/base/route.go @@ -7,5 +7,5 @@ import ( ) func RegisterRoutes(rg *gin.RouterGroup) { - generic.RegisterCRUDRoutes(rg, generic.DefaultCRUDController[BaseLog]()) + generic.RegisterRRoutes(rg, generic.DefaultRController[BaseLog]()) } diff --git a/wagfarm-api/logs/cropprotection/model.go b/wagfarm-api/logs/cropprotection/model.go index b498b1d..95839dc 100644 --- a/wagfarm-api/logs/cropprotection/model.go +++ b/wagfarm-api/logs/cropprotection/model.go @@ -3,7 +3,7 @@ package cropprotection import ( "wagfarm-api/assets/pesticide" "wagfarm-api/database" - "wagfarm-api/logs/base" + "wagfarm-api/logs/generic" "wagfarm-api/taxonomy/worker" "github.com/shopspring/decimal" @@ -11,7 +11,7 @@ import ( ) type CropProtectionLog struct { - base.FieldWorkLog + generic.FieldWorkLog PesticideID uint `gorm:"not null"` Pesticide pesticide.Pesticide `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"` Amount float64 `gorm:"not null"` // in units of the pesticide diff --git a/wagfarm-api/logs/cropprotection/route.go b/wagfarm-api/logs/cropprotection/route.go index 914e314..0300e0b 100644 --- a/wagfarm-api/logs/cropprotection/route.go +++ b/wagfarm-api/logs/cropprotection/route.go @@ -1,11 +1,11 @@ package cropprotection import ( - "wagfarm-api/generic" + "wagfarm-api/logs/generic" "github.com/gin-gonic/gin" ) func RegisterRoutes(rg *gin.RouterGroup) { - generic.RegisterCRUDRoutes(rg.Group("/cropprotection"), generic.DefaultCRUDController[CropProtectionLog]()) + generic.RegisterCRUDRoutes(rg.Group("/cropprotection"), generic.LogCRUDController[CropProtectionLog]()) } diff --git a/wagfarm-api/logs/fertilizing/model.go b/wagfarm-api/logs/fertilizing/model.go index 575d98d..e426eba 100644 --- a/wagfarm-api/logs/fertilizing/model.go +++ b/wagfarm-api/logs/fertilizing/model.go @@ -3,14 +3,14 @@ package fertilizing import ( "wagfarm-api/assets/fertilizer" "wagfarm-api/database" - "wagfarm-api/logs/base" + "wagfarm-api/logs/generic" "github.com/shopspring/decimal" "gorm.io/gorm" ) type FertilizingLog struct { - base.ExtendedLog + generic.BaseExtendedLog FertilizerID uint `gorm:"not null"` Fertilizer fertilizer.Fertilizer `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"` Amount float64 `gorm:"not null"` // in units of the fertilizer diff --git a/wagfarm-api/logs/fertilizing/route.go b/wagfarm-api/logs/fertilizing/route.go index ea6256d..261f780 100644 --- a/wagfarm-api/logs/fertilizing/route.go +++ b/wagfarm-api/logs/fertilizing/route.go @@ -1,11 +1,11 @@ package fertilizing import ( - "wagfarm-api/generic" + "wagfarm-api/logs/generic" "github.com/gin-gonic/gin" ) func RegisterRoutes(rg *gin.RouterGroup) { - generic.RegisterCRUDRoutes(rg.Group("/fertilizer"), generic.DefaultCRUDController[FertilizingLog]()) + generic.RegisterCRUDRoutes(rg.Group("/fertilizer"), generic.LogCRUDController[FertilizingLog]()) } diff --git a/wagfarm-api/logs/generic/controller.go b/wagfarm-api/logs/generic/controller.go new file mode 100644 index 0000000..ceff708 --- /dev/null +++ b/wagfarm-api/logs/generic/controller.go @@ -0,0 +1,112 @@ +package generic + +import ( + "net/http" + "strconv" + "wagfarm-api/database" + "wagfarm-api/generic" + "wagfarm-api/logs/base" + + "github.com/gin-gonic/gin" +) + +func LogCRUDController[T ExtendedLog]() generic.CRUDController { + return generic.CRUDController{ + RController: generic.DefaultRController[T](), + Create: Create[T], + Update: Update[T], + Delete: Delete[T], + } +} + +// CreateLog handles POST /records and creates a new record record. +func Create[T ExtendedLog](c *gin.Context) { + var base base.BaseLog + var record T + if err := c.ShouldBindJSON(&base); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + if err := database.DB.Create(&base).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create record: " + err.Error()}) + return + } + if err := c.ShouldBindJSON(&record); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + if err := database.DB.Create(&record).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create record: " + err.Error()}) + return + } + + // Optionally preload visibility before returning. + database.Preload().First(&record, record.GetID()) + c.JSON(http.StatusCreated, record) +} + +// UpdateLog handles PUT /records/:id and updates an existing record record. +func Update[T ExtendedLog](c *gin.Context) { + idParam := c.Param("id") + id, err := strconv.Atoi(idParam) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid record ID"}) + return + } + + var existing T + if err := database.DB.First(&existing, id).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Record not found"}) + return + } + var base base.BaseLog + if err := c.ShouldBindJSON(&base); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if err := database.DB.Model(&existing).Updates(base).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update record: " + err.Error()}) + return + } + var record T + if err := c.ShouldBindJSON(&record); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if err := database.DB.Model(&existing).Updates(record).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update record: " + err.Error()}) + return + } + + database.Preload().First(&existing, record.GetID()) + c.JSON(http.StatusOK, existing) +} + +// DeleteLog handles DELETE /records/:id and deletes a record record. +func Delete[T ExtendedLog](c *gin.Context) { + idParam := c.Param("id") + id, err := strconv.Atoi(idParam) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid record ID"}) + return + } + + var record T + if err := database.DB.First(&record, id).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Record not found"}) + return + } + + base := record.GetBaseLog() + if err := database.DB.Delete(&base).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete record: " + err.Error()}) + return + } + if err := database.DB.Delete(&record).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete record: " + err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"message": "Record deleted successfully"}) +} diff --git a/wagfarm-api/logs/generic/model.go b/wagfarm-api/logs/generic/model.go new file mode 100644 index 0000000..9a13ffa --- /dev/null +++ b/wagfarm-api/logs/generic/model.go @@ -0,0 +1,37 @@ +package generic + +import ( + "wagfarm-api/assets/machine" + "wagfarm-api/generic" + "wagfarm-api/logs/base" + + "github.com/shopspring/decimal" + "gorm.io/gorm" +) + +type ExtendedLog interface { + generic.Model + GetBaseLog() base.BaseLog +} + +type BaseExtendedLog struct { + ID uint `gorm:"primaryKey"` + BaseLogID uint `gorm:"not null"` + BaseLog base.BaseLog `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"` +} + +func (l BaseExtendedLog) GetID() uint { return l.ID } +func (l BaseExtendedLog) GetBaseLog() base.BaseLog { return l.BaseLog } +func (l *BaseExtendedLog) UpdateCost(db *gorm.DB, newCost decimal.Decimal) (err error) { + err = db.Model(&base.BaseLog{}). + Where("id = ?", l.BaseLogID). + Update("cost", newCost).Error + return +} + +type FieldWorkLog struct { + BaseExtendedLog + WorkedAreaPercentage float64 `gorm:"not null"` + MachineID uint `gorm:"not null"` + Machine machine.Machine `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"` +} diff --git a/wagfarm-api/logs/generic/route.go b/wagfarm-api/logs/generic/route.go new file mode 100644 index 0000000..a3be65b --- /dev/null +++ b/wagfarm-api/logs/generic/route.go @@ -0,0 +1,15 @@ +package generic + +import ( + "wagfarm-api/generic" + + "github.com/gin-gonic/gin" +) + +func RegisterCRUDRoutes(rg *gin.RouterGroup, l generic.CRUDController) { + generic.RegisterCRUDRoutes(rg, l) +} + +func RegisterRRoutes(rg *gin.RouterGroup, l generic.RController) { + generic.RegisterRRoutes(rg, l) +} diff --git a/wagfarm-api/logs/harvest/model.go b/wagfarm-api/logs/harvest/model.go index 9ef43c9..13eb89d 100644 --- a/wagfarm-api/logs/harvest/model.go +++ b/wagfarm-api/logs/harvest/model.go @@ -3,14 +3,14 @@ package harvest import ( "wagfarm-api/assets/product" "wagfarm-api/database" - "wagfarm-api/logs/base" + "wagfarm-api/logs/generic" "github.com/shopspring/decimal" "gorm.io/gorm" ) type HarvestLog struct { - base.FieldWorkLog + generic.FieldWorkLog ProductID uint `gorm:"not null"` Product product.Product `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"` Amount float64 `gorm:"not null"` // in units of the product diff --git a/wagfarm-api/logs/harvest/route.go b/wagfarm-api/logs/harvest/route.go index ec6aaff..8231f2e 100644 --- a/wagfarm-api/logs/harvest/route.go +++ b/wagfarm-api/logs/harvest/route.go @@ -1,11 +1,11 @@ package harvest import ( - "wagfarm-api/generic" + "wagfarm-api/logs/generic" "github.com/gin-gonic/gin" ) func RegisterRoutes(rg *gin.RouterGroup) { - generic.RegisterCRUDRoutes(rg.Group("/harvest"), generic.DefaultCRUDController[HarvestLog]()) + generic.RegisterCRUDRoutes(rg.Group("/harvest"), generic.LogCRUDController[HarvestLog]()) } diff --git a/wagfarm-api/logs/seeding/model.go b/wagfarm-api/logs/seeding/model.go index 30c0567..019d4df 100644 --- a/wagfarm-api/logs/seeding/model.go +++ b/wagfarm-api/logs/seeding/model.go @@ -3,7 +3,7 @@ package seeding import ( "wagfarm-api/assets/seed" "wagfarm-api/database" - "wagfarm-api/logs/base" + "wagfarm-api/logs/generic" "wagfarm-api/taxonomy/seedingcondition" "wagfarm-api/taxonomy/seedingtechnique" @@ -12,7 +12,7 @@ import ( ) type SeedingLog struct { - base.FieldWorkLog + generic.FieldWorkLog SeedID uint `gorm:"not null"` Seed seed.Seed `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null"` TechniqueID uint `gorm:"not null"` diff --git a/wagfarm-api/logs/seeding/route.go b/wagfarm-api/logs/seeding/route.go index d328d1d..8c70e32 100644 --- a/wagfarm-api/logs/seeding/route.go +++ b/wagfarm-api/logs/seeding/route.go @@ -1,11 +1,11 @@ package seeding import ( - "wagfarm-api/generic" + "wagfarm-api/logs/generic" "github.com/gin-gonic/gin" ) func RegisterRoutes(rg *gin.RouterGroup) { - generic.RegisterCRUDRoutes(rg.Group("/seeding"), generic.DefaultCRUDController[SeedingLog]()) + generic.RegisterCRUDRoutes(rg.Group("/seeding"), generic.LogCRUDController[SeedingLog]()) }