From 6e1bad7c2e8bf095a4ca90df91132c00f56dc37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Sakuma?= Date: Mon, 18 Mar 2024 23:24:45 -0300 Subject: [PATCH 1/4] add graduation stats feature --- app/api/graduations/list/func.js | 49 ++++++++++ app/api/graduations/list/route.js | 5 + app/api/graduations/stats/func.js | 145 +++++++++++++++++++++++++++++ app/api/graduations/stats/route.js | 8 ++ 4 files changed, 207 insertions(+) create mode 100644 app/api/graduations/list/func.js create mode 100644 app/api/graduations/list/route.js create mode 100644 app/api/graduations/stats/func.js create mode 100644 app/api/graduations/stats/route.js diff --git a/app/api/graduations/list/func.js b/app/api/graduations/list/func.js new file mode 100644 index 0000000..162db21 --- /dev/null +++ b/app/api/graduations/list/func.js @@ -0,0 +1,49 @@ +const app = require('@/app') + +module.exports = async function func(context) { + const Graduation = await app.models.graduation + const graduations = await Graduation.find({}).lean() + + const Subject = await app.models.subjects + const SubjectGraduations = await app.models.subjectGraduations + + const populatedGraduations = await Promise.all( + graduations.map(async (graduation) => { + const subjectsGraduations = await SubjectGraduations.find({ + graduation: graduation._id, + }).lean() + + const subjects = ( + await Promise.all( + subjectsGraduations.map(async (subjectGraduation) => { + const subject = await Subject.findOne({ + _id: subjectGraduation.subject, + }).lean() + return { + ...subjectGraduation, + subject, + } + }) + ) + ).reduce((acc, subject) => { + const category = subject.category + if (!category) { + console.log('no category', subject) + return acc + } + if (!acc[category]) { + acc[category] = [] + } + acc[category].push(subject) + return acc + }, {}) + + return { + ...graduation, + subjects, + } + }) + ) + + return populatedGraduations +} diff --git a/app/api/graduations/list/route.js b/app/api/graduations/list/route.js new file mode 100644 index 0000000..1f2d0e5 --- /dev/null +++ b/app/api/graduations/list/route.js @@ -0,0 +1,5 @@ +const app = require('@/app') + +module.exports = async (router) => { + router.get('/graduations', app.helpers.routes.func(require('./func.js'))) +} diff --git a/app/api/graduations/stats/func.js b/app/api/graduations/stats/func.js new file mode 100644 index 0000000..ca5032f --- /dev/null +++ b/app/api/graduations/stats/func.js @@ -0,0 +1,145 @@ +const app = require('@/app') + +module.exports = async function func(context) { + const { ra } = context.user + if (!ra) { + return [] + } + const graduationId = context.params.id + const Graduation = await app.models.graduation + const graduation = await Graduation.findOne({ + _id: graduationId, + }).lean() + + const Subject = await app.models.subjects + const SubjectGraduations = await app.models.subjectGraduations + const subjectsGraduations = await SubjectGraduations.find({ + graduation: graduation._id, + }).lean() + + const subjects = ( + await Promise.all( + subjectsGraduations.map(async (subjectGraduation) => { + const subject = await Subject.findOne({ + _id: subjectGraduation.subject, + }).lean() + return { + ...subjectGraduation, + subject, + } + }) + ) + ).reduce((acc, subject) => { + const category = subject.category + if (!category) { + console.log('no category', subject) + return acc + } + if (!acc[category]) { + acc[category] = [] + } + acc[category].push(subject) + return acc + }, {}) + + const populatedGraduation = { + ...graduation, + subjects, + } + + const enrollments = await app.models.enrollments + .find( + { + ra, + conceito: { $in: ['A', 'B', 'C', 'D', 'O', 'F'] }, + }, + { + conceito: 1, + subject: 1, + disciplina: 1, + pratica: 1, + teoria: 1, + year: 1, + quad: 1, + creditos: 1, + updatedAt: 1, + comments: 1, + } + ) + .populate(['pratica', 'teoria', 'subject']) + .lean(true) + + const mandatories = populatedGraduation.subjects.mandatory.filter( + (subject) => { + return enrollments.some((enrollment) => { + if (!enrollment.subject) { + console.log('no subject', enrollment) + return false + } + if (!subject.subject) { + console.log('no subject', subject) + return false + } + return ( + enrollment.subject._id.toString() === subject.subject._id.toString() + ) + }) + } + ) + + const limited = populatedGraduation.subjects.limited.filter((subject) => { + return enrollments.some((enrollment) => { + if (!enrollment.subject) { + console.log('no subject', enrollment) + return false + } + if (!subject.subject) { + console.log('no subject', subject) + return false + } + return ( + enrollment.subject._id.toString() === subject.subject._id.toString() + ) + }) + }) + + return { + percentage: { + mandatories: + mandatories.length / populatedGraduation.subjects.mandatory.length, + limited: limited.length / populatedGraduation.subjects.limited.length, + }, + credits: { + mandatories: { + done: mandatories.reduce((acc, subject) => { + return acc + subject.subject.creditos + }, 0), + total: populatedGraduation.subjects.mandatory.reduce((acc, subject) => { + return acc + subject.subject.creditos + }, 0), + }, + limited: { + done: limited.reduce((acc, subject) => { + return acc + subject.subject.creditos + }, 0), + total: populatedGraduation.limited_credits_number, + }, + }, + missing: { + mandatories: populatedGraduation.subjects.mandatory.filter((subject) => { + return !mandatories.some((enrollment) => { + return ( + enrollment.subject._id.toString() === subject.subject._id.toString() + ) + }) + }), + limited: populatedGraduation.subjects.limited.filter((subject) => { + return !limited.some((enrollment) => { + return ( + enrollment.subject._id.toString() === subject.subject._id.toString() + ) + }) + }), + }, + } +} diff --git a/app/api/graduations/stats/route.js b/app/api/graduations/stats/route.js new file mode 100644 index 0000000..3bf65a6 --- /dev/null +++ b/app/api/graduations/stats/route.js @@ -0,0 +1,8 @@ +const app = require('@/app') + +module.exports = async (router) => { + router.get( + '/graduations/stats/:id', + app.helpers.routes.func(require('./func.js')) + ) +} From 708f5ee052e35a4eb60e3c98cbc223f8a6e19f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Sakuma?= Date: Mon, 18 Mar 2024 23:25:55 -0300 Subject: [PATCH 2/4] add graduation stats feature --- app/setup/api.js | 50 ++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/app/setup/api.js b/app/setup/api.js index cfcc48f..1f8bb6b 100644 --- a/app/setup/api.js +++ b/app/setup/api.js @@ -19,24 +19,28 @@ module.exports = async (app) => { } // Authenticate user - api.use([ - '/users/info', - '/users/complete', - '/users/me/resend', - '/users/me/grades', - '/users/me/delete', - '/users/me/devices', - '/enrollments', - '/comments', - '/reactions', - '/histories/courses', - '/users/me/relationships', - '/graduation', - '/subjectGraduations', - '/historiesGraduations', - '/students/aluno_id', - '/subjects' - ], app.helpers.middlewares.auth) + api.use( + [ + '/users/info', + '/users/complete', + '/users/me/resend', + '/users/me/grades', + '/users/me/delete', + '/users/me/devices', + '/enrollments', + '/comments', + '/reactions', + '/histories/courses', + '/users/me/relationships', + '/graduation', + '/graduations/stats', + '/subjectGraduations', + '/historiesGraduations', + '/students/aluno_id', + '/subjects', + ], + app.helpers.middlewares.auth + ) // Protect Private routes api.use('/private', app.helpers.middlewares.private) @@ -46,7 +50,7 @@ module.exports = async (app) => { let routerPaths = glob.sync('**/*route.js', { cwd }) // Require route files - let routers = routerPaths.map(file => require(path.join(cwd, file))) + let routers = routerPaths.map((file) => require(path.join(cwd, file))) // user a temporary router to order let tmpRoute = express() @@ -60,13 +64,13 @@ module.exports = async (app) => { app.helpers.routes.order(tmpRoute) // get ordered router and apply on api - tmpRoute._router.stack.forEach(function (currentRoute){ + tmpRoute._router.stack.forEach(function (currentRoute) { let path = _.get(currentRoute, 'route.path') let stack = _.get(currentRoute, 'route.stack', []) let method = _.get(currentRoute, 'route.stack[0].method') - let functions = stack.map(s => s.handle) + let functions = stack.map((s) => s.handle) - if(method) { + if (method) { api[method](path, ...functions) } }) @@ -75,7 +79,7 @@ module.exports = async (app) => { // Locate express-restify-mongoose files let restPaths = glob.sync('**/*rest.js', { cwd }) - restPaths.map(file => require(path.join(cwd, file))) + restPaths.map((file) => require(path.join(cwd, file))) // Add rest to api api.use(app.router) From cdab8e7d361a49b2b626697306be4dfe376d1c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Sakuma?= Date: Tue, 19 Mar 2024 10:27:31 -0300 Subject: [PATCH 3/4] fix lint --- app/api/graduations/list/func.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/graduations/list/func.js b/app/api/graduations/list/func.js index 162db21..8676f6c 100644 --- a/app/api/graduations/list/func.js +++ b/app/api/graduations/list/func.js @@ -1,6 +1,6 @@ const app = require('@/app') -module.exports = async function func(context) { +module.exports = async function func() { const Graduation = await app.models.graduation const graduations = await Graduation.find({}).lean() From c1df306ca60c105e0fb314ad3b124280cefc9b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Sakuma?= Date: Tue, 19 Mar 2024 10:32:07 -0300 Subject: [PATCH 4/4] remove missing console.logs --- app/api/graduations/list/func.js | 1 - app/api/graduations/stats/func.js | 23 ++++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/app/api/graduations/list/func.js b/app/api/graduations/list/func.js index 8676f6c..804a223 100644 --- a/app/api/graduations/list/func.js +++ b/app/api/graduations/list/func.js @@ -28,7 +28,6 @@ module.exports = async function func() { ).reduce((acc, subject) => { const category = subject.category if (!category) { - console.log('no category', subject) return acc } if (!acc[category]) { diff --git a/app/api/graduations/stats/func.js b/app/api/graduations/stats/func.js index ca5032f..4535ab5 100644 --- a/app/api/graduations/stats/func.js +++ b/app/api/graduations/stats/func.js @@ -32,7 +32,6 @@ module.exports = async function func(context) { ).reduce((acc, subject) => { const category = subject.category if (!category) { - console.log('no category', subject) return acc } if (!acc[category]) { @@ -72,12 +71,7 @@ module.exports = async function func(context) { const mandatories = populatedGraduation.subjects.mandatory.filter( (subject) => { return enrollments.some((enrollment) => { - if (!enrollment.subject) { - console.log('no subject', enrollment) - return false - } - if (!subject.subject) { - console.log('no subject', subject) + if (!enrollment.subject || !subject.subject) { return false } return ( @@ -89,12 +83,7 @@ module.exports = async function func(context) { const limited = populatedGraduation.subjects.limited.filter((subject) => { return enrollments.some((enrollment) => { - if (!enrollment.subject) { - console.log('no subject', enrollment) - return false - } - if (!subject.subject) { - console.log('no subject', subject) + if (!enrollment.subject || !subject.subject) { return false } return ( @@ -103,6 +92,8 @@ module.exports = async function func(context) { }) }) + const invalidGrades = ['O', 'F'] + return { percentage: { mandatories: @@ -128,6 +119,9 @@ module.exports = async function func(context) { missing: { mandatories: populatedGraduation.subjects.mandatory.filter((subject) => { return !mandatories.some((enrollment) => { + const completed = !invalidGrades.includes(enrollment.conceito) + if (!completed) return false + return ( enrollment.subject._id.toString() === subject.subject._id.toString() ) @@ -135,6 +129,9 @@ module.exports = async function func(context) { }), limited: populatedGraduation.subjects.limited.filter((subject) => { return !limited.some((enrollment) => { + const completed = !invalidGrades.includes(enrollment.conceito) + if (!completed) return false + return ( enrollment.subject._id.toString() === subject.subject._id.toString() )