FuelMan/Gas Man/FuelLogs/FuelLogImporterView.swift

169 lines
7.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
import UniformTypeIdentifiers
import CoreData
struct FuelLogImporterView: View {
@Environment(\.managedObjectContext) private var viewContext
@Environment(\.dismiss) var dismiss
// Pass in the selected vehicle from FuelLogListView.
var selectedVehicle: Vehicle?
@State private var isFileImporterPresented = false
@State private var importError: String? = nil
var body: some View {
VStack(spacing: 20) {
Text("Import Fuel Logs from CSV")
.font(.headline)
Button("Select CSV File") {
isFileImporterPresented = true
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
if let error = importError {
Text("Error: \(error)")
.foregroundColor(.red)
}
}
.fileImporter(
isPresented: $isFileImporterPresented,
allowedContentTypes: [UTType.commaSeparatedText],
allowsMultipleSelection: false
) { result in
switch result {
case .success(let urls):
if let url = urls.first {
// Request access to the file URL.
guard url.startAccessingSecurityScopedResource() else {
importError = "Permission error: Unable to access the selected file."
return
}
defer { url.stopAccessingSecurityScopedResource() }
importCSV(from: url)
}
case .failure(let error):
importError = error.localizedDescription
}
}
.navigationTitle("Import Fuel Logs")
}
private func importCSV(from url: URL) {
do {
let data = try Data(contentsOf: url)
guard let csvString = String(data: data, encoding: .utf8) else {
importError = "Could not decode file."
return
}
// Assume the first line is a header.
let lines = csvString.components(separatedBy: "\n").filter { !$0.isEmpty }
guard lines.count > 1 else {
importError = "CSV file appears empty."
return
}
// For this example, assume the CSV columns are:
// date,odometer,fuelVolume,cost,locationCoordinates,locationName,octane,pricePerGalon,fullTank
let header = lines[0].components(separatedBy: ",")
for line in lines.dropFirst() {
let fields = line.components(separatedBy: ",")
if fields.count < header.count {
print("Skipping row: \(line) not enough fields (found \(fields.count), expected \(header.count))")
continue
}
// Trim whitespace for each field
let dateStr = fields[0].trimmingCharacters(in: .whitespacesAndNewlines)
let odometerStr = fields[1].trimmingCharacters(in: .whitespacesAndNewlines)
let fuelVolumeStr = fields[2].trimmingCharacters(in: .whitespacesAndNewlines)
let costStr = fields[3].trimmingCharacters(in: .whitespacesAndNewlines)
let locationCoordinates = fields[4].trimmingCharacters(in: .whitespacesAndNewlines)
let locationName = fields[5].trimmingCharacters(in: .whitespacesAndNewlines)
let octaneStr = fields[6].trimmingCharacters(in: .whitespacesAndNewlines)
let pricePerGalonStr = fields[7].trimmingCharacters(in: .whitespacesAndNewlines)
let fullTankStr = fields[8].trimmingCharacters(in: .whitespacesAndNewlines)
// Debug prints for each field
print("Row: \(line)")
print("Date: \(dateStr), Odometer: \(odometerStr), Fuel Volume: \(fuelVolumeStr), Cost: \(costStr), Octane: \(octaneStr), Price/Gal: \(pricePerGalonStr), FullTank: \(fullTankStr)")
// Setup date formatter adjust format as needed.
var parsedDate: Date?
let formats = ["M/d/yyyy", "yyyy-MM-dd HH:mm"]
for format in formats {
let formatter = DateFormatter()
formatter.dateFormat = format
formatter.locale = Locale(identifier: "en_US_POSIX")
if let date = formatter.date(from: dateStr) {
parsedDate = date
break
}
}
guard let date = parsedDate else {
print("Skipping row invalid date: \(dateStr)")
continue
}
guard let odometer = Double(odometerStr) else {
print("Skipping row invalid odometer: \(odometerStr)")
continue
}
guard let fuelVolume = Double(fuelVolumeStr) else {
print("Skipping row invalid fuel volume: \(fuelVolumeStr)")
continue
}
guard let cost = Double(costStr) else {
print("Skipping row invalid cost: \(costStr)")
continue
}
guard let octane = Int16(octaneStr) else {
print("Skipping row invalid octane: \(octaneStr)")
continue
}
guard let pricePerGalon = Double(pricePerGalonStr) else {
print("Skipping row invalid price per gallon: \(pricePerGalonStr)")
continue
}
// If all conversions succeed, create the fuel log.
let fullTank = (fullTankStr.lowercased() == "true" || fullTankStr == "1")
let newLog = FuelLog(context: viewContext)
newLog.id = UUID()
newLog.date = date
newLog.odometer = odometer
newLog.fuelVolume = fuelVolume
newLog.cost = cost
newLog.locationCoordinates = locationCoordinates
newLog.locationName = locationName
newLog.octane = octane
newLog.pricePerGalon = pricePerGalon
newLog.fullTank = fullTank
// Optionally assign vehicle if needed.
print("Assigning vehicle: \(selectedVehicle?.make ?? "none")")
newLog.vehicle = selectedVehicle
print("Imported row successfully.")
}
try viewContext.save()
dismiss()
} catch {
importError = error.localizedDescription
}
}
}
struct FuelLogImporterView_Previews: PreviewProvider {
static var previews: some View {
FuelLogImporterView(selectedVehicle: nil)
}
}