Refactor location samples table
This commit is contained in:
@@ -15,6 +15,10 @@ final class HealthDatabase: ObservableObject {
|
||||
|
||||
private let workoutsTable: WorkoutsTable
|
||||
|
||||
private let locationSamples: LocationSeriesDataTable
|
||||
|
||||
private let dataSeries: DataSeriesTable
|
||||
|
||||
@Published
|
||||
var workouts: [Workout] = []
|
||||
|
||||
@@ -28,6 +32,8 @@ final class HealthDatabase: ObservableObject {
|
||||
self.database = database
|
||||
self.samples = .init(database: database)
|
||||
self.workoutsTable = .init(database: database)
|
||||
self.locationSamples = .init(database: database)
|
||||
self.dataSeries = .init(database: database)
|
||||
|
||||
DispatchQueue.global().async {
|
||||
self.readAllWorkouts()
|
||||
@@ -48,11 +54,11 @@ final class HealthDatabase: ObservableObject {
|
||||
}
|
||||
|
||||
func locationSamples(for activity: HKWorkoutActivity) throws -> [LocationSample] {
|
||||
try LocationSample.locationSamples(from: activity.startDate, to: activity.currentEndDate, in: database)
|
||||
try locationSamples.locationSamples(from: activity.startDate, to: activity.currentEndDate)
|
||||
}
|
||||
|
||||
func locationSampleCount(for activity: HKWorkoutActivity) throws -> Int {
|
||||
try LocationSample.locationSampleCount(from: activity.startDate, to: activity.currentEndDate, in: database)
|
||||
try locationSamples.locationSampleCount(from: activity.startDate, to: activity.currentEndDate)
|
||||
}
|
||||
|
||||
func samples(for activity: HKWorkoutActivity) throws -> [Sample.DataType : [Sample]] {
|
||||
@@ -103,6 +109,7 @@ final class HealthDatabase: ObservableObject {
|
||||
func createTables() throws {
|
||||
try samples.createAll()
|
||||
try workoutsTable.createAll()
|
||||
try locationSamples.create(references: dataSeries)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,92 +0,0 @@
|
||||
import Foundation
|
||||
import SQLite
|
||||
import CoreLocation
|
||||
|
||||
typealias LocationSample = CLLocation
|
||||
|
||||
extension LocationSample {
|
||||
|
||||
private static let table = Table("location_series_data")
|
||||
|
||||
/// `location_series_data[series_identifier]` <-> `workout_activities[ROW_ID]`
|
||||
private static let columnSeriesIdentifier = Expression<Int>("series_identifier")
|
||||
|
||||
private static let columnTimestamp = Expression<Double>("timestamp")
|
||||
|
||||
private static let columnLongitude = Expression<Double>("longitude")
|
||||
|
||||
private static let columnLatitude = Expression<Double>("latitude")
|
||||
|
||||
private static let columnAltitude = Expression<Double>("altitude")
|
||||
|
||||
private static let columnSpeed = Expression<Double>("speed")
|
||||
|
||||
private static let columnCourse = Expression<Double>("course")
|
||||
|
||||
private static let columnHorizontalAccuracy = Expression<Double>("horizontal_accuracy")
|
||||
|
||||
private static let columnVerticalAccuracy = Expression<Double>("vertical_accuracy")
|
||||
|
||||
private static let columnSpeedAccuracy = Expression<Double>("speed_accuracy")
|
||||
|
||||
private static let columnCourseAccuracy = Expression<Double>("course_accuracy")
|
||||
|
||||
private static let columnSignalEnvironment = Expression<Double>("signal_environment")
|
||||
|
||||
static func locationSamples(for seriesId: Int, in database: Database) throws -> [LocationSample] {
|
||||
try database.prepare(table.filter(columnSeriesIdentifier == seriesId)).map(location)
|
||||
}
|
||||
|
||||
static func locationSampleCount(for seriesId: Int, in database: Database) throws -> Int {
|
||||
try database.scalar(table.filter(columnSeriesIdentifier == seriesId).count)
|
||||
}
|
||||
|
||||
static func locationSamples(from start: Date, to end: Date, in database: Database) throws -> [LocationSample] {
|
||||
let startTime = start.timeIntervalSinceReferenceDate
|
||||
let endTime = end.timeIntervalSinceReferenceDate
|
||||
return try database.prepare(table.filter(columnTimestamp >= startTime && columnTimestamp <= endTime)).map(location)
|
||||
}
|
||||
|
||||
static func locationSampleCount(from start: Date, to end: Date, in database: Database) throws -> Int {
|
||||
let startTime = start.timeIntervalSinceReferenceDate
|
||||
let endTime = end.timeIntervalSinceReferenceDate
|
||||
return try database.scalar(table.filter(columnTimestamp >= startTime && columnTimestamp <= endTime).count)
|
||||
}
|
||||
|
||||
private static func location(row: Row) -> LocationSample {
|
||||
.init(
|
||||
coordinate: .init(
|
||||
latitude: row[columnLatitude],
|
||||
longitude: row[columnLongitude]),
|
||||
altitude: row[columnAltitude],
|
||||
horizontalAccuracy: row[columnHorizontalAccuracy],
|
||||
verticalAccuracy: row[columnHorizontalAccuracy],
|
||||
course: row[columnCourse],
|
||||
courseAccuracy: row[columnCourseAccuracy],
|
||||
speed: row[columnSpeed],
|
||||
speedAccuracy: row[columnSpeedAccuracy],
|
||||
timestamp: .init(timeIntervalSinceReferenceDate: row[columnTimestamp]),
|
||||
sourceInfo: .init())
|
||||
}
|
||||
|
||||
static func createTable(in database: Connection) throws {
|
||||
try database.execute("CREATE TABLE location_series_data (series_identifier INTEGER NOT NULL REFERENCES data_series(hfd_key) DEFERRABLE INITIALLY DEFERRED, timestamp REAL NOT NULL, longitude REAL NOT NULL, latitude REAL NOT NULL, altitude REAL NOT NULL, speed REAL NOT NULL, course REAL NOT NULL, horizontal_accuracy REAL NOT NULL, vertical_accuracy REAL NOT NULL, speed_accuracy REAL NOT NULL, course_accuracy REAL NOT NULL, signal_environment INTEGER NOT NULL, PRIMARY KEY (series_identifier, timestamp)) WITHOUT ROWID")
|
||||
}
|
||||
|
||||
static func insert(_ sample: LocationSample, in database: Connection, seriesId: Int) throws {
|
||||
try database.run(table.insert(
|
||||
columnSeriesIdentifier <- seriesId,
|
||||
columnTimestamp <- sample.timestamp.timeIntervalSinceReferenceDate,
|
||||
columnLongitude <- sample.coordinate.longitude,
|
||||
columnLatitude <- sample.coordinate.latitude,
|
||||
columnAltitude <- sample.altitude,
|
||||
columnSpeed <- sample.speed,
|
||||
columnCourse <- sample.course,
|
||||
columnHorizontalAccuracy <- sample.horizontalAccuracy,
|
||||
columnHorizontalAccuracy <- sample.verticalAccuracy,
|
||||
columnSpeedAccuracy <- sample.speedAccuracy,
|
||||
columnCourseAccuracy <- sample.courseAccuracy,
|
||||
columnSignalEnvironment <- 1
|
||||
))
|
||||
}
|
||||
}
|
13
HealthImport/Model/Tables/DataSeriesTable.swift
Normal file
13
HealthImport/Model/Tables/DataSeriesTable.swift
Normal file
@@ -0,0 +1,13 @@
|
||||
import Foundation
|
||||
import SQLite
|
||||
|
||||
struct DataSeriesTable {
|
||||
|
||||
private let database: Connection
|
||||
|
||||
init(database: Connection) {
|
||||
self.database = database
|
||||
}
|
||||
|
||||
let table = Table("data_series")
|
||||
}
|
98
HealthImport/Model/Tables/LocationSeriesDataTable.swift
Normal file
98
HealthImport/Model/Tables/LocationSeriesDataTable.swift
Normal file
@@ -0,0 +1,98 @@
|
||||
import Foundation
|
||||
import SQLite
|
||||
import CoreLocation
|
||||
|
||||
typealias LocationSample = CLLocation
|
||||
|
||||
struct LocationSeriesDataTable {
|
||||
|
||||
private let database: Connection
|
||||
|
||||
init(database: Connection) {
|
||||
self.database = database
|
||||
}
|
||||
|
||||
let table = Table("location_series_data")
|
||||
|
||||
/// `location_series_data[series_identifier]` <-> `workout_activities[ROW_ID]`
|
||||
let seriesIdentifier = Expression<Int>("series_identifier")
|
||||
|
||||
let timestamp = Expression<Double>("timestamp")
|
||||
|
||||
let longitude = Expression<Double>("longitude")
|
||||
|
||||
let latitude = Expression<Double>("latitude")
|
||||
|
||||
let altitude = Expression<Double>("altitude")
|
||||
|
||||
let speed = Expression<Double>("speed")
|
||||
|
||||
let course = Expression<Double>("course")
|
||||
|
||||
let horizontalAccuracy = Expression<Double>("horizontal_accuracy")
|
||||
|
||||
let verticalAccuracy = Expression<Double>("vertical_accuracy")
|
||||
|
||||
let speedAccuracy = Expression<Double>("speed_accuracy")
|
||||
|
||||
let courseAccuracy = Expression<Double>("course_accuracy")
|
||||
|
||||
let signalEnvironment = Expression<Double>("signal_environment")
|
||||
|
||||
func locationSamples(for seriesId: Int) throws -> [LocationSample] {
|
||||
try database.prepare(table.filter(seriesIdentifier == seriesId)).map(location)
|
||||
}
|
||||
|
||||
func locationSampleCount(for seriesId: Int) throws -> Int {
|
||||
try database.scalar(table.filter(seriesIdentifier == seriesId).count)
|
||||
}
|
||||
|
||||
func locationSamples(from start: Date, to end: Date) throws -> [LocationSample] {
|
||||
let startTime = start.timeIntervalSinceReferenceDate
|
||||
let endTime = end.timeIntervalSinceReferenceDate
|
||||
return try database.prepare(table.filter(timestamp >= startTime && timestamp <= endTime)).map(location)
|
||||
}
|
||||
|
||||
func locationSampleCount(from start: Date, to end: Date) throws -> Int {
|
||||
let startTime = start.timeIntervalSinceReferenceDate
|
||||
let endTime = end.timeIntervalSinceReferenceDate
|
||||
return try database.scalar(table.filter(timestamp >= startTime && timestamp <= endTime).count)
|
||||
}
|
||||
|
||||
func location(row: Row) -> LocationSample {
|
||||
.init(
|
||||
coordinate: .init(
|
||||
latitude: row[latitude],
|
||||
longitude: row[longitude]),
|
||||
altitude: row[altitude],
|
||||
horizontalAccuracy: row[horizontalAccuracy],
|
||||
verticalAccuracy: row[horizontalAccuracy],
|
||||
course: row[course],
|
||||
courseAccuracy: row[courseAccuracy],
|
||||
speed: row[speed],
|
||||
speedAccuracy: row[speedAccuracy],
|
||||
timestamp: .init(timeIntervalSinceReferenceDate: row[timestamp]),
|
||||
sourceInfo: .init())
|
||||
}
|
||||
|
||||
func create(references dataSeries: DataSeriesTable) throws {
|
||||
try database.execute("CREATE TABLE location_series_data (series_identifier INTEGER NOT NULL REFERENCES data_series(hfd_key) DEFERRABLE INITIALLY DEFERRED, timestamp REAL NOT NULL, longitude REAL NOT NULL, latitude REAL NOT NULL, altitude REAL NOT NULL, speed REAL NOT NULL, course REAL NOT NULL, horizontal_accuracy REAL NOT NULL, vertical_accuracy REAL NOT NULL, speed_accuracy REAL NOT NULL, course_accuracy REAL NOT NULL, signal_environment INTEGER NOT NULL, PRIMARY KEY (series_identifier, timestamp)) WITHOUT ROWID")
|
||||
}
|
||||
|
||||
func insert(_ sample: LocationSample, seriesId: Int) throws {
|
||||
try database.run(table.insert(
|
||||
seriesIdentifier <- seriesId,
|
||||
timestamp <- sample.timestamp.timeIntervalSinceReferenceDate,
|
||||
longitude <- sample.coordinate.longitude,
|
||||
latitude <- sample.coordinate.latitude,
|
||||
altitude <- sample.altitude,
|
||||
speed <- sample.speed,
|
||||
course <- sample.course,
|
||||
horizontalAccuracy <- sample.horizontalAccuracy,
|
||||
horizontalAccuracy <- sample.verticalAccuracy,
|
||||
speedAccuracy <- sample.speedAccuracy,
|
||||
courseAccuracy <- sample.courseAccuracy,
|
||||
signalEnvironment <- 1
|
||||
))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user