diff --git a/Sources/App/Jobs/GPXImportingJob.swift b/Sources/App/Jobs/GPXImportingJob.swift index 67b2353..7286bda 100644 --- a/Sources/App/Jobs/GPXImportingJob.swift +++ b/Sources/App/Jobs/GPXImportingJob.swift @@ -3,6 +3,7 @@ // Shuttle Tracker Server // // Created by Gabriel Jacoby-Cooper on 10/20/20. +// Modified by Dylan Zhou on 10/10/23. // import Foundation @@ -27,6 +28,7 @@ struct GPXImportingJob: AsyncScheduledJob { .filter { (url) in return url.pathExtension == "gpx" } + let routes = try await Route .query(on: context.application.db(.sqlite)) .all() @@ -39,9 +41,27 @@ struct GPXImportingJob: AsyncScheduledJob { for stop in stops { try await stop.delete(on: context.application.db(.sqlite)) } + + let scheduleInfoData = try Data( + contentsOf: URL(fileURLWithPath: FileManager.default.currentDirectoryPath) + .appendingPathComponent("Public", isDirectory: true) + .appendingPathComponent("schedule.json", isDirectory: false) + ) + let schedules = try await Schedule + .query(on: context.application.db(.sqlite)) + .all() + for schedule in schedules { + try await schedule.delete(on: context.application.db(.sqlite)) + } + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + let infoSchedules = try decoder.decode([ScheduleInfo].self, from: scheduleInfoData) /// decodes data into array of individual schedules + for infoSchedule in infoSchedules { + try await Schedule(from: infoSchedule)! + .save(on: context.application.db(.sqlite)) + } + for routesFileURL in routesFileURLs { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .iso8601 let schedule: MapSchedule do { let routesInfoData = try routesInfoParser.get(dataAt: routesFileURL.lastPathComponent, asCollection: [String: Any].self) diff --git a/Sources/App/Migrations/CreateSchedules.swift b/Sources/App/Migrations/CreateSchedules.swift new file mode 100644 index 0000000..e46a708 --- /dev/null +++ b/Sources/App/Migrations/CreateSchedules.swift @@ -0,0 +1,30 @@ +// +// CreateSchedules.swift +// Shuttle Tracker Server +// +// Created by Dylan Zhou on 10/06/23. +// + +import Fluent + +/// A migration to create `Schedule` records. +struct CreateSchedules: AsyncMigration { + + func prepare(on database: any Database) async throws { + try await database + .schema(Schedule.schema) + .id() + .field("name", .string, .required) + .field("start", .date, .required) + .field("end", .date, .required) + .field("content", .array(of: .custom(ScheduleInfo.Content.self)), .required) + .create() + } + + func revert(on database: any Database) async throws { + try await database + .schema(Schedule.schema) + .delete() + } + +} diff --git a/Sources/App/Models/Schedule.swift b/Sources/App/Models/Schedule.swift new file mode 100644 index 0000000..2cef1dc --- /dev/null +++ b/Sources/App/Models/Schedule.swift @@ -0,0 +1,57 @@ +// +// Schedule.swift +// Shuttle Tracker Server +// +// Created by Dylan Zhou on 10/06/23. +// + +import CoreGPX +import FluentKit +import JSONParser +import Vapor + +/// A representation of a schedule. +final class Schedule: Model, Content { + + static let schema = "schedules" + + @ID + var id: UUID? + + /// The human-readable name of this stop. + @Field(key: "name") + var name: String + + /// The start date of the schedule + @Field(key: "start") + var startDate: Date + + /// The end date of the schedule + @Field(key: "end") + var endDate: Date + + /// The actual schedule of when busses run + @Field(key: "content") + var content: ScheduleInfo.Content + + init() { } + + init?(from infoSchedule: ScheduleInfo) { + self.name = infoSchedule.name + self.startDate = infoSchedule.start + self.endDate = infoSchedule.end + self.content = infoSchedule.content + } + + var isActive: Bool { + get { + if endDate > Date.now { + return true + } else { + return false + } + } + } + + +} diff --git a/Sources/App/ScheduleInfo.swift b/Sources/App/ScheduleInfo.swift new file mode 100644 index 0000000..6ced7b9 --- /dev/null +++ b/Sources/App/ScheduleInfo.swift @@ -0,0 +1,54 @@ +// +// ScheduleInfo.swift +// Shuttle Tracker +// +// Created by Emily Ngo on 2/15/22. +// Modified by Dylan Zhou on 10/10/23. +// + +import Foundation + +final class ScheduleInfo: Codable { + + struct Content: Codable { + + struct DaySchedule: Codable { + + let start: String + + let end: String + + } + + let monday: DaySchedule + + let tuesday: DaySchedule + + let wednesday: DaySchedule + + let thursday: DaySchedule + + let friday: DaySchedule + + let saturday: DaySchedule + + let sunday: DaySchedule + + } + + let name: String + + let start: Date + + let end: Date + + let content: Content + + init(name: String, start: Date, end: Date, content: Content) { + self.name = name + self.start = start + self.end = end + self.content = content + } + +} \ No newline at end of file diff --git a/Sources/App/Utilities.swift b/Sources/App/Utilities.swift index d89428f..9e7e72d 100644 --- a/Sources/App/Utilities.swift +++ b/Sources/App/Utilities.swift @@ -107,7 +107,7 @@ enum Constants { /// The current version number for the API. /// /// - Remark: Increment this value every time a breaking change is made to the public-facing API. - static let apiVersion: UInt = 3 + static let apiVersion: UInt = 4 /// The URL of the GPS data-feed. static let datafeedURL: URL = { diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift index c55b6f1..cfa35be 100644 --- a/Sources/App/configure.swift +++ b/Sources/App/configure.swift @@ -70,6 +70,7 @@ public func configure(_ application: Application) async throws { CreateBuses(), CreateRoutes(), CreateStops(), + CreateSchedules(), JobMetadataMigrate(), to: .sqlite ) // Add to the SQLite database diff --git a/Sources/App/routes.swift b/Sources/App/routes.swift index 1429b8c..7666917 100644 --- a/Sources/App/routes.swift +++ b/Sources/App/routes.swift @@ -78,8 +78,21 @@ func routes(_ application: Application) throws { return Constants.apiVersion } + // Return all known schedules application.get("schedule") { (request) in - return request.redirect(to: "/schedule.json") + let schedules = try await Schedule + .query(on: request.db(.sqlite)) + .all() + return Array(schedules) + } + + // Return only currently active schedule + application.get("schedule","active") { (request) in + let schedules = try await Schedule + .query(on: request.db(.sqlite)) + .all() + .filter { $0.isActive == true} + return Array(schedules) } // Get the current milestones @@ -115,6 +128,7 @@ func routes(_ application: Application) throws { guard let milestone = milestone else { throw Abort(.notFound) } + milestone.progress += 1 // Increment the milestone’s counter try await milestone.update(on: request.db(.psql)) // Update the milestone on the database return milestone