AutoCat/AutoCatCore/Models/DTO/VehicleDto.swift

174 lines
6.4 KiB
Swift

//
// VehicleDto.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 12.06.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import Foundation
public struct VehicleDto: Sendable {
public var brand: VehicleBrandDto?
public var model: VehicleModelDto?
public var color: String?
public var year: Int = 0
public var category: String?
public var engine: VehicleEngineDto?
public var number: String = ""
public var currentNumber: String?
public var vin1: String?
public var vin2: String?
public var sts: String?
public var pts: String?
public var isRightWheel: Bool?
public var isJapanese: Bool?
public var addedDate: TimeInterval = 0
public var updatedDate: TimeInterval = 0
public var addedBy: String = ""
public var photos: [VehiclePhotoDto] = []
public var ownershipPeriods: [VehicleOwnershipPeriodDto] = []
public var events: [VehicleEventDto] = []
public var osagoContracts: [OsagoDto] = []
public var ads: [VehicleAdDto] = []
public var notes: [VehicleNoteDto] = []
public var debugInfo: DebugInfoDto?
public var synchronized: Bool = true
public init() { }
}
extension VehicleDto: Identifiable {
public var id: String { number }
}
extension VehicleDto: Decodable {
enum CodingKeys: String, CodingKey {
case brand
case model
case color
case year
case category
case engine
case number
case currentNumber
case vin1
case vin2
case sts
case pts
case isRightWheel
case isJapanese
case addedDate
case updatedDate
case addedBy
case photos
case ownershipPeriods
case events
case osagoContracts
case ads
case notes
case debugInfo
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.brand = try container.decodeIfPresent(VehicleBrandDto.self, forKey: .brand)
self.model = try container.decodeIfPresent(VehicleModelDto.self, forKey: .model)
self.color = try container.decodeIfPresent(String.self, forKey: .color)
self.year = try container.decodeIfPresent(Int.self, forKey: .year) ?? 0
self.category = try container.decodeIfPresent(String.self, forKey: .category)
self.number = try container.decode(String.self, forKey: .number)
self.engine = try container.decodeIfPresent(VehicleEngineDto.self, forKey: .engine)
self.currentNumber = try container.decodeIfPresent(String.self, forKey: .currentNumber)
self.vin1 = try container.decodeIfPresent(String.self, forKey: .vin1)
self.vin2 = try container.decodeIfPresent(String.self, forKey: .vin2)
self.sts = try container.decodeIfPresent(String.self, forKey: .sts)
self.pts = try container.decodeIfPresent(String.self, forKey: .pts)
self.isRightWheel = try container.decodeIfPresent(Bool.self, forKey: .isRightWheel)
self.isJapanese = try container.decodeIfPresent(Bool.self, forKey: .isJapanese)
self.addedDate = (try container.decode(TimeInterval.self, forKey: .addedDate))/1000
self.addedBy = try container.decode(String.self, forKey: .addedBy)
self.debugInfo = try container.decodeIfPresent(DebugInfoDto.self, forKey: .debugInfo)
self.updatedDate = (try container.decode(TimeInterval.self, forKey: .updatedDate))/1000
self.photos = try container.decodeIfPresent([VehiclePhotoDto].self, forKey: .photos) ?? []
self.ownershipPeriods = try container.decodeIfPresent([VehicleOwnershipPeriodDto].self, forKey: .ownershipPeriods) ?? []
self.osagoContracts = try container.decodeIfPresent([OsagoDto].self, forKey: .osagoContracts) ?? []
self.ads = try container.decodeIfPresent([VehicleAdDto].self, forKey: .ads) ?? []
self.notes = try container.decodeIfPresent([VehicleNoteDto].self, forKey: .notes) ?? []
if let events = try container.decodeIfPresent([VehicleEventDto].self, forKey: .events) {
self.events = events.sorted { $0.date > $1.date }
}
// All vehicles received from API are synchronized by definition
self.synchronized = true
}
}
extension VehicleDto: Exportable {
public static var csvHeader: String {
return "Plate Number, Model, Color, Year, Power (HP), Events, Owners, VIN, STS, PTS, Engine number, Added Date, Updated date, Locations, Notes"
}
public var csvLine: String {
let model = self.brand?.name?.original ?? "<unknown>"
let added = Formatters.standard.string(from: Date(timeIntervalSince1970: self.addedDate))
let updated = Formatters.standard.string(from: Date(timeIntervalSince1970: self.updatedDate))
var eventsString = ""
for event in events {
let location = event.address ?? "lat: \(event.latitude), lon: \(event.longitude)"
let date = Formatters.standard.string(from: Date(timeIntervalSince1970: event.date))
eventsString.append(location + "; " + date + "\r\n")
}
let notesString = self.notes.reduce("") { partialResult, note in
partialResult + note.text + "\r\n"
}
let number = self.currentNumber == nil ? self.number : "\(self.number) (\(self.currentNumber ?? ""))"
return String(format: "%@, \"%@\", %@, %d, %f, %d, %d, %@, %@, %@, %@, \"%@\", \"%@\", \"%@\", \"%@\"",
number,
model,
self.color ?? "",
self.year,
self.engine?.powerHp ?? 0.0,
self.events.count,
self.ownershipPeriods.count,
self.vin1 ?? "",
self.sts ?? "",
self.pts ?? "",
self.engine?.number ?? "",
added,
updated,
eventsString,
notesString)
}
}
extension VehicleDto {
public func getNumber() -> String {
return self.number
}
public var unrecognized: Bool {
return self.brand == nil
}
public var outdated: Bool {
if let current = self.currentNumber {
return current != self.number
} else {
return false
}
}
}