Add log view, manual transfer

This commit is contained in:
Christoph Hagen
2023-06-14 16:16:56 +02:00
parent 01a3aac91b
commit 2cb94a12be
12 changed files with 237 additions and 74 deletions

View File

@@ -0,0 +1,48 @@
import Foundation
let log = Log()
final class Log: ObservableObject {
private let df: DateFormatter
init() {
df = .init()
df.dateStyle = .short
df.timeStyle = .medium
}
enum Level: String {
case info = "INFO"
case warning = "WARN"
case error = "ERROR"
}
@Published
var logEntries: [LogEntry] = []
func info(_ message: String) {
log(.info, message)
}
func warning(_ message: String) {
log(.warning, message)
}
func error(_ message: String) {
log(.error, message)
}
func log(_ level: Level, _ message: String) {
let entry = LogEntry(level: level, message: message)
logEntries.insert(entry, at: 0)
print(entry)
}
}
extension Log.Level: CustomStringConvertible {
var description: String {
rawValue
}
}

View File

@@ -0,0 +1,33 @@
import Foundation
struct LogEntry: Identifiable {
let id: TimeInterval
let date: Date
let level: Log.Level
let message: String
init(date: Date = Date(), level: Log.Level, message: String) {
self.id = date.timeIntervalSince1970
self.date = date
self.level = level
self.message = message
}
}
private let df: DateFormatter = {
let df = DateFormatter()
df.dateStyle = .short
df.timeStyle = .medium
return df
}()
extension LogEntry: CustomStringConvertible {
var description: String {
"[\(df.string(from: date))][\(level.rawValue)] \(message)"
}
}

View File

@@ -57,6 +57,7 @@ final class TemperatureStorage: ObservableObject {
if lastMeasurements.isEmpty {
loadLastMeasurements()
loadDailyCounts()
} else {
setDailyCounts(from: lastMeasurements)
}
@@ -71,7 +72,7 @@ final class TemperatureStorage: ObservableObject {
do {
try fm.createDirectory(at: storageFolder, withIntermediateDirectories: true)
} catch {
print("Failed to create folder: \(error)")
log.error("Failed to create folder: \(error)")
}
}
@@ -99,17 +100,20 @@ final class TemperatureStorage: ObservableObject {
let dateIndexOfStart = startDate.dateIndex
guard todayIndex != dateIndexOfStart else {
recentMeasurements = todayValues
log.info("Loaded \(recentMeasurements.count) recent measurements")
return
}
let yesterdayValues = loadMeasurements(for: dateIndexOfStart)
.filter { $0.date >= startDate }
recentMeasurements = yesterdayValues + todayValues
log.info("Loaded \(recentMeasurements.count) recent measurements")
}
private func updateLastMeasurements(_ measurements: [TemperatureMeasurement]) {
let startDate = Date().addingTimeInterval(-lastValueInterval).seconds
let new = recentMeasurements + measurements
recentMeasurements = Array(new.drop { $0.id < startDate })
log.info("\(recentMeasurements.count) recent measurements (of \(measurements.count) new entries)")
}
private func loadMeasurements(for date: Date) -> [TemperatureMeasurement] {
@@ -123,30 +127,30 @@ final class TemperatureStorage: ObservableObject {
private func loadMeasurements(from fileName: String) -> [TemperatureMeasurement] {
let fileUrl = fileUrl(for: fileName)
guard fm.fileExists(atPath: fileUrl.path) else {
print("No measurements for \(fileName)")
log.info("No measurements for \(fileName)")
return []
}
do {
let content = try Data(contentsOf: fileUrl)
let points: [TemperatureMeasurement] = try BinaryDecoder.decode(from: content)
print("Loaded \(points.count) points for \(fileName)")
log.info("Loaded \(points.count) points from \(fileName)")
return points
} catch {
print("Failed to read file \(fileName): \(error)")
log.error("Failed to read file \(fileName): \(error)")
return []
}
}
func add(_ measurements: [TemperatureMeasurement]) {
let lastDate = self.newestMeasurementDate.seconds
let newValues = measurements
.filter { $0.id > lastDate }
.splitByDate()
let newerValues = measurements.filter { $0.id > lastDate }
let newValues = newerValues.splitByDate()
log.info("Adding \(newValues.count) of \(measurements.count) measurements")
for (dateIndex, values) in newValues {
let count = saveNew(values, for: dateIndex)
setDailyCount(count, for: dateIndex)
print("Day \(dateIndex): \(count) values")
//log.info("Day \(dateIndex): \(count) values")
}
saveDailyCounts()
updateLastMeasurements(measurements)
@@ -155,7 +159,7 @@ final class TemperatureStorage: ObservableObject {
func removeMeasurements(for dateIndex: Int) {
let fileUrl = fileUrl(for: dateIndex)
guard fm.fileExists(atPath: fileUrl.path) else {
print("No measurements for \(fileUrl.lastPathComponent)")
log.warning("No measurements for \(fileUrl.lastPathComponent)")
return
}
do {
@@ -163,7 +167,7 @@ final class TemperatureStorage: ObservableObject {
dailyMeasurementCounts = dailyMeasurementCounts.filter { $0.dateIndex != dateIndex }
recentMeasurements = recentMeasurements.filter { $0.date.dateIndex != dateIndex }
} catch {
print("Failed to delete \(fileUrl.lastPathComponent): \(error)")
log.error("Failed to delete \(fileUrl.lastPathComponent): \(error)")
}
}
@@ -183,7 +187,7 @@ final class TemperatureStorage: ObservableObject {
let data = try BinaryEncoder.encode(measurements.sorted())
try data.write(to: fileUrl)
} catch {
print("Failed to save \(fileName): \(error)")
log.error("Failed to save \(fileName): \(error)")
}
}
@@ -219,7 +223,7 @@ final class TemperatureStorage: ObservableObject {
let data = try Data(contentsOf: overviewFileUrl)
dailyMeasurementCounts = try BinaryDecoder.decode(from: data)
} catch {
print("Failed to load overview: \(error)")
log.error("Failed to load overview: \(error)")
}
}
@@ -228,7 +232,7 @@ final class TemperatureStorage: ObservableObject {
let data = try BinaryEncoder.encode(dailyMeasurementCounts)
try data.write(to: overviewFileUrl)
} catch {
print("Failed to write overview: \(error)")
log.error("Failed to write overview: \(error)")
}
}
@@ -242,9 +246,12 @@ final class TemperatureStorage: ObservableObject {
func recalculateDailyCounts() {
do {
let newValues: [Int: Int] = try fm.contentsOfDirectory(atPath: storageFolder.path)
let files = try fm.contentsOfDirectory(atPath: storageFolder.path)
let newValues: [Int: Int] = files
.reduce(into: [:]) { counts, fileName in
guard let dateIndex = Int(fileName) else {
let dateString = fileName.replacingOccurrences(of: ".bin", with: "")
guard let dateIndex = Int(dateString) else {
log.warning("Found file with invalid name \(fileName)")
return
}
counts[dateIndex] = loadMeasurements(from: fileName).count
@@ -253,9 +260,10 @@ final class TemperatureStorage: ObservableObject {
self.dailyMeasurementCounts = newValues
.map { .init(dateIndex: $0.key, count: $0.value) }
.sorted()
log.info("Daily counts recalculated from \(files.count) files")
}
} catch {
print("Failed to load daily counts: \(error)")
log.error("Failed to load daily counts: \(error)")
}
}