|
| 1 | +// |
| 2 | +// Course.swift |
| 3 | +// PennMobile |
| 4 | +// |
| 5 | +// Created by Josh Doman on 2/24/19. |
| 6 | +// Copyright © 2019 PennLabs. All rights reserved. |
| 7 | +// |
| 8 | + |
| 9 | +import Foundation |
| 10 | + |
| 11 | +struct CoursesJSON: Codable { |
| 12 | + let accountID: String |
| 13 | + let courses: Set<Course> |
| 14 | +} |
| 15 | + |
| 16 | +class Course: Codable, Hashable { |
| 17 | + let name: String |
| 18 | + let term: String |
| 19 | + let dept: String |
| 20 | + let code: String |
| 21 | + let section: String |
| 22 | + let building: String? |
| 23 | + let room: String? |
| 24 | + let weekdays: String |
| 25 | + let startDate: String |
| 26 | + let endDate: String |
| 27 | + let startTime: String |
| 28 | + let endTime: String |
| 29 | + let instructors: [String] |
| 30 | + |
| 31 | + let meetingTimes: [CourseMeetingTime]? |
| 32 | + |
| 33 | + init(name: String, term: String, dept: String, code: String, section: String, building: String?, room: String?, weekdays: String, startDate: String, endDate: String, startTime: String, endTime: String, instructors: [String], meetingTimes: [CourseMeetingTime]?) { |
| 34 | + self.name = name |
| 35 | + self.term = term |
| 36 | + self.dept = dept |
| 37 | + self.code = code |
| 38 | + self.section = section |
| 39 | + self.instructors = instructors |
| 40 | + self.building = building |
| 41 | + self.room = room |
| 42 | + self.weekdays = weekdays |
| 43 | + self.startDate = startDate |
| 44 | + self.endDate = endDate |
| 45 | + self.startTime = startTime |
| 46 | + self.endTime = endTime |
| 47 | + self.meetingTimes = meetingTimes |
| 48 | + } |
| 49 | + |
| 50 | + var description: String { |
| 51 | + let instructorStr = instructors.joined(separator: ", ") |
| 52 | + var str = "\(term) \(name) \(dept)-\(code)-\(section) \(instructorStr) \(weekdays) \(startDate) - \(endDate) \(startTime) \(endTime)" |
| 53 | + if let building = building, let room = room { |
| 54 | + str = "\(str) \(building) \(room)" |
| 55 | + } |
| 56 | + if let meetingTimes = meetingTimes { |
| 57 | + for meeting in meetingTimes { |
| 58 | + str = str + "\n\(meeting.description)" |
| 59 | + } |
| 60 | + } |
| 61 | + return str |
| 62 | + } |
| 63 | + |
| 64 | + var hashValue: Int { |
| 65 | + return "\(term)\(dept)\(code)\(section)".hashValue |
| 66 | + } |
| 67 | + |
| 68 | + static func == (lhs: Course, rhs: Course) -> Bool { |
| 69 | + return lhs.term == rhs.term && lhs.dept == rhs.dept && lhs.code == rhs.code && lhs.section == rhs.section |
| 70 | + } |
| 71 | + |
| 72 | + func getEvent() -> Event? { |
| 73 | + guard let startTime = getTime(from: startTime), let endTime = getTime(from: endTime) else { return nil } |
| 74 | + var location: String? = nil |
| 75 | + if let building = building, let room = room { |
| 76 | + location = "\(building) \(room)" |
| 77 | + } |
| 78 | + return Event(name: "\(dept)\(code)", location: location, startTime: startTime, endTime: endTime) |
| 79 | + } |
| 80 | + |
| 81 | + private func getTime(from str: String) -> Time? { |
| 82 | + guard let hourStr = str.getMatches(for: "^(.*?):").first, let hour = Int(String(hourStr)) else { return nil } |
| 83 | + guard let minuteStr = str.getMatches(for: ":(.*?) ").first, let minutes = Int(String(minuteStr)) else { return nil } |
| 84 | + guard let amStr = str.getMatches(for: " (.*?)$").first else { return nil } |
| 85 | + return Time(hour: hour, minutes: minutes, isAm: amStr == "AM") |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | +extension Course { |
| 90 | + static var weekdayAbbreviations: [String] { |
| 91 | + return ["S", "M", "T", "W", "R", "F", "S"] |
| 92 | + } |
| 93 | + |
| 94 | + var isTaughtToday: Bool { |
| 95 | + get { |
| 96 | + return isTaughtInNDays(days: 0) |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + var isTaughtTomorrow: Bool { |
| 101 | + get { |
| 102 | + return isTaughtInNDays(days: 1) |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + func isTaughtInNDays(days: Int) -> Bool { |
| 107 | + let weekday = Date().integerDayOfWeek |
| 108 | + return weekdays.contains(Course.weekdayAbbreviations[(weekday + days) % 7]) |
| 109 | + } |
| 110 | + |
| 111 | + func hasSameMeetingTime(as course: Course) -> Bool { |
| 112 | + return startTime == course.startTime && endTime == course.endTime && building == course.building && room == course.room |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +extension Course: Comparable { |
| 117 | + static func < (lhs: Course, rhs: Course) -> Bool { |
| 118 | + let formatter = DateFormatter() |
| 119 | + formatter.dateFormat = "h:mm a" |
| 120 | + let date1 = formatter.date(from: lhs.startTime) |
| 121 | + let date2 = formatter.date(from: rhs.startTime) |
| 122 | + if date1 == nil { return true } |
| 123 | + if date2 == nil { return false } |
| 124 | + return date1! < date2! |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +extension Array where Element == Course { |
| 129 | + // weekday: "Today", "Tomorrow", "Monday", "Tuesday", etc. |
| 130 | + func filterByWeekday(for fullWeekday: String) -> [Course] { |
| 131 | + var courses = [Course]() |
| 132 | + let weekdayAbbr = weekdayMapping(for: fullWeekday) |
| 133 | + for course in self { |
| 134 | + if let meetingTimes = course.meetingTimes { |
| 135 | + for meetingTime in meetingTimes { |
| 136 | + if meetingTime.weekday == weekdayAbbr { |
| 137 | + let courseEvent = Course(name: course.name, term: course.term, dept: course.dept, code: course.code, section: course.section, building: meetingTime.building, room: meetingTime.room, weekdays: weekdayAbbr, startDate: course.startDate, endDate: course.endDate, startTime: meetingTime.startTime, endTime: meetingTime.endTime, instructors: course.instructors, meetingTimes: nil) |
| 138 | + courses.append(courseEvent) |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + } |
| 143 | + return courses |
| 144 | + } |
| 145 | + |
| 146 | + private func weekdayMapping(for weekday: String) -> String { |
| 147 | + switch weekday { |
| 148 | + case "Monday": |
| 149 | + return "M" |
| 150 | + case "Tuesday": |
| 151 | + return "T" |
| 152 | + case "Wednesday": |
| 153 | + return "W" |
| 154 | + case "Thursday": |
| 155 | + return "R" |
| 156 | + case "Friday": |
| 157 | + return "F" |
| 158 | + case "Saturday": |
| 159 | + return "S" |
| 160 | + case "Sunday": |
| 161 | + return "S" |
| 162 | + case "Today": |
| 163 | + let dateFormatter = DateFormatter() |
| 164 | + dateFormatter.dateFormat = "EEEE" |
| 165 | + return weekdayMapping(for: dateFormatter.string(from: Date())) |
| 166 | + case "Tomorrow": |
| 167 | + let dateFormatter = DateFormatter() |
| 168 | + dateFormatter.dateFormat = "EEEE" |
| 169 | + return weekdayMapping(for: dateFormatter.string(from: Date().tomorrow)) |
| 170 | + default: |
| 171 | + return "" |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + func equalsCourseEvents(_ courses: [Course]) -> Bool { |
| 176 | + for c1 in courses { |
| 177 | + var exists = false |
| 178 | + let courseCandidates = self.filter { c1 == $0 } |
| 179 | + for course in courseCandidates { |
| 180 | + if c1.hasSameMeetingTime(as: course) { |
| 181 | + exists = true |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + if !exists { |
| 186 | + return false |
| 187 | + } |
| 188 | + } |
| 189 | + |
| 190 | + for c2 in self { |
| 191 | + var exists = false |
| 192 | + let courseCandidates = courses.filter { c2 == $0 } |
| 193 | + for course in courseCandidates { |
| 194 | + if c2.hasSameMeetingTime(as: course) { |
| 195 | + exists = true |
| 196 | + } |
| 197 | + } |
| 198 | + |
| 199 | + if !exists { |
| 200 | + return false |
| 201 | + } |
| 202 | + } |
| 203 | + return true |
| 204 | + } |
| 205 | +} |
0 commit comments