diff --git a/controllers/contracts_controller.go b/controllers/contracts_controller.go new file mode 100644 index 0000000..c215b3e --- /dev/null +++ b/controllers/contracts_controller.go @@ -0,0 +1,61 @@ +package controllers + +import ( + "errors" + "log" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/jinzhu/gorm" + "gitlab.com/pactual1/backend/models" + "gitlab.com/pactual1/backend/shared" +) + +func GetLatestContracts(c *gin.Context) { + // Get limit and offset from query parameter with defaults + limitStr := c.DefaultQuery("limit", "99999999999999999999999999999999") + offsetStr := c.DefaultQuery("offset", "0") + status := c.DefaultQuery("status", models.ContractStatusActive) + + // Convert limit and offset to int + limit, err := strconv.Atoi(limitStr) + if err != nil { + log.Printf("GetLatestContracts Error: Invalid limit value: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid limit value"}) + return + } + + offset, err := strconv.Atoi(offsetStr) + if err != nil { + log.Printf("GetLatestContracts Error: Invalid offset value: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid offset value"}) + return + } + + // Create a slice to hold the contracts + var contracts []models.Contract + + // Count the total number of contracts + var total int64 + if err := shared.GetDb().Where("status = ?", status).Model(&models.Contract{}).Count(&total).Error; err != nil { + log.Printf("GetLatestContracts Error: Database error: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) + return + } + + // Fetch the latest contracts from the database with LIMIT and OFFSET + if err := shared.GetDb().Where("status = ?", status).Order("created_at desc").Limit(limit).Offset(offset).Find(&contracts).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Printf("GetLatestContracts Error: No contracts found: %v", err) + c.JSON(http.StatusNotFound, gin.H{"error": "No contracts found"}) + } else { + log.Printf("GetLatestContracts Error: Database error: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) + } + return + } + + // Respond with the contracts and the total count + c.JSON(http.StatusOK, gin.H{"total": total, "contracts": contracts}) +} diff --git a/controllers/devices_controller.go b/controllers/devices_controller.go index d781095..99091cc 100644 --- a/controllers/devices_controller.go +++ b/controllers/devices_controller.go @@ -3,8 +3,11 @@ package controllers import ( "encoding/json" "errors" + "fmt" "log" "net/http" + "strconv" + "strings" "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" @@ -20,55 +23,212 @@ func SaveDeviceInfo(c *gin.Context) { err := json.Unmarshal(rawData, &deviceInfo) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON payload"}) - log.Printf("Invalid json pyload : %v", err) + log.Printf("Invalid json pyload : %v",err) return } + deviceInfo.RawJSON = string(rawData) + deviceInfo.X = deviceInfo.AccInfo.X + deviceInfo.Y = deviceInfo.AccInfo.Y + deviceInfo.Z = deviceInfo.AccInfo.Z - // Attempt to find the device by IMEI; if not found, create a new device - var device models.Device - if err := shared.GetDb().Unscoped().Where("imei = ?", deviceInfo.IMEI).First(&device).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - // Create new device - newDevice := models.Device{ - IMEI: deviceInfo.IMEI, - IMSI: deviceInfo.IMSI, - DeviceConfiguration: string(rawData), - } - if err := shared.GetDb().Create(&newDevice).Error; err != nil { - log.Printf("CREATE -Device DB Error: %v", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not create new device"}) - return - } - deviceInfo.DeviceID = newDevice.ID - } else { - log.Printf("CREATE -Device DB Error: %v", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) - return - } - } else { - log.Printf("Current device deleted at: %v", device.DeletedAt) - - if device.DeletedAt != nil { - // Use raw SQL to update the record - if err := shared.GetDb().Exec("UPDATE devices SET deleted_at = NULL, company_id = NULL WHERE id = ?", device.ID).Error; err != nil { - log.Printf("UNDELETE -Device DB Error: %v", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not revive deleted device"}) - return + // Attempt to find the device by IMEI; if not found, create a new device + var device models.Device + if err := shared.GetDb().Unscoped().Where("device_id = ?", deviceInfo.ExternalDeviceID).First(&device).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + // Create new device + newDevice := models.Device{ + IMEI: deviceInfo.IMEI, + IMSI: deviceInfo.IMSI, + DeviceID: deviceInfo.ExternalDeviceID, + DeviceConfiguration: string(rawData), + } + if err := shared.GetDb().Create(&newDevice).Error; err != nil { + log.Printf("CREATE -Device DB Error: %v",err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not create new device"}) + return + } + deviceInfo.DeviceID = newDevice.ID } else { - log.Printf("Device undeleted successfuly : %v", device.DeletedAt) + log.Printf("CREATE -Device DB Error: %v",err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) + return } + } else { + log.Printf("Current device deleted at: %v",device.DeletedAt) + + if device.DeletedAt != nil { + // Use raw SQL to update the record + if err := shared.GetDb().Exec("UPDATE devices SET deleted_at = NULL, company_id = NULL WHERE id = ?", device.ID).Error; err != nil { + log.Printf("UNDELETE -Device DB Error: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not revive deleted device"}) + return + } else { + log.Printf("Device undeleted successfuly : %v",device.DeletedAt) + } + } + deviceInfo.DeviceID = device.ID } - deviceInfo.DeviceID = device.ID - } + // Save deviceInfo to your database if err := shared.GetDb().Create(&deviceInfo).Error; err != nil { - log.Printf("SaveDeviceInfo CREATE -DeviceInfo DB Error: %v", err) + log.Printf("SaveDeviceInfo CREATE -DeviceInfo DB Error: %v",err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not save device info"}) return } - log.Printf("Successfully received and saved device info: %v", deviceInfo) + log.Printf("Successfully received and saved device info: %v",deviceInfo) c.JSON(http.StatusOK, gin.H{"message": "Successfully received and saved device info", "data": deviceInfo}) } + +func GetDeviceData(c *gin.Context) { + // Get the device ID and contract ID from query parameters + deviceIDStr := c.DefaultQuery("device_id", "") + contractIDStr := c.DefaultQuery("contract_id", "") + + if deviceIDStr == "" { + log.Printf("GetDeviceData Error: Device ID is required") + c.JSON(http.StatusBadRequest, gin.H{"error": "Device ID is required"}) + return + } + + deviceID, err := strconv.ParseUint(deviceIDStr, 10, 32) + if err != nil { + log.Printf("GetDeviceData Error: Invalid Device ID: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Device ID"}) + return + } + + contractID, err := strconv.ParseUint(contractIDStr, 10, 32) + if err != nil { + log.Printf("GetDeviceData Error: Invalid Contract ID: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Contract ID"}) + return + } + + // Fetch the contract creation date based on contractID + var contract models.Contract + if err := shared.GetDb().Where("id = ?", contractID).First(&contract).Error; err != nil { + log.Printf("GetDeviceData Error: Could not fetch contract: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not fetch contract"}) + return + } + + deviceConnectedToContract := false + for _, contractDeviceID := range contract.DeviceIDs { + if deviceID == uint64(contractDeviceID) { + deviceConnectedToContract = true + } + } + + if !deviceConnectedToContract { + log.Printf("Device %v is not connected to contract %v", deviceID, contractID ) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Device is not present int his contract"}) + return + } + + var deviceInfos []models.DeviceInfo + // Modify your query to include filtering based on the contract's creation date + query := shared.GetDb().Where("device_id = ?", deviceID).Where("created_at >= ? AND created_at <= ?", contract.StartTime,contract.EndTime) + + if err := query.Find(&deviceInfos).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Printf("GetDeviceData Error: No device info found: %v", err) + c.JSON(http.StatusNotFound, gin.H{"error": "No device info found"}) + } else { + log.Printf("GetDeviceData Error: Database error: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) + } + return + } + + // Create a GeoJSON feature collection + var featureCollection models.GeoJSONFeatureCollection + featureCollection.Type = "FeatureCollection" + featureCollection.Features = []models.GeoJSONFeature{} + + // Loop through each deviceInfo to create GeoJSON features + for _, info := range deviceInfos { + info.RawJSON = "" + feature := models.GeoJSONFeature{ + Type: "Feature", + DeviceInfo: &info, + Geometry: models.GeoJSONGeometry{ + Type: "Point", + Coordinates: []float64{info.Lon, info.Lat}, + }, + Properties: map[string]interface{}{}, + } + featureCollection.Features = append(featureCollection.Features, feature) + } + + // Respond with the GeoJSON feature collection + c.JSON(http.StatusOK, featureCollection) +} + + + +func GetDevicesByContract(c *gin.Context) { + // Get the contract ID from query parameter + contractIDStr := c.DefaultQuery("contract_id", "") + if contractIDStr == "" { + log.Printf("GetDevicesByContract Error: Contract ID is required") + c.JSON(http.StatusBadRequest, gin.H{"error": "Contract ID is required"}) + return + } + + // Convert string to uint + contractID, err := strconv.ParseUint(contractIDStr, 10, 32) + if err != nil { + log.Printf("GetDevicesByContract Error: Invalid Contract ID: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Contract ID"}) + return + } + + log.Printf("This is the ID: %v", contractID) + // Fetch the contract from the database + var contract models.Contract + if err := shared.GetDb().Where("id = ?", contractID).First(&contract).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Printf("GetDevicesByContract Error: No contract found: %v", err) + c.JSON(http.StatusNotFound, gin.H{"error": "No contract found"}) + return + } else { + log.Printf("GetDevicesByContract Error: Database error: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) + return + } + } + log.Printf("This is the device IDS ID: %v", contract.DeviceIDs) + // Create a slice to hold the devices + var devices []models.Device + + // Convert the integer array to a comma-separated string + idStrings := []string{} + for _, id := range contract.DeviceIDs { + idStrings = append(idStrings, strconv.FormatInt(id, 10)) + } + idStr := strings.Join(idStrings, ",") + + // Construct the SQL query manually + sqlQuery := fmt.Sprintf("SELECT * FROM devices WHERE id IN (%s)", idStr) + + // Fetch devices from the database based on DeviceIDs in the contract + // Execute the query + if err := shared.GetDb().Raw(sqlQuery).Scan(&devices).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Printf("GetDevicesByContract Error: No devices found: %v", err) + c.JSON(http.StatusNotFound, gin.H{"error": "No devices found"}) + } else { + log.Printf("GetDevicesByContract Error: Database error: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"}) + } + return + } + + // Respond with the devices + c.JSON(http.StatusOK, devices) +} + + diff --git a/models/company.go b/models/company.go index 2cd5611..61ab57c 100644 --- a/models/company.go +++ b/models/company.go @@ -5,21 +5,10 @@ import "github.com/jinzhu/gorm" type Company struct { gorm.Model Name string - Password string - Email string - Avatar string Users []User Devices []Device } -// func FetchCompanies(companies *[]Company) (err error) { -// db := gorm.GetDb() - -// if err = db.Find(companies).Error; err != nil { -// return err -// } -// return nil -// } func (Company) Update() (bool, error) { return false, nil diff --git a/models/contract.go b/models/contract.go index f87ac21..1402d2d 100644 --- a/models/contract.go +++ b/models/contract.go @@ -23,6 +23,9 @@ type Contract struct { ContractInfos []ContractInfo } +const ContractStatusActive = "active" +const ContractStatusPending = "pending" + func (Contract) Update() (bool, error) { return false, nil } diff --git a/models/device.go b/models/device.go index 8d7133a..498eac6 100644 --- a/models/device.go +++ b/models/device.go @@ -6,6 +6,7 @@ import ( type Device struct { gorm.Model + DeviceID string `json:"deviceId"` DeviceName string IMEI string `json:"imei"` IMSI string `json:"imsi"` diff --git a/models/device_info.go b/models/device_info.go index fa124ff..72edafb 100644 --- a/models/device_info.go +++ b/models/device_info.go @@ -18,6 +18,10 @@ type SensorData struct { WifiLoc Location `json:"wifi_location"` CellLoc Location `json:"cell_location"` Temperature float64 `json:"temperature"` + // Used to parse incoming json data + AccInfo AccelerometerInfo `json:"accelerometerInfo"` + // Used to store coordinates of accelerometer to DB + AccelerometerInfo } type DeviceInfo struct { @@ -25,8 +29,34 @@ type DeviceInfo struct { RawJSON string `json:"raw_json" gorm:"type:json"` SensorData DeviceID uint + ExternalDeviceID string `json:"deviceId"` } + +type AccelerometerInfo struct { + X int64 + Y int64 + Z int64 +} + +type GeoJSONFeatureCollection struct { + Type string `json:"type"` + Features []GeoJSONFeature `json:"features"` +} + +type GeoJSONFeature struct { + Type string `json:"type"` + Geometry GeoJSONGeometry `json:"geometry"` + DeviceInfo *DeviceInfo `json:",omitempty"` + Properties map[string]interface{} `json:"properties"` +} + +type GeoJSONGeometry struct { + Type string `json:"type"` + Coordinates []float64 `json:"coordinates"` +} + + func (DeviceInfo) Update() (bool, error) { return false, nil } @@ -36,3 +66,6 @@ func (DeviceInfo) Create() (bool, error) { func (DeviceInfo) Delete() (bool, error) { return false, nil } + + + diff --git a/routes/public_routes.go b/routes/public_routes.go index e45a6ba..cfe54fb 100644 --- a/routes/public_routes.go +++ b/routes/public_routes.go @@ -8,6 +8,10 @@ import ( func RegisterPublicRoutes(r *gin.Engine) { - r.GET("/publicmessage", controllers.GetPublicText) - r.POST("/device_info", controllers.SaveDeviceInfo) + r.GET("/dashboard/map/contract/devices", controllers.GetDevicesByContract) + r.GET("/dashboard/map/contracts", controllers.GetLatestContracts) + r.GET("/dashboard/map/device_data", controllers.GetDeviceData) + + r.POST("/device_data/save", controllers.SaveDeviceInfo) + }