Rewrote DataFetcher.swift to make it maintainable; added sorting algorithm, TODO: switch sorting algorithms

This commit is contained in:
Deniz Duezgoeren
2019-09-24 20:47:52 +02:00
parent d133a056ac
commit 8f261a019f
6 changed files with 209 additions and 118 deletions
+10 -10
View File
@@ -198,30 +198,30 @@
children = (
A99AB5F7230155DE00555295 /* GoogleService-Info.plist */,
A961337F2299867400B541D4 /* AppDelegate.swift */,
A9B0464B22E1F255005FB0DB /* CustomTabBarController.swift */,
A9378D342336015000C07D07 /* LoginViewController.swift */,
A9378D382336300700C07D07 /* FirstTimeViewController.swift */,
A96133812299867400B541D4 /* PlanViewController.swift */,
A96D4CC722DDE01A006BAB34 /* GeneralPlanViewController.swift */,
A96D4CC922DDE184006BAB34 /* PersonalPlanViewController.swift */,
A9C1D2FF22D656DD00139043 /* MenuViewController.swift */,
A9B0464D22E1F75A005FB0DB /* CustomizationViewController.swift */,
A9B0464F22E21207005FB0DB /* ColourPickerView.swift */,
A9C1D30122D7237200139043 /* DataFetcher.swift */,
A96133852299867400B541D4 /* Main.storyboard */,
A96133882299867A00B541D4 /* Assets.xcassets */,
A958A92422DF5388007052B0 /* Localizable.strings */,
A961338A2299867A00B541D4 /* LaunchScreen.storyboard */,
A996BFED22E0586500E7BE42 /* Info.plist */,
A996BFFB22E05AAB00E7BE42 /* InfoPlist.strings */,
A9A3496B22D4859200F35AE0 /* SubstModel.swift */,
A9C1D30122D7237200139043 /* DataFetcher.swift */,
A957A74F22DDC15500E0A2ED /* MenuViewCell.swift */,
A957A75022DDC15500E0A2ED /* MenuViewCell.xib */,
A9D830A522DE258C00D7EA05 /* PlanViewCell.swift */,
A9D830A622DE258C00D7EA05 /* PlanViewCell.xib */,
A96D4CC722DDE01A006BAB34 /* GeneralPlanViewController.swift */,
A96D4CC922DDE184006BAB34 /* PersonalPlanViewController.swift */,
A958A92422DF5388007052B0 /* Localizable.strings */,
A996BFFB22E05AAB00E7BE42 /* InfoPlist.strings */,
A9B0464B22E1F255005FB0DB /* CustomTabBarController.swift */,
A9B0464D22E1F75A005FB0DB /* CustomizationViewController.swift */,
A9B0464F22E21207005FB0DB /* ColourPickerView.swift */,
A9B0465322E448EC005FB0DB /* GenericViewCell.swift */,
A9B033D622E4D0AB009ACF8A /* ColourViewCell.swift */,
A9B033D722E4D0AB009ACF8A /* ColourViewCell.xib */,
A9378D342336015000C07D07 /* LoginViewController.swift */,
A9378D382336300700C07D07 /* FirstTimeViewController.swift */,
);
path = "AvH Plan";
sourceTree = "<group>";
+184 -99
View File
@@ -18,11 +18,13 @@ class DataFetcher {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let prefs = UserDefaults.standard
let juniors = ["5", "6", "7", "8", "9"]
let substitutions = Table("substitutions")
let personal = Table("personal")
let information = Table("information")
let foodmenu = Table("foodmenu")
let id = Expression<Int64>("id")
let group = Expression<String>("group")
let course = Expression<String>("course")
@@ -31,10 +33,14 @@ class DataFetcher {
let time = Expression<String>("time")
let room = Expression<String>("room")
let teacher = Expression<String>("teacher")
let substType = Expression<String>("type")
let subst_type = Expression<String>("type")
let group_priority = Expression<Int64>("group_priority")
let date_priority = Expression<Int64>("date_priority")
let website_priority = Expression<Int64>("website_priority")
let text = Expression<String>("text")
func doAsync(do task: String, completionHandler: @escaping (_ substitutions: [Any]) -> ()) {
func doAsync(do task: String, completionHandler: @escaping (_ substitutions: Any) -> ()) {
DispatchQueue(label: "work-queue").async {
let foodUrl = "https://djd4rkn355.github.io/food.html"
let url = "https://djd4rkn355.github.io/avh_substitutions.html"
@@ -50,24 +56,8 @@ class DataFetcher {
}
}
func parseHTML(html: String, food: String, type: String) -> [Any] {
var subst = [SubstModel]()
var personalSubst = [SubstModel]()
var info = ""
var infoList = [String]()
var menuList = [String]()
var isPersonalEmpty = true
var personalPlanCount = 0
private func createAndClearTables(in db: Connection) -> Bool {
do {
let doc = try SwiftSoup.parse(html)
let rows: Elements = try doc.select("tr")
let courses = prefs.string(forKey: "courses")
let classes = prefs.string(forKey: "classes")
let db = try Connection("\(path)/db.sqlite3")
try db.run(substitutions.create(ifNotExists: true) { t in
t.column(id, primaryKey: true)
t.column(group)
@@ -77,7 +67,10 @@ class DataFetcher {
t.column(time)
t.column(room)
t.column(teacher)
t.column(substType)
t.column(subst_type)
t.column(group_priority)
t.column(date_priority)
t.column(website_priority)
})
try db.run(personal.create(ifNotExists: true) { t in
@@ -89,15 +82,108 @@ class DataFetcher {
t.column(time)
t.column(room)
t.column(teacher)
t.column(substType)
t.column(subst_type)
t.column(group_priority)
t.column(date_priority)
t.column(website_priority)
})
try db.run(foodmenu.create(ifNotExists: true) { t in
t.column(id, primaryKey: true)
t.column(text)
})
try db.run(substitutions.delete())
try db.run(personal.delete())
try db.run(foodmenu.delete())
return true
} catch {
return false
}
}
private func insert(into table: Table, substitution s: SubstModel) -> Insert {
return table.insert(group <- s.group, course <- s.course, additional <- s.additional, date <- s.date, time <- s.time, room <- s.room, teacher <- s.teacher, subst_type <- s.type, group_priority <- Int64(s.groupPriority), date_priority <- Int64(s.datePriority), website_priority <- Int64(s.websitePriority))
}
private func isPersonal(with g: String, and c: String, for s: SubstModel) -> Bool {
if !g.isEmpty && c.isEmpty {
if !s.group.isEmpty {
if g.contains(s.group) || s.group.contains(g) {
return true
}
}
} else if !g.isEmpty && !c.isEmpty {
if s.group != "" && s.course != "" {
if (g.contains(s.group) || s.group.contains(g)) && c.contains(s.course) {
return true
}
}
}
return false
}
private func assignRanking(for group: String, _ isPSA: Bool) -> Int {
if isPSA {
return -31
}
if group.count > 0 {
let a = String(group[...group.startIndex])
if self.checkStringForArray(s: a, checking: juniors) == true {
return -30
} else if a == "1" || a == "2" {
if group.count > 1 {
let b = Int(String(group[group.index(group.startIndex, offsetBy: 1)...group.index(group.startIndex, offsetBy: 1)]))
if b == nil {
return 0
} else {
return (b! - (b! * 2))
}
} else {
return 0
}
} else {
return -29
}
}
return 0
}
private func assignDatePriority(for d: String, _ isPSA: Bool) -> Int? {
if isPSA {
return -1
}
let periodIndex = d.firstIndex(of: ".")
return Int(String(d[d.index(periodIndex ?? d.startIndex, offsetBy: 1)...d.index(d.startIndex, offsetBy: d.count - 1)]))
}
private func parseHTML(html: String, food: String, type: String) -> Any {
var infoString = ""
var isPersonalEmpty = true
var personalPlanCount = 0
var mWebsitePriority = 0
do {
let db = try Connection("\(path)/db.sqlite3")
_ = self.createAndClearTables(in: db)
let substitutionsDocument = try SwiftSoup.parse(html)
let rows = try substitutionsDocument.select("tr")
let groupPreference = prefs.string(forKey: "classes")
let coursePreference = prefs.string(forKey: "courses")
for i in (0..<rows.size()) {
let row = rows.get(i)
let cols = try row.select("th")
let cols = try rows.get(i).select("th")
let mGroup = try cols.get(0).text()
let mCourse = try cols.get(3).text()
@@ -108,101 +194,76 @@ class DataFetcher {
let mTeacher = try cols.get(6).text()
let mType = try cols.get(7).text()
subst.append(SubstModel(group: mGroup, course: mCourse, additional: mAdditional,
date: mDate, time: mTime, room: mRoom, teacher: mTeacher, type: mType))
let insert = substitutions.insert(group <- mGroup, course <- mCourse, additional <- mAdditional,
date <- mDate, time <- mTime, room <- mRoom, teacher <- mTeacher, substType <- mType)
try db.run(insert)
var isPSA = false
if mDate.count > 2 && mDate[...mDate.index(mDate.startIndex, offsetBy: 2)] == "psa" {
personalSubst.append(SubstModel(group: mGroup, course: mCourse, additional: mAdditional,
date: mDate, time: mTime, room: mRoom, teacher: mTeacher, type: mType))
let insertPersonal = personal.insert(group <- mGroup, course <- mCourse, additional <- mAdditional,
date <- mDate, time <- mTime, room <- mRoom, teacher <- mTeacher, substType <- mType)
try db.run(insertPersonal)
} else if courses == nil || courses == "", classes != nil && classes != "" {
if !mGroup.isEmpty && mGroup != "" {
if classes!.contains(mGroup) || mGroup.contains(classes!) {
personalSubst.append(SubstModel(group: mGroup, course: mCourse, additional: mAdditional,
date: mDate, time: mTime, room: mRoom, teacher: mTeacher, type: mType))
let insertPersonal = personal.insert(group <- mGroup, course <- mCourse, additional <- mAdditional,
date <- mDate, time <- mTime, room <- mRoom, teacher <- mTeacher, substType <- mType)
try db.run(insertPersonal)
isPSA = true
}
let mDatePriority = self.assignDatePriority(for: mDate, isPSA) ?? 0
let mGroupPriority = self.assignRanking(for: mGroup, isPSA)
let substitution = SubstModel(group: mGroup, course: mCourse, additional: mAdditional, date: mDate, time: mTime, room: mRoom, teacher: mTeacher, type: mType, groupPriority: mGroupPriority, datePriority: mDatePriority, websitePriority: mWebsitePriority)
mWebsitePriority += 1
try db.run(self.insert(into: substitutions, substitution: substitution))
if self.isPersonal(with: groupPreference ?? "", and: coursePreference ?? "", for: substitution) || isPSA {
try db.run(self.insert(into: personal, substitution: substitution))
if !isPSA {
isPersonalEmpty = false
personalPlanCount += 1
}
}
} else if courses != nil && courses != "" && classes != nil && classes != "" {
if mGroup != "" && mCourse != "" {
if courses!.contains(mCourse) {
if classes!.contains(mGroup) || mGroup.contains(classes!) {
personalSubst.append(SubstModel(group: mGroup, course: mCourse, additional: mAdditional,
date: mDate, time: mTime, room: mRoom, teacher: mTeacher, type: mType))
let insertPersonal = personal.insert(group <- mGroup, course <- mCourse, additional <- mAdditional,
date <- mDate, time <- mTime, room <- mRoom, teacher <- mTeacher, substType <- mType)
try db.run(insertPersonal)
isPersonalEmpty = false
personalPlanCount += 1
}
}
}
}
}
if type == "personal" {
personalPlanCount = 0
}
prefs.set(personalPlanCount, forKey: "personalPlanCount")
if isPersonalEmpty {
personalSubst.append(SubstModel(group: "", course: NSLocalizedString("personal_plan_empty", comment: ""), additional: "", date: "", time: "", room: "", teacher: "", type: ""))
let insertPersonal = personal.insert(group <- "", course <- NSLocalizedString("personal_plan_empty", comment: ""), additional <- "", date <- "", time <- "", room <- "", teacher <- "", substType <- "")
try db.run(insertPersonal)
let emptySubstitution = SubstModel(group: "", course: NSLocalizedString("personal_plan_empty", comment: ""), additional: "", date: "", time: "", room: "", teacher: "", type: "", groupPriority: 0, datePriority: 0, websitePriority: 0)
try db.run(self.insert(into: personal, substitution: emptySubstitution))
}
let lastUpdated = try doc.select("h1").get(0).text()
info = "\(NSLocalizedString("last_updated", comment: ""))\(lastUpdated)."
let pi: Elements = try doc.select("p")
// start info fetch
// TODO: better documentation, please
for item in pi {
info += "\n\n\(try item.text())"
let lastUpdated = try substitutionsDocument.select("h1").get(0).text()
infoString = "\(NSLocalizedString("last_updated", comment: ""))\(lastUpdated)."
let informationTableElements = try substitutionsDocument.select("p")
for item in informationTableElements {
infoString += "\n\n\(try item.text())"
}
prefs.set(info.trimmingCharacters(in: .whitespacesAndNewlines), forKey: "information")
} catch {}
prefs.set(infoString.trimmingCharacters(in: .whitespacesAndNewlines), forKey: "information")
infoList.append(info.trimmingCharacters(in: .whitespacesAndNewlines))
// start food fetch
do {
let foodDoc: Document = try SwiftSoup.parse(food)
let ef: Elements = try foodDoc.select("th")
let db = try Connection("\(path)/db.sqlite3")
try db.run(foodmenu.create(ifNotExists: true) { t in
t.column(id, primaryKey: true)
t.column(text)
})
try db.run(foodmenu.delete())
let foodDocument = try SwiftSoup.parse(food)
let foodItems = try foodDocument.select("th")
var indices = [Int]()
let daysAndVon = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "von"]
for i in 0..<ef.size() {
if checkStringForArray(s: try ef.get(i).text(), checking: daysAndVon) {
for i in 0..<foodItems.size() {
if checkStringForArray(s: try foodItems.get(i).text(), checking: daysAndVon) {
indices.append(i)
}
}
indices.append(ef.size())
indices.append(foodItems.size())
for l in 0..<indices.count - 1 {
var s = ""
for i2 in indices[l]..<indices[l + 1] {
if (s.isEmpty) {
s = try ef.get(i2).text()
s = try foodItems.get(i2).text()
} else {
s += "\n\(try ef.get(i2).text())"
s += "\n\(try foodItems.get(i2).text())"
}
}
menuList.append(s)
try db.run(foodmenu.insert(text <- s))
}
@@ -211,13 +272,13 @@ class DataFetcher {
switch type {
case "plan":
return subst
return self.getSubstitutionsFromDatabase()
case "personal":
return personalSubst
return self.getPersonalSubstitutionsFromDatabase()
case "info":
return infoList
return infoString
default:
return menuList
return self.readMenu()
}
}
@@ -231,26 +292,50 @@ class DataFetcher {
}
func getFromDatabase() -> [SubstModel] {
func getSubstitutionsFromDatabase() -> [SubstModel] {
var substs = [SubstModel]()
do {
let db = try Connection("\(path)/db.sqlite3")
for subst in try db.prepare(substitutions) {
substs.append(SubstModel(group: subst[group], course: subst[course], additional: subst[additional], date: subst[date], time: subst[time], room: subst[room], teacher: subst[teacher], type: subst[substType]))
var table: AnySequence<Row>
if !prefs.bool(forKey: "original_sorting") {
table = try db.prepare(substitutions.order(date_priority.asc, date.asc, group_priority.asc, group.asc, time.asc))
} else {
table = try db.prepare(substitutions.order(website_priority.asc))
}
for subst in table {
let substitution = SubstModel(group: subst[group], course: subst[course], additional: subst[additional], date: subst[date], time: subst[time], room: subst[room], teacher: subst[teacher], type: subst[subst_type], groupPriority: Int(subst[group_priority]), datePriority: Int(subst[date_priority]), websitePriority: Int(subst[website_priority]))
substs.append(substitution)
}
} catch {
}
} catch {}
return substs
}
func getPersonalFromDatabase() -> [SubstModel] {
var substs = [SubstModel]()
func getPersonalSubstitutionsFromDatabase() -> [SubstModel] {
var personalSubsts = [SubstModel]()
do {
let db = try Connection("\(path)/db.sqlite3")
for subst in try db.prepare(personal) {
substs.append(SubstModel(group: subst[group], course: subst[course], additional: subst[additional], date: subst[date], time: subst[time], room: subst[room], teacher: subst[teacher], type: subst[substType]))
var table: AnySequence<Row>
if !prefs.bool(forKey: "original_sorting") {
table = try db.prepare(personal.order(date_priority.asc, date.asc, group_priority.asc, group.asc, time.asc))
} else {
table = try db.prepare(personal.order(website_priority.asc))
}
} catch {}
return substs
for subst in table {
let substitution = SubstModel(group: subst[group], course: subst[course], additional: subst[additional], date: subst[date], time: subst[time], room: subst[room], teacher: subst[teacher], type: subst[subst_type], groupPriority: Int(subst[group_priority]), datePriority: Int(subst[date_priority]), websitePriority: Int(subst[website_priority]))
personalSubsts.append(substitution)
}
} catch {
}
return personalSubsts
}
func readInformation() -> String {
+1 -1
View File
@@ -30,7 +30,7 @@ class GeneralViewController: PlanViewController {
}
override func getFromDatabase() -> [SubstModel] {
return df.getFromDatabase()
return df.getSubstitutionsFromDatabase()
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ class PersonalPlanViewController: PlanViewController {
}
override func getFromDatabase() -> [SubstModel] {
return df.getPersonalFromDatabase()
return df.getPersonalSubstitutionsFromDatabase()
}
override func getIndex() -> Int {
+3 -3
View File
@@ -91,7 +91,7 @@ class PlanViewController : UICollectionViewController, UICollectionViewDelegateM
}
func getFromDatabase() -> [SubstModel] {
return self.df.getFromDatabase()
return self.df.getSubstitutionsFromDatabase()
}
func getRefreshViewString() -> String {
@@ -128,8 +128,9 @@ class PlanViewController : UICollectionViewController, UICollectionViewDelegateM
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath as IndexPath) as! PlanViewCell
var psa = false
let course = self.substs[indexPath.item].course
var image = df.getImage(from: course)
@@ -149,7 +150,6 @@ class PlanViewController : UICollectionViewController, UICollectionViewDelegateM
cell.group.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
cell.course.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
cell.additional.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
psa = true
} else {
cell.date.text = self.substs[indexPath.item].date
cell.group.textColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
+7 -1
View File
@@ -17,8 +17,11 @@ class SubstModel {
var room: String
var teacher: String
var type: String
var groupPriority: Int
var datePriority: Int
var websitePriority: Int
init(group: String, course: String, additional: String, date: String, time: String, room: String, teacher: String, type: String) {
init(group: String, course: String, additional: String, date: String, time: String, room: String, teacher: String, type: String, groupPriority: Int, datePriority: Int, websitePriority: Int) {
self.group = group
self.course = course
self.additional = additional
@@ -27,5 +30,8 @@ class SubstModel {
self.room = room
self.teacher = teacher
self.type = type
self.groupPriority = groupPriority
self.datePriority = datePriority
self.websitePriority = websitePriority
}
}