From 499906f88355033407bc4c9cfc9ec8f34707b1ff Mon Sep 17 00:00:00 2001 From: Nedim Date: Tue, 19 Sep 2023 08:15:42 +0200 Subject: [PATCH] Added fetch device info --- controllers/contracts_controller.go | 101 +++++++++++++++++++ controllers/devices_controller.go | 147 +++++++++++++++++++++++++++- models/company.go | 11 --- models/contract.go | 3 + models/device.go | 1 + models/device_info.go | 33 +++++++ routes/public_routes.go | 8 +- 7 files changed, 290 insertions(+), 14 deletions(-) create mode 100644 controllers/contracts_controller.go diff --git a/controllers/contracts_controller.go b/controllers/contracts_controller.go new file mode 100644 index 0000000..8d7d53e --- /dev/null +++ b/controllers/contracts_controller.go @@ -0,0 +1,101 @@ +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", "active") + + // 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 + } + + // TEMP STRUCT TO RETURN Contract staring and ending coordinates + type ContractsResponse struct { + Contract models.Contract + MapPoints models.GeoJSONFeatureCollection + } + contractsResponses := make([]ContractsResponse,0) + + for _, contract := range contracts { + // Create a GeoJSON feature collection + var featureCollection models.GeoJSONFeatureCollection + featureCollection.Type = "FeatureCollection" + featureCollection.Features = []models.GeoJSONFeature{} + + + feature := models.GeoJSONFeature{ + Type: "Feature", + Geometry: models.GeoJSONGeometry{ + Type: "Point", + Coordinates: []float64{51.4972914, -0.0733907}, + }, + Properties: map[string]interface{}{ + // Add any additional properties you want to include here + }, + } + + feature2 := models.GeoJSONFeature{ + Type: "Feature", + Geometry: models.GeoJSONGeometry{ + Type: "Point", + Coordinates: []float64{52.2156166, 0.1143158}, + }, + Properties: map[string]interface{}{ + // Add any additional properties you want to include here + }, + } + featureCollection.Features = append(featureCollection.Features, feature) + featureCollection.Features = append(featureCollection.Features, feature2) + contractsResponses = append(contractsResponses, ContractsResponse{Contract: contract, MapPoints: featureCollection}) + } + + // Respond with the contracts and the total count + c.JSON(http.StatusOK, gin.H{"total": total, "contracts": contractsResponses}) +} diff --git a/controllers/devices_controller.go b/controllers/devices_controller.go index 5868132..e536372 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" @@ -26,15 +29,19 @@ func SaveDeviceInfo(c *gin.Context) { 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 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 { @@ -74,3 +81,141 @@ func SaveDeviceInfo(c *gin.Context) { 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 + } + + 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 >= ?", contract.CreatedAt) + + 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 536d31c..ee43216 100644 --- a/models/contract.go +++ b/models/contract.go @@ -15,6 +15,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) + }