192 lines
6.9 KiB
Swift
192 lines
6.9 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, Equatable, Hashable {
|
|
|
|
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() { }
|
|
|
|
public init(number: String) {
|
|
|
|
self.number = number
|
|
self.addedDate = Date().timeIntervalSince1970
|
|
self.updatedDate = self.addedDate
|
|
self.synchronized = false
|
|
}
|
|
}
|
|
|
|
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 {
|
|
|
|
// For testing
|
|
public init(number: String, color: String? = nil) {
|
|
self.number = number
|
|
self.color = color
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
public var needSync: Bool {
|
|
!synchronized && !unrecognized
|
|
}
|
|
}
|