diff --git a/client/src/components/metrics/MetricsMain.tsx b/client/src/components/metrics/MetricsMain.tsx
index 0aa3898..247238d 100644
--- a/client/src/components/metrics/MetricsMain.tsx
+++ b/client/src/components/metrics/MetricsMain.tsx
@@ -1,26 +1,32 @@
-import React from 'react';
+import React, {useContext} from 'react';
import {
Typography,
} from '@mui/material';
-import DateTimeSelector from './DateTimeSelector';
import PersonalStats from './PersonalStats';
import OverallStats from './OverallStats';
import CumulativeStats from './CumulativeStats';
import Graph from './Graph';
+import {UserDataContext} from '../../contexts/UserDataContext';
+import AdminMetrics from './AdminMetrics';
export default function MetricsMain(props) {
+ const {userData} = useContext(UserDataContext);
+
return (
Metrics
- {/*
*/}
+
+ {
+ userData.isAdmin &&
+ }
);
}
diff --git a/client/src/components/settings/admin/DayPicker.tsx b/client/src/components/settings/admin/DayPicker.tsx
index bbd6d34..fbc8352 100644
--- a/client/src/components/settings/admin/DayPicker.tsx
+++ b/client/src/components/settings/admin/DayPicker.tsx
@@ -8,7 +8,7 @@ import {Delete as DeleteIcon} from '@mui/icons-material';
import SettingsService from '../../../services/SettingsService';
export default function DayPicker(props) {
- const {convertIdxToDays, daysOfWeek, room, roomDictionary, setRoomDictionary} = props;
+ const {convertIdxToDays, daysOfWeek, room, roomDictionary} = props;
const [newDays, setNewDays] = useState(convertIdxToDays(roomDictionary[room]));
const convertDaysToIdx = (daysArr) => {
diff --git a/client/src/components/settings/admin/Locations.tsx b/client/src/components/settings/admin/Locations.tsx
index ae8b009..a820d10 100644
--- a/client/src/components/settings/admin/Locations.tsx
+++ b/client/src/components/settings/admin/Locations.tsx
@@ -1,4 +1,4 @@
-import React, {useState, useEffect, useContext, useMemo} from 'react';
+import React, {useState, useContext, useMemo} from 'react';
import {
TableCell, Typography,
} from '@mui/material';
diff --git a/client/src/components/settings/admin/TASettings.tsx b/client/src/components/settings/admin/TASettings.tsx
index f7f52f8..21e1b11 100644
--- a/client/src/components/settings/admin/TASettings.tsx
+++ b/client/src/components/settings/admin/TASettings.tsx
@@ -1,4 +1,4 @@
-import React, {useState, useEffect, useContext, useMemo} from 'react';
+import React, {useState, useContext, useMemo} from 'react';
import {
Button, Checkbox, FormControlLabel, Grid, TableCell, TableRow, Typography, useTheme,
} from '@mui/material';
diff --git a/client/src/services/MetricsService.tsx b/client/src/services/MetricsService.tsx
index 3fc323d..07f65e8 100644
--- a/client/src/services/MetricsService.tsx
+++ b/client/src/services/MetricsService.tsx
@@ -1,6 +1,12 @@
import http from '../http-common';
class MetricsDataService {
+ getRankedTAs() {
+ return http.get('/metrics/rankedTAs');
+ }
+ getRankedStudents() {
+ return http.get('/metrics/rankedStudents');
+ }
getHelpedStudents() {
return http.get('/metrics/helpedStudents');
}
diff --git a/server/controllers/metrics.js b/server/controllers/metrics.js
index 25b4038..4b242ae 100644
--- a/server/controllers/metrics.js
+++ b/server/controllers/metrics.js
@@ -1,6 +1,7 @@
const Sequelize = require('sequelize');
const Promise = require("bluebird");
const moment = require("moment-timezone");
+let settings = require('./settings');
const models = require('../models');
const { sequelize } = require('../models');
@@ -32,7 +33,8 @@ exports.get_helped_students = (req, res) => {
ta_id: req.user.ta.ta_id,
help_time: {
[Sequelize.Op.ne]: null
- }
+ },
+ sem_id: settings.get_admin_settings().currSem,
},
order: [['entry_time', 'DESC']]
}).then((questionModels) => {
@@ -76,7 +78,8 @@ exports.get_num_questions_answered = (req, res) => {
ta_id: req.user.ta.ta_id,
help_time: {
[Sequelize.Op.ne]: null
- }
+ },
+ sem_id: settings.get_admin_settings().currSem,
}
}).then(({count, rows}) => {
respond(req, res, "Got number of questions answered", { numQuestions: count }, 200);
@@ -94,7 +97,8 @@ exports.get_avg_time_per_question = (req, res) => {
ta_id: req.user.ta.ta_id,
help_time: {
[Sequelize.Op.ne]: null,
- }
+ },
+ sem_id: settings.get_admin_settings().currSem,
}
}).then(({count, rows}) => {
let averageTime = 0;
@@ -120,7 +124,8 @@ exports.get_num_questions_today = (req, res) => {
where: {
entry_time: {
[Sequelize.Op.gte]: new Date(today - 8 * 60 * 60 * 1000),
- }
+ },
+ sem_id: settings.get_admin_settings().currSem,
}
}).then(({count}) => {
respond(req, res, "Got number of questions today", { numQuestionsToday: count }, 200);
@@ -141,7 +146,8 @@ exports.get_num_bad_questions_today = (req, res) => {
},
num_asked_to_fix: {
[Sequelize.Op.gt]: 0
- }
+ },
+ sem_id: settings.get_admin_settings().currSem,
}
}).then(({count}) => {
respond(req, res, "Got number of bad questions today", { numBadQuestionsToday: count }, 200);
@@ -161,7 +167,8 @@ exports.get_avg_wait_time_today = (req, res) => {
},
help_time: {
[Sequelize.Op.ne]: null,
- }
+ },
+ sem_id: settings.get_admin_settings().currSem,
}
}).then(({count, rows}) => {
@@ -188,7 +195,8 @@ exports.get_ta_student_ratio_today = (req, res) => {
where: {
entry_time: {
[Sequelize.Op.gte]: new Date(today - 8 * 60 * 60 * 1000),
- }
+ },
+ sem_id: settings.get_admin_settings().currSem,
}
}).then(({count, rows}) => {
const taCount = rows.reduce((acc, questionModel) => {
@@ -227,6 +235,10 @@ exports.get_total_num_questions = (req, res) => {
models.question.findAndCountAll({
where: {
+ sem_id: settings.get_admin_settings().currSem,
+ help_time: {
+ [Sequelize.Op.ne]: null
+ },
}
}).then(({count}) => {
respond(req, res, "Got number of questions answered", { numQuestions: count }, 200);
@@ -243,7 +255,8 @@ exports.get_total_avg_time_per_question = (req, res) => {
where: {
help_time: {
[Sequelize.Op.ne]: null,
- }
+ },
+ sem_id: settings.get_admin_settings().currSem,
}
}).then(({count, rows}) => {
let averageTime = 0;
@@ -269,7 +282,8 @@ exports.get_total_avg_wait_time = (req, res) => {
where: {
help_time: {
[Sequelize.Op.ne]: null,
- }
+ },
+ sem_id: settings.get_admin_settings().currSem,
}
}).then(({count, rows}) => {
@@ -300,7 +314,11 @@ exports.get_num_students_per_day_last_week = (req, res) => {
where: {
entry_time: {
[Sequelize.Op.gte]: new Date(today - 7 * 24 * 60 * 60 * 1000),
- }
+ },
+ help_time: {
+ [Sequelize.Op.ne]: null
+ },
+ sem_id: settings.get_admin_settings().currSem,
},
group: [Sequelize.fn('date', Sequelize.literal(`"entry_time" AT TIME ZONE 'EST'`))],
order: [[Sequelize.col('day'), 'ASC']]
@@ -328,6 +346,12 @@ exports.get_num_students_per_day = (req, res) => {
[Sequelize.fn('date_part', 'dow', Sequelize.literal(`"entry_time" AT TIME ZONE 'EST'`)), 'day_of_week'],
[Sequelize.fn('count', Sequelize.col('question_id')), 'count']
],
+ where: {
+ sem_id: settings.get_admin_settings().currSem,
+ help_time: {
+ [Sequelize.Op.ne]: null
+ },
+ },
group: [Sequelize.fn('date_part', 'dow', Sequelize.literal(`"entry_time" AT TIME ZONE 'EST'`))],
order: [[Sequelize.col('count'), 'DESC']]
}).then((data) => {
@@ -355,6 +379,12 @@ exports.get_num_students_overall = (req, res) => {
[Sequelize.fn('date', Sequelize.literal(`"entry_time" AT TIME ZONE 'EST'`)), 'day'],
[Sequelize.fn('count', Sequelize.col('question_id')), 'count']
],
+ where: {
+ sem_id: settings.get_admin_settings().currSem,
+ help_time: {
+ [Sequelize.Op.ne]: null
+ },
+ },
group: [Sequelize.fn('date', Sequelize.literal(`"entry_time" AT TIME ZONE 'EST'`))],
order: [[Sequelize.col('day'), 'ASC']]
}).then((data) => {
@@ -368,3 +398,135 @@ exports.get_num_students_overall = (req, res) => {
respond(req, res, "Got number of students overall", { numStudentsOverall: numStudentsOverall }, 200);
});
}
+
+exports.get_ranked_students = (req, res) => {
+ if (!req.user || !req.user.isTA || !req.user.isAdmin) {
+ respond_error(req, res, "You don't have permission to perform this operation", 403);
+ return;
+ }
+
+ let studentMap = {};
+ models.question.findAll({
+ where: {
+ sem_id: settings.get_admin_settings().currSem,
+ help_time: {
+ [Sequelize.Op.ne]: null
+ }
+ }
+ }).then((questionModels) => {
+
+
+ for (const questionModel of questionModels) {
+ let question = questionModel.dataValues;
+
+ if (question.student_id in studentMap) {
+ studentMap[question.student_id].count++;
+ studentMap[question.student_id].timeHelped += (question.exit_time - question.help_time) / 1000 / 60;
+ studentMap[question.student_id].badCount += parseInt(question.num_asked_to_fix);
+ } else {
+ studentMap[question.student_id] = {
+ count: 1,
+ timeHelped: (question.exit_time - question.help_time) / 1000 / 60,
+ badCount: parseInt(question.num_asked_to_fix)
+ };
+ }
+ }
+
+ let accountReqs = [];
+ for (const student_id in studentMap) {
+ accountReqs.push(models.account.findByPk(student_id));
+ }
+
+ return Promise.all(accountReqs);
+ }).then((accounts) => {
+ let rankedStudents = [];
+
+ for (const account of accounts) {
+ let accountData = account.dataValues;
+ let user_id = accountData.user_id;
+
+ if (user_id in studentMap) {
+ rankedStudents.push({
+ student_name: accountData.preferred_name,
+ student_andrew: accountData.email.split("@")[0],
+ count: studentMap[user_id].count,
+ badCount: studentMap[user_id].badCount,
+ timeHelped: Math.round(studentMap[user_id].timeHelped * 10) / 10,
+ });
+ }
+ }
+
+ rankedStudents.sort((a, b) => {
+ if (a.count != b.count) {
+ return b.count - a.count;
+ } else {
+ return b.timeHelped - a.timeHelped;
+ }
+ });
+ respond(req, res, "Got ranked students", { rankedStudents: rankedStudents }, 200);
+ });
+}
+
+exports.get_ranked_tas = (req, res) => {
+ if (!req.user || !req.user.isTA || !req.user.isAdmin) {
+ respond_error(req, res, "You don't have permission to perform this operation", 403);
+ return;
+ }
+
+ let taMap = {};
+ models.question.findAll({
+ where: {
+ sem_id: settings.get_admin_settings().currSem,
+ help_time: {
+ [Sequelize.Op.ne]: null
+ }
+ }
+ }).then((questionModels) => {
+
+ for (const questionModel of questionModels) {
+ let question = questionModel.dataValues;
+
+ if (question.ta_id in taMap) {
+ taMap[question.ta_id].count++;
+ taMap[question.ta_id].timeHelping += (question.exit_time - question.help_time) / 1000 / 60;
+ } else {
+ taMap[question.ta_id] = {
+ count: 1,
+ timeHelping: (question.exit_time - question.help_time) / 1000 / 60,
+ };
+ }
+ }
+
+ let accountReqs = [];
+ for (const ta_id in taMap) {
+ accountReqs.push(models.account.findByPk(ta_id));
+ }
+
+ return Promise.all(accountReqs);
+ }).then((accounts) => {
+ let rankedTAs = [];
+
+ for (const account of accounts) {
+ let accountData = account.dataValues;
+ let user_id = accountData.user_id;
+
+ if (user_id in taMap) {
+ rankedTAs.push({
+ ta_name: accountData.preferred_name,
+ ta_andrew: accountData.email.split("@")[0],
+ count: taMap[user_id].count,
+ timeHelping: Math.round(taMap[user_id].timeHelping * 10) / 10,
+ });
+ }
+ }
+
+ rankedTAs.sort((a, b) => {
+ if (a.count != b.count) {
+ return b.count - a.count;
+ } else {
+ return b.timeHelped - a.timeHelped;
+ }
+ });
+ respond(req, res, "Got ranked TAs", { rankedTAs: rankedTAs }, 200);
+ });
+}
diff --git a/server/routes/metrics.js b/server/routes/metrics.js
index 3f58280..ecec056 100644
--- a/server/routes/metrics.js
+++ b/server/routes/metrics.js
@@ -16,4 +16,6 @@ router.get('/totalAvgWaitTime', metrics.get_total_avg_wait_time);
router.get('/numStudentsPerDayLastWeek', metrics.get_num_students_per_day_last_week);
router.get('/numStudentsPerDay', metrics.get_num_students_per_day);
router.get('/numStudentsOverall', metrics.get_num_students_overall);
+router.get('/rankedStudents', metrics.get_ranked_students);
+router.get('/rankedTAs', metrics.get_ranked_tas);
module.exports = router;