Files
multy-stub/server/routers/assessment-tools/routes/ratings.js
2025-11-21 16:19:47 +03:00

216 lines
6.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const router = require('express').Router();
const { Rating, Team, Expert, Criteria } = require('../models');
// GET /api/ratings - получить все оценки (с фильтрами)
router.get('/', async (req, res) => {
try {
const { expertId, teamId } = req.query;
const filter = {};
if (expertId) filter.expertId = expertId;
if (teamId) filter.teamId = teamId;
const ratings = await Rating.find(filter)
.populate('expertId', 'fullName')
.populate('teamId', 'name type')
.sort({ createdAt: -1 });
res.json(ratings);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// GET /api/ratings/team/:teamId - оценки конкретной команды
router.get('/team/:teamId', async (req, res) => {
try {
const ratings = await Rating.find({ teamId: req.params.teamId })
.populate('expertId', 'fullName')
.populate('teamId', 'name type projectName');
res.json(ratings);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// GET /api/ratings/expert/:expertId - оценки конкретного эксперта
router.get('/expert/:expertId', async (req, res) => {
try {
const ratings = await Rating.find({ expertId: req.params.expertId })
.populate('teamId', 'name type projectName');
res.json(ratings);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// GET /api/ratings/statistics - статистика с группировкой по командам
router.get('/statistics', async (req, res) => {
try {
const { type } = req.query;
// Получаем все команды
const teamFilter = type ? { type, isActive: true } : { isActive: true };
const teams = await Team.find(teamFilter);
// Получаем все оценки
const ratings = await Rating.find()
.populate('expertId', 'fullName')
.populate('teamId', 'name type projectName');
// Группируем оценки по командам
const statistics = teams.map(team => {
const teamRatings = ratings.filter(r => r.teamId && r.teamId._id.toString() === team._id.toString());
// Считаем средние оценки по критериям
const criteriaStats = {};
teamRatings.forEach(rating => {
rating.ratings.forEach(item => {
if (!criteriaStats[item.criterionName]) {
criteriaStats[item.criterionName] = {
name: item.criterionName,
scores: [],
average: 0
};
}
criteriaStats[item.criterionName].scores.push(item.score);
});
});
// Вычисляем средние значения
Object.keys(criteriaStats).forEach(key => {
const scores = criteriaStats[key].scores;
criteriaStats[key].average = scores.reduce((sum, s) => sum + s, 0) / scores.length;
});
// Считаем общий балл команды (среднее от всех экспертов)
const totalScore = teamRatings.length > 0
? teamRatings.reduce((sum, r) => sum + r.totalScore, 0) / teamRatings.length
: 0;
return {
team: {
_id: team._id,
name: team.name,
type: team.type,
projectName: team.projectName
},
ratings: teamRatings.map(r => ({
expert: r.expertId ? r.expertId.fullName : 'Unknown',
criteria: r.ratings,
totalScore: r.totalScore
})),
criteriaStats: Object.values(criteriaStats),
totalScore: totalScore,
ratingsCount: teamRatings.length
};
});
res.json(statistics);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// GET /api/ratings/top3 - топ-3 команды/участники
router.get('/top3', async (req, res) => {
try {
const { type } = req.query;
// Получаем статистику
const teamFilter = type ? { type, isActive: true } : { isActive: true };
const teams = await Team.find(teamFilter);
const ratings = await Rating.find()
.populate('teamId', 'name type projectName');
// Группируем и считаем средние баллы
const teamScores = teams.map(team => {
const teamRatings = ratings.filter(r => r.teamId && r.teamId._id.toString() === team._id.toString());
const totalScore = teamRatings.length > 0
? teamRatings.reduce((sum, r) => sum + r.totalScore, 0) / teamRatings.length
: 0;
return {
team: {
_id: team._id,
name: team.name,
type: team.type,
projectName: team.projectName
},
totalScore: totalScore,
ratingsCount: teamRatings.length
};
});
// Сортируем по баллам и берем топ-3
const top3 = teamScores
.filter(t => t.ratingsCount > 0)
.sort((a, b) => b.totalScore - a.totalScore)
.slice(0, 3);
res.json(top3);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// POST /api/ratings - создать/обновить оценку эксперта
router.post('/', async (req, res) => {
try {
const { expertId, teamId, ratings } = req.body;
if (!expertId || !teamId || !ratings || !Array.isArray(ratings)) {
return res.status(400).json({ error: 'Expert ID, team ID, and ratings array are required' });
}
// Проверяем существование эксперта и команды
const expert = await Expert.findById(expertId);
const team = await Team.findById(teamId);
if (!expert) {
return res.status(404).json({ error: 'Expert not found' });
}
if (!team) {
return res.status(404).json({ error: 'Team not found' });
}
// Проверяем, активна ли команда
if (!team.isActive) {
return res.status(400).json({ error: 'Team voting is disabled' });
}
// Ищем существующую оценку
let rating = await Rating.findOne({ expertId, teamId });
if (rating) {
// Обновляем существующую оценку
rating.ratings = ratings;
await rating.save();
} else {
// Создаем новую оценку
rating = await Rating.create({
expertId,
teamId,
ratings
});
}
// Возвращаем с populate
rating = await Rating.findById(rating._id)
.populate('expertId', 'fullName')
.populate('teamId', 'name type projectName');
res.status(rating ? 200 : 201).json(rating);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
module.exports = router;