feat: implement recording stats update mechanism for auto-matching
Add automatic tracking of recording statistics (recordingsDone/recordingsReceived) for users participating in auto-matched collaborations. Stats are updated when both users complete mutual ratings after a recording session. Changes: - Add suggestionId, source, and statsApplied fields to Match model - Implement applyRecordingStatsForMatch() helper with user role convention (user1 = dancer, user2 = recorder) - Update suggestion status endpoint to create Match on acceptance - Update ratings endpoint to apply stats when match is completed - Add comprehensive unit tests (5) and integration tests (5) Convention: Stats only updated for auto-matches (source='auto') to ensure fairness metrics reflect actual algorithmic assignments, not manual matches. Test Results: 304/305 tests passing (99.7%) Coverage: 74.53% (+1.48%)
This commit is contained in:
@@ -3,7 +3,7 @@ const { prisma } = require('../utils/db');
|
||||
const { authenticate } = require('../middleware/auth');
|
||||
const { getIO } = require('../socket');
|
||||
const matchingService = require('../services/matching');
|
||||
const { SUGGESTION_STATUS } = require('../constants');
|
||||
const { SUGGESTION_STATUS, MATCH_STATUS } = require('../constants');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -1260,21 +1260,77 @@ router.put('/:slug/match-suggestions/:suggestionId/status', authenticate, async
|
||||
});
|
||||
}
|
||||
|
||||
// Update status
|
||||
const updated = await prisma.recordingSuggestion.update({
|
||||
where: { id: parseInt(suggestionId) },
|
||||
data: { status },
|
||||
select: {
|
||||
id: true,
|
||||
status: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
});
|
||||
// If accepted, create Match (if doesn't exist) and chat room
|
||||
if (status === 'accepted') {
|
||||
const result = await prisma.$transaction(async (tx) => {
|
||||
// Update suggestion status
|
||||
const updatedSuggestion = await tx.recordingSuggestion.update({
|
||||
where: { id: parseInt(suggestionId) },
|
||||
data: { status },
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: updated,
|
||||
});
|
||||
// Check if Match already exists for this suggestion (idempotency)
|
||||
const existingMatch = await tx.match.findUnique({
|
||||
where: { suggestionId: suggestion.id },
|
||||
});
|
||||
|
||||
if (existingMatch) {
|
||||
// Match already exists - just return the updated suggestion
|
||||
return { suggestion: updatedSuggestion, match: existingMatch };
|
||||
}
|
||||
|
||||
// Create private chat room
|
||||
const chatRoom = await tx.chatRoom.create({
|
||||
data: {
|
||||
type: 'private',
|
||||
eventId: event.id,
|
||||
},
|
||||
});
|
||||
|
||||
// Create Match with convention: user1 = dancer, user2 = recorder
|
||||
const match = await tx.match.create({
|
||||
data: {
|
||||
user1Id: suggestion.heat.userId, // dancer
|
||||
user2Id: suggestion.recorderId, // recorder
|
||||
eventId: event.id,
|
||||
suggestionId: suggestion.id,
|
||||
source: 'auto',
|
||||
status: MATCH_STATUS.ACCEPTED,
|
||||
roomId: chatRoom.id,
|
||||
statsApplied: false,
|
||||
},
|
||||
});
|
||||
|
||||
return { suggestion: updatedSuggestion, match };
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
id: result.suggestion.id,
|
||||
status: result.suggestion.status,
|
||||
updatedAt: result.suggestion.updatedAt,
|
||||
matchId: result.match.id,
|
||||
matchSlug: result.match.slug,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// Rejected - just update status
|
||||
const updated = await prisma.recordingSuggestion.update({
|
||||
where: { id: parseInt(suggestionId) },
|
||||
data: { status },
|
||||
select: {
|
||||
id: true,
|
||||
status: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: updated,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ const { prisma } = require('../utils/db');
|
||||
const { authenticate } = require('../middleware/auth');
|
||||
const { getIO } = require('../socket');
|
||||
const { MATCH_STATUS } = require('../constants');
|
||||
const matchingService = require('../services/matching');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -826,10 +827,32 @@ router.post('/:slug/ratings', authenticate, async (req, res, next) => {
|
||||
});
|
||||
|
||||
if (otherUserRating) {
|
||||
// Both users have rated - mark match as completed
|
||||
// Both users have rated - mark match as completed and apply stats
|
||||
|
||||
// Get full match with required fields for stats update
|
||||
const fullMatch = await prisma.match.findUnique({
|
||||
where: { id: match.id },
|
||||
select: {
|
||||
id: true,
|
||||
user1Id: true,
|
||||
user2Id: true,
|
||||
source: true,
|
||||
statsApplied: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Apply recording stats if not already applied (idempotency)
|
||||
if (fullMatch && !fullMatch.statsApplied) {
|
||||
await matchingService.applyRecordingStatsForMatch(fullMatch);
|
||||
}
|
||||
|
||||
// Update match status to completed and mark stats as applied
|
||||
await prisma.match.update({
|
||||
where: { id: match.id },
|
||||
data: { status: MATCH_STATUS.COMPLETED },
|
||||
data: {
|
||||
status: MATCH_STATUS.COMPLETED,
|
||||
statsApplied: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user