Initial commit: teaching institution management API.
Express/MongoDB backend with JWT auth, RBAC, S3 uploads, notifications, and scheduled jobs.
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
// /components/dashboard/dashboardService.js
|
||||
'use strict';
|
||||
|
||||
const User = require('../users/userModel');
|
||||
const Professor = require('../professors/professorModel');
|
||||
const Course = require('../courses/courseModel');
|
||||
const Session = require('../sessions/sessionModel');
|
||||
|
||||
/**
|
||||
* Returns aggregated statistics for the admin dashboard
|
||||
*/
|
||||
const getAdminStats = async () => {
|
||||
const [
|
||||
totalUsers,
|
||||
activeUsers,
|
||||
totalProfessors,
|
||||
activeProfessors,
|
||||
totalCourses,
|
||||
totalSessions,
|
||||
recentSessions
|
||||
] = await Promise.all([
|
||||
User.countDocuments({}),
|
||||
User.countDocuments({ isActive: true }),
|
||||
Professor.countDocuments({}),
|
||||
Professor.countDocuments({ isActive: true }),
|
||||
Course.countDocuments({}),
|
||||
Session.countDocuments({}),
|
||||
Session.find({})
|
||||
.sort({ day: -1 })
|
||||
.limit(8)
|
||||
.populate('course', 'title type')
|
||||
.populate('class', 'name')
|
||||
.populate('professor', 'name surname')
|
||||
.lean()
|
||||
]);
|
||||
|
||||
// Daily enrollment trend (last 14 days via User.createdAt)
|
||||
const daysBack = 13;
|
||||
const rangeStart = new Date();
|
||||
rangeStart.setHours(0, 0, 0, 0);
|
||||
rangeStart.setDate(rangeStart.getDate() - daysBack);
|
||||
|
||||
const dailyUsersRaw = await User.aggregate([
|
||||
{ $match: { createdAt: { $gte: rangeStart } } },
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
year: { $year: '$createdAt' },
|
||||
month: { $month: '$createdAt' },
|
||||
day: { $dayOfMonth: '$createdAt' }
|
||||
},
|
||||
count: { $sum: 1 }
|
||||
}
|
||||
},
|
||||
{ $sort: { '_id.year': 1, '_id.month': 1, '_id.day': 1 } }
|
||||
]);
|
||||
|
||||
// Fill every day in the range so the chart stays continuous
|
||||
const dailyUsers = [];
|
||||
for (let i = 0; i <= daysBack; i += 1) {
|
||||
const date = new Date(rangeStart);
|
||||
date.setDate(rangeStart.getDate() + i);
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const day = date.getDate();
|
||||
const match = dailyUsersRaw.find(
|
||||
(item) => item._id.year === year && item._id.month === month && item._id.day === day
|
||||
);
|
||||
dailyUsers.push({
|
||||
_id: { year, month, day },
|
||||
date: date.toISOString(),
|
||||
count: match ? match.count : 0
|
||||
});
|
||||
}
|
||||
|
||||
// Daily sessions trend (same window)
|
||||
const dailySessionsRaw = await Session.aggregate([
|
||||
{ $match: { createdAt: { $gte: rangeStart } } },
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
year: { $year: '$createdAt' },
|
||||
month: { $month: '$createdAt' },
|
||||
day: { $dayOfMonth: '$createdAt' }
|
||||
},
|
||||
count: { $sum: 1 }
|
||||
}
|
||||
},
|
||||
{ $sort: { '_id.year': 1, '_id.month': 1, '_id.day': 1 } }
|
||||
]);
|
||||
|
||||
const dailySessions = dailyUsers.map((day) => {
|
||||
const match = dailySessionsRaw.find(
|
||||
(item) =>
|
||||
item._id.year === day._id.year &&
|
||||
item._id.month === day._id.month &&
|
||||
item._id.day === day._id.day
|
||||
);
|
||||
return {
|
||||
_id: day._id,
|
||||
date: day.date,
|
||||
count: match ? match.count : 0
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
totals: {
|
||||
users: totalUsers,
|
||||
activeUsers,
|
||||
professors: totalProfessors,
|
||||
activeProfessors,
|
||||
courses: totalCourses,
|
||||
sessions: totalSessions
|
||||
},
|
||||
recentSessions,
|
||||
charts: {
|
||||
dailyUsers,
|
||||
dailySessions
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = { getAdminStats };
|
||||
Reference in New Issue
Block a user