diff --git a/SPAR/SPAR.xcodeproj/project.pbxproj b/SPAR/SPAR.xcodeproj/project.pbxproj index 7e53bc0..a375826 100644 --- a/SPAR/SPAR.xcodeproj/project.pbxproj +++ b/SPAR/SPAR.xcodeproj/project.pbxproj @@ -68,6 +68,8 @@ 3D98C7F42D4FE04500462EF4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D98C7F32D4FE04500462EF4 /* ContentView.swift */; }; 3D98C7F62D4FE04700462EF4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3D98C7F52D4FE04700462EF4 /* Assets.xcassets */; }; 3D98C7F92D4FE04700462EF4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3D98C7F82D4FE04700462EF4 /* Preview Assets.xcassets */; }; + 3DA1AA442DBDB01300678160 /* DiskIOModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D505B6E2DBACDCA00510486 /* DiskIOModel.swift */; }; + 3DA1AA452DBDB08700678160 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D505B5C2DBA657B00510486 /* AppSettings.swift */; }; 3DCE45892D68C19200FACBB8 /* Constant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCE45882D68C19200FACBB8 /* Constant.swift */; }; 3DCE458B2D68C19F00FACBB8 /* SplashScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCE458A2D68C19F00FACBB8 /* SplashScreenView.swift */; }; 3DCE458D2D68C1A900FACBB8 /* Logger+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DCE458C2D68C1A900FACBB8 /* Logger+Extension.swift */; }; @@ -216,7 +218,6 @@ 3D505B5A2DB9E3F700510486 /* ProcessViewModel.swift */, 3D505B642DBAAE4D00510486 /* DiskUsageViewModel.swift */, 3D505B6A2DBAC5EC00510486 /* CpuUsageViewModel.swift */, - 3D505B6E2DBACDCA00510486 /* DiskIOModel.swift */, 3D505B702DBACDE500510486 /* DiskIOViewModel.swift */, ); path = ViewModel; @@ -235,6 +236,7 @@ 3D505B5E2DBA94B200510486 /* LoginModel.swift */, 3D505B622DBAAE2C00510486 /* DiskUsage.swift */, 3D505B682DBAB31D00510486 /* CPUUsage.swift */, + 3D505B6E2DBACDCA00510486 /* DiskIOModel.swift */, ); path = Data; sourceTree = ""; @@ -445,6 +447,7 @@ 3D5995102DB413E300E9215B /* NetworkManagerTests.swift in Sources */, 3D59950B2DB4137E00E9215B /* MockNetworkService.swift in Sources */, 3D5995112DB4162400E9215B /* NetworkManager.swift in Sources */, + 3DA1AA442DBDB01300678160 /* DiskIOModel.swift in Sources */, 3D5995152DB4167A00E9215B /* MemoryUsage.swift in Sources */, 3D505B762DBACFB100510486 /* DiskUsage.swift in Sources */, 3D5995132DB4167400E9215B /* ProcessStatus.swift in Sources */, @@ -458,6 +461,7 @@ 3D505B772DBACFB500510486 /* CPUUsage.swift in Sources */, 3D5995142DB4167700E9215B /* BatteryInfo.swift in Sources */, 3D505B612DBA9EF200510486 /* Onboarding.swift in Sources */, + 3DA1AA452DBDB08700678160 /* AppSettings.swift in Sources */, 3D59950C2DB4138E00E9215B /* NetworkService.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/SPAR/SPAR.xcodeproj/xcuserdata/abhijeetcherungottil.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/SPAR/SPAR.xcodeproj/xcuserdata/abhijeetcherungottil.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 4b4b981..975704e 100644 --- a/SPAR/SPAR.xcodeproj/xcuserdata/abhijeetcherungottil.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/SPAR/SPAR.xcodeproj/xcuserdata/abhijeetcherungottil.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -20,5 +20,69 @@ landmarkType = "3"> + + + + + + + + + + + + + + + + diff --git a/SPAR/SPAR/Data/BatteryInfo.swift b/SPAR/SPAR/Data/BatteryInfo.swift index 11aab19..49bcb29 100644 --- a/SPAR/SPAR/Data/BatteryInfo.swift +++ b/SPAR/SPAR/Data/BatteryInfo.swift @@ -9,7 +9,7 @@ import Foundation struct BatteryInfo: Codable, Identifiable { let id: Int - let userId: String + let userId: Int let hasBattery: Bool let batteryPercentage: Int let powerConsumption: Double diff --git a/SPAR/SPAR/Data/CPUUsage.swift b/SPAR/SPAR/Data/CPUUsage.swift index 34f8835..8752f58 100644 --- a/SPAR/SPAR/Data/CPUUsage.swift +++ b/SPAR/SPAR/Data/CPUUsage.swift @@ -12,55 +12,44 @@ struct CpuCoreUsage: Codable { let usage: Double } -struct CpuUsage: Codable, Identifiable { +struct CpuUsage: Decodable { let id: Int let totalCpuLoad: Double - let perCoreUsage: [CpuCoreUsage] let userId: Int let deviceId: String let timestamp: String - - private enum CodingKeys: String, CodingKey { - case id, totalCpuLoad, perCoreUsageJson, userId, deviceId, timestamp - } - - // Manual initializer for use in mock/sample data - init(id: Int, totalCpuLoad: Double, perCoreUsage: [CpuCoreUsage], userId: Int, deviceId: String, timestamp: String) { - self.id = id - self.totalCpuLoad = totalCpuLoad - self.perCoreUsage = perCoreUsage - self.userId = userId - self.deviceId = deviceId - self.timestamp = timestamp + let perCoreUsage: [CpuCoreUsage] + + enum CodingKeys: String, CodingKey { + case id, totalCpuLoad, userId, deviceId, timestamp, perCoreUsageJson } - - // Decoding from API response + init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - + id = try container.decode(Int.self, forKey: .id) totalCpuLoad = try container.decode(Double.self, forKey: .totalCpuLoad) userId = try container.decode(Int.self, forKey: .userId) deviceId = try container.decode(String.self, forKey: .deviceId) timestamp = try container.decode(String.self, forKey: .timestamp) - + let jsonString = try container.decode(String.self, forKey: .perCoreUsageJson) - let jsonData = Data(jsonString.utf8) - perCoreUsage = try JSONDecoder().decode([CpuCoreUsage].self, from: jsonData) + + // No cleaning, just decode directly + if let jsonData = jsonString.data(using: .utf8) { + perCoreUsage = (try? JSONDecoder().decode([CpuCoreUsage].self, from: jsonData)) ?? [] + } else { + perCoreUsage = [] + } } - - // Encoding back to JSON string - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: .id) - try container.encode(totalCpuLoad, forKey: .totalCpuLoad) - try container.encode(userId, forKey: .userId) - try container.encode(deviceId, forKey: .deviceId) - try container.encode(timestamp, forKey: .timestamp) - - let jsonData = try JSONEncoder().encode(perCoreUsage) - let jsonString = String(data: jsonData, encoding: .utf8)! - try container.encode(jsonString, forKey: .perCoreUsageJson) + + // Manual initializer for sample/mock data + init(id: Int, totalCpuLoad: Double, perCoreUsage: [CpuCoreUsage], userId: Int, deviceId: String, timestamp: String) { + self.id = id + self.totalCpuLoad = totalCpuLoad + self.perCoreUsage = perCoreUsage + self.userId = userId + self.deviceId = deviceId + self.timestamp = timestamp } } diff --git a/SPAR/SPAR/Data/DeviceSpecification.swift b/SPAR/SPAR/Data/DeviceSpecification.swift index 38cfab3..9369982 100644 --- a/SPAR/SPAR/Data/DeviceSpecification.swift +++ b/SPAR/SPAR/Data/DeviceSpecification.swift @@ -9,7 +9,7 @@ import Foundation struct DeviceSpecification: Codable, Identifiable { let id: Int - let userId: String + let userId: Int let deviceName: String let manufacturer: String let model: String diff --git a/SPAR/SPAR/ViewModel/DiskIOModel.swift b/SPAR/SPAR/Data/DiskIOModel.swift similarity index 100% rename from SPAR/SPAR/ViewModel/DiskIOModel.swift rename to SPAR/SPAR/Data/DiskIOModel.swift diff --git a/SPAR/SPAR/Data/LoginModel.swift b/SPAR/SPAR/Data/LoginModel.swift index bc87909..4a645ad 100644 --- a/SPAR/SPAR/Data/LoginModel.swift +++ b/SPAR/SPAR/Data/LoginModel.swift @@ -14,5 +14,5 @@ struct LoginRequest: Encodable { struct LoginResponse: Decodable { let token: String - let id: Int + let userId: Int } diff --git a/SPAR/SPAR/Data/MemoryUsage.swift b/SPAR/SPAR/Data/MemoryUsage.swift index 5029f8c..5a302d1 100644 --- a/SPAR/SPAR/Data/MemoryUsage.swift +++ b/SPAR/SPAR/Data/MemoryUsage.swift @@ -9,7 +9,7 @@ import Foundation struct MemoryUsage: Codable, Identifiable { let id: Int - let userId: String + let userId: Int let totalMemory: Double let usedMemory: Double let availableMemory: Double diff --git a/SPAR/SPAR/Data/MockData.swift b/SPAR/SPAR/Data/MockData.swift index c033a8e..f930ca9 100644 --- a/SPAR/SPAR/Data/MockData.swift +++ b/SPAR/SPAR/Data/MockData.swift @@ -11,7 +11,7 @@ struct MockData { static let sampleDeviceData = """ [ { - "userId": "user123", + "userId": 1, "deviceId": 5, "deviceName": "MyComputer", "manufacturer": "Dell", @@ -26,15 +26,15 @@ struct MockData { "timestamp": "2025-03-28T16:03:30.041384" }, { - "userId": "user123", + "userId": 1, "deviceId": 13, - "deviceName": "MyComputer", + "deviceName": "Home", "manufacturer": "Dell", "model": "Inspiron 15", "processor": "Intel Core i7 2.8 GHz", "cpuPhysicalCores": 4, "cpuLogicalCores": 8, - "installedRam": 16.0, + "installedRam": 32.0, "graphics": "NVIDIA GTX 1650", "operatingSystem": "Windows 10 x64", "systemType": "x64 operating system, x64-based processor", @@ -46,7 +46,7 @@ struct MockData { static let sampleProcessData = """ [ { - "userId": "user123", + "userId": 1, "id": 101, "pid": 1234, "name": "chrome.exe", @@ -55,7 +55,7 @@ struct MockData { "timestamp": "2025-04-13T15:29:00.236114" }, { - "userId": "user123", + "userId": 1, "id": 102, "pid": 5678, "name": "node.exe", @@ -67,9 +67,8 @@ struct MockData { """.data(using: .utf8)! static let sampleBatteryData = """ - [ { - "userId": "user123", + "userId": 1, "id": 7, "hasBattery": true, "batteryPercentage": 85, @@ -77,20 +76,54 @@ struct MockData { "timestamp": "2025-04-13T15:29:10.549936", "charging": false } - ] """.data(using: .utf8)! static let sampleMemoryUsageData = """ - [ { - "userId": "user123", + "userId": 1, "id": 7, "totalMemory": 16.0, "usedMemory": 8.5, "availableMemory": 7.5, "timestamp": "2025-04-13T15:28:49.261218" } - ] + """.data(using: .utf8)! + static let sampleDiskUsageData = """ + { + "id": 5, + "filesystem": "/dev/sda1", + "sizeGB": 512.0, + "usedGB": 200.0, + "availableGB": 312.0, + "userId": 1, + "deviceId": "331330ac-5f82-43b0-9d39-84e1f7e7e358", + "timestamp": "2025-04-22T15:57:10.390972" + } + """.data(using: .utf8)! + static let sampleCPUUsageData = """ + { + "id": 7, + "totalCpuLoad": 42.5, + "perCoreUsageJson": "[{\"core\":1,\"usage\":35.0}]", + "userId": 1, + "deviceId": "331330ac-5f82-43b0-9d39-84e1f7e7e358", + "timestamp": "2025-04-22T15:57:10.351457" + } + """.data(using: .utf8)! + static let sampleDiskIOUsageData = """ + {"id": 5, + "readSpeedMBps": 120.0, + "writeSpeedMBps": 80.0, + "userId": 1, + "deviceId": "331330ac-5f82-43b0-9d39-84e1f7e7e358", + "timestamp": "2025-04-22T15:57:10.377292" + } + """.data(using: .utf8)! + static let sampleLoginData = """ + { + "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbGljZSIsImlhdCI6MTc0NTM1MDY1NCwiZXhwIjo5MjIzNTQ2NTcxOTIwMn0.BypJMZiF7ooVbYXCioOQAljTyjmR9ET5aJTC9auiVxw", + "userId": 1 + } """.data(using: .utf8)! } diff --git a/SPAR/SPAR/Data/ProcessStatus.swift b/SPAR/SPAR/Data/ProcessStatus.swift index fe68e21..3636d7b 100644 --- a/SPAR/SPAR/Data/ProcessStatus.swift +++ b/SPAR/SPAR/Data/ProcessStatus.swift @@ -9,7 +9,7 @@ import Foundation struct ProcessStatus: Codable, Identifiable { let id: Int - let userId: String + let userId: Int let pid: Int let name: String let cpuUsage: Double diff --git a/SPAR/SPAR/Device/BatteryDetailView.swift b/SPAR/SPAR/Device/BatteryDetailView.swift index 3f4fc49..05ddef4 100644 --- a/SPAR/SPAR/Device/BatteryDetailView.swift +++ b/SPAR/SPAR/Device/BatteryDetailView.swift @@ -69,7 +69,7 @@ struct BatteryDetailView: View { InfoRow(label: StringConstant.deviceName, value: device.deviceName) InfoRow(label: StringConstant.Charging, value: viewModel.batteryInfo.charging ? StringConstant.batYes : StringConstant.batNo) InfoRow(label: StringConstant.power, value: String(format: "%.2f W", viewModel.batteryInfo.powerConsumption)) - InfoRow(label: StringConstant.timestamp, value: viewModel.batteryInfo.timestamp) + InfoRow(label: StringConstant.timestamp, value: viewModel.batteryInfo.timestamp.toFormattedDate()) } } .padding() @@ -110,7 +110,7 @@ struct BatteryDetailView: View { #Preview { BatteryDetailView(device: DeviceSpecification( id: 1, - userId: "User123", + userId: 1, deviceName: "MyComputer", manufacturer: "Dell", model: "Inspiron 15", diff --git a/SPAR/SPAR/Device/CpuUsageDetailView.swift b/SPAR/SPAR/Device/CpuUsageDetailView.swift index 6c5b8da..416f1ca 100644 --- a/SPAR/SPAR/Device/CpuUsageDetailView.swift +++ b/SPAR/SPAR/Device/CpuUsageDetailView.swift @@ -105,7 +105,7 @@ struct CpuUsageDetailView: View { #Preview { CpuUsageDetailView(device: DeviceSpecification( id: 1, - userId: "User123", + userId: 1, deviceName: "MyComputer", manufacturer: "Dell", model: "Inspiron 15", diff --git a/SPAR/SPAR/Device/DeviceOptions.swift b/SPAR/SPAR/Device/DeviceOptions.swift index d6e502f..8a5631a 100644 --- a/SPAR/SPAR/Device/DeviceOptions.swift +++ b/SPAR/SPAR/Device/DeviceOptions.swift @@ -109,7 +109,7 @@ struct DeviceInfoRow: View { NavigationStack { DeviceOptions(currentView: .constant(.detailPage), device: DeviceSpecification( id: 1, - userId: "User123", + userId: 1, deviceName: "MyComputer", manufacturer: "Dell", model: "Inspiron 15", diff --git a/SPAR/SPAR/Device/DiskIODetailView.swift b/SPAR/SPAR/Device/DiskIODetailView.swift index 29dad7e..a46d331 100644 --- a/SPAR/SPAR/Device/DiskIODetailView.swift +++ b/SPAR/SPAR/Device/DiskIODetailView.swift @@ -86,7 +86,7 @@ struct DiskIODetailView: View { #Preview { DiskIODetailView(device: DeviceSpecification( id: 1, - userId: "User123", + userId: 1, deviceName: "MyComputer", manufacturer: "Dell", model: "Inspiron 15", diff --git a/SPAR/SPAR/Device/DiskUsageDetailView.swift b/SPAR/SPAR/Device/DiskUsageDetailView.swift index 5903d5a..955ecb3 100644 --- a/SPAR/SPAR/Device/DiskUsageDetailView.swift +++ b/SPAR/SPAR/Device/DiskUsageDetailView.swift @@ -65,7 +65,7 @@ struct DiskUsageDetailView: View { #Preview { DiskUsageDetailView(device: DeviceSpecification( id: 1, - userId: "User123", + userId: 1, deviceName: "MyComputer", manufacturer: "Dell", model: "Inspiron 15", diff --git a/SPAR/SPAR/Device/MemoryUsageDetailView.swift b/SPAR/SPAR/Device/MemoryUsageDetailView.swift index d647a77..bfab2c1 100644 --- a/SPAR/SPAR/Device/MemoryUsageDetailView.swift +++ b/SPAR/SPAR/Device/MemoryUsageDetailView.swift @@ -37,7 +37,7 @@ struct MemoryUsageDetailView: View { InfoRow(label: StringConstant.totalMemeory, value: String(format: "%.2f GB", viewModel.memoryInfo.totalMemory)) InfoRow(label: StringConstant.usedMemory, value: String(format: "%.2f GB", viewModel.memoryInfo.usedMemory)) InfoRow(label: StringConstant.availableMemory, value: String(format: "%.2f GB", viewModel.memoryInfo.availableMemory)) - InfoRow(label: StringConstant.timestamp, value: viewModel.memoryInfo.timestamp) + InfoRow(label: StringConstant.timestamp, value: viewModel.memoryInfo.timestamp.toFormattedDate()) } .padding() .frame(maxWidth: 320) @@ -58,7 +58,7 @@ struct MemoryUsageDetailView: View { #Preview { MemoryUsageDetailView(device: DeviceSpecification( id: 1, - userId: "User123", + userId: 1, deviceName: "MyComputer", manufacturer: "Dell", model: "Inspiron 15", diff --git a/SPAR/SPAR/Device/ProcessDetailPage.swift b/SPAR/SPAR/Device/ProcessDetailPage.swift index 8e8e586..0938442 100644 --- a/SPAR/SPAR/Device/ProcessDetailPage.swift +++ b/SPAR/SPAR/Device/ProcessDetailPage.swift @@ -113,7 +113,7 @@ struct ProcessDetailPage: View { #Preview { ProcessDetailPage( device: DeviceSpecification( id: 1, - userId: "User123", + userId: 1, deviceName: "MyComputer", manufacturer: "Dell", model: "Inspiron 15", diff --git a/SPAR/SPAR/HomeView.swift b/SPAR/SPAR/HomeView.swift index d1aaab3..e9bb473 100644 --- a/SPAR/SPAR/HomeView.swift +++ b/SPAR/SPAR/HomeView.swift @@ -7,7 +7,6 @@ import SwiftUI -import SwiftUI struct HomeView: View { @Binding var currentView: AppView @@ -77,7 +76,7 @@ struct HomeView: View { // Devices List ScrollView { LazyVStack(spacing: 15) { - ForEach(viewModel.devices, id: \.id) { device in + ForEach(viewModel.filteredDevices, id: \.id) { device in // Replaced NavigationLink with NavigationButton NavigationButton(title: device.deviceName) { DeviceOptions(currentView: $currentView, device: device) // Pass device to next page diff --git a/SPAR/SPAR/NetworkService/MockNetworkService.swift b/SPAR/SPAR/NetworkService/MockNetworkService.swift index 0758ad3..9331b7b 100644 --- a/SPAR/SPAR/NetworkService/MockNetworkService.swift +++ b/SPAR/SPAR/NetworkService/MockNetworkService.swift @@ -7,27 +7,59 @@ import Foundation -class MockNetworkService: NetworkServicing { - func post(to url: URL, body: U) async throws -> T where T : Decodable, U : Encodable { - throw URLError(.unsupportedURL) - } +final class MockNetworkService: NetworkServicing { + + private let sampleDataMapping: [String: Data] = [ + "device-specifications": MockData.sampleDeviceData, + "process-status": MockData.sampleProcessData, + "battery-info": MockData.sampleBatteryData, + "memory-usage": MockData.sampleMemoryUsageData, + "disk-usage": MockData.sampleDiskUsageData, + "disk-io": MockData.sampleDiskIOUsageData, + "cpu-usage": MockData.sampleCPUUsageData, + "auth/signin": MockData.sampleLoginData + ] func get(from url: URL, token: String?) async throws -> T { - let sampleData: Data - - if url.absoluteString.contains("device-specifications") { - sampleData = MockData.sampleDeviceData - } else if url.absoluteString.contains("process-status") { - sampleData = MockData.sampleProcessData - } else if url.absoluteString.contains("battery-info") { - sampleData = MockData.sampleBatteryData - } else if url.absoluteString.contains("memory-usage") { - sampleData = MockData.sampleMemoryUsageData - } else { + guard let (key, data) = sampleDataMapping.first(where: { url.absoluteString.contains($0.key) }) else { throw URLError(.badURL) } - return try JSONDecoder().decode(T.self, from: sampleData) + var responseData = data + + if key == "cpu-usage" { + + do { + var temp = try JSONDecoder().decode(TempCPUUsage.self, from: data) + + // Clean perCoreUsageJson + temp.perCoreUsageJson = temp.perCoreUsageJson + .replacingOccurrences(of: "\\", with: "") + print(temp) + + // responseData = try JSONEncoder().encode(temp) + } catch { + print("Failed to prepare CPU mock data: \(error.localizedDescription)") + } + } + + return try JSONDecoder().decode(T.self, from: responseData) } + + + + func post(to url: URL, body: U) async throws -> T { + // Always returning login sample for mock POST + return try JSONDecoder().decode(T.self, from: MockData.sampleLoginData) + } +} + +struct TempCPUUsage: Codable { + var id: Int + var totalCpuLoad: Double + var perCoreUsageJson: String + var userId: Int + var deviceId: String + var timestamp: String } diff --git a/SPAR/SPAR/NetworkService/NetworkManager.swift b/SPAR/SPAR/NetworkService/NetworkManager.swift index 410c371..ef54de2 100644 --- a/SPAR/SPAR/NetworkService/NetworkManager.swift +++ b/SPAR/SPAR/NetworkService/NetworkManager.swift @@ -11,40 +11,60 @@ class NetworkManager { private let networkService: NetworkServicing private let baseURL = "http://localhost:8080/api/metrics" - init(networkService: NetworkServicing = NetworkService()) { + init(networkService: NetworkServicing = MockNetworkService()) { self.networkService = networkService } - private func makeURL(endpoint: String, userId: String) -> URL? { - URL(string: "\(baseURL)/\(endpoint)/\(userId)") + private func makeURL(endpoint: String, userId: Int, deviceId: Int) -> URL? { + URL(string: "\(baseURL)/\(endpoint)/\(userId)/\(deviceId)") } - func fetchDeviceSpecifications(for userId: String) async throws -> [DeviceSpecification] { - guard let url = makeURL(endpoint: "device-specifications", userId: userId) else { + func fetchDeviceSpecifications(for userId: Int) async throws -> [DeviceSpecification] { + guard let url = URL(string: "http://localhost:8080/api/device-specifications/\(userId)"),let token = AppSettings.shared.authToken else { throw URLError(.badURL) } - return try await networkService.get(from: url,token: nil) + return try await networkService.get(from: url,token: token) + } + func fetchCPUUsageInfo(for userId: Int, deviceId: Int) async throws -> CpuUsage { + guard let url = makeURL(endpoint: "cpu-usage", userId: userId, deviceId: deviceId),let token = AppSettings.shared.authToken else { + throw URLError(.badURL) + } + + return try await networkService.get(from: url, token: token) + } + func fetchProcessStatus(for userId: Int, deviceId: Int) async throws -> [ProcessStatus] { + guard let url = makeURL(endpoint: "process-status", userId: userId, deviceId: deviceId),let token = AppSettings.shared.authToken else { + throw URLError(.badURL) + } + return try await networkService.get(from: url, token: token) + } + + func fetchBatteryInfo(for userId: Int, deviceId: Int) async throws -> BatteryInfo { + guard let url = makeURL(endpoint: "battery-info", userId: userId, deviceId: deviceId),let token = AppSettings.shared.authToken else { + throw URLError(.badURL) + } + return try await networkService.get(from: url, token: token) } - func fetchProcessStatus(for userId: String) async throws -> [ProcessStatus] { - guard let url = makeURL(endpoint: "process-status", userId: userId) else { + func fetchMemoryUsage(for userId: Int, deviceId: Int) async throws -> MemoryUsage { + guard let url = makeURL(endpoint: "memory-usage", userId: userId, deviceId: deviceId),let token = AppSettings.shared.authToken else { throw URLError(.badURL) } - return try await networkService.get(from: url, token: nil) + return try await networkService.get(from: url, token: token) } - func fetchBatteryInfo(for userId: String) async throws -> [BatteryInfo] { - guard let url = makeURL(endpoint: "battery-info", userId: userId) else { + func fetchDiskUsage(for userId: Int, deviceId: Int) async throws -> DiskUsage { + guard let url = makeURL(endpoint: "disk-usage", userId: userId, deviceId: deviceId),let token = AppSettings.shared.authToken else { throw URLError(.badURL) } - return try await networkService.get(from: url, token: nil) + return try await networkService.get(from: url, token: token) } - func fetchMemoryUsage(for userId: String) async throws -> [MemoryUsage] { - guard let url = makeURL(endpoint: "memory-usage", userId: userId) else { + func fetchDiskIO(for userId: Int, deviceId: Int) async throws -> DiskIO { + guard let url = makeURL(endpoint: "disk-io", userId: userId, deviceId: deviceId),let token = AppSettings.shared.authToken else { throw URLError(.badURL) } - return try await networkService.get(from: url, token: nil) + return try await networkService.get(from: url, token: token) } func login(username: String, password: String) async throws -> LoginResponse { diff --git a/SPAR/SPAR/Utilities/Constant.swift b/SPAR/SPAR/Utilities/Constant.swift index 2193303..b6695c1 100644 --- a/SPAR/SPAR/Utilities/Constant.swift +++ b/SPAR/SPAR/Utilities/Constant.swift @@ -21,7 +21,7 @@ enum StringConstant { static let onboardingImages = "Onbording image of %@" static let getstarted = "Get Started" static let Dashboard = "Dashboard" - static let incorrectCredentials = "Incorrect username or password." + static let incorrectCredentials = "Invalid credentials or network error." static let welcomeBack = "Welcome Back" static let login = "LOGIN" static let Username = "Username" diff --git a/SPAR/SPAR/ViewModel/BatteryViewModel.swift b/SPAR/SPAR/ViewModel/BatteryViewModel.swift index f5cb199..0e83d07 100644 --- a/SPAR/SPAR/ViewModel/BatteryViewModel.swift +++ b/SPAR/SPAR/ViewModel/BatteryViewModel.swift @@ -9,18 +9,38 @@ import Foundation class BatteryViewModel: ObservableObject { @Published var batteryInfo: BatteryInfo + private let networkManager = NetworkManager() init(device: DeviceSpecification) { - // Initialize with device information or make an API call to get the battery info + // 1. Set a placeholder batteryInfo first self.batteryInfo = BatteryInfo( - id: 1, - userId: device.userId, // Pass userId from device + id: 0, + userId: device.userId, hasBattery: true, - batteryPercentage: 80, - powerConsumption: 12.5, - timestamp: "2025-04-23 14:35".toFormattedDate(), + batteryPercentage: 0, + powerConsumption: 0, + timestamp: "bb", charging: false ) + + // 2. Then fetch actual data from API + fetchBatteryInfo(device: device) + } + + func fetchBatteryInfo(device: DeviceSpecification) { + Task { + do { + guard let userId = AppSettings.shared.userId else { return } + let response = try await networkManager.fetchBatteryInfo(for: userId, deviceId: device.id) + + + DispatchQueue.main.async { + self.batteryInfo = response + } + + } catch { + print("Failed to fetch battery info: \(error)") + } + } } - } diff --git a/SPAR/SPAR/ViewModel/CpuUsageViewModel.swift b/SPAR/SPAR/ViewModel/CpuUsageViewModel.swift index 8f6e020..1936bc6 100644 --- a/SPAR/SPAR/ViewModel/CpuUsageViewModel.swift +++ b/SPAR/SPAR/ViewModel/CpuUsageViewModel.swift @@ -15,37 +15,66 @@ class CpuUsageViewModel: ObservableObject { @Published var chartData: ChartData? private let logger = Logger.fileLocation + private let networkManager = NetworkManager() init(device: DeviceSpecification) { // Sample data - let sampleCoreData = [ - CpuCoreUsage(core: 1, usage: 35.0), - CpuCoreUsage(core: 2, usage: 45.0), - CpuCoreUsage(core: 3, usage: 50.0), - CpuCoreUsage(core: 4, usage: 40.0), - CpuCoreUsage(core: 5, usage: 35.0), - CpuCoreUsage(core: 6, usage: 45.0), - CpuCoreUsage(core: 7, usage: 50.0), - CpuCoreUsage(core: 8, usage: 40.0) - ] - + let sampleCoreData:[CpuCoreUsage] = [] // Empty array as JSON string + + let sampleUsage = CpuUsage( + id: 7, + totalCpuLoad: 32.1, + perCoreUsage: sampleCoreData, + userId: 1, + deviceId: "1", + timestamp: "2025-04-22T15:57:10.351457" + ) + + self.cpuUsage = sampleUsage + + self.chartData = ChartData( + color: .orange, + type: "CPU", + percent: CGFloat(sampleUsage.totalCpuLoad) + ) + + // Call fetchCPUInfo to load real data + fetchCPUInfo(device: device) + } - let sampleUsage = CpuUsage( - id: 7, - totalCpuLoad: 42.5, - perCoreUsage: sampleCoreData, - userId: 1, - deviceId: "331330ac-5f82-43b0-9d39-84e1f7e7e358", - timestamp: "2025-04-22T15:57:10.351457".toFormattedDate() - ) - - self.cpuUsage = sampleUsage - - self.chartData = ChartData( - color: .orange, - type: "CPU", - percent: CGFloat(sampleUsage.totalCpuLoad) - ) + func fetchCPUInfo(device: DeviceSpecification) { + // Print to ensure this function is being called + print("Fetching CPU info for device: \(device.id)") + + Task { + do { + guard let userId = AppSettings.shared.userId else { + print("User ID is nil. Aborting fetch.") + return + } + print("User ID: \(userId)") + + let response = try await networkManager.fetchCPUUsageInfo(for: userId, deviceId: device.id) + + print("response",response) + + // Update on main thread + DispatchQueue.main.async { + print("Received CPU usage data: \(response)") + self.cpuUsage = response + self.chartData = ChartData( + color: Color.purple, + type: "CPU", + percent: CGFloat(response.totalCpuLoad) + ) + } + + } catch { + print("Failed to fetch CPU info: \(error)") + DispatchQueue.main.async { + self.errorMessage = "Failed to fetch CPU info: \(error.localizedDescription)" + } + } + } } } - diff --git a/SPAR/SPAR/ViewModel/DiskIOViewModel.swift b/SPAR/SPAR/ViewModel/DiskIOViewModel.swift index 66ca7d4..e688053 100644 --- a/SPAR/SPAR/ViewModel/DiskIOViewModel.swift +++ b/SPAR/SPAR/ViewModel/DiskIOViewModel.swift @@ -11,6 +11,7 @@ import OSLog class DiskIOViewModel: ObservableObject { @Published var diskIO: DiskIO? @Published var errorMessage: String = "" + private let networkManager = NetworkManager() private let logger = Logger.fileLocation @@ -21,32 +22,27 @@ class DiskIOViewModel: ObservableObject { readSpeedMBps: 120.0, writeSpeedMBps: 80.0, userId: 1, - deviceId: "331330ac-5f82-43b0-9d39-84e1f7e7e358", + deviceId: "1", timestamp: "2025-04-22T15:57:10.377292".toFormattedDate() ) self.diskIO = sampleDiskIO + fetchDiskIOInfo(device: device) } // Fetch Disk I/O Data from the API - /*func fetchDiskIO(userId: Int, deviceId: String) async { - guard let url = URL(string: "\(Constants.baseURL)/api/metrics/disk-io/\(userId)/\(deviceId)") else { - self.errorMessage = "Invalid URL" - return - } - - do { - let (data, _) = try await URLSession.shared.data(from: url) - let decoder = JSONDecoder() - let diskIO = try decoder.decode(DiskIO.self, from: data) - - DispatchQueue.main.async { - self.diskIO = diskIO - } - } catch { - DispatchQueue.main.async { - self.logger.error("Failed to fetch disk I/O data: \(error.localizedDescription)") - self.errorMessage = "Failed to load disk I/O data" + func fetchDiskIOInfo(device: DeviceSpecification) { + Task { + do { + guard let userId = AppSettings.shared.userId else { return } + let response = try await networkManager.fetchDiskIO(for: userId, deviceId: device.id) + + DispatchQueue.main.async { + self.diskIO = response + } + + } catch { + print("Failed to fetch Disk io info: \(error)") } } - }*/ + } } diff --git a/SPAR/SPAR/ViewModel/DiskUsageViewModel.swift b/SPAR/SPAR/ViewModel/DiskUsageViewModel.swift index 833338b..8d73932 100644 --- a/SPAR/SPAR/ViewModel/DiskUsageViewModel.swift +++ b/SPAR/SPAR/ViewModel/DiskUsageViewModel.swift @@ -7,11 +7,14 @@ import Foundation import OSLog +import SwiftUI class DiskUsageViewModel: ObservableObject { @Published var diskUsage: DiskUsage? @Published var errorMessage: String = "" @Published var chartData: ChartData = ChartData(color: .gray, type: "Disk", percent: 0) + private let networkManager = NetworkManager() + private let logger = Logger.fileLocation @@ -23,7 +26,7 @@ class DiskUsageViewModel: ObservableObject { usedGB: 200.0, availableGB: 312.0, userId: 1, - deviceId: "331330ac-5f82-43b0-9d39-84e1f7e7e358", + deviceId: "1", timestamp: "2025-04-22T15:57:10.390972".toFormattedDate() ) @@ -35,6 +38,30 @@ class DiskUsageViewModel: ObservableObject { type: "Disk", percent: CGFloat(usedPercent) ) + + fetchDiskUsageInfo(device: device) + } + + func fetchDiskUsageInfo(device: DeviceSpecification) { + Task { + do { + guard let userId = AppSettings.shared.userId else { return } + let response = try await networkManager.fetchDiskUsage(for: userId, deviceId: device.id) + + DispatchQueue.main.async { + self.diskUsage = response + let usedPercent = (response.usedGB / response.availableGB) * 100 + self.chartData = ChartData( + color: .green, + type: "Disk", + percent: CGFloat(usedPercent) + ) + } + + } catch { + print("Failed to fetch Disk Usage info: \(error)") + } + } } } diff --git a/SPAR/SPAR/ViewModel/HomeViewModel.swift b/SPAR/SPAR/ViewModel/HomeViewModel.swift index e4671f8..8c8995a 100644 --- a/SPAR/SPAR/ViewModel/HomeViewModel.swift +++ b/SPAR/SPAR/ViewModel/HomeViewModel.swift @@ -12,37 +12,37 @@ class HomeViewModel: ObservableObject { @Published var isSearching = false @Published var searchText = "" @Published var animate: Bool = false - + private let networkManager = NetworkManager() // Accepts list of DeviceSpecification - let devices: [DeviceSpecification] = [ - DeviceSpecification( - id: 1, - userId: "User123", - deviceName: "MyComputer", - manufacturer: "Dell", - model: "Inspiron 15", - processor: "Intel Core i7 2.8 GHz", - cpuPhysicalCores: 4, - cpuLogicalCores: 8, - installedRam: 16.0, - graphics: "NVIDIA GTX 1650", - operatingSystem: "Windows 10 x64", - systemType: "x64-based processor", - timestamp: "2025-03-28T16:03:30.041384" - ) - ] + @Published var devices: [DeviceSpecification] = [] + + init() { + getDeviceData() + } var filteredDevices: [DeviceSpecification] { if searchText.isEmpty { return devices } else { return devices.filter { - $0.deviceName.localizedCaseInsensitiveContains(searchText) || - $0.manufacturer.localizedCaseInsensitiveContains(searchText) || - $0.model.localizedCaseInsensitiveContains(searchText) || - $0.processor.localizedCaseInsensitiveContains(searchText) || - $0.graphics.localizedCaseInsensitiveContains(searchText) || - $0.operatingSystem.localizedCaseInsensitiveContains(searchText) + $0.deviceName.localizedCaseInsensitiveContains(searchText) + } + } + } + + func getDeviceData() { + Task { + do { + guard let id = AppSettings.shared.userId else {return } + let response = try await networkManager.fetchDeviceSpecifications(for: id) + print(response) + DispatchQueue.main.async { + self.devices = response + } + + } catch { + print("⚠️ Decoding error: \(error)") + } } } @@ -60,3 +60,20 @@ class HomeViewModel: ObservableObject { } } } + + +//DeviceSpecification( +// id: 1, +// userId: "User123", +// deviceName: "MyComputer", +// manufacturer: "Dell", +// model: "Inspiron 15", +// processor: "Intel Core i7 2.8 GHz", +// cpuPhysicalCores: 4, +// cpuLogicalCores: 8, +// installedRam: 16.0, +// graphics: "NVIDIA GTX 1650", +// operatingSystem: "Windows 10 x64", +// systemType: "x64-based processor", +// timestamp: "2025-03-28T16:03:30.041384" +//) diff --git a/SPAR/SPAR/ViewModel/LoginViewModel.swift b/SPAR/SPAR/ViewModel/LoginViewModel.swift index 99f5f4d..af8e256 100644 --- a/SPAR/SPAR/ViewModel/LoginViewModel.swift +++ b/SPAR/SPAR/ViewModel/LoginViewModel.swift @@ -36,30 +36,30 @@ class LoginViewModel: ObservableObject { return } - if username.lowercased() == "user" && password == "Password" { - errorMessage = "" - self.delegate?.didLoginSuccessfully() - print("Login successful!") - } else { - errorMessage = StringConstant.incorrectCredentials - } - -// Task { -// do { -// let response = try await networkManager.login(username: username, password: password) -// -// AppSettings.shared.authToken = response.token -// AppSettings.shared.userId = response.id -// -// DispatchQueue.main.async { -// self.delegate?.didLoginSuccessfully() -// } -// } catch { -// DispatchQueue.main.async { -// self.errorMessage = "Invalid credentials or network error." -// } -// } +// if username.lowercased() == "user" && password == "Password" { +// errorMessage = "" +// self.delegate?.didLoginSuccessfully() +// print("Login successful!") +// } else { +// errorMessage = StringConstant.incorrectCredentials // } + + Task { + do { + let response = try await networkManager.login(username: username, password: password) + + AppSettings.shared.authToken = response.token + AppSettings.shared.userId = response.userId + + DispatchQueue.main.async { + self.delegate?.didLoginSuccessfully() + } + } catch { + DispatchQueue.main.async { + self.errorMessage = StringConstant.incorrectCredentials + } + } + } } } diff --git a/SPAR/SPAR/ViewModel/MemoryUsageViewModel.swift b/SPAR/SPAR/ViewModel/MemoryUsageViewModel.swift index 120dfd1..76350b1 100644 --- a/SPAR/SPAR/ViewModel/MemoryUsageViewModel.swift +++ b/SPAR/SPAR/ViewModel/MemoryUsageViewModel.swift @@ -9,14 +9,15 @@ import SwiftUI class MemoryUsageViewModel: ObservableObject { @Published var memoryInfo: MemoryUsage - + private let networkManager = NetworkManager() + @Published var chartData: ChartData init(device: DeviceSpecification) { // Initializing with some default data or fetching from an API or database. let memoryInfo = MemoryUsage( id: 1, - userId: "User", + userId: 1, totalMemory: 16.0, usedMemory: 8.5, availableMemory: 7.5, @@ -30,6 +31,29 @@ class MemoryUsageViewModel: ObservableObject { type: "Memory", percent: CGFloat(usedPercent) ) + + fetchRamInfo(device: device) + } + func fetchRamInfo(device: DeviceSpecification) { + Task { + do { + guard let userId = AppSettings.shared.userId else { return } + let response = try await networkManager.fetchMemoryUsage(for: userId, deviceId: device.id) + + DispatchQueue.main.async { + self.memoryInfo = response + let usedPercent = (response.usedMemory / response.totalMemory) * 100 + self.chartData = ChartData( + color: Color.purple, + type: "Memory", + percent: CGFloat(usedPercent) + ) + } + + } catch { + print("Failed to fetch memory info: \(error)") + } + } } } diff --git a/SPAR/SPAR/ViewModel/ProcessViewModel.swift b/SPAR/SPAR/ViewModel/ProcessViewModel.swift index 44d323c..e54c5f5 100644 --- a/SPAR/SPAR/ViewModel/ProcessViewModel.swift +++ b/SPAR/SPAR/ViewModel/ProcessViewModel.swift @@ -9,15 +9,28 @@ import Foundation class ProcessViewModel: ObservableObject { @Published var processList: [ProcessStatus] + private let networkManager = NetworkManager() + init(device: DeviceSpecification) { - processList - = [ - ProcessStatus(id: 101, userId: "user123", pid: 1234, name: "chrome.exe", cpuUsage: 12.5, memoryMB: 200.0, timestamp: "2025-04-13T15:29:00.236114".toFormattedDate()), - ProcessStatus(id: 102, userId: "user123", pid: 5678, name: "node.exe", cpuUsage: 5.0, memoryMB: 150.0, timestamp: "2025-04-13T15:29:00.236114".toFormattedDate()), - ProcessStatus(id: 103, userId: "user123", pid: 1234, name: "chrome.exe", cpuUsage: 12.5, memoryMB: 200.0, timestamp: "2025-04-19T11:33:17.675373".toFormattedDate()), - ProcessStatus(id: 104, userId: "user123", pid: 5678, name: "node.exe", cpuUsage: 5.0, memoryMB: 150.0, timestamp: "2025-04-19T11:33:17.675373".toFormattedDate()) - ] + processList = [] + fetchProcessInfo(device: device) + } + + func fetchProcessInfo(device: DeviceSpecification) { + Task { + do { + guard let userId = AppSettings.shared.userId else { return } + let response = try await networkManager.fetchProcessStatus(for: userId, deviceId: device.id) + + DispatchQueue.main.async { + self.processList = response + } + } catch { + print("Failed to fetch Process info: \(error)") + } + } } + } diff --git a/SPAR/SPARTests/NetworkManagerTests.swift b/SPAR/SPARTests/NetworkManagerTests.swift index c41bcb3..9d8e2d9 100644 --- a/SPAR/SPARTests/NetworkManagerTests.swift +++ b/SPAR/SPARTests/NetworkManagerTests.swift @@ -8,6 +8,7 @@ final class NetworkManagerTests: XCTestCase { override func setUp() { super.setUp() networkManager = NetworkManager(networkService: MockNetworkService()) + AppSettings.shared.authToken = "dummyToken" // Set a dummy token } override func tearDown() { @@ -16,22 +17,46 @@ final class NetworkManagerTests: XCTestCase { } func testFetchDeviceSpecifications() async throws { - let deviceSpecifications: [DeviceSpecification] = try await networkManager.fetchDeviceSpecifications(for: "user123") - - XCTAssertEqual(deviceSpecifications.count, 2) - XCTAssertEqual(deviceSpecifications.first?.deviceName, "MyComputer") + let devices = try await networkManager.fetchDeviceSpecifications(for: 1) + XCTAssertEqual(devices.count, 2) + XCTAssertEqual(devices.first?.deviceName, "MyComputer") } + func testFetchCPUUsageInfo() async throws { + // + } + + func testFetchProcessStatus() async throws { + let processes = try await networkManager.fetchProcessStatus(for: 1, deviceId: 5) + XCTAssertEqual(processes.count, 2) + XCTAssertEqual(processes.first?.name, "chrome.exe") + } func testFetchBatteryInfo() async throws { - let batteryInfo: [BatteryInfo] = try await networkManager.fetchBatteryInfo(for: "user123") - - XCTAssertEqual(batteryInfo.count, 1) + let batteryInfo = try await networkManager.fetchBatteryInfo(for: 1, deviceId: 5) + XCTAssertTrue(batteryInfo.hasBattery) + XCTAssertEqual(batteryInfo.batteryPercentage, 85) } func testFetchMemoryUsage() async throws { - let memoryUsage: [MemoryUsage] = try await networkManager.fetchMemoryUsage(for: "user123") - - XCTAssertEqual(memoryUsage.count, 1) + let memoryUsage = try await networkManager.fetchMemoryUsage(for: 1, deviceId: 5) + XCTAssertEqual(memoryUsage.totalMemory, 16.0) + XCTAssertEqual(memoryUsage.usedMemory, 8.5) + } + + func testFetchDiskUsage() async throws { + let diskUsage = try await networkManager.fetchDiskUsage(for: 1, deviceId: 5) + XCTAssertEqual(diskUsage.sizeGB, 512.0) + } + + func testFetchDiskIO() async throws { + let diskIO = try await networkManager.fetchDiskIO(for: 1, deviceId: 5) + XCTAssertEqual(diskIO.readSpeedMBps, 120.0) + XCTAssertEqual(diskIO.writeSpeedMBps, 80.0) + } + + func testLogin() async throws { + let loginResponse = try await networkManager.login(username: "testUser", password: "testPassword") + XCTAssertNotNil(loginResponse.token) } }