// // FuelEntryController.swift // // // Created by Bill Ryckman on 4/24/23. // import Vapor import FluentKit import FluentSQL struct FuelEntryV2Controller: RouteCollection { func boot(routes: RoutesBuilder) throws { let fuelEntries = routes.grouped("fuel_entries") let tokenAuthMiddleware = Token.authenticator() let guardMiddleware = User.guardMiddleware() let tokenAuthGroup = fuelEntries.grouped(tokenAuthMiddleware, guardMiddleware) tokenAuthGroup.get(use: filterLoadDate) tokenAuthGroup.get("main", use: filterLoadDate) tokenAuthGroup.get("timestamp", use: filterTimeStamp) tokenAuthGroup.get("images", use: filterImages) tokenAuthGroup.get("test", use: filtered) tokenAuthGroup.get("rpm", use: rpm) tokenAuthGroup.get("maxim", use: filtered) tokenAuthGroup.post(use: create) tokenAuthGroup.put( use: softdelete) tokenAuthGroup.group(":fuelEntryID") { fuelEntry in fuelEntry.delete(use: delete) } } func index(req: Request) async throws -> [FuelEntry] { return try await FuelEntry.query(on: req.db).all() } struct filteredreturn: Content{ var fuelentries: [FuelEntry] var pages: Int } struct dload: Content{ var id: UUID } @Sendable func softdelete(req: Request) async throws -> Response { let ids = try req.content.decode([dload].self) var msg: [String] = [] for id in ids { guard let fe = try await FuelEntry.find(id.id, on: req.db) else { msg.append("create error message for item") continue } fe.downloaded = true try await fe.save(on: req.db) } let response = Response(status: .ok, body: "good") return response } fileprivate func queryParamExists(_ req: Request, _ param: String) async throws -> Bool { let x = try req.query.get(Bool.self, at: param) ? true : false print ("x: \(x)") return x } fileprivate func parseDate2(_ dateString: String, _ start: Bool = true) throws -> Date? { let currentDate = Calendar.current.startOfDay(for: Date()) if(dateString.count == 0 && start){ if(start) { return Calendar.current.date(byAdding: .day, value: -2, to: currentDate) } else { return currentDate } } else { let parts = dateString.split(separator: "-") if(parts.count != 3) { return nil } let year = Int(parts[0]) ?? nil if(year == nil) { return nil } let month = Int(parts[1]) ?? nil if(month == nil) { return nil } let day = Int(parts[2]) ?? nil if( day == nil ) { return nil } 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) } } fileprivate func parseDate(_ dateString: String, _ start: Bool = true) throws -> Date? { if(dateString.count == 0){ return nil } else { let parts = dateString.split(separator: "-") if(parts.count != 3) { throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted.")) } let year = Int(parts[0]) ?? nil if(year == nil) { throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Year component must be in the format yyyy and must be an integer")) } let month = Int(parts[1]) ?? nil if(month == nil) { throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Month component must be in the format MM and must be an integer")) } let day = Int(parts[2]) ?? nil if( day == nil ) { throw Abort(.custom(code: 400,reasonPhrase: "Value at path '\(start ? "startdate" : "enddate")' was not of format 'yyyy-MM-dd'. Data found at '\(start ? "startdate" : "enddate")' was not properly formatted. Day component must be in the format dd and must be an integer")) } 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) } } fileprivate func generateSQLNoImage(_ startdate: Date? = nil, _ enddate: Date? = nil) -> SQLQueryString { if(startdate == nil) { return SQLQueryString("select count(*) as total from \"fuel_entries\";") } if(enddate != nil) { return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate) and \"loadDate\" <= \(bind: enddate) ;") } else { return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate);") } } fileprivate func generateSQLImage(_ startdate: Date? = nil, _ enddate: Date? = nil) -> SQLQueryString { var sql: String = "" if(startdate == nil) { return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"image\" is not null;") } if(enddate != nil) { sql = "select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(String(describing: startdate)) and \"loadDate\" <= \(String(describing: enddate)) and \"image\" is not null;" return SQLQueryString("select count(*) as total from \"fuel_entries\" where \"loadDate\" >= \(bind: startdate) and \"loadDate\" <= \(bind: enddate) and \"image\" is not null;") } else { sql = "select count(*) as total from \"fuel_entries\" where \"image\" is not null and \"loadDate\" >= '\(startdate ?? Date())';" return SQLQueryString(sql) } } struct CountRecords: Content { let total: Int } fileprivate func getCount(_ req: Request, _ sql: SQLQueryString) async throws -> Int { print(sql) if let db = req.db as? SQLDatabase { let answer = try await db .raw(sql) .first(decoding: CountRecords.self) return answer?.total ?? 0 } else { return 0 } } fileprivate func getPaging(_ page: Int, _ per: Int, _ records: Int) async throws -> [String: Any] { var vals: [String: Any] = [:] vals["page"] = page vals["per"] = per vals["records"] = records let lastpage = Int(ceil(Double(records) / Double(per))) vals["lastpage"] = (page == lastpage) ? true : false vals["offset"] = ( page * per) - per return vals } fileprivate func getParams(_ req: Request) async throws -> [qparam] { let urlComponents = URLComponents(string: req.url.description)! var params: [qparam] = [] for queryItem in urlComponents.queryItems ?? [] { params.append(qparam(name: queryItem.name, value: queryItem.value ?? "")) } return params } fileprivate func getMaximError(_ req: Request, _ message: String) async throws -> returnObj { let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "") let method = req.method.rawValue let baseurl = "https://fueling.prelub.com" let data : [FuelEntry.Output] = [] let meta = Meta(page: 0, per: 0, total: 0, lastpage: true, error: true, message: message, method: method, baseurl: baseurl, route: rt, querystring: "" ) return returnObj(data: data, meta: meta) } @Sendable func filterLoadDate(req: Request) async throws -> Response { let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "") let method = req.method.rawValue let baseurl = "https://fueling.prelub.com" let qstring = req.query let components: Dictionary = try req.query.decode(Dictionary.self) var startdate : Date? var qs: [String] = [] for component in components { qs.append( "\(component.key)=\(component.value ?? "nil")") } if(try qstring.get(String?.self, at: "startdate") != nil) { startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "") if(startdate == nil) { return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req) } } else { //throw error return try await getMaximError(req, "startdate is required").encodeResponse(for: req) } var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false) if(try qstring.get(String?.self, at: "enddate") != nil) { enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false) if(enddate == nil) { return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req) } } let emptyData: Data? = Data() let page = try qstring.get(Int?.self, at: "page") ?? 1 let per = try qstring.get(Int?.self, at: "per") ?? 10 let qsStr = qs.joined(separator: "&") let records = try await getCount(req, generateSQLNoImage(startdate ?? nil, enddate ?? nil) ) let paging = try await getPaging(page, per, records) let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qsStr)") let query = try await FuelEntry.query(on: req.db) .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) .group(.and) { grp in grp.filter(\.$loaddate >= startdate) if( enddate != nil) { grp.filter(\.$loaddate <= enddate!) } } .sort(\.$loaddate, .ascending) .range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int)) .all().map{ FuelEntry.Output(id: $0.id, timestamp: $0.timestamp, ssid: $0.userID, latitude: $0.latitude, longitude: $0.longitude, product: $0.product, make: $0.make, model: $0.model, serialNumber: $0.serialNumber, tankID: $0.tankID, fuelAmount: $0.fuelAmount, fuelType: $0.fuelType, isOverride: $0.isOverride, image: emptyData, loaddate: $0.loaddate) } let response = returnObj(data: query, meta: meta) return try await response.encodeResponse(for: req) } @Sendable func filterImages(req: Request) async throws -> Response { let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "") let method = req.method.rawValue let baseurl = "https://fueling.prelub.com" let qstring = req.query let components: Dictionary = try req.query.decode(Dictionary.self) var startdate : Date? var qs: [String] = [] for component in components { qs.append( "\(component.key)=\(component.value ?? "nil")") } if(try qstring.get(String?.self, at: "startdate") != nil) { startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "") if(startdate == nil) { return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req) } } else { //throw error return try await getMaximError(req, "startdate is required").encodeResponse(for: req) } var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false) if(try qstring.get(String?.self, at: "enddate") != nil) { enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false) if(enddate == nil) { return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req) } } let page = try qstring.get(Int?.self, at: "page") ?? 1 let per = try qstring.get(Int?.self, at: "per") ?? 5 var records : Int = 0 if(enddate == nil) { records = try await FuelEntry.query(on: req.db) .field(\.$id) .group(.and) { grp in grp.filter(\.$loaddate >= startdate) .filter(\.$image != nil) } .all() .count } else { records = try await FuelEntry.query(on: req.db) .field(\.$id) .group(.and) { grp in grp.filter(\.$loaddate >= startdate) .filter(\.$image != nil) .filter(\.$loaddate <= enddate) } .all() .count } let qsStr = qs.joined(separator: "&") let paging = try await getPaging(page, per, records) let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qsStr)") let query = try await FuelEntry.query(on: req.db) .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) .field(\.$image) .group(.and) { grp in if(enddate != nil) { grp.filter(\.$loaddate >= startdate) .filter(\.$image != nil) .filter(\.$loaddate <= enddate!) } else { grp.filter(\.$loaddate >= startdate) .filter(\.$image != nil) } } .sort(\.$loaddate, .ascending) .range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int)) .all().map{ FuelEntry.Output(id: $0.id, timestamp: $0.timestamp, ssid: $0.userID, latitude: $0.latitude, longitude: $0.longitude, product: $0.product, make: $0.make, model: $0.model, serialNumber: $0.serialNumber, tankID: $0.tankID, fuelAmount: $0.fuelAmount, fuelType: $0.fuelType, isOverride: $0.isOverride, image: $0.image, loaddate: $0.loaddate) } let response = returnObj(data: query, meta: meta) return try await response.encodeResponse(for: req) } @Sendable func filterTimeStamp(req: Request) async throws -> Response { let rt = req.route!.description.replacingOccurrences(of: "GET ", with: "").replacingOccurrences(of: "PUT ", with: "").replacingOccurrences(of: "POST ", with: "").replacingOccurrences(of: "DELETE ", with: "") let method = req.method.rawValue let baseurl = "https://fueling.prelub.com" let qstring = req.query let components: Dictionary = try req.query.decode(Dictionary.self) var startdate : Date? var qs: [String] = [] for component in components { qs.append( "\(component.key)=\(component.value ?? "nil")") } if(try qstring.get(String?.self, at: "startdate") != nil) { startdate = try parseDate2(qstring.get(String?.self, at: "startdate") ?? "") if(startdate == nil) { return try await(getMaximError(req, "startdate is invalid format. YYYY-MM-DD")) .encodeResponse(for: req) } } else { //throw error return try await getMaximError(req, "startdate is required").encodeResponse(for: req) } var enddate : Date?//= try parseDate(qstring.get(String?.self, at: "enddate") ?? "", false) if(try qstring.get(String?.self, at: "enddate") != nil) { enddate = try parseDate2(qstring.get(String?.self, at: "enddate") ?? "", false) if(enddate == nil) { return try await getMaximError(req, "enddate is invalid format. YYYY-MM-DD").encodeResponse(for: req) } } let emptyData: Data? = Data() let page = try qstring.get(Int?.self, at: "page") ?? 1 let per = try qstring.get(Int?.self, at: "per") ?? 10 let records = try await getCount(req, generateSQLNoImage(startdate ?? nil, enddate ?? nil) ) let paging = try await getPaging(page, per, records) let meta = Meta(page: page, per: per, total: records, lastpage: (paging["lastpage"] != nil) ? paging["lastpage"] as! Bool : false, error: false, message: "", method: method, baseurl: baseurl, route: rt, querystring: "?\(qs.joined(separator: "&"))") let query = try await FuelEntry.query(on: req.db) .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) .group(.and) { grp in grp.filter(\.$timestamp >= startdate ?? Calendar.current.startOfDay(for: Calendar.current.date(byAdding: .day, value: -2, to: Date())!)) if( enddate != nil) { grp.filter(\.$timestamp <= enddate!) } } .sort(\.$loaddate, .ascending) .range(lower: (paging["offset"] as! Int), upper: (paging["offset"] as! Int) + (paging["per"] as! Int)) .all().map{ FuelEntry.Output(id: $0.id, timestamp: $0.timestamp, ssid: $0.userID, latitude: $0.latitude, longitude: $0.longitude, product: $0.product, make: $0.make, model: $0.model, serialNumber: $0.serialNumber, tankID: $0.tankID, fuelAmount: $0.fuelAmount, fuelType: $0.fuelType, isOverride: $0.isOverride, image: emptyData, loaddate: $0.loaddate) } let response = returnObj(data: query, meta: meta) return try await response.encodeResponse(for: req) } @Sendable func filtered(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){ 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 { 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) } 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) 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, longitude: 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: previous == true ? img.pulldate : Date()) } if(!previous){ wimage.items.forEach{ _ = $0.toFuelEntry().save(on: req.db) } } 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, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, isOverride: img.isOverride, downloaded: img.downloaded == nil ? false : img.downloaded!, loaddate: img.loaddate == nil ? nil : img.loaddate, pulldate: previous == true ? img.pulldate : Date()) } if(!previous) { 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){ 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) } else { 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) } 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, longitude: 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, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, 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) } } func create(req: Request) async throws -> [FuelEntry] { let fuelEntry = try req.content.decode([FuelEntry].self) for entry in fuelEntry { let data = entry.image ?? nil let length = data?.count if(data == nil || length == 0){ entry.image = nil } } try await fuelEntry.create(on: req.db) return fuelEntry } func delete(req: Request) async throws -> HTTPStatus { guard let fuelEntry = try await FuelEntry.find(req.parameters.get("fuelEntryID"), on: req.db) else { throw Abort(.notFound) } try await fuelEntry.delete(on: req.db) return .noContent } @Sendable func getrpm(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){ 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) } else { 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) } 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, longitude: 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, longitude: img.longitude, product: img.product, make: img.make, model: img.model, serialNumber: img.serialNumber, tankID: img.tankID, fuelAmount: img.fuelAmount, fuelType: img.fuelType, image: nil, 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) } } } struct custMeta: Content { let per: Int let page: Int let total: Int let status: String? } struct PageRequest: Content { var page: Int var size: Int var status: String? // Add any other properties needed for pagination init(page: Int, size: Int, status: String? = nil) { self.page = page self.size = size self.status = status } }