diff --git a/.swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate b/.swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate index b0a3005..135f485 100644 Binary files a/.swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate and b/.swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/.swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/.swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 1101066..0f9c242 100644 --- a/.swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/.swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -3,66 +3,4 @@ uuid = "83CCDCAB-5627-45C3-BC68-1A59A1D654E0" type = "1" version = "2.0"> - - - - - - - - - - - - - - diff --git a/Sources/App/Controllers/FuelEntryV2Controller.swift b/Sources/App/Controllers/FuelEntryV2Controller.swift index 4ab233f..2fa8b85 100644 --- a/Sources/App/Controllers/FuelEntryV2Controller.swift +++ b/Sources/App/Controllers/FuelEntryV2Controller.swift @@ -16,10 +16,10 @@ struct FuelEntryV2Controller: RouteCollection { let tokenAuthGroup = fuelEntries.grouped(tokenAuthMiddleware, guardMiddleware) tokenAuthGroup.get(use: index) tokenAuthGroup.get("test", use: filtered) -// fuelEntries.get("with-images", use: getAllEntriesWithImages) + tokenAuthGroup.get("rpm", use: rpm) + tokenAuthGroup.get("maxim", use: filtered) tokenAuthGroup.post(use: create) tokenAuthGroup.put( use: softdelete) -// fuelEntries.post("upload-image", use: uploadImage) tokenAuthGroup.group(":fuelEntryID") { fuelEntry in fuelEntry.delete(use: delete) } @@ -55,28 +55,134 @@ struct FuelEntryV2Controller: RouteCollection { return response } + fileprivate func parseDate(_ dateString: String, _ start: Bool = true) -> Date? { + if(dateString.count == 0){ + return nil + } else { + let parts = dateString.split(separator: "-") + let year = Int(parts[0])! + let month = Int(parts[1])! + let day = Int(parts[2])! + var dateComponents = DateComponents() + dateComponents.year = year + dateComponents.month = month + dateComponents.day = day + if(start) { + dateComponents.hour = 0 + dateComponents.minute = 0 + dateComponents.second = 0 + } else { + dateComponents.hour = 23 + dateComponents.minute = 59 + dateComponents.second = 59 + } + let calendar = Calendar(identifier: .gregorian) + return calendar.date(from: dateComponents) + } + } + @Sendable func filtered(req: Request) async throws -> Response { - - var image: Bool - var serial: String - var tank: String let qstring = req.query - if let qimage = try? qstring.get(Bool.self, at: "image"){ - image = qimage + let startdate = try parseDate(qstring.get(String?.self, at: "startdate") ?? "") + let enddate = try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false) + let image = try qstring.get(Bool?.self, at: "image") ?? false + let serial = try qstring.get(String?.self, at: "serial") ?? nil + let tank = try qstring.get(String?.self, at: "tank") ?? nil + let previous = try qstring.get(Bool?.self, at: "previous") ?? false + + + var query = FuelEntry.query(on: req.db) + if(image){ + query = query + .field(\.$id) + .field(\.$timestamp) + .field(\.$userID) + .field(\.$product) + .field(\.$make) + .field(\.$model) + .field(\.$serialNumber) + .field(\.$tankID) + .field(\.$fuelAmount) + .field(\.$fuelType) + .field(\.$isOverride) + .field(\.$latitude) + .field(\.$longitude) + .field(\.$image) + .field(\.$downloaded) + .field(\.$loaddate) + //.filter(\.$image != nil) + } else { - image = false + query = query + .field(\.$id) + .field(\.$timestamp) + .field(\.$userID) + .field(\.$product) + .field(\.$make) + .field(\.$model) + .field(\.$serialNumber) + .field(\.$tankID) + .field(\.$fuelAmount) + .field(\.$fuelType) + .field(\.$isOverride) + .field(\.$latitude) + .field(\.$longitude) + .field(\.$downloaded) + .field(\.$loaddate) } - if let qserial = try? qstring.get(String.self, at: "serial"){ - serial = qserial - } else { - serial = "" + + query = query.group(.and){ + grp in + if( startdate != nil) { + grp.filter(\.$loaddate >= startdate!) + } + if( enddate != nil) { + grp.filter(\.$loaddate <= enddate!) + } + if( serial != nil) { + grp.filter(\.$serialNumber == serial!) + } + if( tank != nil) { + grp.filter(\.$tankID == tank!) + } + if(!previous){ + grp.filter(\.$pulldate == nil) + } + } - if let qtank = try? qstring.get(String.self, at: "tank"){ - tank = qtank + query = query.sort(\.$loaddate, .ascending) + + if(image){ + //query = query.filter(\.$image) + let wimage = try await query.paginate(for: req).map{ + img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitutde: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date()) + } + wimage.items.forEach{ + _ = $0.toFuelEntry().save(on: req.db) + } + return try await wimage.encodeResponse(for: req) } else { - tank = "" + let woimage = try await query.paginate(for: req).map{ + img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitutde: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date()) + } + woimage.items.forEach { + _ = $0.toFuelEntry().save(on: req.db) + } + return try await woimage.encodeResponse(for: req) } + } + + @Sendable + func rpm(req: Request) async throws -> Response { + let qstring = req.query + let startdate = try parseDate(qstring.get(String?.self, at: "startdate") ?? "") + let enddate = try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false) + let image = try qstring.get(Bool?.self, at: "image") ?? false + let serial = try qstring.get(String?.self, at: "serial") ?? nil + let tank = try qstring.get(String?.self, at: "tank") ?? nil + let previous = try qstring.get(Bool?.self, at: "previous") ?? false + var query = FuelEntry.query(on: req.db) if(image){ @@ -96,6 +202,7 @@ struct FuelEntryV2Controller: RouteCollection { .field(\.$longitude) .field(\.$image) .field(\.$downloaded) + .field(\.$loaddate) //.filter(\.$image != nil) } else { @@ -114,44 +221,45 @@ struct FuelEntryV2Controller: RouteCollection { .field(\.$latitude) .field(\.$longitude) .field(\.$downloaded) + .field(\.$loaddate) } - if(serial.count > 0){ - query = query - .filter(\.$serialNumber == serial) - } - if(tank.count > 0){ - query = query - .filter(\.$tankID == tank) + + query = query.group(.and){ + grp in + if( startdate != nil) { + grp.filter(\.$loaddate >= startdate!) + } + if( enddate != nil) { + grp.filter(\.$loaddate <= enddate!) + } + if( serial != nil) { + grp.filter(\.$serialNumber == serial!) + } + if( tank != nil) { + grp.filter(\.$tankID == tank!) + } + if(!previous){ + grp.filter(\.$pulldate == nil) + } + } + query = query.sort(\.$loaddate, .ascending).sort(\.$timestamp).sort(\.$serialNumber) + if(image){ //query = query.filter(\.$image) let wimage = try await query.paginate(for: req).map{ - img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitutde: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!) + img in FuelEntry.wimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitutde: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, image: img.image == nil ? Data(): img.image, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date()) } return try await wimage.encodeResponse(for: req) } else { let woimage = try await query.paginate(for: req).map{ - img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitutde: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!) + img in FuelEntry.woimage(id: img.id, timestamp: img.timestamp, userID: img.userID, latitude: img.latitude, longitutde: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: Date()) } return try await woimage.encodeResponse(for: req) } -// let result = try await query.paginate(for: req) -// return try await result.encodeResponse(for: req) } -// func getAllEntriesWithImages(req: Request) async throws -> [FuelEntry] { -// let fuelEntries = try await FuelEntry.query(on: req.db).all() -// -// for var fuelEntry in fuelEntries { -// if let imagePath = fuelEntry.image { -// let fileURL = URL(fileURLWithPath: imagePath) -// if let imageData = try? Data(contentsOf: fileURL) { -// fuelEntry.imageData = imageData -// } -// } -// } -// return fuelEntries -// } + func create(req: Request) async throws -> [FuelEntry] { let fuelEntry = try req.content.decode([FuelEntry].self) @@ -159,37 +267,7 @@ struct FuelEntryV2Controller: RouteCollection { return fuelEntry } - //Create with image file written to server storage instead of database -// func create(req: Request) async throws -> [FuelEntry] { -// let fuelEntries = try await req.content.decode([FuelEntry].self) -// let directory = DirectoryConfiguration.detect().workingDirectory + "Images/" -// -// var updatedFuelEntries: [FuelEntry] = [] -// -// for fuelEntry in fuelEntries { -// let updatedFuelEntry = try await { () async throws -> FuelEntry in -// var updatedFuelEntry = fuelEntry -// -// if let imageData = updatedFuelEntry.imageData { -// let imageFilename = "\(updatedFuelEntry.id!).png" -// let tempImageURL = URL(fileURLWithPath: directory + imageFilename) -// try await imageData.write(to: tempImageURL) -// updatedFuelEntry.image = tempImageURL.absoluteString -// } -// -// do { -// _ = try await updatedFuelEntry.save(on: req.db) -// return updatedFuelEntry -// } catch { -// throw Abort(.internalServerError, reason: "Error saving FuelEntry: \(error)") -// } -// }() -// -// updatedFuelEntries.append(updatedFuelEntry) -// } -// -// return updatedFuelEntries -// } + func delete(req: Request) async throws -> HTTPStatus { guard let fuelEntry = try await FuelEntry.find(req.parameters.get("fuelEntryID"), on: req.db) else { @@ -198,31 +276,6 @@ struct FuelEntryV2Controller: RouteCollection { try await fuelEntry.delete(on: req.db) return .noContent } - -// func uploadImage(req: Request) async throws -> FuelEntry { -// let fuelEntryID: UUID = try req.parameters.require("id") -// -// let file = try await req.content.decode(File.self) -// let filename = fuelEntryID.uuidString + ".png" // Generate a unique filename for the uploaded image -// let directory = DirectoryConfiguration.detect().workingDirectory + "Images/" -// -// if let imageData = file.data.getData(at: 0, length: file.data.readableBytes) { -// do { -// try Data(imageData).write(to: URL(fileURLWithPath: directory + filename)) // Save the uploaded image to server -// } catch { -// throw Abort(.internalServerError, reason: "Failed to save image file") -// } -// -// // Update FuelEntry model after saving the image -// if let fuelEntry = try await FuelEntry.find(fuelEntryID, on: req.db) { -// return fuelEntry -// } else { -// throw Abort(.notFound, reason: "FuelEntry not found") -// } -// } else { -// throw Abort(.badRequest, reason: "Failed to extract image data") -// } -// } } struct PageRequest: Content { diff --git a/Sources/App/Migrations/CreateFuelEntry.swift b/Sources/App/Migrations/CreateFuelEntry.swift index 197121d..591435d 100755 --- a/Sources/App/Migrations/CreateFuelEntry.swift +++ b/Sources/App/Migrations/CreateFuelEntry.swift @@ -96,3 +96,19 @@ struct AddDownloadedBool: AsyncMigration { } } +struct AddLoadDateAndPullDate: AsyncMigration { + func prepare(on database: Database) async throws { + try await database.schema("fuel_entries") + .field("loadDate", .datetime) + .field("pullDate", .datetime) + .update() + } + + func revert(on database: Database) async throws { + try await database.schema("fuel_entries") + .deleteField("loadDate") + .deleteField("pullDate") + .update() + } +} + diff --git a/Sources/App/Models/FuelEntry.swift b/Sources/App/Models/FuelEntry.swift index 507dce0..12ff4c9 100755 --- a/Sources/App/Models/FuelEntry.swift +++ b/Sources/App/Models/FuelEntry.swift @@ -26,6 +26,29 @@ final class FuelEntry: Model, Content { let fuelType: String let isOverride: Bool let downloaded: Bool? + let loaddate: Date? + let pulldate: Date? + func toFuelEntry() -> FuelEntry { + var entry = FuelEntry() + entry.id = self.id + entry.timestamp = self.timestamp + entry.userID = self.userID + entry.latitude = self.latitude + entry.longitude = self.longitutde + entry.product = self.product + entry.make = self.make + entry.model = self.model + entry.serialNumber = self.serialNumber + entry.tankID = self.tankID + entry.fuelAmount = self.fuelAmount + entry.fuelType = self.fuelType + entry.isOverride = self.isOverride + entry.downloaded = self.downloaded + entry.loaddate = self.loaddate + entry.pulldate = self.pulldate + entry._$idExists = self.id == nil ? false : true + return entry + } } struct wimage: Content{ @@ -44,6 +67,30 @@ final class FuelEntry: Model, Content { let isOverride: Bool let image: Data? let downloaded: Bool? + let loaddate: Date? + let pulldate: Date? + func toFuelEntry() -> FuelEntry { + var entry = FuelEntry() + entry.id = self.id + entry.timestamp = self.timestamp + entry.userID = self.userID + entry.latitude = self.latitude + entry.longitude = self.longitutde + entry.product = self.product + entry.make = self.make + entry.model = self.model + entry.serialNumber = self.serialNumber + entry.tankID = self.tankID + entry.fuelAmount = self.fuelAmount + entry.fuelType = self.fuelType + entry.isOverride = self.isOverride + entry.image = self.image + entry.downloaded = self.downloaded + entry.loaddate = self.loaddate + entry.pulldate = self.pulldate + entry._$idExists = self.id == nil ? false : true + return entry + } } @ID(key: .id) @@ -91,6 +138,9 @@ final class FuelEntry: Model, Content { @Field(key: "downloaded") var downloaded: Bool? + @Timestamp(key: "loadDate", on: .create) var loaddate: Date? + @Timestamp(key: "pullDate", on: .none) var pulldate: Date? + // var imageData: Data? init() {} diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift index 9946914..ffaf095 100755 --- a/Sources/App/configure.swift +++ b/Sources/App/configure.swift @@ -10,7 +10,7 @@ public func configure(_ app: Application) async throws { app.routes.defaultMaxBodySize = "128mb" // let timeout = HTTPClient.Configuration.Timeout(connect: .seconds(300), read: .seconds(300)) // app.http.client.configuration.timeout = timeout - app.databases.use(.postgres(hostname: "127.0.0.1", port: 5432, username: "mcarman", password: "@ng31F@rm0823262", database: "fueling"), as: .psql) + app.databases.use(.postgres(hostname: "127.0.0.1", port: 5433, username: "mcarman", password: "@ng31F@rm0823262", database: "fueling"), as: .psql) // app.databases.use(DatabaseConfigurationFactory.postgres(configuration: .init( // hostname: Environment.get("DATABASE_HOST") ?? "localhost", // port: Environment.get("DATABASE_PORT").flatMap(Int.init(_:)) ?? 5433, @@ -31,6 +31,7 @@ public func configure(_ app: Application) async throws { app.migrations.add(UpdateTokens1()) app.migrations.add(UpdateUser2_AddTokenfield()) app.migrations.add(UpdateUser3_RemoveTokenfield()) + app.migrations.add(AddLoadDateAndPullDate()) try await app.autoMigrate().get() // register routes