From 7f7fed1f0a4ce223f45201d335a534a5355cfd27 Mon Sep 17 00:00:00 2001 From: KEZEL BENOIT p1907091 Date: Mon, 23 Nov 2020 20:34:46 +0100 Subject: [PATCH 01/16] Adding edt popup template --- sass/style.sass | 27 ++++++- views/pages/edt.pug | 181 ++++++++++++++++++++++++-------------------- 2 files changed, 123 insertions(+), 85 deletions(-) diff --git a/sass/style.sass b/sass/style.sass index c0593fc..6607ce8 100644 --- a/sass/style.sass +++ b/sass/style.sass @@ -219,6 +219,7 @@ h3 .midi td height: 10vh + cursor: auto th background-color: $secondary text-align: center @@ -234,6 +235,7 @@ h3 td border-bottom: 1px solid white td + cursor: pointer text-align: center font-size: 15px background-color: $medium @@ -487,4 +489,27 @@ div#visible + div border-radius: 2px display: block margin: auto - cursor: pointer \ No newline at end of file + cursor: pointer + +.detailsedt + z-index: 1 + display: none + position: fixed + top: 20vh + left: 30% + background-color: $dark2 + width: 40% + border-radius: 50px + border: 1px solid $light3 + p + font-size: 20px + padding: 5px + color: white + margin: 0 + text-align: center + p:first-child + font-size: 25px + background-color: $secondary + border-top-right-radius: 50px + border-top-left-radius: 50px + diff --git a/views/pages/edt.pug b/views/pages/edt.pug index ace08ac..7634abf 100644 --- a/views/pages/edt.pug +++ b/views/pages/edt.pug @@ -1,87 +1,100 @@ extends ../template/navbar block content - table(id="edttable") - tr - th Monday - th Tuesday - th Wednesday - th Thursday - th Friday - tr - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - tr - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - tr(class="midi") - td - td - td - td - td - tr - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - tr - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - td - p Maths - p S26 - div(class="row" id="edtweek") - div(class="col s3 offset-s1") - i(class="large material-icons") fast_rewind - div(class="col s4") - h3 Week of the 02/11/2020 - div(class="col s3") - i(class="large material-icons") fast_forward \ No newline at end of file + - let variable = [{name:"",ue:{id:0,name:""},teachers:[{id:0,firstName:"",lastName:""}],location:[""],dateStart:Date,dateEnd:Date,group:{id:0,number:0,promotion:{id:0,name:""}},promotion:{id:0,name:""}}]; + div.detailsedt + p Maths + p S26 + p Mr. Jaloux + p G4S3 + p 08h à 10h + p UE------------ + + div + table(id="edttable") + tr + th Monday + th Tuesday + th Wednesday + th Thursday + th Friday + tr + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + tr + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + tr(class="midi") + td + td + td + td + td + tr + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + tr + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + td + p Maths + p S26 + + div(class="row" id="edtweek") + div(class="col s3 offset-s1") + i(class="large material-icons") fast_rewind + div(class="col s4") + h3 Week of the 02/11/2020 + div(class="col s3") + i(class="large material-icons") fast_forward + + From 1ad5056ba79288d6971c5f5e61d39c62ebdd4985 Mon Sep 17 00:00:00 2001 From: flifloo Date: Sun, 22 Nov 2020 16:48:06 +0100 Subject: [PATCH 02/16] Add DB models --- .gitignore | 4 ++++ config/config_example.json | 1 + models/UE.js | 23 +++++++++++++++++++++++ models/event.js | 36 ++++++++++++++++++++++++++++++++++++ models/group.js | 23 +++++++++++++++++++++++ models/semester.js | 28 ++++++++++++++++++++++++++++ models/user.js | 3 +++ 7 files changed, 118 insertions(+) create mode 100644 models/UE.js create mode 100644 models/event.js create mode 100644 models/group.js create mode 100644 models/semester.js diff --git a/.gitignore b/.gitignore index e073a39..05878a8 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,7 @@ config/config.json # Sass output public/stylesheets + +# Floobits +.floo +.flooignore diff --git a/config/config_example.json b/config/config_example.json index 6e93448..702ca17 100644 --- a/config/config_example.json +++ b/config/config_example.json @@ -18,6 +18,7 @@ "contact": "Your Contact ", "mailPath": "https://yourServer.com" }, + "edt": "https://adelb.univ-lyon1.fr/jsp/custom/modules/plannings/anonymous_cal.jsp?resources=XXXX&projectId=XXXX&calType=ical&firstDate=", "secret": "keyboard cat", "passwordPrivateKey": "ecc635295f200847b79299df48e15759" } diff --git a/models/UE.js b/models/UE.js new file mode 100644 index 0000000..acda65b --- /dev/null +++ b/models/UE.js @@ -0,0 +1,23 @@ +"use strict"; + +const { Model } = require("sequelize"); + +module.exports = (sequelize, DataTypes) => { + class UE extends Model { + static associate(models) { + UE.hasMany(models.Event); + UE.belongsToMany(models.User, {through: "UEUser"}); + } + } + UE.init({ + name : { + type: DataTypes.STRING, + allowNull: false, + unique: true + } + }, { + sequelize, + modelName: "UE", + }); + return UE; +}; diff --git a/models/event.js b/models/event.js new file mode 100644 index 0000000..8e0da6e --- /dev/null +++ b/models/event.js @@ -0,0 +1,36 @@ +"use strict"; + +const { Model } = require("sequelize"); + +module.exports = (sequelize, DataTypes) => { + class Event extends Model { + static associate(models) { + Event.belongsToMany(models.Semester, {through: "EventSemester"}); + Event.belongsToMany(models.Group, {through: "EventGroup"}); + Event.belongsToMany(models.User, {through: "EventTeacher"}); + Event.belongsTo(models.UE); + } + } + Event.init({ + name: { + type: DataTypes.STRING, + allowNull: false + }, + locations: { + type: DataTypes.STRING, + allowNull: false + }, + startDate: { + type: DataTypes.DATE, + allowNull: false + }, + endDate: { + type: DataTypes.DATE, + allowNull: false + } + }, { + sequelize, + modelName: "Event", + }); + return Event; +}; diff --git a/models/group.js b/models/group.js new file mode 100644 index 0000000..d9a2cf7 --- /dev/null +++ b/models/group.js @@ -0,0 +1,23 @@ +"use strict"; + +const { Model } = require("sequelize"); + +module.exports = (sequelize, DataTypes) => { + class Group extends Model { + static associate(models) { + Group.belongsTo(models.Semester); + Group.belongsToMany(models.User, {through: "UserGroup"}); + Group.belongsToMany(models.Event, {through: "EventGroup"}); + } + } + Group.init({ + number: { + type: DataTypes.STRING, + allowNull: false + } + }, { + sequelize, + modelName: "Group", + }); + return Group; +}; diff --git a/models/semester.js b/models/semester.js new file mode 100644 index 0000000..fac1dad --- /dev/null +++ b/models/semester.js @@ -0,0 +1,28 @@ +"use strict"; + +const { Model } = require("sequelize"); + +module.exports = (sequelize, DataTypes) => { + class Semester extends Model { + static associate(models) { + Semester.hasMany(models.Group); + Semester.belongsToMany(models.Event, {through: "EventSemester"}); + } + } + Semester.init({ + year: { + type: DataTypes.INTEGER, + allowNull: false, + unique: "semester" + }, + name : { + type: DataTypes.STRING, + allowNull: false, + unique: "semester" + } + }, { + sequelize, + modelName: "Semester", + }); + return Semester; +}; diff --git a/models/user.js b/models/user.js index 82657b0..5847790 100644 --- a/models/user.js +++ b/models/user.js @@ -18,6 +18,9 @@ module.exports = (sequelize, DataTypes) => { class User extends Model { static associate(models) { + User.belongsToMany(models.Group, {through: "UserGroup"}); + User.belongsToMany(models.Event, {through: "EventTeacher"}) + User.belongsToMany(models.UE, {through: "UEUser"}); } checkPassword(password) { From 84c932d3f93d4ad1e64574716e7e4e615c0f4117 Mon Sep 17 00:00:00 2001 From: flifloo Date: Tue, 24 Nov 2020 01:09:20 +0100 Subject: [PATCH 03/16] Add agenda worker setup --- agenda/index.js | 18 ++++++++++++++++++ bin/www | 1 + 2 files changed, 19 insertions(+) create mode 100644 agenda/index.js diff --git a/agenda/index.js b/agenda/index.js new file mode 100644 index 0000000..813cb12 --- /dev/null +++ b/agenda/index.js @@ -0,0 +1,18 @@ +const { Worker } = require("worker_threads"); +let nb = 0; + + +function startWorker(workerArgs) { + let worker = new Worker(...workerArgs); + worker.on("error", (err) => { + console.error(err); + nb--; + if (nb > 0) + startWorker(workerArgs); + }); +} + +module.exports = (app) => { + const workerArgs = ["./agenda/worker.js", {workerData: app.get("config")}]; + startWorker(workerArgs); +}; diff --git a/bin/www b/bin/www index e8cd115..0e0c83c 100755 --- a/bin/www +++ b/bin/www @@ -119,4 +119,5 @@ function onListening() { ? "pipe " + addr : "port " + addr.port; debug("Listening on " + bind); + require("../agenda")(app); } From 9de9cf7b37979b19831b530355a113bb0ad6f6dc Mon Sep 17 00:00:00 2001 From: flifloo Date: Tue, 24 Nov 2020 08:47:13 +0100 Subject: [PATCH 04/16] Socket getter for database --- sockets/get/agendaGet.js | 39 ++++++++++++++++++++++++++++++++++++++ sockets/get/groupGet.js | 21 ++++++++++++++++++++ sockets/get/semesterGet.js | 15 +++++++++++++++ sockets/get/userGet.js | 17 +++++++++++++++++ sockets/index.js | 3 ++- 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 sockets/get/agendaGet.js create mode 100644 sockets/get/groupGet.js create mode 100644 sockets/get/semesterGet.js create mode 100644 sockets/get/userGet.js diff --git a/sockets/get/agendaGet.js b/sockets/get/agendaGet.js new file mode 100644 index 0000000..c27a05c --- /dev/null +++ b/sockets/get/agendaGet.js @@ -0,0 +1,39 @@ +const models = require("../../models"); + +module.exports = socket => { + return async (data) => { + let startDate = data.startDate ? data.startDate : new Date(), endDate = data.endDate ? data.endDate : new Date(); + let options = {where: {startDate: startDate, endDate: endDate}, include: []}; + if (data.semesters) { + for (let semester of data.semesters) { + let s = await models.Semester.findByPk(semester); + if (!s) { + socket.emit("agendaGet", {error: {message: "semester_not_found"}}); + return + } else + options.include.push({model: models.Semester, where: {id: s.id}, require: true}); + } + } + if (data.groups) { + for (let group of data.groups) { + let g = await models.Group.findByPk(group); + if (!g) { + socket.emit("agendaGet", {error: {message: "group_not_found"}}); + return + } else + options.include.push({model: models.Group, where: {id: g.id}, require: true}); + } + } + if (data.teachers) { + for (let teacher of data.teachers) { + let t = await models.User.findByPk(teacher); + if (!t) { + socket.emit("agendaGet", {error: {message: "teacher_not_found"}}); + return + } else + options.include.push({model: models.User, where: {id: t.email}, require: true}); + } + } + socket.emit("agendaGet", await models.Event.findAll(options)); + } +}; diff --git a/sockets/get/groupGet.js b/sockets/get/groupGet.js new file mode 100644 index 0000000..12f16ba --- /dev/null +++ b/sockets/get/groupGet.js @@ -0,0 +1,21 @@ +const models = require("../../models"); + +module.exports = socket => { + return async (data) => { + let options = {where: {}, include: [{model: models.Semester, require: true}]}; + if (data.number) + options.where.number = data.number; + if (data.semester) { + let s = await models.Semester.findByPk(data.semester); + if (!s) { + socket.emit("groupGet", {error: {message: "semester_not_found"}}); + return + } + options.include[0].where = {id: s.id}; + } + if (data.users) + options.include.push({model: models.User, require: true}); + + socket.emit("groupGet", await models.Group.findAll(options)); + } +}; diff --git a/sockets/get/semesterGet.js b/sockets/get/semesterGet.js new file mode 100644 index 0000000..a7ef457 --- /dev/null +++ b/sockets/get/semesterGet.js @@ -0,0 +1,15 @@ +const models = require("../../models"); + +module.exports = socket => { + return async (data) => { + let options = {where: {}, include: []}; + if (data.name) + options.where.name = data.name; + if (data.year) + options.where.year = data.year; + if (data.groups) + options.include.push({model: models.Group, require: true}); + + socket.emit("semesterGet", await models.Semester.findAll(options)); + } +}; diff --git a/sockets/get/userGet.js b/sockets/get/userGet.js new file mode 100644 index 0000000..0b905c5 --- /dev/null +++ b/sockets/get/userGet.js @@ -0,0 +1,17 @@ +const models = require("../../models"); + +module.exports = socket => { + return async (data) => { + let options = {where: {}}; + if (data.email) + options.where.email = data.email; + if (data.firstName && data.lastName) { + options.where.firstName = data.firstName; + options.where.lastName = data.lastName; + } + if (data.permissions) + options.where.permissions = data.permissions; + + socket.emit("userGet", await models.User.findAll(options)); + } +}; diff --git a/sockets/index.js b/sockets/index.js index 604338a..b420000 100644 --- a/sockets/index.js +++ b/sockets/index.js @@ -10,6 +10,7 @@ module.exports = socket => { } else { socket.on("profileEdit", require("./profile/edit")(socket)); socket.on("logout", require("./logout")(socket)); + socket.on("agendaGet", require("./get/agendaGet")(socket)); } socket.emit("connected"); -} +}; From 2fec536d09907b5addd72e30a2f631eb8861a057 Mon Sep 17 00:00:00 2001 From: flifloo Date: Thu, 26 Nov 2020 12:28:55 +0100 Subject: [PATCH 05/16] Base of worker --- agenda/worker.js | 222 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 agenda/worker.js 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)); From fdfbcceb010b47ece96560a36e142d0ee9dd9a7a Mon Sep 17 00:00:00 2001 From: flifloo Date: Sat, 12 Dec 2020 18:20:02 +0100 Subject: [PATCH 06/16] Fix semicolons --- app.js | 4 ++-- models/user.js | 2 +- public/javascripts/login.js | 4 ++-- public/javascripts/main.js | 2 +- public/javascripts/profil.js | 2 +- public/javascripts/register.js | 2 +- sockets/email/checkResend.js | 2 +- sockets/email/forgotPassword.js | 2 +- sockets/email/setPassword.js | 4 ++-- sockets/login.js | 2 +- sockets/logout.js | 2 +- sockets/profile/edit.js | 6 +++--- sockets/register.js | 2 +- sockets/utils/emailPassword.js | 2 +- test/test-pages.js | 2 +- views/template/layout.pug | 6 +++--- 16 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app.js b/app.js index 5d204c4..caba270 100644 --- a/app.js +++ b/app.js @@ -12,8 +12,8 @@ const edtRouter = require("./routes/edt"); const homeRouter = require("./routes/home"); const marksRouter = require("./routes/marks"); const registerRouter = require("./routes/register"); -const viescolRouter = require("./routes/viescol") -const profilRouter = require("./routes/profil") +const viescolRouter = require("./routes/viescol"); +const profilRouter = require("./routes/profil"); let app = express(); const sessionMiddleware = session({ diff --git a/models/user.js b/models/user.js index 5847790..6868932 100644 --- a/models/user.js +++ b/models/user.js @@ -19,7 +19,7 @@ module.exports = (sequelize, DataTypes) => { class User extends Model { static associate(models) { User.belongsToMany(models.Group, {through: "UserGroup"}); - User.belongsToMany(models.Event, {through: "EventTeacher"}) + User.belongsToMany(models.Event, {through: "EventTeacher"}); User.belongsToMany(models.UE, {through: "UEUser"}); } diff --git a/public/javascripts/login.js b/public/javascripts/login.js index ece14e1..1f04c1b 100644 --- a/public/javascripts/login.js +++ b/public/javascripts/login.js @@ -11,7 +11,7 @@ document.getElementById("login").addEventListener("submit", e=>{ alert('Format d\'email incorrect.'); } return false; -}) +}); socket.on("login", data=>{ if(data.error){ @@ -19,4 +19,4 @@ socket.on("login", data=>{ }else{ window.location.href = "/"; } -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/public/javascripts/main.js b/public/javascripts/main.js index 20b1d66..74ddccb 100644 --- a/public/javascripts/main.js +++ b/public/javascripts/main.js @@ -18,7 +18,7 @@ socket.on("logout", data=>{ }else{ window.location.href = "/"; } -}) +}); function profilRedirect(){ diff --git a/public/javascripts/profil.js b/public/javascripts/profil.js index c2c044b..3d2b7f8 100644 --- a/public/javascripts/profil.js +++ b/public/javascripts/profil.js @@ -18,4 +18,4 @@ socket.on("profileEdit", data=>{ }else{ window.location.href = "/"; } -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/public/javascripts/register.js b/public/javascripts/register.js index 3b5833a..aad9acf 100644 --- a/public/javascripts/register.js +++ b/public/javascripts/register.js @@ -20,4 +20,4 @@ socket.on("register", data=>{ }else{ window.location.href = "/"; } -}) +}); diff --git a/sockets/email/checkResend.js b/sockets/email/checkResend.js index 07d85c5..d0de62a 100644 --- a/sockets/email/checkResend.js +++ b/sockets/email/checkResend.js @@ -11,4 +11,4 @@ module.exports = socket => { else await emailCheck(socket, user, null); } -} +}; diff --git a/sockets/email/forgotPassword.js b/sockets/email/forgotPassword.js index d415c15..b13bccc 100644 --- a/sockets/email/forgotPassword.js +++ b/sockets/email/forgotPassword.js @@ -10,4 +10,4 @@ module.exports = socket => { else await emailPassword(socket, user, null); } -} +}; diff --git a/sockets/email/setPassword.js b/sockets/email/setPassword.js index 809de18..7c3399a 100644 --- a/sockets/email/setPassword.js +++ b/sockets/email/setPassword.js @@ -5,7 +5,7 @@ module.exports = socket => { return async (data) => { let user = await models.User.findOne({where: {passwordToken: data.token}}); if (!user) - socket.emit("setPassword", {error: {message: "invalid_token"}}) + socket.emit("setPassword", {error: {message: "invalid_token"}}); else if (user.passwordTokenDate && ((new Date().getTime() - user.passwordTokenDate.getTime()) / 1000 > 3600)) socket.emit("setPassword", {error: {message: "expired_token"}}); else { @@ -16,4 +16,4 @@ module.exports = socket => { socket.emit("setPassword", true); } } -} +}; diff --git a/sockets/login.js b/sockets/login.js index e1a7341..6d0b89a 100644 --- a/sockets/login.js +++ b/sockets/login.js @@ -15,4 +15,4 @@ module.exports = socket => { socket.emit("login", user) } } -} +}; diff --git a/sockets/logout.js b/sockets/logout.js index 2244722..6e7be2f 100644 --- a/sockets/logout.js +++ b/sockets/logout.js @@ -7,4 +7,4 @@ module.exports = socket => { socket.emit("logout", {error: { message: "not_logged_in"}}); } } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/sockets/profile/edit.js b/sockets/profile/edit.js index 99565a4..1b93aec 100644 --- a/sockets/profile/edit.js +++ b/sockets/profile/edit.js @@ -6,7 +6,7 @@ module.exports = socket => { if (!user) socket.emit("profileEdit", {error: {message: "not_found"}}); else if (!user.checkPassword(data.oldPassword)) - socket.emit("profileEdit", {error: {message: "invalid_password"}}) + socket.emit("profileEdit", {error: {message: "invalid_password"}}); else { if (data.firstName !== user.firstName) user.firstName = data.firstName; @@ -14,10 +14,10 @@ module.exports = socket => { user.lastName = data.lastName; user.newPassword = data.newPassword; if (data.password && !user.checkPassword(data.password)) - user.passwordHash = data.password + user.passwordHash = data.password; socket.request.session.user = user; socket.request.session.save(); socket.emit("profileEdit", user) } } -} +}; diff --git a/sockets/register.js b/sockets/register.js index 740b2c8..eaa8381 100644 --- a/sockets/register.js +++ b/sockets/register.js @@ -18,4 +18,4 @@ module.exports = socket => { await emailCheck(socket, user, null); } } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/sockets/utils/emailPassword.js b/sockets/utils/emailPassword.js index 3bc7132..281499d 100644 --- a/sockets/utils/emailPassword.js +++ b/sockets/utils/emailPassword.js @@ -16,7 +16,7 @@ module.exports = async (socket, user, callBack) => { subject: "forgot password" }), async (err, message) => { if (err) - socket.emit("forgotPassword", {error: {message: "fail_send_mail"}}) + socket.emit("forgotPassword", {error: {message: "fail_send_mail"}}); else { user.passwordToken = token; user.passwordTokenDate = new Date(); diff --git a/test/test-pages.js b/test/test-pages.js index f4afba0..c75dee1 100644 --- a/test/test-pages.js +++ b/test/test-pages.js @@ -20,7 +20,7 @@ async function clean() { before(async () => { [app, models] = await setup(); -}) +}); it("Main page content", async () => { await request(app) diff --git a/views/template/layout.pug b/views/template/layout.pug index 5fa9957..825a399 100644 --- a/views/template/layout.pug +++ b/views/template/layout.pug @@ -7,9 +7,9 @@ html link(rel="stylesheet", href="/stylesheets/style.css") script(src="/socket.io/socket.io.js") body - - var student = false - - var teacher = true - - var admin = false + - let student = false + - let teacher = true + - let admin = false div(class="row" id="page") div(class="col s2" id="panel") block navbar From e6470383c312cf5b6d99f9c91beee1588859275b Mon Sep 17 00:00:00 2001 From: flifloo Date: Sat, 12 Dec 2020 18:28:44 +0100 Subject: [PATCH 07/16] Fix const vars --- agenda/worker.js | 11 +++++++++-- bin/www | 12 ++++++------ models/user.js | 2 +- routes/edt.js | 6 +++--- routes/email.js | 4 ++-- routes/home.js | 6 +++--- routes/index.js | 4 ++-- routes/login.js | 4 ++-- routes/marks.js | 4 ++-- routes/profil.js | 4 ++-- routes/register.js | 4 ++-- routes/utils/sessionCheck.js | 2 +- routes/viescol.js | 6 +++--- 13 files changed, 38 insertions(+), 31 deletions(-) diff --git a/agenda/worker.js b/agenda/worker.js index a26f450..5a5f934 100644 --- a/agenda/worker.js +++ b/agenda/worker.js @@ -4,6 +4,7 @@ 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_teachers = /^(?:((?:[a-zA-Z]*[ -]{0,}){0,}) ([a-zA-Z]*))$/m; 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"]; @@ -82,8 +83,14 @@ function fetchEvents(days = 1, TS_Start = new Date()) { /* Teachers */ - event["teachers"] = m[6].split("\\n"); - event["teachers"].pop(); + event['teachers'] = [] + let fullTeachers = m[6].split('\\n'); + fullTeachers.forEach(e => { + if(e !== ""){ + let splittedTeachers = reg_teachers.exec(e); + event['teachers'].push([splittedTeachers[1],splittedTeachers[2]]); + } + }); output.push(event); } resolve(output); diff --git a/bin/www b/bin/www index 0e0c83c..b75ec66 100755 --- a/bin/www +++ b/bin/www @@ -30,7 +30,7 @@ fs.writeFileSync("public/stylesheets/style.css", css.css); * Get port from environment and store in Express. */ -let port = normalizePort(process.env.PORT || "3000"); +const port = normalizePort(process.env.PORT || "3000"); app.set("port", port); app.set("mailClient", mailClient); @@ -38,7 +38,7 @@ app.set("mailClient", mailClient); * Create HTTP server. */ -let server = http.createServer(app); +const server = http.createServer(app); /** * Create socket.io server @@ -66,7 +66,7 @@ models.sequelize.sync().then(() => { */ function normalizePort(val) { - let port = parseInt(val, 10); + const port = parseInt(val, 10); if (isNaN(port)) { // named pipe @@ -90,7 +90,7 @@ function onError(error) { throw error; } - let bind = typeof port === "string" + const bind = typeof port === "string" ? "Pipe " + port : "Port " + port; @@ -114,8 +114,8 @@ function onError(error) { */ function onListening() { - let addr = server.address(); - let bind = typeof addr === "string" + const addr = server.address(); + const bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port; debug("Listening on " + bind); diff --git a/models/user.js b/models/user.js index 6868932..d2ccef9 100644 --- a/models/user.js +++ b/models/user.js @@ -8,7 +8,7 @@ const { } = require("sequelize"); module.exports = (sequelize, DataTypes) => { function hash(password, email) { - let cipher = crypto.createCipheriv( + const cipher = crypto.createCipheriv( "aes-256-cbc", privateKey, crypto.createHash("md5").update(email).digest("base64").slice(0, 16) diff --git a/routes/edt.js b/routes/edt.js index 6e74d6b..753a638 100644 --- a/routes/edt.js +++ b/routes/edt.js @@ -1,8 +1,8 @@ -let express = require("express"); -let router = express.Router(); +const express = require("express"); +const router = express.Router(); router.get("/", (req, res) => { res.render("pages/edt", { title: "L'ETU" }); }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/routes/email.js b/routes/email.js index 0597f15..0771a69 100644 --- a/routes/email.js +++ b/routes/email.js @@ -8,7 +8,7 @@ const sessionCheck = require("./utils/sessionCheck"); router.get("/check", async (req, res) => { if (!req.query.token) return error(req, res, "Missing argument", 400); - let user = await models.User.findOne({where: {emailToken: req.query.token}}); + const user = await models.User.findOne({where: {emailToken: req.query.token}}); if (user) { user.emailVerified = true; if (user.email.endsWith("@etu.univ-lyon1.fr")) @@ -25,7 +25,7 @@ router.get("/forget", sessionCheck(-1), async (req, res) => { if (!req.query.token) res.render("forget", {title: "L'ETU"}); else { - let user = await models.User.findOne({where: {passwordToken: data.token}}); + const user = await models.User.findOne({where: {passwordToken: data.token}}); if (!user) return error(req, res, "Invalid token", 400); else if (user.passwordTokenDate && ((new Date().getTime() - user.passwordTokenDate.getTime()) / 1000 > 3600)) diff --git a/routes/home.js b/routes/home.js index 10276d9..0e85584 100644 --- a/routes/home.js +++ b/routes/home.js @@ -1,9 +1,9 @@ -let express = require("express"); -let router = express.Router(); +const express = require("express"); +const router = express.Router(); /* GET home page. */ router.get("/", (req, res) => { res.render("pages/home", { title: "L'ETU" }); }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/routes/index.js b/routes/index.js index ced825d..fee5fcb 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,5 +1,5 @@ -let express = require("express"); -let router = express.Router(); +const express = require("express"); +const router = express.Router(); const sessionCheck = require("./utils/sessionCheck"); router.get("/", sessionCheck(1), (req, res) => { diff --git a/routes/login.js b/routes/login.js index 0a899f0..1f1aef2 100644 --- a/routes/login.js +++ b/routes/login.js @@ -1,5 +1,5 @@ -let express = require("express"); -let router = express.Router(); +const express = require("express"); +const router = express.Router(); const sessionCheck = require("./utils/sessionCheck"); router.get("/",sessionCheck(-1), (req, res) => { diff --git a/routes/marks.js b/routes/marks.js index c4d5cb9..db46ce0 100644 --- a/routes/marks.js +++ b/routes/marks.js @@ -1,5 +1,5 @@ -let express = require("express"); -let router = express.Router(); +const express = require("express"); +const router = express.Router(); router.get("/", (req, res) => { res.render("pages/marks", { title: "L'ETU" }); diff --git a/routes/profil.js b/routes/profil.js index 529ff2b..82c23b8 100644 --- a/routes/profil.js +++ b/routes/profil.js @@ -1,5 +1,5 @@ -let express = require("express"); -let router = express.Router(); +const express = require("express"); +const router = express.Router(); router.get("/", (req, res) => { res.render("pages/profil", { title: "L'ETU" }); diff --git a/routes/register.js b/routes/register.js index 9e99f1c..a7410dd 100644 --- a/routes/register.js +++ b/routes/register.js @@ -1,5 +1,5 @@ -let express = require("express"); -let router = express.Router(); +const express = require("express"); +const router = express.Router(); const sessionCheck = require("./utils/sessionCheck"); router.get("/",sessionCheck(-1), (req, res) => { diff --git a/routes/utils/sessionCheck.js b/routes/utils/sessionCheck.js index 5a54bc5..5c66215 100644 --- a/routes/utils/sessionCheck.js +++ b/routes/utils/sessionCheck.js @@ -1,4 +1,4 @@ -let error = require("./error"); +const error = require("./error"); function sessionCheck(permission) { return (req, res, next) => { diff --git a/routes/viescol.js b/routes/viescol.js index bc0c341..e846717 100644 --- a/routes/viescol.js +++ b/routes/viescol.js @@ -1,8 +1,8 @@ -let express = require("express"); -let router = express.Router(); +const express = require("express"); +const router = express.Router(); router.get("/", (req, res) => { res.render("pages/viescol", { title: "L'ETU" }); }); -module.exports = router; \ No newline at end of file +module.exports = router; From 0966d2c17b38799eec05685020d95bf40fc3bc11 Mon Sep 17 00:00:00 2001 From: Kezel Benoit Date: Sat, 12 Dec 2020 18:38:03 +0100 Subject: [PATCH 08/16] Adding responsive --- sass/style.sass | 22 ++++---- views/pages/edt.pug | 2 +- views/pages/viescol.pug | 2 +- views/template/layout.pug | 13 ++--- views/template/navbar.pug | 111 +++++++++++++++++++++----------------- 5 files changed, 81 insertions(+), 69 deletions(-) diff --git a/sass/style.sass b/sass/style.sass index 6607ce8..ddf66ff 100644 --- a/sass/style.sass +++ b/sass/style.sass @@ -71,16 +71,8 @@ h2 i color: $primary -#panel - background-color: $dark2 - height: 100vh - border-right: 2px solid $light3 - padding: 0 - position: fixed - #main background-color: $dark1 - margin-left: 16vw #navtop font-size: 22px @@ -130,11 +122,11 @@ i cursor: pointer .student - background: radial-gradient(circle, rgb(1, 79, 116) 20%, rgb(15, 15, 99) 100%) + background-image: linear-gradient(to top, #4481eb 0%, #04befe 100%) .teacher - background: radial-gradient(circle, rgb(5, 116, 1) 20%, rgb(11, 68, 10) 100%) + background-image: radial-gradient( circle farthest-corner at -1% 57.5%, rgba(19,170,82,1) 0%, rgba(0,102,43,1) 90% ) .admin - background: radial-gradient(circle, rgb(116, 74, 1) 20%, rgb(99, 42, 15) 100%) + background-image: radial-gradient( circle farthest-corner at 7.2% 19%, rgba(120,0,0,1) 0%, rgba(239,75,75,1) 100.2% ) #page margin: 0 @@ -513,3 +505,11 @@ div#visible + div border-top-right-radius: 50px border-top-left-radius: 50px +#slide-out + background-color: $dark2 + a + color: white + font-size: 20px + +#hamburger + position: fixed \ No newline at end of file diff --git a/views/pages/edt.pug b/views/pages/edt.pug index 7634abf..028d26c 100644 --- a/views/pages/edt.pug +++ b/views/pages/edt.pug @@ -2,7 +2,7 @@ extends ../template/navbar block content - let variable = [{name:"",ue:{id:0,name:""},teachers:[{id:0,firstName:"",lastName:""}],location:[""],dateStart:Date,dateEnd:Date,group:{id:0,number:0,promotion:{id:0,name:""}},promotion:{id:0,name:""}}]; - div.detailsedt + div.detailsedt#visibl p Maths p S26 p Mr. Jaloux diff --git a/views/pages/viescol.pug b/views/pages/viescol.pug index e3efa17..ffc10ed 100644 --- a/views/pages/viescol.pug +++ b/views/pages/viescol.pug @@ -1,7 +1,7 @@ extends ../template/navbar block content - div.managesub#visible + div.managesub#visibl p.topicname Tick the topics you would like to see in your news feed form p diff --git a/views/template/layout.pug b/views/template/layout.pug index 5fa9957..bb707d2 100644 --- a/views/template/layout.pug +++ b/views/template/layout.pug @@ -7,13 +7,14 @@ html link(rel="stylesheet", href="/stylesheets/style.css") script(src="/socket.io/socket.io.js") body - - var student = false - - var teacher = true - - var admin = false + - let student = true + - let teacher = false + - let admin = false div(class="row" id="page") - div(class="col s2" id="panel") - block navbar - div(class="col s10" id="main") + block navbar + div(class="col s12" id="main") block content script(src="/javascripts/main.js") script(src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js") + script. + M.AutoInit(); diff --git a/views/template/navbar.pug b/views/template/navbar.pug index d738661..09b9b88 100644 --- a/views/template/navbar.pug +++ b/views/template/navbar.pug @@ -1,54 +1,65 @@ extends layout block navbar - if student === true - div(id="navprofile" class="student") - p Kezel Benoit - p G4S3 - a(id="logout") Logout - if teacher === true - div(id="navprofile" class="teacher") - p Kezel Benoit - a(id="logout") Logout - if admin === true - div(id="navprofile" class="admin") - p Kezel Benoit - a(id="logout") Logout + ul#slide-out.sidenav + if student === true + li + .user-view.student + p + span.white-text.name Benoit Kezel + p + span.white-text.name G4S3 + p + span.white-text.email benoit.kezel@etu.univ-lyon1.fr - div(id="navtop") - ul - if student === true - li(class="active") - a(href="/") Home - li - a(href="/edt") Planning - li - a(href="/marks") Marks - li - a(href="https://mail.univ-lyon1.fr/owa/") Mail - li - a(href="https://clarolineconnect.univ-lyon1.fr/") Claroline - li - a(href="/viescol") School and student life - if teacher === true - li(class="active") - a(href="/") Home - li - a(href="/edt") Planning - li - a(href="/marks") Marks - li - a(href="https://mail.univ-lyon1.fr/owa/") Mail - li - a(href="https://clarolineconnect.univ-lyon1.fr/") Claroline Connect - li - a(href="/viescol") School and student life - if admin === true - li(class="active") - a Accueil - li - a Notes - li - a Vie scolaire et étudiante - li - a Gestion des profils + li + a(href="/" class="waves-effect") Home + li + a(href="/edt" class="waves-effect") Planning + li(class="active") + a(href="/marks" class="waves-effect") Marks + li + a(href="https://mail.univ-lyon1.fr/owa/" class="waves-effect") Mail + li + a(href="https://clarolineconnect.univ-lyon1.fr/" class="waves-effect") Claroline + li + a(href="/viescol" class="waves-effect") School and student life + + if teacher === true + li + .user-view.teacher + p(href='#name') + span.white-text.name Benoit Kezel + p(href='#email') + span.white-text.email benoit.kezel@etu.univ-lyon1.fr + li + a(href="/" class="waves-effect") Home + li + a(href="/edt" class="waves-effect") Planning + li(class="active") + a(href="/marks" class="waves-effect") Marks + li + a(href="https://mail.univ-lyon1.fr/owa/" class="waves-effect") Mail + li + a(href="https://clarolineconnect.univ-lyon1.fr/" class="waves-effect") Claroline + li + a(href="/viescol" class="waves-effect") School and student life + + if admin === true + li + .user-view.admin + p(href='#name') + span.white-text.name Benoit Kezel + p(href='#email') + span.white-text.email benoit.kezel@etu.univ-lyon1.fr + li + a(href="/" class="waves-effect") Home + li(class="active") + a(href="/marks" class="waves-effect") Marks + li + a(href="/viescol" class="waves-effect") School and student life + li + a(href="/viescol" class="waves-effect") Profil Edition + + a.sidenav-trigger(href='#' data-target='slide-out')#hamburger + i.material-icons.medium menu \ No newline at end of file From 9940b11cf44bf079d7113e7a54721f9aa4432269 Mon Sep 17 00:00:00 2001 From: flifloo Date: Sat, 12 Dec 2020 21:52:55 +0100 Subject: [PATCH 09/16] Update worker with better name management and event compare --- agenda/worker.js | 88 +++++++++++++++++++++++------------------------- bin/www | 2 +- 2 files changed, 44 insertions(+), 46 deletions(-) diff --git a/agenda/worker.js b/agenda/worker.js index 5a5f934..366dc31 100644 --- a/agenda/worker.js +++ b/agenda/worker.js @@ -2,12 +2,14 @@ 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_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){1,})((?:(?:[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_teachers = /^(?:((?:[a-zA-Z]*[ -]{0,}){0,}) ([a-zA-Z]*))$/m; 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 reg_group = /(?:(LP)[ -]{0,}(.*)|(?:(G[0-9])(S[0-9])))/; const base_url = config["edt"]; +const capitalizeFirstLetter = (string) => string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); + function fetchEvents(days = 1, TS_Start = new Date()) { return new Promise((resolve, reject) => { @@ -32,8 +34,8 @@ function fetchEvents(days = 1, TS_Start = new Date()) { reg_event.lastIndex++; } let event = []; - let promotion = []; - let classe = []; + let semester = []; + let group = []; /* m = [ FullMatch, @@ -47,21 +49,30 @@ function fetchEvents(days = 1, TS_Start = new Date()) { */ 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); - }); } + let csplit = m[5].split("\\n"); + csplit.pop(); + csplit.forEach(e => { + if (e === "ASPE") { + semester.push(e); + }else if(/^S[0-9]$/.test(e)) { + semester.push(e); + }else{ + let groupSplit = reg_group.exec(e); + if (groupSplit[1] === 'LP') { + group.push([groupSplit[2], groupSplit[1]]); + } else { + group.push([groupSplit[3], groupSplit[4]]); + } + } + }); + event["title"] = m[3]; - event["promotion"] = promotion; - event["class"] = classe; + event["semesters"] = semester; + event["groups"] = group; /* Date */ @@ -83,7 +94,7 @@ function fetchEvents(days = 1, TS_Start = new Date()) { /* Teachers */ - event['teachers'] = [] + event['teachers'] = []; let fullTeachers = m[6].split('\\n'); fullTeachers.forEach(e => { if(e !== ""){ @@ -128,11 +139,11 @@ function compare(a, b) { } function compareGroups(list1, list2) { - return compare(list1, list2.map(g => [g.number, g.Semester.name, g.Semester.year])); + return compare(list1.map(g => g[0] + " " + g[1]), list2.map(g => g.number + " " + g.Semester.name)); } function compareTeachers(list1, list2) { - return compare(list1, list2.map(t => t.lastName.toUpperCase() + " " + t.firstName.toUpperCase())); + return compare(list1.map(t => t[0].toUpperCase() + " " + t[1].toUpperCase()), list2.map(t => t.lastName.toUpperCase() + " " + t.firstName.toUpperCase())); } function compareSemesters(list1, list2) { @@ -147,13 +158,14 @@ async function updateDatabase() { include: {model: models.Semester, required: true} }, models.User, models.Semester] })) { - if (!events.find(e => (e.title === event.name && e.startDate.getTime() === event.startDate.getTime() && + let ev = 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)))) + compareGroups(e.groups, event.Groups) && compareTeachers(e.teachers, event.Users) && + compareSemesters(e.semesters, event.Semesters))); + if (!ev) await event.destroy(); else - delete events[events.indexOf(event)]; + events = events.filter(e => e !== ev); } for (let event of events) { @@ -166,12 +178,12 @@ async function updateDatabase() { let teachers = []; for (let teacher of event.teachers) { - let t = await models.User.findOne({where: {permissions: 2, lastName: teacher[0], firstName: teacher[1]}}); + let t = await models.User.findOne({where: {permissions: 2, lastName: teacher[0].toUpperCase(), firstName: capitalizeFirstLetter(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], + email: teacher[1].toLowerCase().replaceAll(" ", "-") + "." + teacher[0].toLowerCase().replaceAll(" ", "-") + "@univ-lyon1.fr", + firstName: capitalizeFirstLetter(teacher[1]), + lastName: teacher[0].toUpperCase(), permissions: 2, passwordHash: Math.round((Math.pow(36, 12 + 1) - Math.random() * Math.pow(36, 12))).toString(36).slice(1) }); @@ -180,7 +192,7 @@ async function updateDatabase() { await e.addUsers(teachers); let semesters = []; - for (let semester of event.promotion) { + for (let semester of event.semesters) { let s = await models.Semester.findOne({where: {name: semester, year: event.startDate.getFullYear()}}); if (!s) s = await models.Semester.create({ @@ -192,32 +204,18 @@ async function updateDatabase() { 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()}}); + for (let group of event.groups) { + let s = await models.Semester.findOne({where: {name: group[1], year: event.startDate.getFullYear()}}); if (!s) s = await models.Semester.create({ - name: semesterName, + name: group[1], year: event.startDate.getFullYear() }); - let g = await models.Group.findOne({where: {number: groupName, SemesterId: s.id}}); + let g = await models.Group.findOne({where: {number: group[0], SemesterId: s.id}}); if (!g) g = await models.Group.create({ - number: groupName, + number: group[0], SemesterId: s.id }); groups.push(g); diff --git a/bin/www b/bin/www index b75ec66..aad9016 100755 --- a/bin/www +++ b/bin/www @@ -17,7 +17,7 @@ const mailClient = new SMTPClient(process.env.NODE_ENV === "test" ? {} : require /** * Render all Sass to css */ -css = sass.renderSync({ +let css = sass.renderSync({ file: "sass/style.sass", includePaths: ["sass/"], outputStyle: "compressed" From 5b9c7b4d969f1373226647690692bd3904ddbb4d Mon Sep 17 00:00:00 2001 From: flifloo Date: Sat, 12 Dec 2020 22:49:09 +0100 Subject: [PATCH 10/16] Add grade and evaluation models and getter socket --- models/evaluation.js | 27 +++++++++++++++++++++++++++ models/grade.js | 30 ++++++++++++++++++++++++++++++ models/user.js | 3 +++ sockets/get/evaluationGet.js | 22 ++++++++++++++++++++++ sockets/get/gradeGet.js | 22 ++++++++++++++++++++++ sockets/index.js | 4 +++- 6 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 models/evaluation.js create mode 100644 models/grade.js create mode 100644 sockets/get/evaluationGet.js create mode 100644 sockets/get/gradeGet.js diff --git a/models/evaluation.js b/models/evaluation.js new file mode 100644 index 0000000..216a075 --- /dev/null +++ b/models/evaluation.js @@ -0,0 +1,27 @@ +"use strict"; + +const { Model } = require("sequelize"); + +module.exports = (sequelize, DataTypes) => { + class Evaluation extends Model { + static associate(models) { + Evaluation.belongsToMany(models.User, {through: "EvaluationTeacher"}); + Evaluation.hasMany(models.Grade); + Evaluation.belongsTo(models.UE); + } + } + Evaluation.init({ + name: { + type: DataTypes.STRING, + allowNull: false + }, + date: { + type: DataTypes.DATE, + allowNull: false + } + }, { + sequelize, + modelName: "Evaluation", + }); + return Evaluation; +}; diff --git a/models/grade.js b/models/grade.js new file mode 100644 index 0000000..1f75ee4 --- /dev/null +++ b/models/grade.js @@ -0,0 +1,30 @@ +"use strict"; + +const { Model } = require("sequelize"); + +module.exports = (sequelize, DataTypes) => { + class Grade extends Model { + static associate(models) { + Grade.belongsTo(models.Evaluation); + Grade.belongsTo(models.User, {as: "TeacherGrade"}); + Grade.belongsTo(models.User, {as: "StudentGrade"}); + } + } + Grade.init({ + score: { + type: DataTypes.FLOAT, + allowNull: false + }, + limit: { + type: DataTypes.FLOAT, + allowNull: false + }, + comment: { + type: DataTypes.TEXT + } + }, { + sequelize, + modelName: "Grade", + }); + return Grade; +}; diff --git a/models/user.js b/models/user.js index d2ccef9..0eacb8f 100644 --- a/models/user.js +++ b/models/user.js @@ -20,7 +20,10 @@ module.exports = (sequelize, DataTypes) => { static associate(models) { User.belongsToMany(models.Group, {through: "UserGroup"}); User.belongsToMany(models.Event, {through: "EventTeacher"}); + User.belongsToMany(models.Evaluation, {through: "EvaluationTeacher"}); User.belongsToMany(models.UE, {through: "UEUser"}); + //User.hasMany(models.Grade, {as : "TeacherGrade"}); + //User.hasMany(models.Grade, {as: "StudentGrade"}); } checkPassword(password) { diff --git a/sockets/get/evaluationGet.js b/sockets/get/evaluationGet.js new file mode 100644 index 0000000..55c3beb --- /dev/null +++ b/sockets/get/evaluationGet.js @@ -0,0 +1,22 @@ +const models = require("../../models"); + +module.exports = socket => { + return async (data) => { + const options = {where: {}, + include: [models.UE, {model: models.User}, + { + model: models.Grade, + include: [{model: models.User, as: "TeacherGrade"}, {model: models.User, as: "StudentGrade"}] + }] + }; + + if (data && data.id) + options.where.id = data.id; + + if (socket.request.session.user.permissions === 2) { + options.include[1].where = {email: socket.request.session.user.email}; + options.include[1].required = true; + } + socket.emit("evaluationGet", await models.Evaluation.findAll(options)); + } +}; diff --git a/sockets/get/gradeGet.js b/sockets/get/gradeGet.js new file mode 100644 index 0000000..f0a1174 --- /dev/null +++ b/sockets/get/gradeGet.js @@ -0,0 +1,22 @@ +const models = require("../../models"); + +module.exports = socket => { + return async (data) => { + const options = {where: {}, include: [models.Evaluation, + {model: models.User, as: "TeacherGrade"}, + {model: models.User, as: "StudentGrade"}]}; + + if (data && data.id) + options.where.id = data.id; + + switch (socket.request.session.user.permissions) { + case 1: + options.where.StudentGradeEmail = socket.request.session.user.email; + break; + case 2: + options.where.TeacherGradeEmail = socket.request.session.user.email; + break; + } + socket.emit("gradeGet", await models.Grade.findAll(options)); + } +}; diff --git a/sockets/index.js b/sockets/index.js index b420000..8055765 100644 --- a/sockets/index.js +++ b/sockets/index.js @@ -1,6 +1,5 @@ module.exports = socket => { console.log("New connection !"); - console.log(socket.request.session.user); if (!socket.request.session.user) { socket.on("login", require("./login")(socket)); socket.on("register", require("./register")(socket)); @@ -11,6 +10,9 @@ module.exports = socket => { socket.on("profileEdit", require("./profile/edit")(socket)); socket.on("logout", require("./logout")(socket)); socket.on("agendaGet", require("./get/agendaGet")(socket)); + socket.on("gradeGet", require("./get/gradeGet")(socket)); + if (socket.request.session.user.permissions > 1) + socket.on("evaluationGet", require("./get/evaluationGet")(socket)); } socket.emit("connected"); }; From 755fce2f7f585512e97fbc6f85e06c886ec93a6b Mon Sep 17 00:00:00 2001 From: flifloo Date: Mon, 14 Dec 2020 21:55:55 +0100 Subject: [PATCH 11/16] Some fox on agendaGet --- sockets/get/agendaGet.js | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/sockets/get/agendaGet.js b/sockets/get/agendaGet.js index c27a05c..8472920 100644 --- a/sockets/get/agendaGet.js +++ b/sockets/get/agendaGet.js @@ -1,37 +1,59 @@ const models = require("../../models"); + +function addWhere(options, index, attr, value) { + if (!options.include[index].where) + options.include[index].where = {}; + if (!options.include[index].where[attr]) + options.include[index].where[attr] = {}; + if (!options.include[index].where[attr][models.Sequelize.Op.or]) + options.include[index].where[attr][models.Sequelize.Op.or] = []; + options.include[index].where[attr][models.Sequelize.Op.or].push(value); + options.include[index].require = true; +} + module.exports = socket => { return async (data) => { - let startDate = data.startDate ? data.startDate : new Date(), endDate = data.endDate ? data.endDate : new Date(); - let options = {where: {startDate: startDate, endDate: endDate}, include: []}; - if (data.semesters) { + let options = { + where: {}, + include: [ + {model: models.User, attributes: ["email", "firstName", "lastName"]}, + {model: models.Group, include: models.Semester}, + {model: models.Semester, include: models.Group} + ] + }; + if (data && data.startDate) + options.where.startDate = data.startDate; + if (data && data.endDate) + options.where.endDate = data.endDate; + if (data && data.semesters) { for (let semester of data.semesters) { let s = await models.Semester.findByPk(semester); if (!s) { socket.emit("agendaGet", {error: {message: "semester_not_found"}}); return } else - options.include.push({model: models.Semester, where: {id: s.id}, require: true}); + addWhere(options, 2, "id", s.id); } } - if (data.groups) { + if (data && data.groups) { for (let group of data.groups) { let g = await models.Group.findByPk(group); if (!g) { socket.emit("agendaGet", {error: {message: "group_not_found"}}); return } else - options.include.push({model: models.Group, where: {id: g.id}, require: true}); + addWhere(options, 1, "id", g.id); } } - if (data.teachers) { + if (data && data.teachers) { for (let teacher of data.teachers) { let t = await models.User.findByPk(teacher); if (!t) { socket.emit("agendaGet", {error: {message: "teacher_not_found"}}); return } else - options.include.push({model: models.User, where: {id: t.email}, require: true}); + addWhere(options, 0, "email", t.email); } } socket.emit("agendaGet", await models.Event.findAll(options)); From 4bc5d00344f0135fabc636d068193c8451ca213c Mon Sep 17 00:00:00 2001 From: flifloo Date: Mon, 14 Dec 2020 22:43:08 +0100 Subject: [PATCH 12/16] Fix routes --- app.js | 2 -- routes/edt.js | 3 ++- routes/home.js | 9 --------- routes/marks.js | 3 ++- routes/profil.js | 3 ++- routes/viescol.js | 3 ++- 6 files changed, 8 insertions(+), 15 deletions(-) delete mode 100644 routes/home.js diff --git a/app.js b/app.js index caba270..3b56354 100644 --- a/app.js +++ b/app.js @@ -9,7 +9,6 @@ let config = process.env.NODE_ENV === "test" ? {} : require("./config/config.jso let indexRouter = require("./routes/index"); const loginRouter = require("./routes/login"); const edtRouter = require("./routes/edt"); -const homeRouter = require("./routes/home"); const marksRouter = require("./routes/marks"); const registerRouter = require("./routes/register"); const viescolRouter = require("./routes/viescol"); @@ -41,7 +40,6 @@ app.use("/", indexRouter); app.use("/login", loginRouter); app.use("/email", require("./routes/email")); app.use("/edt", edtRouter); -app.use("/home", homeRouter); app.use("/marks", marksRouter); app.use('/register', registerRouter); app.use('/viescol', viescolRouter); diff --git a/routes/edt.js b/routes/edt.js index 753a638..8b9291c 100644 --- a/routes/edt.js +++ b/routes/edt.js @@ -1,7 +1,8 @@ const express = require("express"); +const sessionCheck = require("./utils/sessionCheck"); const router = express.Router(); -router.get("/", (req, res) => { +router.get("/", sessionCheck(1), (req, res) => { res.render("pages/edt", { title: "L'ETU" }); }); diff --git a/routes/home.js b/routes/home.js deleted file mode 100644 index 0e85584..0000000 --- a/routes/home.js +++ /dev/null @@ -1,9 +0,0 @@ -const express = require("express"); -const router = express.Router(); - -/* GET home page. */ -router.get("/", (req, res) => { - res.render("pages/home", { title: "L'ETU" }); -}); - -module.exports = router; diff --git a/routes/marks.js b/routes/marks.js index db46ce0..8b3338a 100644 --- a/routes/marks.js +++ b/routes/marks.js @@ -1,7 +1,8 @@ const express = require("express"); +const sessionCheck = require("./utils/sessionCheck"); const router = express.Router(); -router.get("/", (req, res) => { +router.get("/", sessionCheck(1), (req, res) => { res.render("pages/marks", { title: "L'ETU" }); }); diff --git a/routes/profil.js b/routes/profil.js index 82c23b8..3b0066b 100644 --- a/routes/profil.js +++ b/routes/profil.js @@ -1,7 +1,8 @@ const express = require("express"); +const sessionCheck = require("./utils/sessionCheck"); const router = express.Router(); -router.get("/", (req, res) => { +router.get("/", sessionCheck(1), (req, res) => { res.render("pages/profil", { title: "L'ETU" }); }); diff --git a/routes/viescol.js b/routes/viescol.js index e846717..4bd1b47 100644 --- a/routes/viescol.js +++ b/routes/viescol.js @@ -1,7 +1,8 @@ const express = require("express"); +const sessionCheck = require("./utils/sessionCheck"); const router = express.Router(); -router.get("/", (req, res) => { +router.get("/", sessionCheck(1), (req, res) => { res.render("pages/viescol", { title: "L'ETU" }); }); From f08e249701f5376b39d52161febd513885b61566 Mon Sep 17 00:00:00 2001 From: flifloo Date: Mon, 14 Dec 2020 23:00:47 +0100 Subject: [PATCH 13/16] Fix name and group display on views --- sockets/login.js | 2 +- views/template/navbar.pug | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/sockets/login.js b/sockets/login.js index 6d0b89a..70b4707 100644 --- a/sockets/login.js +++ b/sockets/login.js @@ -2,7 +2,7 @@ const models = require("../models"); module.exports = socket => { return async (data) => { - let user = await models.User.findByPk(data.email); + let user = await models.User.findByPk(data.email, {include: {model: models.Group, include: models.Semester}}); if (!user) socket.emit("login", {error: {message: "not_found"}}); else if (!user.checkPassword(data.password)) diff --git a/views/template/navbar.pug b/views/template/navbar.pug index 2da3620..99b6c62 100644 --- a/views/template/navbar.pug +++ b/views/template/navbar.pug @@ -1,18 +1,21 @@ extends layout block navbar - if student === true + if session.user.permissions === 1 div(id="navprofile" class="student" onclick="profilRedirect()") - p Kezel Benoit - p G4S3 + p=session.user.firstName + " " + session.user.lastName + if session.user.Groups[session.user.Groups.length-1].number.startsWith("G") + p=session.user.Groups[session.user.Groups.length-1].number + session.user.Groups[session.user.Groups.length-1].Semester.name + else + p=session.user.Groups[session.user.Groups.length-1].Semester.name + " " + session.user.Groups[session.user.Groups.length-1].number a(id="logout") Logout - if teacher === true + if session.user.permissions === 2 div(id="navprofile" class="teacher" onclick="profilRedirect()") - p Kezel Benoit + p=session.user.firstName + " " + session.user.lastName a(id="logout") Logout - if admin === true + if session.user.permissions === 3 div(id="navprofile" class="admin" onclick="profilRedirect()") - p Kezel Benoit + p=session.user.firstName + " " + session.user.lastName a(id="logout") Logout div(id="navtop") From b930cd11eadcade3a94e8a94ed0c32e03c18c2ef Mon Sep 17 00:00:00 2001 From: flifloo Date: Mon, 14 Dec 2020 23:04:32 +0100 Subject: [PATCH 14/16] Add userSet socket --- sockets/index.js | 2 ++ sockets/set/userSet.js | 43 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 sockets/set/userSet.js diff --git a/sockets/index.js b/sockets/index.js index 8055765..6a74c08 100644 --- a/sockets/index.js +++ b/sockets/index.js @@ -13,6 +13,8 @@ module.exports = socket => { socket.on("gradeGet", require("./get/gradeGet")(socket)); if (socket.request.session.user.permissions > 1) socket.on("evaluationGet", require("./get/evaluationGet")(socket)); + if (socket.request.session.user.permissions > 2) + socket.on("userSet", require("./set/userSet")(socket)); } socket.emit("connected"); }; diff --git a/sockets/set/userSet.js b/sockets/set/userSet.js new file mode 100644 index 0000000..ee3ee59 --- /dev/null +++ b/sockets/set/userSet.js @@ -0,0 +1,43 @@ +const models = require("../../models"); + +module.exports = socket => { + return async (data) => { + if (!data || !data.user) { + socket.emit("userSet", {error: {message: "missing_arguments"}}); + return; + } + let u = await models.User.findByPk(data.user); + if (!u) { + socket.emit("userSet", {error: {message: "user_not_found"}}); + return; + } + for (let i in data) + switch (i) { + case "firstName": + u.firstName = data.firstName; + break; + case "lastName": + u.lastName = data.lastName; + break; + case "password": + u.passwordHash = data.password; + break; + case "permissions": + u.permissions = data.permissions; + break; + case "groups": + let groups = []; + for (let g of data.groups) { + let gp = await models.Group.findByPk(g); + if (!gp) { + socket.emit("userGet", {error: {message: "group_not_found"}}); + return + } + groups.push(gp); + } + u.setGroups(groups); + break; + } + await u.save(); + } +}; From 25d228a0353519eaca136d4509fc703258602b51 Mon Sep 17 00:00:00 2001 From: flifloo Date: Mon, 14 Dec 2020 23:13:32 +0100 Subject: [PATCH 15/16] Update groupGet and semesterGet --- sockets/get/groupGet.js | 15 ++++++++++----- sockets/get/semesterGet.js | 14 +++++++++----- sockets/index.js | 5 ++++- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/sockets/get/groupGet.js b/sockets/get/groupGet.js index 12f16ba..48ae8ed 100644 --- a/sockets/get/groupGet.js +++ b/sockets/get/groupGet.js @@ -2,19 +2,24 @@ const models = require("../../models"); module.exports = socket => { return async (data) => { - let options = {where: {}, include: [{model: models.Semester, require: true}]}; - if (data.number) + let options = {include: [{model: models.Semester}]}; + if (data && (data.id || data.number || data.semester)) + options.where = {}; + if (data && data.id) + options.where.id = data.id; + if (data && data.number) options.where.number = data.number; - if (data.semester) { + if (data && data.semester) { let s = await models.Semester.findByPk(data.semester); if (!s) { socket.emit("groupGet", {error: {message: "semester_not_found"}}); return } options.include[0].where = {id: s.id}; + options.include[0].require = true; } - if (data.users) - options.include.push({model: models.User, require: true}); + if (data && data.users) + options.include.push({model: models.User, where: {email: {[models.Sequelize.Op.in]: data.users}}, require: true}); socket.emit("groupGet", await models.Group.findAll(options)); } diff --git a/sockets/get/semesterGet.js b/sockets/get/semesterGet.js index a7ef457..e7b0bee 100644 --- a/sockets/get/semesterGet.js +++ b/sockets/get/semesterGet.js @@ -2,13 +2,17 @@ const models = require("../../models"); module.exports = socket => { return async (data) => { - let options = {where: {}, include: []}; - if (data.name) + let options = {include: [{model: models.Group}]}; + if (data && (data.id || data.name || data.year)) + options.where = {}; + if (data && data.id) + options.where.id = data.id; + if (data && data.name) options.where.name = data.name; - if (data.year) + if (data && data.year) options.where.year = data.year; - if (data.groups) - options.include.push({model: models.Group, require: true}); + if (data && data.groups) + options.include[0].where = {id: {[models.Sequelize.Op.or]: data.groups}}; socket.emit("semesterGet", await models.Semester.findAll(options)); } diff --git a/sockets/index.js b/sockets/index.js index 6a74c08..fb53df2 100644 --- a/sockets/index.js +++ b/sockets/index.js @@ -13,8 +13,11 @@ module.exports = socket => { socket.on("gradeGet", require("./get/gradeGet")(socket)); if (socket.request.session.user.permissions > 1) socket.on("evaluationGet", require("./get/evaluationGet")(socket)); - if (socket.request.session.user.permissions > 2) + if (socket.request.session.user.permissions > 2) { + socket.on("groupGet", require("./get/groupGet")(socket)); + socket.on("semesterGet", require("./get/semesterGet")(socket)); socket.on("userSet", require("./set/userSet")(socket)); + } } socket.emit("connected"); }; From 47c557fb7c543fdb1cddd1e95040137eced725ca Mon Sep 17 00:00:00 2001 From: flifloo Date: Mon, 14 Dec 2020 23:39:44 +0100 Subject: [PATCH 16/16] Add favicon --- public/browserconfig.xml | 9 ++ public/images/android-chrome-192x192.png | Bin 0 -> 3744 bytes public/images/android-chrome-512x512.png | Bin 0 -> 11290 bytes public/images/apple-touch-icon.png | Bin 0 -> 2411 bytes public/images/favicon-16x16.png | Bin 0 -> 626 bytes public/images/favicon-32x32.png | Bin 0 -> 1005 bytes public/images/favicon.ico | Bin 0 -> 15086 bytes public/images/mstile-150x150.png | Bin 0 -> 3060 bytes public/images/safari-pinned-tab.svg | 169 +++++++++++++++++++++++ public/site.webmanifest | 19 +++ views/template/layout.pug | 8 ++ views/template/navbar.pug | 15 +- 12 files changed, 213 insertions(+), 7 deletions(-) create mode 100644 public/browserconfig.xml create mode 100644 public/images/android-chrome-192x192.png create mode 100644 public/images/android-chrome-512x512.png create mode 100644 public/images/apple-touch-icon.png create mode 100644 public/images/favicon-16x16.png create mode 100644 public/images/favicon-32x32.png create mode 100644 public/images/favicon.ico create mode 100644 public/images/mstile-150x150.png create mode 100644 public/images/safari-pinned-tab.svg create mode 100644 public/site.webmanifest diff --git a/public/browserconfig.xml b/public/browserconfig.xml new file mode 100644 index 0000000..35157e2 --- /dev/null +++ b/public/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/public/images/android-chrome-192x192.png b/public/images/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..dbd96ea7e8c2fb2bedd795beb40f26b8f11af14d GIT binary patch literal 3744 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 literal 0 HcmV?d00001 diff --git a/public/images/android-chrome-512x512.png b/public/images/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..b3c8eb7df275e0ac00ac0f1486eedbbb9092a78f GIT binary patch literal 11290 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 literal 0 HcmV?d00001 diff --git a/public/images/apple-touch-icon.png b/public/images/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1ec4672de391c79e6cbcb306bd00369b87827f1a GIT binary patch literal 2411 zcmZ`*dpOe#8~)kMp_$VNOIl7*4%qQ|slp;%Q zYa&ZR5o)PalMarX8Yv-#cm4T&*Y*AJ-1qf7_jO;-f6x8odmeUyZ9!}S004$_b@CE3 z`%lS;0Z?!BpBIy4guS~x0NgHt{tkhNdnC!#%N+pH^Z|gL4FGH65nTWPNhkoA3kCqI z3jm-NSKUFd5euMDHy0;hGq+@@OWa9Qaqb7Dr{v_-G_^wPI)?#3MiJ*^@14RIRnt6G z$eNE=ihV5~OzIsvr&fwOM0}x{;gx#(R#;r#A>2S48(pm#rhn@BX^o!KZLZfwoS)-d zR4$;*2ucQIy7D2n_bL#*l1~Rytu5v`&ag4p<2mJ(A6Nkc?#VDyg-`ob7o=7tM{&mbaD7*OILjLq;Vl&5biTBgse* zQy+zO`zO4*Uv3N#PZC>H+e|fqZpn+%rKrmO8r{Z#JO~=F8mWGmp5ByvKA~3GZY4?A8Z!h}6KbU(VN|>H2^3E@bZ~ zXr!J5=B7s0h|!JtI2IFHG-!!Ueda@KztzcD`nwl7Hz@gwCqpQ7v-6Mm%4k?B^kz>I-^qs7vu=Yt}bv5`(R6yIyOY$s>^_u1^qogX-J5jg6wN z^2f#QDLlQt0lO!@uxdchD#O4g_4O+g5qwW?B{*}N2wLRE_h!fXeDMqN<^JPjF=VQp zo|GKVN)P#=ay4*<5Osz!QLw`C-uw?MalQHMcjbxD(23b0H`>DF_+!)eDQ_a~3dk$U znT7+>S7}@>ZN%QJiMAKQx4cZIc$(X^W)zoxl|*ncK4gj9H&OJSc8Q|*A5^z}5t*eu zwWl{rN7UYyZCp%Cf`4>A_4L3|ZSiT`iEYf7pVu1J-S?7VIzjbsQ?eyArmW6#`S5$4 zxi%N$1#kiZxAKZWKF+?Wyz$8HE>?$st8qc+a3@B`DxrN@KBs)j;MG8(Gdqk4Phy6SA>dTz; zgKC@~MVbcFr0%=tG+9>D({HMl5wB1y9JV)D-`YP5Gho|AZ08@UXTfvJg;<9_H=U6& zdO0u!IL0(dyfL$Weix7bunt_JG8O&LwF=uf<$#X@r_wm6X>HPyhH35D80~_8K#8RXqtMt zk|x$#`v$KsR5Jd3YZDsQ(jyOL930NyDTr6d3G)`-l+(zW56U+GMRSbl`}}62_?Q4D zw|Pckl2_BZi&#bl$sicrD+PD;w05_49razFHaZi#0 zkpOnthSO}GDMM@xx3BaH*C}T9n7>)JDm|0;gtjth*P-rU@Dt1Xk_%FkW*s#d( z)c83+)=D$#1NE!eD literal 0 HcmV?d00001 diff --git a/public/images/favicon-16x16.png b/public/images/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..f9a356e4c1de77050e604e2e01633c3e841ef0c2 GIT binary patch literal 626 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YDR+ueoXe|!I#{Xiaj ziKnkC`x72MaT&Qp`#onF7#PJpT^vI+&evW%uYH(N;`qnw<=5&=vNkra5(m0j0~| zoa(#IR`9^#$R5L5H<4J4F3npCcS?c{)2}TQ-aBoN_KutxS2j8K@7dF~x$Bkxg1pdU zD|W21);8Kz=O3|MTleYNwbI@3ru09P%OsXp7$DQ_w_rh9~h zwdg;%xpP*vs;Fpc>0PC)mKGHi71cYhrU@1+zPqs`TZP&6_^J8T0t@sqmO1SET3-K^ zvHgCia~RVKEnujrmbgZgq$HN4S|t~y0x1R~10w@nLnB>7lMo|gDk4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-G$+Qd;gjJKpuOE zr>`sf6COTs8M#FJJ!gUPlRRA%#V0>|55ypkX1sH^DiN;|Y<{vDC4*E`NinMPOgmsVZ+>?U(HIsEK)ZKGwqvp=;@ zW#=rbK65#)BqnN8>kE&A`f9b$XGtq1MKViF7VrH3b@Rhj2j3e1xX0cmdo}uch4Px2 zg-2C)o;6K-zkcK0#c|&kdbYORostssVYa^Pja#vA#onDfv15Vrt>Cy%J&gQ{Wv6>M zAJ=VrU@UTE?KO)hrqkz^`rqQN@|_*@VZ&p;ig=Yj>hfQzu3P{6)O>p1)ba~61l!+C z|GHCdA>a0B!6P0ouD=g;b)9=ld*Y_V^IW{YSBV}hD^|~r5#068f8p!3syQ9A?@Wj| zm%H$&R%hZgNVrS!UMlb%&(f8gq{y?3IRBGczy zU+>@EVrqW&w5H;g80)3SWKE9i9p?$UoidKCaSUGfs_4v> zW#X^fcU+$xH0hpk!u%;_)l27m>6&tvuPVC3`QEKlYu+ntTe9ZtCjB$qF8kc(_xKye zcyg~YUZJk>>%^qLyXI`vdB11rHRYA+9Zv*zJQn6Y)m8d8_Zr`}cS**|tk-mBOkVxF zRcuK~;sxEOVRknTXs-6Mn|5%nm->F`a<{no#aE*?S}xvXdXK;KcC*JhDXW|i4WUbu zo;}?zyyl17#x{k0>kEU6b52)pY6!0ii6{w5ELSKf%1_J8NmVGREJ#(zEGS84V5pe$_!AFDVVH)-DgV=FJf8+J zFe`KGC36ca3wuu%VHQ?!X)rmQ!mPYGMB(&}D<_VeIU;j}{d9xJ0xvy=SK@*tpPWpm Q01boFyt=akR{0M(bTs{jB1 literal 0 HcmV?d00001 diff --git a/public/images/favicon.ico b/public/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..780f472be780ac19486141905b6289c1737d95a7 GIT binary patch literal 15086 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 literal 0 HcmV?d00001 diff --git a/public/images/mstile-150x150.png b/public/images/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..d3d36af674de8c25171548efc0fc511df534d2bf GIT binary patch literal 3060 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 + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + + + + + + + diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 0000000..99062eb --- /dev/null +++ b/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "Lyon's Exceptional Tool for University", + "short_name": "L'ETU", + "icons": [ + { + "src": "/images/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/images/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/views/template/layout.pug b/views/template/layout.pug index bb707d2..07b2950 100644 --- a/views/template/layout.pug +++ b/views/template/layout.pug @@ -5,6 +5,14 @@ html link(href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet") link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css") link(rel="stylesheet", href="/stylesheets/style.css") + link(rel="icon" type="image/png" href="/images/favicon.ico") + link(rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon.png") + link(rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.png") + link(rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.png") + link(rel="manifest" href="/site.webmanifest") + link(rel="mask-icon" href="/images/safari-pinned-tab.svg" color="#5bbad5") + meta(name="msapplication-TileColor" content="#da532c") + meta(name="theme-color" content="#ffffff") script(src="/socket.io/socket.io.js") body - let student = true diff --git a/views/template/navbar.pug b/views/template/navbar.pug index b2b64b7..4b0baaa 100644 --- a/views/template/navbar.pug +++ b/views/template/navbar.pug @@ -2,16 +2,17 @@ extends layout block navbar ul#slide-out.sidenav - if student === true + if session.user.permissions === 1 li .user-view.student p span.white-text.name=session.user.firstName + " " + session.user.lastName p - if session.user.Groups[session.user.Groups.length-1].number.startsWith("G") - span.white-text.name=session.user.Groups[session.user.Groups.length-1].number + session.user.Groups[session.user.Groups.length-1].Semester.name - else - span.white-text.name=session.user.Groups[session.user.Groups.length-1].Semester.name + " " + session.user.Groups[session.user.Groups.length-1].number + if session.user.Groups[session.user.Groups.length-1] + if session.user.Groups[session.user.Groups.length-1].number.startsWith("G") + span.white-text.name=session.user.Groups[session.user.Groups.length-1].number + session.user.Groups[session.user.Groups.length-1].Semester.name + else + span.white-text.name=session.user.Groups[session.user.Groups.length-1].Semester.name + " " + session.user.Groups[session.user.Groups.length-1].number p span.white-text.email=session.user.email @@ -28,7 +29,7 @@ block navbar li a(href="/viescol" class="waves-effect") School and student life - if teacher === true + if session.user.permissions === 2 li .user-view.teacher p(href='#name') @@ -48,7 +49,7 @@ block navbar li a(href="/viescol" class="waves-effect") School and student life - if admin === true + if session.user.permissions === 3 li .user-view.admin p(href='#name')