@@ -67,6 +67,7 @@ if (!_.includes(SUPPORTED_STATS_READ_SOURCES, configuredStatsReadSource)) {
6767const USE_LEGACY_STATS_READS = configuredStatsReadSource === LEGACY_STATS_READ_SOURCE
6868const RATING_SOURCE_DEVELOPMENT = 'DEVELOPMENT_CHALLENGE'
6969const RATING_SOURCE_DATA_SCIENCE_CHALLENGE = 'DATA_SCIENCE_CHALLENGE'
70+ const RATING_SOURCE_QUALITY_ASSURANCE_CHALLENGE = 'QUALITY_ASSURANCE_CHALLENGE'
7071const RATING_SOURCE_MARATHON_MATCH = 'MARATHON_MATCH'
7172const RERATE_MARATHON_ACTOR = 'rerate-mm-stats'
7273const CHALLENGE_TRACK_QUALITY_ASSURANCE = 'QUALITY_ASSURANCE'
@@ -379,7 +380,7 @@ function normalizeChallengeSourceTrack (value) {
379380
380381/**
381382 * Check whether a challenge source track is Quality Assurance.
382- * QA Challenge results are rated in the public Data Science Challenge bucket.
383+ * QA Challenge results are surfaced in the legacy Testing bucket.
383384 * @param {* } value raw challenge track label, enum value, or abbreviation
384385 * @returns {boolean } true when the source track is QA
385386 */
@@ -390,11 +391,18 @@ function isQualityAssuranceChallengeSourceTrack (value) {
390391
391392/**
392393 * Build the source tracks replayed for Data Science Challenge ratings.
393- * QA Challenges share the public Data Science Challenge rating bucket.
394394 * @returns {Array<string> } challenge source track labels
395395 */
396396function getDataScienceChallengeSourceTrackNames ( ) {
397- return [ TRACK_NAMES . DATA_SCIENCE , CHALLENGE_TRACK_QUALITY_ASSURANCE ]
397+ return [ TRACK_NAMES . DATA_SCIENCE ]
398+ }
399+
400+ /**
401+ * Build the source tracks replayed for Quality Assurance Challenge ratings.
402+ * @returns {Array<string> } challenge source track labels
403+ */
404+ function getQualityAssuranceChallengeSourceTrackNames ( ) {
405+ return [ CHALLENGE_TRACK_QUALITY_ASSURANCE ]
398406}
399407
400408/**
@@ -474,8 +482,11 @@ function resolveChallengeRatingSource (challenge) {
474482 return RATING_SOURCE_DEVELOPMENT
475483 }
476484
477- if ( ( trackName === TRACK_NAMES . DATA_SCIENCE || isQualityAssuranceChallengeSourceTrack ( rawTrackName ) ) &&
478- typeName === TYPE_NAMES . CHALLENGE ) {
485+ if ( isQualityAssuranceChallengeSourceTrack ( rawTrackName ) && typeName === TYPE_NAMES . CHALLENGE ) {
486+ return RATING_SOURCE_QUALITY_ASSURANCE_CHALLENGE
487+ }
488+
489+ if ( trackName === TRACK_NAMES . DATA_SCIENCE && typeName === TYPE_NAMES . CHALLENGE ) {
479490 return RATING_SOURCE_DATA_SCIENCE_CHALLENGE
480491 }
481492
@@ -513,6 +524,14 @@ function buildBaseRatingJob (challenge, source) {
513524 }
514525 }
515526
527+ if ( source === RATING_SOURCE_QUALITY_ASSURANCE_CHALLENGE ) {
528+ return {
529+ source,
530+ trackId : TRACK_NAMES . DEVELOP ,
531+ typeId : TYPE_NAMES . BUG_HUNT
532+ }
533+ }
534+
516535 if ( source === RATING_SOURCE_MARATHON_MATCH ) {
517536 return {
518537 source,
@@ -650,7 +669,9 @@ async function fetchMarathonMatchParticipantIds (reviewDbClient, challengeId) {
650669 */
651670async function fetchRatingParticipantIds ( reviewDbClient , challengeClient , challengeId , source ) {
652671 const challengeResultUserIds = await fetchChallengeResultParticipantIds ( reviewDbClient , challengeId )
653- if ( source === RATING_SOURCE_DEVELOPMENT || source === RATING_SOURCE_DATA_SCIENCE_CHALLENGE ) {
672+ if ( source === RATING_SOURCE_DEVELOPMENT ||
673+ source === RATING_SOURCE_DATA_SCIENCE_CHALLENGE ||
674+ source === RATING_SOURCE_QUALITY_ASSURANCE_CHALLENGE ) {
654675 return _ . uniqBy (
655676 challengeResultUserIds . concat ( await fetchChallengeWinnerParticipantIds ( challengeClient , challengeId ) ) ,
656677 stringifyUserId
@@ -726,15 +747,24 @@ async function rerateChallengeRatingJobForMember (challengeClient, reviewDbClien
726747 }
727748
728749 if ( job . source === RATING_SOURCE_DEVELOPMENT ||
729- job . source === RATING_SOURCE_DATA_SCIENCE_CHALLENGE ) {
730- const rerateOptions = job . source === RATING_SOURCE_DATA_SCIENCE_CHALLENGE
731- ? {
750+ job . source === RATING_SOURCE_DATA_SCIENCE_CHALLENGE ||
751+ job . source === RATING_SOURCE_QUALITY_ASSURANCE_CHALLENGE ) {
752+ let rerateOptions
753+ if ( job . source === RATING_SOURCE_DATA_SCIENCE_CHALLENGE ) {
754+ rerateOptions = {
732755 targetTrackName : TRACK_NAMES . DATA_SCIENCE ,
733756 targetTypeName : TYPE_NAMES . CHALLENGE ,
734757 challengeTrackNames : getDataScienceChallengeSourceTrackNames ( ) ,
735758 challengeTypeNames : [ TYPE_NAMES . CHALLENGE ]
736759 }
737- : undefined
760+ } else if ( job . source === RATING_SOURCE_QUALITY_ASSURANCE_CHALLENGE ) {
761+ rerateOptions = {
762+ targetTrackName : TRACK_NAMES . DEVELOP ,
763+ targetTypeName : TYPE_NAMES . BUG_HUNT ,
764+ challengeTrackNames : getQualityAssuranceChallengeSourceTrackNames ( ) ,
765+ challengeTypeNames : [ TYPE_NAMES . CHALLENGE ]
766+ }
767+ }
738768
739769 return rerateDevTrack (
740770 prisma ,
@@ -963,8 +993,9 @@ function isMarathonMatchType (typeName) {
963993
964994/**
965995 * Resolve the public stats dimensions for a challenge-backed row.
966- * Marathon Match and QA Challenge rows are part of the public DATA_SCIENCE
967- * bucket even when source challenge metadata uses a different track.
996+ * Marathon Match rows are part of the public DATA_SCIENCE bucket even when
997+ * source challenge metadata uses a different track. QA Challenge rows retain
998+ * the legacy Testing surface under DEVELOP / BUG_HUNT.
968999 * @param {Object } row row containing trackId and typeId
9691000 * @param {Object } dimensionLookup shared challenge dimension lookup
9701001 * @returns {Object } normalized track/type ids and names
@@ -983,12 +1014,16 @@ function resolveStatsDimensionForChallengeRow (row, dimensionLookup) {
9831014
9841015 const trackName = resolveTrackNameFromLookup ( dimensionLookup , row . trackId )
9851016 if ( typeName === TYPE_NAMES . CHALLENGE && isQualityAssuranceChallengeSourceTrack ( trackName ) ) {
986- const dataScienceTrackId = resolveTrackIdFromLookup ( dimensionLookup , TRACK_NAMES . DATA_SCIENCE )
987- return {
988- trackId : dataScienceTrackId || row . trackId ,
989- typeId : row . typeId ,
990- trackName : TRACK_NAMES . DATA_SCIENCE ,
991- typeName
1017+ const developTrackId = resolveTrackIdFromLookup ( dimensionLookup , TRACK_NAMES . DEVELOP )
1018+ const bugHuntTypeId = resolveTypeIdFromLookup ( dimensionLookup , TYPE_NAMES . BUG_HUNT )
1019+
1020+ if ( developTrackId && bugHuntTypeId ) {
1021+ return {
1022+ trackId : developTrackId ,
1023+ typeId : bugHuntTypeId ,
1024+ trackName : TRACK_NAMES . DEVELOP ,
1025+ typeName : TYPE_NAMES . BUG_HUNT
1026+ }
9921027 }
9931028 }
9941029
@@ -1236,9 +1271,10 @@ function filterUnifiedHistoryRowsToCompletedChallenges (rows, challengeMetadataB
12361271 * the response payload consumed by the profiles UI.
12371272 * @param {Array<Object> } rows unified history rows loaded from members.memberStatsHistory
12381273 * @param {Map<string, Object> } challengeMetadataById challenge metadata keyed by UUID and legacy ids
1274+ * @param {Object } dimensionLookup shared challenge dimension lookup
12391275 * @returns {Array<Object> } rows enriched with canonical challenge ids and names when available
12401276 */
1241- function enrichUnifiedHistoryRowsWithChallengeMetadata ( rows , challengeMetadataById ) {
1277+ function enrichUnifiedHistoryRowsWithChallengeMetadata ( rows , challengeMetadataById , dimensionLookup ) {
12421278 return _ . map ( rows || [ ] , ( row ) => {
12431279 const challengeId = _ . isNil ( row . challengeId ) ? null : String ( row . challengeId ) . trim ( )
12441280 if ( ! challengeId ) {
@@ -1257,9 +1293,16 @@ function enrichUnifiedHistoryRowsWithChallengeMetadata (rows, challengeMetadataB
12571293 : _ . get ( challenge , 'legacyRecord.legacySystemId' )
12581294 )
12591295 const preserveLegacyChallengeId = isLegacyNumericMarathonHistoryRow ( row )
1296+ const dimension = challenge . trackId && challenge . typeId
1297+ ? resolveStatsDimensionForChallengeRow ( {
1298+ trackId : String ( challenge . trackId ) ,
1299+ typeId : String ( challenge . typeId )
1300+ } , dimensionLookup )
1301+ : { }
12601302
12611303 return {
12621304 ...row ,
1305+ ...dimension ,
12631306 challengeId : preserveLegacyChallengeId ? challengeId : canonicalChallengeId ,
12641307 canonicalChallengeId,
12651308 legacyChallengeId,
@@ -1532,10 +1575,7 @@ function buildAggregatedStatsFromReviewResults (reviewRows, challengeMetadataByI
15321575 return
15331576 }
15341577
1535- const dimension = resolveStatsDimensionForChallengeRow ( {
1536- trackId : String ( challenge . trackId ) ,
1537- typeId : String ( challenge . typeId )
1538- } , dimensionLookup )
1578+ const dimension = resolveStatsDimensionForChallengeRow ( challenge , dimensionLookup )
15391579 const trackId = dimension . trackId
15401580 const typeId = dimension . typeId
15411581 if ( ! trackId || ! typeId ) {
@@ -1647,10 +1687,7 @@ function buildFallbackHistoryRowsFromReviewResults (reviewRows, challengeMetadat
16471687 return
16481688 }
16491689
1650- const dimension = resolveStatsDimensionForChallengeRow ( {
1651- trackId : String ( challenge . trackId ) ,
1652- typeId : String ( challenge . typeId )
1653- } , dimensionLookup )
1690+ const dimension = resolveStatsDimensionForChallengeRow ( challenge , dimensionLookup )
16541691 const trackId = dimension . trackId
16551692 const typeId = dimension . typeId
16561693 const pairKey = buildStatsTrackTypeKey ( trackId , typeId )
@@ -1731,10 +1768,7 @@ function buildFallbackHistoryRowsFromChallengeWinners (winnerRows, dimensionLook
17311768 return
17321769 }
17331770
1734- const dimension = resolveStatsDimensionForChallengeRow ( {
1735- trackId : String ( challenge . trackId ) ,
1736- typeId : String ( challenge . typeId )
1737- } , dimensionLookup )
1771+ const dimension = resolveStatsDimensionForChallengeRow ( challenge , dimensionLookup )
17381772 const trackId = dimension . trackId
17391773 const typeId = dimension . typeId
17401774 const pairKey = buildStatsTrackTypeKey ( trackId , typeId )
@@ -3425,7 +3459,8 @@ async function getHistoryStats (currentUser, handle, query) {
34253459 let annotatedRows = dedupeUnifiedHistoryRows ( filterUnifiedHistoryRowsToCompletedChallenges (
34263460 enrichUnifiedHistoryRowsWithChallengeMetadata (
34273461 annotateUnifiedDimensionRows ( historyRows , dimensionLookup ) ,
3428- challengeMetadataById
3462+ challengeMetadataById ,
3463+ dimensionLookup
34293464 ) ,
34303465 challengeMetadataById
34313466 ) )
@@ -4318,7 +4353,7 @@ refreshMemberStats.schema = {
43184353 * rating dimensions. This includes the native challenge track/type rating when
43194354 * supported and any configured named rating paths whose tags/skills match the
43204355 * challenge, such as the default AI path. Quality Assurance Challenge rows are
4321- * replayed into the public DATA_SCIENCE / Challenge rating bucket .
4356+ * replayed into the legacy Testing bucket under DEVELOP / BUG_HUNT .
43224357 * @param {Object } currentUser the user who performs operation
43234358 * @param {Object } data rerate payload containing the completed challenge id
43244359 * @returns {Object } summary of participants, rating jobs, updates, and per-member failures
@@ -4481,8 +4516,6 @@ rerateChallengeSubmitterRatings.schema = {
44814516 * Trigger a DEVELOPMENT / Challenge, DATA_SCIENCE / Challenge,
44824517 * DATA_SCIENCE / MARATHON_MATCH, or configured tag- or skill-based rating path
44834518 * re-rating pass beginning with the supplied challenge.
4484- * DATA_SCIENCE / Challenge rerates also replay Quality Assurance Challenge
4485- * source rows because QA history is surfaced in that public rating bucket.
44864519 * The relevant review-api results are reprocessed in chronological order and
44874520 * persisted into the existing unified rating tables for the member.
44884521 * @param {Object } currentUser the user who performs operation
0 commit comments