diff --git a/agenda/worker.js b/agenda/worker.js new file mode 100644 index 0000000..a26f450 --- /dev/null +++ b/agenda/worker.js @@ -0,0 +1,222 @@ +const https = require("https"); +const config = require("worker_threads").workerData; +const models = require("../models"); + +const reg_event = /(?:(?:BEGIN:VEVENT\nDTSTAMP:(?:[A-Z0-9]*?)\nDTSTART:([A-Z0-9]*?)\nDTEND:([A-Z0-9]*?)\nSUMMARY: {0,}([a-zéèàA-Z0-9-. \, \\/ô]*?)\nLOCATION:([a-zA-Zéèà0-9-. \,\\]*?)\nDESCRIPTION:(?:\\n){0,}(LP(?:[ a-zA-Z0-9]*))\\n((?:(?:[A-Z]*) (?:[A-Z]*)(?: (?:[A-Z]*)){0,}\\n){0,})(?:.*?)\nEND:VEVENT)|(?:BEGIN:VEVENT\nDTSTAMP:(?:[A-Z0-9]*?)\nDTSTART:([A-Z0-9]*?)\nDTEND:([A-Z0-9]*?)\nSUMMARY: {0,}((?:S(?:[A-Z0-9-]*)|M(?:[A-Z0-9-]*)(?:\/M(?:[A-Z0-9-]*)){0,}|Conférence)[a-zéèàA-Z0-9-. \, \\/]*?)\nLOCATION:([a-zA-Zéèà0-9-. \,\\]*?)\nDESCRIPTION:(?:\\n){0,}((?:(?:G[0-9]S[0-9]|S[0-9]|ASPE)\\n){0,})((?:(?:[A-Z]*) (?:[A-Z]*)(?: (?:[A-Z]*)){0,}\\n){0,})(?:.*?)\nEND:VEVENT))/gs; +const reg_location = /((?:[SH0-9][0-9]{2})|(?:(?:Préfa |Amphi)[0-9]))/g; +const reg_date = /([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z/; +const reg_classe = /(?:(LP)[ -]{0,}(.*)|(?:(G[0-9])(S[0-9])))/; +const base_url = config["edt"]; + +function fetchEvents(days = 1, TS_Start = new Date()) { + return new Promise((resolve, reject) => { + let start = new Date(TS_Start); + if (start == "Invalid Date") + start = new Date(); + let end = new Date(start); + end.setDate(end.getDate() + days); + + let url = base_url + start.getFullYear() + "-" + start.getMonth() + "-" + start.getDate() + "&lastDate=" + end.getFullYear() + "-" + end.getMonth() + "-" + end.getDate(); + https.get(url, (resp) => { + let data = ""; + resp.on("data", (chunk) => { + data += chunk; + }); + resp.on("end", () => { + let output = []; + data = data.replace(/\r/g, ""); + let m; + while ((m = reg_event.exec(data)) !== null) { + if (m.index === reg_event.lastIndex) { + reg_event.lastIndex++; + } + let event = []; + let promotion = []; + let classe = []; + /* + m = [ + FullMatch, + StartTime, + EndTime, + EventDescription ( SubjectID,Name,Class?) , + Location, + ClassGroup, + Teacher(s) + ] + */ + if (m[1] !== undefined) { + // LPXXXX + classe.push(m[5]); + } else { + m.splice(1, 6); + // GXSX | SX | ASPE + let csplit = m[5].split("\\n"); + csplit.pop(); + csplit.forEach(e => { + if (e === "ASPE") promotion.push(e); + if (/^S[0-9]$/.test(e)) promotion.push(e); + if (/^G[0-9]S[0-9]$/.test(e)) classe.push(e); + }); + } + event["title"] = m[3]; + event["promotion"] = promotion; + event["class"] = classe; + /* + Date + */ + let d1 = reg_date.exec(m[1]); + let d2 = reg_date.exec(m[2]); + event["startDate"] = new Date(d1[1] + "-" + d1[2] + "-" + d1[3] + "T" + d1[4] + ":" + d1[5] + ":" + d1[6] + ".00Z"); + event["endDate"] = new Date(d2[1] + "-" + d2[2] + "-" + d2[3] + "T" + d2[4] + ":" + d2[5] + ":" + d2[6] + ".00Z"); + /* + Location + */ + event["locations"] = []; + let loc; + while ((loc = reg_location.exec(m[4])) !== null) { + if (loc.index === reg_location.lastIndex) { + reg_location.lastIndex++; + } + event["locations"].push(loc[1]); + } + /* + Teachers + */ + event["teachers"] = m[6].split("\\n"); + event["teachers"].pop(); + output.push(event); + } + resolve(output); + }); + }).on("error", (err) => { + reject(err); + }); + }); +} + +function compare(a, b) { + if (a.length !== b.length) { + return false; + } + let set = {}; + a.forEach((i) => { + if (set[i] !== undefined) { + set[i]++; + } else { + set[i] = 1; + } + }); + let difference = b.every((i) => { + if (set[i] === undefined) { + return false; + } else { + set[i]--; + if (set[i] === 0) { + delete set[i]; + } + return true; + } + }); + return Object.keys(set) == 0 && difference; +} + +function compareGroups(list1, list2) { + return compare(list1, list2.map(g => [g.number, g.Semester.name, g.Semester.year])); +} + +function compareTeachers(list1, list2) { + return compare(list1, list2.map(t => t.lastName.toUpperCase() + " " + t.firstName.toUpperCase())); +} + +function compareSemesters(list1, list2) { + return compare(list1, list2.map(s => s.name)); +} + +async function updateDatabase() { + let events = await fetchEvents(365, new Date(2020, 9, 1)); + for (let event of await models.Event.findAll({ + include: [{ + model: models.Group, + include: {model: models.Semester, required: true} + }, models.User, models.Semester] + })) { + if (!events.find(e => (e.title === event.name && e.startDate.getTime() === event.startDate.getTime() && + e.endDate.getTime() === event.endDate.getTime() && e.locations.join(", ") === event.locations && + compareGroups(e.class, event.Groups) && compareTeachers(e.teachers, event.Users) && + compareSemesters(e.semesters, event.Semesters)))) + await event.destroy(); + else + delete events[events.indexOf(event)]; + } + + for (let event of events) { + let e = await models.Event.create({ + name: event.title, + startDate: event.startDate, + endDate: event.endDate, + locations: event.locations.join(", ") + }); + + let teachers = []; + for (let teacher of event.teachers) { + let t = await models.User.findOne({where: {permissions: 2, lastName: teacher[0], firstName: teacher[1]}}); + if (!t) + t = await models.User.create({ + email: teacher[1].toLowerCase().replace(" ", "-") + "." + teacher[0].toLowerCase().replace(" ", "-") + "@univ-lyon1.fr", + firstName: teacher[1], + lastName: teacher[0], + permissions: 2, + passwordHash: Math.round((Math.pow(36, 12 + 1) - Math.random() * Math.pow(36, 12))).toString(36).slice(1) + }); + teachers.push(t); + } + await e.addUsers(teachers); + + let semesters = []; + for (let semester of event.promotion) { + let s = await models.Semester.findOne({where: {name: semester, year: event.startDate.getFullYear()}}); + if (!s) + s = await models.Semester.create({ + name: semester, + year: event.startDate.getFullYear() + }); + semesters.push(s); + } + await e.addSemesters(semesters); + + let groups = []; + for (let group of event.class) { + let rGroup = reg_classe.exec(group); + let semesterName; + let groupName; + if (rGroup[1] !== undefined) { + // LP + semesterName = 'LP'; + groupName = rGroup[2]; + } else { + // GXSX + semesterName = rGroup[4]; + groupName = rGroup[3]; + } + + + let s = await models.Semester.findOne({where: {name: semesterName, year: event.startDate.getFullYear()}}); + if (!s) + s = await models.Semester.create({ + name: semesterName, + year: event.startDate.getFullYear() + }); + + let g = await models.Group.findOne({where: {number: groupName, SemesterId: s.id}}); + if (!g) + g = await models.Group.create({ + number: groupName, + SemesterId: s.id + }); + groups.push(g); + } + await e.addGroups(groups); + } +} + +updateDatabase().then(() => setInterval(updateDatabase, 30000));