mirror of
https://github.com/Mueller-Patrick/DHBW-Service-App.git
synced 2024-11-22 17:33:57 +00:00
Patrick Müller
f50ba8f19d
- Also adding lecturer entity and relationship between event and lecturers. - Preparation for CoreData refactoring
219 lines
8.8 KiB
Swift
219 lines
8.8 KiB
Swift
//
|
|
// RaPlaFetcher.swift
|
|
// DHBW-Service
|
|
//
|
|
// Created by Patrick Müller on 29.01.21.
|
|
//
|
|
|
|
import Foundation
|
|
import CoreData
|
|
|
|
class RaPlaFetcher {
|
|
public class iCalEvent {
|
|
var startDate: Date = Date() //DTSTART
|
|
var endDate: Date = Date() //DTEND
|
|
var summary: String = "" //SUMMARY
|
|
var description: String = "" //DESCRIPTION
|
|
var location: String = "" //LOCATION
|
|
var category: String = "" //CATEGORIES
|
|
var uid: String = "" //UID
|
|
var lecturers: [LecturerObj] = [] //ATTENDEE
|
|
}
|
|
|
|
public class LecturerObj {
|
|
var name: String = ""
|
|
var email: String = ""
|
|
}
|
|
|
|
// Get the RaPla file from the given URL and save the events to CoreData
|
|
public class func getRaplaFileAndSaveToCoreData(from urlString: String) -> Bool {
|
|
let file = getFileAsString(from: urlString)
|
|
|
|
let eventStrings = splitIntoEvents(file: file)
|
|
|
|
let eventObjects = convertStringsToObjects(eventStrings: eventStrings)
|
|
|
|
return saveToCoreData(eventObjects: eventObjects)
|
|
}
|
|
|
|
// Get the RaPla files from the given URL and return the event objects
|
|
public class func getRaplaFileAndReturnEvents(from urlString: String) -> [iCalEvent] {
|
|
let file = getFileAsString(from: urlString)
|
|
|
|
let eventStrings = splitIntoEvents(file: file)
|
|
|
|
return convertStringsToObjects(eventStrings: eventStrings)
|
|
}
|
|
|
|
// GET the file from the given URL and convert it to a String that is then returned
|
|
private class func getFileAsString(from urlString: String) -> String {
|
|
let url = URL(string: urlString)!
|
|
var file: String = ""
|
|
|
|
do {
|
|
file = try String(contentsOf: url, encoding: .utf8)
|
|
} catch let error {
|
|
print(error.localizedDescription)
|
|
}
|
|
|
|
return file
|
|
}
|
|
|
|
// Split the given ical file string into individual event strings and return them as a list
|
|
private class func splitIntoEvents(file: String) -> [String] {
|
|
let regexOptions: NSRegularExpression.Options = [.dotMatchesLineSeparators]
|
|
// Regex explanation: Matches BEGIN:VEVENT "Any character" END:VEVENT across multiple lines. The *? assures that we receive the
|
|
// maximum amount of matches, i.e. it makes the regex non-greedy as we would otherwise just receive one giant match
|
|
let eventStrings = UtilityFunctions.regexMatches(for: "BEGIN:VEVENT.*?END:VEVENT", with: regexOptions, in: file)
|
|
|
|
return eventStrings
|
|
}
|
|
|
|
// Convert an ical event String into an iCalEvent Codable object as defined above
|
|
private class func convertStringsToObjects(eventStrings: [String]) -> [iCalEvent] {
|
|
var events: [iCalEvent] = []
|
|
|
|
for eventString in eventStrings {
|
|
var lines = eventString.components(separatedBy: .newlines)
|
|
// Remove all blank lines that somehow are generated by the .components function
|
|
lines = removeBlankLines(lines: lines)
|
|
|
|
let evt = iCalEvent()
|
|
|
|
// Iterate over all lines and merge lines that have been split by rapla first
|
|
for lineNr in 0...lines.count-1 {
|
|
if(lines[lineNr].hasPrefix(" ")){
|
|
lines[lineNr] = String(lines[lineNr].dropFirst())
|
|
lines[lineNr-1].append(lines[lineNr])
|
|
lines[lineNr] = ""
|
|
}
|
|
}
|
|
|
|
// Remove all blank lines again as we produced some while merging the lines
|
|
lines = removeBlankLines(lines: lines)
|
|
|
|
for line in lines {
|
|
var lineWithoutPrefix = line
|
|
if(!line.contains(":")) {
|
|
continue
|
|
}
|
|
lineWithoutPrefix.removeSubrange(lineWithoutPrefix.startIndex...lineWithoutPrefix.firstIndex(of: ":")!)
|
|
|
|
if(line.hasPrefix("DTSTART")){
|
|
//Date format: 20181101T080000
|
|
let dateFormatter = DateFormatter()
|
|
if(lineWithoutPrefix.contains("Z")){
|
|
dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
|
|
} else {
|
|
dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss"
|
|
}
|
|
let date = dateFormatter.date(from: lineWithoutPrefix)!
|
|
evt.startDate = date
|
|
} else if(line.hasPrefix("DTEND")){
|
|
let dateFormatter = DateFormatter()
|
|
if(lineWithoutPrefix.contains("Z")){
|
|
dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
|
|
} else {
|
|
dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss"
|
|
}
|
|
let date = dateFormatter.date(from: lineWithoutPrefix)!
|
|
evt.endDate = date
|
|
} else if(line.hasPrefix("SUMMARY")){
|
|
evt.summary = lineWithoutPrefix
|
|
} else if(line.hasPrefix("DESCRIPTION")){
|
|
evt.description = lineWithoutPrefix
|
|
} else if(line.hasPrefix("LOCATION")){
|
|
evt.location = lineWithoutPrefix
|
|
} else if(line.hasPrefix("CATEGORIES")){
|
|
evt.category = lineWithoutPrefix
|
|
} else if(line.hasPrefix("UID")){
|
|
evt.uid = lineWithoutPrefix
|
|
} else if(line.hasPrefix("ATTENDEE;ROLE=REQ-PARTICIPANT;")) {
|
|
var lecturerName = line
|
|
|
|
let begin = lecturerName.firstIndex(of: "\"")!
|
|
lecturerName.removeSubrange(lecturerName.startIndex...begin)
|
|
|
|
let end = lecturerName.lastIndex(of: "\"")!
|
|
lecturerName = String(lecturerName[..<end])
|
|
|
|
let lecturerEmail = String(lineWithoutPrefix[String.Index(utf16Offset: 7, in: lineWithoutPrefix)..<lineWithoutPrefix.endIndex])
|
|
|
|
let lecturer = LecturerObj()
|
|
lecturer.name = lecturerName
|
|
lecturer.email = lecturerEmail
|
|
|
|
evt.lecturers.append(lecturer)
|
|
}
|
|
}
|
|
|
|
events.append(evt)
|
|
}
|
|
|
|
return events
|
|
}
|
|
|
|
// Save the given iCalEvent objects to CoreData
|
|
// Updates the events if they already exist and deletes old (/invalid) ones
|
|
private class func saveToCoreData(eventObjects: [iCalEvent]) -> Bool{
|
|
let existingEvents: [RaPlaEvent] = [] //RaPlaEvent.getAll()
|
|
var existingEventsDict: [String:RaPlaEvent] = [:]
|
|
for event in existingEvents {
|
|
existingEventsDict[event.value(forKey: "uid") as! String] = event
|
|
}
|
|
let newEventUIDs = eventObjects.map{$0.uid}
|
|
|
|
for event in eventObjects {
|
|
// If the event already exists locally, update it. Otherwise, create a new record
|
|
let evt: RaPlaEvent
|
|
if existingEventsDict.keys.contains(event.uid) {
|
|
evt = existingEventsDict[event.uid]!
|
|
} else {
|
|
evt = RaPlaEvent(context: PersistenceController.shared.context)
|
|
|
|
// Set default values for new object
|
|
evt.isHidden = false
|
|
}
|
|
evt.startDate = event.startDate
|
|
evt.endDate = event.endDate
|
|
evt.summary = event.summary
|
|
evt.descr = event.description
|
|
evt.location = event.location
|
|
evt.category = event.category
|
|
evt.uid = event.uid
|
|
for lecturer in event.lecturers {
|
|
let lect = Lecturer(context: PersistenceController.shared.context)
|
|
lect.name = lecturer.name
|
|
lect.email = lecturer.email
|
|
lect.event = evt
|
|
}
|
|
}
|
|
|
|
// Now we also have to delete locally stored events that have been deleted from RaPla
|
|
for localUid in existingEventsDict.keys {
|
|
if(!newEventUIDs.contains(localUid)){
|
|
// Locally stored event does not exist in RaPla anymore, delete it
|
|
let evt = existingEventsDict[localUid]
|
|
PersistenceController.shared.context.delete(evt!)
|
|
}
|
|
}
|
|
|
|
PersistenceController.shared.save()
|
|
|
|
return true
|
|
}
|
|
|
|
private class func removeBlankLines(lines: [String]) -> [String] {
|
|
var newLines = lines
|
|
|
|
// Remove all blank lines that somehow are generated by the .components function
|
|
for line in newLines {
|
|
if(line.isEmpty){
|
|
newLines.remove(at: newLines.firstIndex(of: line)!)
|
|
}
|
|
}
|
|
|
|
return newLines
|
|
}
|
|
}
|