Skip to content

Commit 8910f6e

Browse files
committed
PM-5221: include Data Science Challenge winners
What was broken The completion rerate endpoint only added ChallengeWinner placement participants for Development Challenge ratings. A Data Science Challenge winner without a review-api challengeResult row was never passed to the rating replay, so no DATA_SCIENCE / Challenge rating or history row was created for that profile. Root cause (if identifiable) The previous PM-5221 fix added Data Science Challenge rating jobs and Data Science rating dimensions, but participant discovery for DATA_SCIENCE_CHALLENGE still returned only challengeResult submitters while DEVELOPMENT_CHALLENGE also appended ChallengeWinner placement ids. What was changed Data Science Challenge participant discovery now includes ChallengeWinner placement ids using the existing winner fallback and de-duplication path, matching the Development Challenge behavior. Any added/updated tests Added unit coverage for rerating Data Science Challenge winners when challengeResult rows are absent.
1 parent 045c2ad commit 8910f6e

2 files changed

Lines changed: 75 additions & 4 deletions

File tree

src/services/StatisticsService.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,10 @@ async function fetchMarathonMatchParticipantIds (reviewDbClient, challengeId) {
602602

603603
/**
604604
* Resolve submitter ids for the challenge and rating source.
605-
* Marathon Match submitters are loaded from both challengeResult and final
606-
* review summations so partially synced result rows cannot omit lower-placed
607-
* participants from rerating.
605+
* Challenge ratings include placement winners so completed challenges without
606+
* challengeResult rows still rerate paid winners. Marathon Match submitters
607+
* are loaded from both challengeResult and final review summations so partially
608+
* synced result rows cannot omit lower-placed participants from rerating.
608609
* @param {Object} reviewDbClient raw pg review database client
609610
* @param {Object} challengeClient challenge Prisma client
610611
* @param {string|number} challengeId challenge identifier
@@ -613,7 +614,7 @@ async function fetchMarathonMatchParticipantIds (reviewDbClient, challengeId) {
613614
*/
614615
async function fetchRatingParticipantIds (reviewDbClient, challengeClient, challengeId, source) {
615616
const challengeResultUserIds = await fetchChallengeResultParticipantIds(reviewDbClient, challengeId)
616-
if (source === RATING_SOURCE_DEVELOPMENT) {
617+
if (source === RATING_SOURCE_DEVELOPMENT || source === RATING_SOURCE_DATA_SCIENCE_CHALLENGE) {
617618
return _.uniqBy(
618619
challengeResultUserIds.concat(await fetchChallengeWinnerParticipantIds(challengeClient, challengeId)),
619620
stringifyUserId

test/unit/StatisticsService.test.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,76 @@ describe('statistics service unit tests', () => {
943943
}
944944
})
945945

946+
it('rerateChallengeSubmitterRatings should include Data Science Challenge winners when result rows are absent', async () => {
947+
const dsRerateCalls = []
948+
const { service, restore } = loadStatisticsService({
949+
challengeRow: {
950+
id: 'ds-winner-only-challenge',
951+
status: 'COMPLETED',
952+
endDate: new Date('2026-06-09T08:45:00.000Z'),
953+
trackId: 'track-ds-id',
954+
typeId: 'type-challenge-id',
955+
track: { name: 'Data Science' },
956+
type: { name: 'Challenge' },
957+
tags: [],
958+
skills: [],
959+
metadata: []
960+
},
961+
reviewRows: [],
962+
challengeWinnerRows: [
963+
{ challengeId: 'ds-winner-only-challenge', userId: 301, type: 'PLACEMENT', placement: 1 }
964+
],
965+
prismaStub: {
966+
member: {
967+
findMany: async ({ where }) => where.userId.in.map((userId) => ({ userId }))
968+
}
969+
},
970+
rerateDevTrack: async (membersClient, challengeClient, reviewDbClient, userId, challengeId, options) => {
971+
dsRerateCalls.push({
972+
userId: String(userId),
973+
challengeId,
974+
options
975+
})
976+
return {
977+
challengesProcessed: 1,
978+
ratingsUpdated: 1
979+
}
980+
}
981+
})
982+
983+
try {
984+
const result = await service.rerateChallengeSubmitterRatings({ isMachine: true }, {
985+
challengeId: 'ds-winner-only-challenge'
986+
})
987+
988+
result.rerated.should.equal(true)
989+
result.membersProcessed.should.equal(1)
990+
result.ratingsAttempted.should.equal(1)
991+
result.ratingsUpdated.should.equal(1)
992+
result.participantIds.should.deep.equal(['301'])
993+
result.ratings.should.deep.equal([
994+
{
995+
trackId: 'DATA_SCIENCE',
996+
typeId: 'Challenge'
997+
}
998+
])
999+
dsRerateCalls.should.deep.equal([
1000+
{
1001+
userId: '301',
1002+
challengeId: 'ds-winner-only-challenge',
1003+
options: {
1004+
targetTrackName: 'DATA_SCIENCE',
1005+
targetTypeName: 'Challenge',
1006+
challengeTrackNames: ['DATA_SCIENCE'],
1007+
challengeTypeNames: ['Challenge']
1008+
}
1009+
}
1010+
])
1011+
} finally {
1012+
restore()
1013+
}
1014+
})
1015+
9461016
it('rerateChallengeSubmitterRatings should include Marathon Match final summation submitters when result rows are partial', async () => {
9471017
const mmRerateCalls = []
9481018
const { service, restore } = loadStatisticsService({

0 commit comments

Comments
 (0)