Ver código fonte

Added V2.1

v2_uploadImages
Mike Carman 6 horas atrás
pai
commit
5633bb9b72
9 arquivos alterados com 966 adições e 8 exclusões
  1. BIN
      .swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate
  2. +23
    -7
      .swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
  3. +793
    -1
      Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift
  4. +67
    -0
      Sources/App/DTO/V2.1/FuelEntryDTO.swift
  5. +5
    -0
      Sources/App/Helpers/PageHelper.swift
  6. +14
    -0
      Sources/App/Migrations/CreateFuelEntry.swift
  7. +61
    -0
      Sources/App/Models/FuelEntry.swift
  8. +1
    -0
      Sources/App/configure.swift
  9. +2
    -0
      Sources/App/routes.swift

BIN
.swiftpm/xcode/package.xcworkspace/xcuserdata/mcarman.xcuserdatad/UserInterfaceState.xcuserstate Ver arquivo


+ 23
- 7
.swiftpm/xcode/xcuserdata/mcarman.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist Ver arquivo

@@ -14,8 +14,8 @@
filePath = "Sources/App/Models/FuelEntry.swift" filePath = "Sources/App/Models/FuelEntry.swift"
startingColumnNumber = "9223372036854775807" startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807"
startingLineNumber = "215"
endingLineNumber = "215"
startingLineNumber = "240"
endingLineNumber = "240"
landmarkName = "init(id:timestamp:userID:latitude:longitude:product:make:model:serialNumber:tankID:fuelAmount:fuelType:isOverride:tabletserial:)" landmarkName = "init(id:timestamp:userID:latitude:longitude:product:make:model:serialNumber:tankID:fuelAmount:fuelType:isOverride:tabletserial:)"
landmarkType = "7"> landmarkType = "7">
</BreakpointContent> </BreakpointContent>
@@ -30,8 +30,8 @@
filePath = "Sources/App/Models/FuelEntry.swift" filePath = "Sources/App/Models/FuelEntry.swift"
startingColumnNumber = "9223372036854775807" startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807"
startingLineNumber = "195"
endingLineNumber = "195"
startingLineNumber = "220"
endingLineNumber = "220"
landmarkName = "init(id:timestamp:userID:latitude:longitude:image:product:make:model:serialNumber:tankID:fuelAmount:fuelType:isOverride:tabletserial:)" landmarkName = "init(id:timestamp:userID:latitude:longitude:image:product:make:model:serialNumber:tankID:fuelAmount:fuelType:isOverride:tabletserial:)"
landmarkType = "7"> landmarkType = "7">
</BreakpointContent> </BreakpointContent>
@@ -40,14 +40,14 @@
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent <BreakpointContent
uuid = "71BCB6D1-5BA1-44A4-B4BB-3CF6616E1B45" uuid = "71BCB6D1-5BA1-44A4-B4BB-3CF6616E1B45"
shouldBeEnabled = "Yes"
shouldBeEnabled = "No"
ignoreCount = "0" ignoreCount = "0"
continueAfterRunningActions = "No" continueAfterRunningActions = "No"
filePath = "Sources/App/Models/FuelEntry.swift" filePath = "Sources/App/Models/FuelEntry.swift"
startingColumnNumber = "9223372036854775807" startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807"
startingLineNumber = "175"
endingLineNumber = "175"
startingLineNumber = "200"
endingLineNumber = "200"
landmarkName = "init(id:timestamp:userID:latitude:longitude:image:product:make:model:serialNumber:tankID:fuelAmount:fuelType:isOverride:downloaded:tabletserial:)" landmarkName = "init(id:timestamp:userID:latitude:longitude:image:product:make:model:serialNumber:tankID:fuelAmount:fuelType:isOverride:downloaded:tabletserial:)"
landmarkType = "7"> landmarkType = "7">
</BreakpointContent> </BreakpointContent>
@@ -148,5 +148,21 @@
landmarkType = "7"> landmarkType = "7">
</BreakpointContent> </BreakpointContent>
</BreakpointProxy> </BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "EDB87104-3630-4689-B6E2-9D8D6579F668"
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Sources/App/configure.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "54"
endingLineNumber = "54"
landmarkName = "configure(_:)"
landmarkType = "9">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints> </Breakpoints>
</Bucket> </Bucket>

+ 793
- 1
Sources/App/Controllers/V2.1/FuelEntryControllerV2_1.swift Ver arquivo

@@ -1,7 +1,799 @@
// //
// FuelEntryControllerV2.1.swift
// FuelEntryControllerV2_1.swift
// Fueling // Fueling
// //
// Created by Michiel Carman on 9/15/26. // Created by Michiel Carman on 9/15/26.
// //


import Vapor
import FluentKit
import FluentSQL

struct FuelEntryV2ControllerV2_1: 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)
}
}
@Sendable
func index(req: Request) async throws -> Response {
return try await FuelEntry.query(on: req.db).all().map{
try FuelEntry.V2_1_Output.init(from: $0)
}.encodeResponse(for: req)
}
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 -> returnObjV2_1 {
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.V2_1_Output] = []
let meta = Meta(page: 0, per: 0, total: 0, lastpage: true, error: true, message: message, method: method, baseurl: baseurl, route: rt, querystring: "" )
return returnObjV2_1(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<String, String?> = try req.query.decode(Dictionary<String, String?>.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)
.field(\.$tabletserial)
.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{
try FuelEntry.V2_1_Output.init(from: $0)
}
let response = returnObjV2_1(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<String, String?> = try req.query.decode(Dictionary<String, String?>.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)
.field(\.$tabletserial)
.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{
try FuelEntry.V2_1_Output.init(from: $0)
}
let response = returnObjV2_1(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<String, String?> = try req.query.decode(Dictionary<String, String?>.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)
.field(\.$tabletserial)
.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{
try FuelEntry.V2_1_Output.init(from: $0)
}
let response = returnObjV2_1(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)
.field(\.$tabletserial)
//.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(\.$tabletserial)
.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(), useremail: img.tabletserial)
}
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(), useremail: img.tabletserial)
}
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
}
if(entry.id != nil){
let dbentry = try await FuelEntry.find(entry.id!, on: req.db)
if(dbentry != nil){
entry._$idExists = true
}
}
entry.save(on: req.db).whenFailure({ error in
})
}
// 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(), useremail: img.tabletserial)
}
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
// }
//}











+ 67
- 0
Sources/App/DTO/V2.1/FuelEntryDTO.swift Ver arquivo

@@ -0,0 +1,67 @@
//
// FuelEntryDTO.swift
// Fueling
//
// Created by Michiel Carman on 9/15/26.
//

import Vapor

extension FuelEntry {
struct V2_1_Input: Content {
var id: UUID?
var tabletSerial: String?
var fuelAmount: Double?
var fuelType: String?
var timeStamp: Date?
var appVersion: String?
var longitude: Double?
var product: String?
var latitude: Double?
var serialNumber: String?
var isOverride: Bool?
var make: String?
var model: String?
var tankId: String?
var image: Data?
}
struct V2_1_Output: Content {
var id: UUID?
var tabletSerial: String?
var fuelAmount: Double?
var fuelType: String?
var timeStamp: Date?
var appVersion: String?
var longitude: Double?
var product: String?
var latitude: Double?
var serialNumber: String?
var isOverride: Bool?
var make: String?
var model: String?
var tankId: String?
var image: Data?
init(from decoder: FuelEntry, img: Bool = false) throws {
self.id = decoder.id
self.tabletSerial = decoder.tabletserial
self.fuelAmount = decoder.fuelAmount
self.fuelType = decoder.fuelType
self.timeStamp = decoder.timestamp
self.appVersion = decoder.appVersion
self.longitude = decoder.longitude
self.product = decoder.product
self.latitude = decoder.latitude
self.serialNumber = decoder.serialNumber
self.isOverride = decoder.isOverride
self.make = decoder.make
self.model = decoder.model
self.tankId = decoder.tankID
if img {
self.image = decoder.image
}
}
}
}

+ 5
- 0
Sources/App/Helpers/PageHelper.swift Ver arquivo

@@ -24,6 +24,11 @@ final class PageHelper {
} }


public struct returnObjV2_1: Content{
let data: [FuelEntry.V2_1_Output]
let meta: Meta
}

public struct returnObj: Content{ public struct returnObj: Content{
let data: [FuelEntry.Output] let data: [FuelEntry.Output]
let meta: Meta let meta: Meta


+ 14
- 0
Sources/App/Migrations/CreateFuelEntry.swift Ver arquivo

@@ -96,6 +96,20 @@ struct AddDownloadedBool: AsyncMigration {
} }
} }


struct AddAppVersion: AsyncMigration {
func prepare(on database: Database) async throws {
try await database.schema("fuel_entries")
.field("app_version", .string)
.update()
}
func revert(on database: Database) async throws {
try await database.schema("fuel_entries")
.deleteField("app_version")
.update()
}
}

struct AddLoadDateAndPullDate: AsyncMigration { struct AddLoadDateAndPullDate: AsyncMigration {
func prepare(on database: Database) async throws { func prepare(on database: Database) async throws {
try await database.schema("fuel_entries") try await database.schema("fuel_entries")


+ 61
- 0
Sources/App/Models/FuelEntry.swift Ver arquivo

@@ -164,12 +164,37 @@ final class FuelEntry: Model, Content {
@Field(key: "tablet_serial") @Field(key: "tablet_serial")
var tabletserial: String? var tabletserial: String?
@Field(key: "app_version")
var appVersion: String?
@Timestamp(key: "loadDate", on: .create) var loaddate: Date? @Timestamp(key: "loadDate", on: .create) var loaddate: Date?
@Timestamp(key: "pullDate", on: .none) var pulldate: Date? @Timestamp(key: "pullDate", on: .none) var pulldate: Date?
// var imageData: Data? // var imageData: Data?


init() {} init() {}
init(id: UUID? = nil, timestamp: Date, userID: String, latitude: Double, longitude: Double, image: Data?, product: String, make: String, model: String, serialNumber: String, tankID: String, fuelAmount: Double, fuelType: String, isOverride: Bool, downloaded: Bool? = nil, tabletserial: String? = nil, appversion: String? = nil) { //imageData: Data?
self.id = id
self.timestamp = timestamp
self.userID = userID
self.latitude = latitude
self.longitude = longitude
self.image = image ?? nil
self.product = product
self.make = make
self.model = model
self.serialNumber = serialNumber
self.tankID = tankID
self.fuelAmount = fuelAmount
self.fuelType = fuelType
self.isOverride = isOverride
self.downloaded = downloaded ?? nil
self.image = image ?? nil
self.tabletserial = tabletserial ?? nil
self.appVersion = appversion ?? nil
// self.imageData = imageData
}


init(id: UUID? = nil, timestamp: Date, userID: String, latitude: Double, longitude: Double, image: Data?, product: String, make: String, model: String, serialNumber: String, tankID: String, fuelAmount: Double, fuelType: String, isOverride: Bool, downloaded: Bool? = nil, tabletserial: String? = nil) { //imageData: Data? init(id: UUID? = nil, timestamp: Date, userID: String, latitude: Double, longitude: Double, image: Data?, product: String, make: String, model: String, serialNumber: String, tankID: String, fuelAmount: Double, fuelType: String, isOverride: Bool, downloaded: Bool? = nil, tabletserial: String? = nil) { //imageData: Data?
self.id = id self.id = id
@@ -229,4 +254,40 @@ final class FuelEntry: Model, Content {
self.tabletserial = tabletserial ?? nil self.tabletserial = tabletserial ?? nil
// self.imageData = imageData // self.imageData = imageData
} }
// struct V2_1_Input: Content {
// var id: UUID?
// var tabletSerial: Date?
// var fuelAmount: Double?
// var fuelType: String?
// var timeStamp: Date?
// var appVersion: String?
// var longitude: String?
// var product: String?
// var latitude: String?
// var serialNumber: String?
// var isOverride: Bool?
// var make: String?
// var model: String?
// var tankId: String?
// var image: Data?
// }
init(from decoder: FuelEntry.V2_1_Input) throws {
self.id = decoder.id
self.timestamp = decoder.timeStamp ?? Date()
self.fuelAmount = decoder.fuelAmount ?? 0
self.fuelType = decoder.fuelType ?? ""
self.appVersion = decoder.appVersion ?? ""
self.longitude = decoder.longitude ?? 0
self.product = decoder.product ?? ""
self.latitude = decoder.latitude ?? 0
self.serialNumber = decoder.serialNumber ?? ""
self.isOverride = decoder.isOverride ?? false
self.make = decoder.make ?? ""
self.model = decoder.model ?? ""
self.tankID = decoder.tankId ?? ""
self.image = decoder.image
}
} }

+ 1
- 0
Sources/App/configure.swift Ver arquivo

@@ -51,6 +51,7 @@ public func configure(_ app: Application) async throws {
app.migrations.add(UpdateUser3_RemoveTokenfield()) app.migrations.add(UpdateUser3_RemoveTokenfield())
app.migrations.add(AddLoadDateAndPullDate()) app.migrations.add(AddLoadDateAndPullDate())
app.migrations.add(AddDeviceSerial()) app.migrations.add(AddDeviceSerial())
app.migrations.add(AddAppVersion())
try await app.autoMigrate().get() try await app.autoMigrate().get()


// register routes // register routes


+ 2
- 0
Sources/App/routes.swift Ver arquivo

@@ -4,6 +4,8 @@ func routes(_ app: Application) throws {
app.routes.caseInsensitive = true app.routes.caseInsensitive = true
app.routes.defaultMaxBodySize = "64mb"; app.routes.defaultMaxBodySize = "64mb";
let V2_1 = app.grouped("api").grouped("v2.1")
try V2_1.register(collection: FuelEntryV2ControllerV2_1())
let v2 = app.grouped("api").grouped("v2") let v2 = app.grouped("api").grouped("v2")
try v2.register(collection: FuelEntryV2Controller()) try v2.register(collection: FuelEntryV2Controller())
try v2.register(collection: UserController()) try v2.register(collection: UserController())


Carregando…
Cancelar
Salvar