From 0c3927d5c72ce795fcf23cbd547a133d26aa1eeb Mon Sep 17 00:00:00 2001 From: Justin Gasper Date: Mon, 24 Nov 2025 17:19:37 +1100 Subject: [PATCH 1/4] Tie-breaker fix for CORE-2303 --- src/shared/utils/mm-review-summations.js | 374 +++++++++++++++-------- 1 file changed, 255 insertions(+), 119 deletions(-) diff --git a/src/shared/utils/mm-review-summations.js b/src/shared/utils/mm-review-summations.js index 282c6effe..22663264e 100644 --- a/src/shared/utils/mm-review-summations.js +++ b/src/shared/utils/mm-review-summations.js @@ -1,7 +1,7 @@ -import _ from 'lodash'; +import _ from "lodash"; function normalizeScoreValue(score) { - if (_.isNil(score) || score === '' || score === '-') { + if (_.isNil(score) || score === "" || score === "-") { return null; } const parsed = Number(score); @@ -13,12 +13,12 @@ function normalizeScoreValue(score) { function getSummationTimestamp(summation) { const candidates = [ - _.get(summation, 'createdAt'), - _.get(summation, 'created'), - _.get(summation, 'reviewedDate'), - _.get(summation, 'updatedAt'), + _.get(summation, "createdAt"), + _.get(summation, "created"), + _.get(summation, "reviewedDate"), + _.get(summation, "updatedAt"), ]; - return _.find(candidates, value => !!value) || null; + return _.find(candidates, (value) => !!value) || null; } function toTimestampValue(value) { @@ -33,31 +33,34 @@ function toTimestampValue(value) { } function getSummationHandle(summation) { - const handle = _.get(summation, 'submitterHandle'); + const handle = _.get(summation, "submitterHandle"); if (!handle || !_.isString(handle) || !handle.trim()) { - return 'unknown'; + return "unknown"; } return handle; } function getSummationMemberId(summation) { - const memberId = _.get(summation, 'submitterId'); + const memberId = _.get(summation, "submitterId"); return _.isNil(memberId) ? null : _.toString(memberId); } function getSummationRating(summation) { - const rating = _.get(summation, 'submitterMaxRating'); + const rating = _.get(summation, "submitterMaxRating"); return _.isNil(rating) ? null : rating; } -function ensureSubmissionEntry(existingEntry, { submissionId, timestamp, timestampValue }) { +function ensureSubmissionEntry( + existingEntry, + { submissionId, timestamp, timestampValue }, +) { const baseEntry = { submissionId, submissionTime: timestamp || null, isLatest: null, provisionalScore: null, finalScore: null, - status: 'completed', + status: "completed", reviewSummations: [], reviewSummation: [], latestTimestamp: timestampValue, @@ -81,8 +84,11 @@ function ensureSubmissionEntry(existingEntry, { submissionId, timestamp, timesta ...existingEntry, submissionId: existingEntry.submissionId || submissionId, submissionTime: existingEntry.submissionTime || baseEntry.submissionTime, - isLatest: existingEntry.isLatest === undefined ? baseEntry.isLatest : existingEntry.isLatest, - status: existingEntry.status || 'completed', + isLatest: + existingEntry.isLatest === undefined + ? baseEntry.isLatest + : existingEntry.isLatest, + status: existingEntry.status || "completed", reviewSummations, reviewSummation, latestTimestamp: _.isFinite(existingEntry.latestTimestamp) @@ -97,7 +103,13 @@ function ensureSubmissionEntry(existingEntry, { submissionId, timestamp, timesta }; } -function mergeScoreData(meta, currentValue, score, timestampValue, options = {}) { +function mergeScoreData( + meta, + currentValue, + score, + timestampValue, + options = {}, +) { const { allowOlderTimestampUpdate = true } = options; const nextMeta = { ...meta }; let nextValue = currentValue; @@ -107,11 +119,18 @@ function mergeScoreData(meta, currentValue, score, timestampValue, options = {}) nextMeta.score = score; nextValue = _.isNil(score) ? null : score; } else if (timestampValue === nextMeta.timestamp) { - if (!_.isNil(score) && (_.isNil(nextMeta.score) || score > nextMeta.score)) { + if ( + !_.isNil(score) && + (_.isNil(nextMeta.score) || score > nextMeta.score) + ) { nextMeta.score = score; nextValue = score; } - } else if (allowOlderTimestampUpdate && _.isNil(nextValue) && !_.isNil(score)) { + } else if ( + allowOlderTimestampUpdate && + _.isNil(nextValue) && + !_.isNil(score) + ) { nextMeta.timestamp = timestampValue; nextMeta.score = score; nextValue = score; @@ -123,15 +142,18 @@ function mergeScoreData(meta, currentValue, score, timestampValue, options = {}) }; } -function updateSubmissionEntry(existingEntry, { - submissionId, - timestamp, - timestampValue, - normalizedScore, - summation, - isProvisional, - isLatest, -}) { +function updateSubmissionEntry( + existingEntry, + { + submissionId, + timestamp, + timestampValue, + normalizedScore, + summation, + isProvisional, + isLatest, + }, +) { const baseEntry = ensureSubmissionEntry(existingEntry, { submissionId, timestamp, @@ -160,21 +182,21 @@ function updateSubmissionEntry(existingEntry, { const provisionalResult = isProvisional ? mergeScoreData( - baseEntry.provisionalMeta, - baseEntry.provisionalScore, - normalizedScore, - timestampValue, - ) + baseEntry.provisionalMeta, + baseEntry.provisionalScore, + normalizedScore, + timestampValue, + ) : { meta: baseEntry.provisionalMeta, value: baseEntry.provisionalScore }; const finalResult = isProvisional ? { meta: baseEntry.finalMeta, value: baseEntry.finalScore } : mergeScoreData( - baseEntry.finalMeta, - baseEntry.finalScore, - normalizedScore, - timestampValue, - ); + baseEntry.finalMeta, + baseEntry.finalScore, + normalizedScore, + timestampValue, + ); const reviewSummations = [...baseEntry.reviewSummations, summation]; @@ -192,31 +214,58 @@ function updateSubmissionEntry(existingEntry, { }; } -function assignRanks(members, scoreKey, rankKey) { +function assignRanks(members, scoreKey, rankKey, options = {}) { + const { tieBreaker } = options; + const rankedEntries = members - .map(member => ({ - key: `${member.memberId || member.member || ''}`, + .map((member) => ({ + key: `${member.memberId || member.member || ""}`, score: member[scoreKey], + tieBreaker: tieBreaker ? tieBreaker(member) : null, })) - .filter(entry => !_.isNil(entry.score)) - .sort((a, b) => b.score - a.score); + .filter((entry) => !_.isNil(entry.score)) + .sort((a, b) => { + const scoreDiff = b.score - a.score; + if (scoreDiff !== 0) { + return scoreDiff; + } + if (tieBreaker) { + const aTieValue = _.isNil(a.tieBreaker) + ? Number.POSITIVE_INFINITY + : a.tieBreaker; + const bTieValue = _.isNil(b.tieBreaker) + ? Number.POSITIVE_INFINITY + : b.tieBreaker; + if (aTieValue !== bTieValue) { + return aTieValue - bTieValue; + } + } + return 0; + }); - let processed = 0; - let previousScore = null; - let currentRank = 0; const rankByKey = new Map(); - rankedEntries.forEach((entry) => { - processed += 1; - if (previousScore === null || entry.score !== previousScore) { - currentRank = processed; - previousScore = entry.score; - } - rankByKey.set(entry.key, currentRank); - }); + if (tieBreaker) { + rankedEntries.forEach((entry, index) => { + rankByKey.set(entry.key, index + 1); + }); + } else { + let processed = 0; + let previousScore = null; + let currentRank = 0; + + rankedEntries.forEach((entry) => { + processed += 1; + if (previousScore === null || entry.score !== previousScore) { + currentRank = processed; + previousScore = entry.score; + } + rankByKey.set(entry.key, currentRank); + }); + } return members.map((member) => { - const key = `${member.memberId || member.member || ''}`; + const key = `${member.memberId || member.member || ""}`; const rank = rankByKey.get(key); return { ...member, @@ -243,27 +292,34 @@ function createStatisticsSubmission({ }; } -function updateStatisticsSubmission(submission, { - timestamp, - timestampValue, - score, -}) { +function updateStatisticsSubmission( + submission, + { timestamp, timestampValue, score }, +) { const base = { ...submission, - meta: submission.meta ? { ...submission.meta } : { timestamp: -Infinity, score: null }, + meta: submission.meta + ? { ...submission.meta } + : { timestamp: -Infinity, score: null }, }; const previousMeta = base.meta; - const { meta, value } = mergeScoreData(previousMeta, base.score, score, timestampValue, { - allowOlderTimestampUpdate: false, - }); + const { meta, value } = mergeScoreData( + previousMeta, + base.score, + score, + timestampValue, + { + allowOlderTimestampUpdate: false, + }, + ); const hasNewerTimestamp = meta.timestamp > previousMeta.timestamp; return { ...base, - created: hasNewerTimestamp ? (timestamp || base.created) : base.created, - createdAt: hasNewerTimestamp ? (timestamp || base.createdAt) : base.createdAt, + created: hasNewerTimestamp ? timestamp || base.created : base.created, + createdAt: hasNewerTimestamp ? timestamp || base.createdAt : base.createdAt, score: value, meta, }; @@ -303,64 +359,110 @@ export function buildMmSubmissionData(reviewSummations = []) { memberEntry.rating = rating; } - const rawSubmissionId = _.get(summation, 'submissionId', _.get(summation, 'id')); - const submissionId = rawSubmissionId ? _.toString(rawSubmissionId) : `unknown-${handle}-${index}`; + const rawSubmissionId = _.get( + summation, + "submissionId", + _.get(summation, "id"), + ); + const submissionId = rawSubmissionId + ? _.toString(rawSubmissionId) + : `unknown-${handle}-${index}`; const timestamp = getSummationTimestamp(summation); const timestampValue = toTimestampValue(timestamp); - const normalizedScore = normalizeScoreValue(_.get(summation, 'aggregateScore')); + const normalizedScore = normalizeScoreValue( + _.get(summation, "aggregateScore"), + ); const isProvisional = Boolean(summation.isProvisional); - const isLatest = _.isNil(summation.isLatest) ? null : Boolean(summation.isLatest); + const isLatest = _.isNil(summation.isLatest) + ? null + : Boolean(summation.isLatest); - const updatedEntry = updateSubmissionEntry(memberEntry.submissionsMap.get(submissionId), { - submissionId, - timestamp, - timestampValue, - normalizedScore, - summation, - isProvisional, - isLatest, - }); + const updatedEntry = updateSubmissionEntry( + memberEntry.submissionsMap.get(submissionId), + { + submissionId, + timestamp, + timestampValue, + normalizedScore, + summation, + isProvisional, + isLatest, + }, + ); memberEntry.submissionsMap.set(submissionId, updatedEntry); }); const members = Array.from(membersByHandle.values()).map((memberEntry) => { const submissions = Array.from(memberEntry.submissionsMap.values()) - .map(submission => ({ + .map((submission) => ({ submissionId: submission.submissionId, submissionTime: submission.submissionTime, isLatest: submission.isLatest, - provisionalScore: _.isNil(submission.provisionalScore) ? null : submission.provisionalScore, - finalScore: _.isNil(submission.finalScore) ? null : submission.finalScore, - status: submission.status || 'completed', + provisionalScore: _.isNil(submission.provisionalScore) + ? null + : submission.provisionalScore, + finalScore: _.isNil(submission.finalScore) + ? null + : submission.finalScore, + status: submission.status || "completed", reviewSummations: [...submission.reviewSummations], reviewSummation: [...submission.reviewSummations], })) - .sort((a, b) => toTimestampValue(b.submissionTime) - toTimestampValue(a.submissionTime)); - - const hasLatestFlag = submissions.some(s => !_.isNil(s.isLatest)); - const latestSubmissions = hasLatestFlag ? submissions.filter(s => s.isLatest) : submissions; - const candidates = latestSubmissions.length ? latestSubmissions : submissions; - const bestProvisionalScore = _.chain(candidates) - .map(s => (_.isNil(s.provisionalScore) ? null : s.provisionalScore)) - .filter(s => !_.isNil(s)) - .max() - .value() || null; - const bestFinalScore = _.chain(candidates) - .map(s => (_.isNil(s.finalScore) ? null : s.finalScore)) - .filter(s => !_.isNil(s)) - .max() - .value() || null; + .sort( + (a, b) => + toTimestampValue(b.submissionTime) - + toTimestampValue(a.submissionTime), + ); + + const hasLatestFlag = submissions.some((s) => !_.isNil(s.isLatest)); + const latestSubmissions = hasLatestFlag + ? submissions.filter((s) => s.isLatest) + : submissions; + const candidates = latestSubmissions.length + ? latestSubmissions + : submissions; + const bestProvisionalScore = + _.chain(candidates) + .map((s) => (_.isNil(s.provisionalScore) ? null : s.provisionalScore)) + .filter((s) => !_.isNil(s)) + .max() + .value() || null; + const bestProvisionalTimestampValue = _.min( + _.chain(candidates) + .filter( + (s) => + !_.isNil(s.provisionalScore) && + s.provisionalScore === bestProvisionalScore, + ) + .map((s) => toTimestampValue(s.submissionTime)) + .filter((value) => value > 0) + .value(), + ); + const bestProvisionalTimestamp = _.isFinite(bestProvisionalTimestampValue) + ? bestProvisionalTimestampValue + : null; + const bestFinalScore = + _.chain(candidates) + .map((s) => (_.isNil(s.finalScore) ? null : s.finalScore)) + .filter((s) => !_.isNil(s)) + .max() + .value() || null; const rating = _.isNil(memberEntry.rating) ? null : memberEntry.rating; - const memberId = memberEntry.memberId ? _.toString(memberEntry.memberId) : null; - - const registrant = memberId ? { - userId: memberId, - memberHandle: memberEntry.handle === 'unknown' ? null : memberEntry.handle, - rating, - } : null; + const memberId = memberEntry.memberId + ? _.toString(memberEntry.memberId) + : null; + + const registrant = memberId + ? { + userId: memberId, + memberHandle: + memberEntry.handle === "unknown" ? null : memberEntry.handle, + rating, + } + : null; return { member: memberEntry.handle, @@ -371,15 +473,37 @@ export function buildMmSubmissionData(reviewSummations = []) { finalRank: null, submissions, bestProvisionalScore, + bestProvisionalTimestamp, bestFinalScore, }; }); - const withProvisionalRanks = assignRanks(members, 'bestProvisionalScore', 'provisionalRank'); - const withFinalRanks = assignRanks(withProvisionalRanks, 'bestFinalScore', 'finalRank'); + const withProvisionalRanks = assignRanks( + members, + "bestProvisionalScore", + "provisionalRank", + { + tieBreaker: (entry) => { + const timestamp = _.get(entry, "bestProvisionalTimestamp"); + return _.isFinite(timestamp) ? timestamp : Number.POSITIVE_INFINITY; + }, + }, + ); + const withFinalRanks = assignRanks( + withProvisionalRanks, + "bestFinalScore", + "finalRank", + ); return withFinalRanks - .map(({ bestProvisionalScore, bestFinalScore, ...rest }) => rest) + .map( + ({ + bestProvisionalScore, + bestFinalScore, + bestProvisionalTimestamp, + ...rest + }) => rest, + ) .sort((a, b) => { if (!_.isNil(a.finalRank) && !_.isNil(b.finalRank)) { return a.finalRank - b.finalRank; @@ -433,36 +557,48 @@ export function buildStatisticsData(reviewSummations = []) { const timestamp = getSummationTimestamp(summation); const timestampValue = toTimestampValue(timestamp); - const score = normalizeScoreValue(_.get(summation, 'aggregateScore')); + const score = normalizeScoreValue(_.get(summation, "aggregateScore")); - const rawSubmissionId = _.get(summation, 'submissionId', _.get(summation, 'id')); - const submissionId = rawSubmissionId ? _.toString(rawSubmissionId) : `unknown-${handle}-${index}`; + const rawSubmissionId = _.get( + summation, + "submissionId", + _.get(summation, "id"), + ); + const submissionId = rawSubmissionId + ? _.toString(rawSubmissionId) + : `unknown-${handle}-${index}`; const existingSubmission = entry.submissionsMap.get(submissionId); const updatedSubmission = existingSubmission - ? updateStatisticsSubmission(existingSubmission, { timestamp, timestampValue, score }) + ? updateStatisticsSubmission(existingSubmission, { + timestamp, + timestampValue, + score, + }) : createStatisticsSubmission({ - submissionId, - timestamp, - timestampValue, - score, - }); + submissionId, + timestamp, + timestampValue, + score, + }); entry.submissionsMap.set(submissionId, updatedSubmission); }); - return Array.from(grouped.values()).map(entry => ({ + return Array.from(grouped.values()).map((entry) => ({ handle: entry.handle, rating: entry.rating, submissions: Array.from(entry.submissionsMap.values()) - .map(submission => ({ + .map((submission) => ({ submissionId: submission.submissionId, created: submission.created, createdAt: submission.createdAt, score: submission.score, })) - .sort((a, b) => toTimestampValue(b.createdAt) - toTimestampValue(a.createdAt)), + .sort( + (a, b) => toTimestampValue(b.createdAt) - toTimestampValue(a.createdAt), + ), })); } From 4d8973783578c1d98cfe1ca4a0b7c887660ad318 Mon Sep 17 00:00:00 2001 From: Justin Gasper Date: Mon, 24 Nov 2025 17:28:07 +1100 Subject: [PATCH 2/4] Lint --- src/shared/utils/mm-review-summations.js | 162 +++++++++++------------ 1 file changed, 79 insertions(+), 83 deletions(-) diff --git a/src/shared/utils/mm-review-summations.js b/src/shared/utils/mm-review-summations.js index 22663264e..03158bf11 100644 --- a/src/shared/utils/mm-review-summations.js +++ b/src/shared/utils/mm-review-summations.js @@ -1,7 +1,7 @@ -import _ from "lodash"; +import _ from 'lodash'; function normalizeScoreValue(score) { - if (_.isNil(score) || score === "" || score === "-") { + if (_.isNil(score) || score === '' || score === '-') { return null; } const parsed = Number(score); @@ -13,12 +13,12 @@ function normalizeScoreValue(score) { function getSummationTimestamp(summation) { const candidates = [ - _.get(summation, "createdAt"), - _.get(summation, "created"), - _.get(summation, "reviewedDate"), - _.get(summation, "updatedAt"), + _.get(summation, 'createdAt'), + _.get(summation, 'created'), + _.get(summation, 'reviewedDate'), + _.get(summation, 'updatedAt'), ]; - return _.find(candidates, (value) => !!value) || null; + return _.find(candidates, value => !!value) || null; } function toTimestampValue(value) { @@ -33,20 +33,20 @@ function toTimestampValue(value) { } function getSummationHandle(summation) { - const handle = _.get(summation, "submitterHandle"); + const handle = _.get(summation, 'submitterHandle'); if (!handle || !_.isString(handle) || !handle.trim()) { - return "unknown"; + return 'unknown'; } return handle; } function getSummationMemberId(summation) { - const memberId = _.get(summation, "submitterId"); + const memberId = _.get(summation, 'submitterId'); return _.isNil(memberId) ? null : _.toString(memberId); } function getSummationRating(summation) { - const rating = _.get(summation, "submitterMaxRating"); + const rating = _.get(summation, 'submitterMaxRating'); return _.isNil(rating) ? null : rating; } @@ -60,7 +60,7 @@ function ensureSubmissionEntry( isLatest: null, provisionalScore: null, finalScore: null, - status: "completed", + status: 'completed', reviewSummations: [], reviewSummation: [], latestTimestamp: timestampValue, @@ -88,7 +88,7 @@ function ensureSubmissionEntry( existingEntry.isLatest === undefined ? baseEntry.isLatest : existingEntry.isLatest, - status: existingEntry.status || "completed", + status: existingEntry.status || 'completed', reviewSummations, reviewSummation, latestTimestamp: _.isFinite(existingEntry.latestTimestamp) @@ -120,16 +120,16 @@ function mergeScoreData( nextValue = _.isNil(score) ? null : score; } else if (timestampValue === nextMeta.timestamp) { if ( - !_.isNil(score) && - (_.isNil(nextMeta.score) || score > nextMeta.score) + !_.isNil(score) + && (_.isNil(nextMeta.score) || score > nextMeta.score) ) { nextMeta.score = score; nextValue = score; } } else if ( - allowOlderTimestampUpdate && - _.isNil(nextValue) && - !_.isNil(score) + allowOlderTimestampUpdate + && _.isNil(nextValue) + && !_.isNil(score) ) { nextMeta.timestamp = timestampValue; nextMeta.score = score; @@ -182,21 +182,21 @@ function updateSubmissionEntry( const provisionalResult = isProvisional ? mergeScoreData( - baseEntry.provisionalMeta, - baseEntry.provisionalScore, - normalizedScore, - timestampValue, - ) + baseEntry.provisionalMeta, + baseEntry.provisionalScore, + normalizedScore, + timestampValue, + ) : { meta: baseEntry.provisionalMeta, value: baseEntry.provisionalScore }; const finalResult = isProvisional ? { meta: baseEntry.finalMeta, value: baseEntry.finalScore } : mergeScoreData( - baseEntry.finalMeta, - baseEntry.finalScore, - normalizedScore, - timestampValue, - ); + baseEntry.finalMeta, + baseEntry.finalScore, + normalizedScore, + timestampValue, + ); const reviewSummations = [...baseEntry.reviewSummations, summation]; @@ -218,12 +218,12 @@ function assignRanks(members, scoreKey, rankKey, options = {}) { const { tieBreaker } = options; const rankedEntries = members - .map((member) => ({ - key: `${member.memberId || member.member || ""}`, + .map(member => ({ + key: `${member.memberId || member.member || ''}`, score: member[scoreKey], tieBreaker: tieBreaker ? tieBreaker(member) : null, })) - .filter((entry) => !_.isNil(entry.score)) + .filter(entry => !_.isNil(entry.score)) .sort((a, b) => { const scoreDiff = b.score - a.score; if (scoreDiff !== 0) { @@ -265,7 +265,7 @@ function assignRanks(members, scoreKey, rankKey, options = {}) { } return members.map((member) => { - const key = `${member.memberId || member.member || ""}`; + const key = `${member.memberId || member.member || ''}`; const rank = rankByKey.get(key); return { ...member, @@ -361,8 +361,8 @@ export function buildMmSubmissionData(reviewSummations = []) { const rawSubmissionId = _.get( summation, - "submissionId", - _.get(summation, "id"), + 'submissionId', + _.get(summation, 'id'), ); const submissionId = rawSubmissionId ? _.toString(rawSubmissionId) @@ -371,7 +371,7 @@ export function buildMmSubmissionData(reviewSummations = []) { const timestampValue = toTimestampValue(timestamp); const normalizedScore = normalizeScoreValue( - _.get(summation, "aggregateScore"), + _.get(summation, 'aggregateScore'), ); const isProvisional = Boolean(summation.isProvisional); const isLatest = _.isNil(summation.isLatest) @@ -396,7 +396,7 @@ export function buildMmSubmissionData(reviewSummations = []) { const members = Array.from(membersByHandle.values()).map((memberEntry) => { const submissions = Array.from(memberEntry.submissionsMap.values()) - .map((submission) => ({ + .map(submission => ({ submissionId: submission.submissionId, submissionTime: submission.submissionTime, isLatest: submission.isLatest, @@ -406,49 +406,45 @@ export function buildMmSubmissionData(reviewSummations = []) { finalScore: _.isNil(submission.finalScore) ? null : submission.finalScore, - status: submission.status || "completed", + status: submission.status || 'completed', reviewSummations: [...submission.reviewSummations], reviewSummation: [...submission.reviewSummations], })) .sort( - (a, b) => - toTimestampValue(b.submissionTime) - - toTimestampValue(a.submissionTime), + (a, b) => toTimestampValue(b.submissionTime) + - toTimestampValue(a.submissionTime), ); - const hasLatestFlag = submissions.some((s) => !_.isNil(s.isLatest)); + const hasLatestFlag = submissions.some(s => !_.isNil(s.isLatest)); const latestSubmissions = hasLatestFlag - ? submissions.filter((s) => s.isLatest) + ? submissions.filter(s => s.isLatest) : submissions; const candidates = latestSubmissions.length ? latestSubmissions : submissions; - const bestProvisionalScore = - _.chain(candidates) - .map((s) => (_.isNil(s.provisionalScore) ? null : s.provisionalScore)) - .filter((s) => !_.isNil(s)) - .max() - .value() || null; + const bestProvisionalScore = _.chain(candidates) + .map(s => (_.isNil(s.provisionalScore) ? null : s.provisionalScore)) + .filter(s => !_.isNil(s)) + .max() + .value() || null; const bestProvisionalTimestampValue = _.min( _.chain(candidates) .filter( - (s) => - !_.isNil(s.provisionalScore) && - s.provisionalScore === bestProvisionalScore, + s => !_.isNil(s.provisionalScore) + && s.provisionalScore === bestProvisionalScore, ) - .map((s) => toTimestampValue(s.submissionTime)) - .filter((value) => value > 0) + .map(s => toTimestampValue(s.submissionTime)) + .filter(value => value > 0) .value(), ); const bestProvisionalTimestamp = _.isFinite(bestProvisionalTimestampValue) ? bestProvisionalTimestampValue : null; - const bestFinalScore = - _.chain(candidates) - .map((s) => (_.isNil(s.finalScore) ? null : s.finalScore)) - .filter((s) => !_.isNil(s)) - .max() - .value() || null; + const bestFinalScore = _.chain(candidates) + .map(s => (_.isNil(s.finalScore) ? null : s.finalScore)) + .filter(s => !_.isNil(s)) + .max() + .value() || null; const rating = _.isNil(memberEntry.rating) ? null : memberEntry.rating; const memberId = memberEntry.memberId @@ -457,11 +453,11 @@ export function buildMmSubmissionData(reviewSummations = []) { const registrant = memberId ? { - userId: memberId, - memberHandle: - memberEntry.handle === "unknown" ? null : memberEntry.handle, - rating, - } + userId: memberId, + memberHandle: + memberEntry.handle === 'unknown' ? null : memberEntry.handle, + rating, + } : null; return { @@ -480,19 +476,19 @@ export function buildMmSubmissionData(reviewSummations = []) { const withProvisionalRanks = assignRanks( members, - "bestProvisionalScore", - "provisionalRank", + 'bestProvisionalScore', + 'provisionalRank', { tieBreaker: (entry) => { - const timestamp = _.get(entry, "bestProvisionalTimestamp"); + const timestamp = _.get(entry, 'bestProvisionalTimestamp'); return _.isFinite(timestamp) ? timestamp : Number.POSITIVE_INFINITY; }, }, ); const withFinalRanks = assignRanks( withProvisionalRanks, - "bestFinalScore", - "finalRank", + 'bestFinalScore', + 'finalRank', ); return withFinalRanks @@ -557,12 +553,12 @@ export function buildStatisticsData(reviewSummations = []) { const timestamp = getSummationTimestamp(summation); const timestampValue = toTimestampValue(timestamp); - const score = normalizeScoreValue(_.get(summation, "aggregateScore")); + const score = normalizeScoreValue(_.get(summation, 'aggregateScore')); const rawSubmissionId = _.get( summation, - "submissionId", - _.get(summation, "id"), + 'submissionId', + _.get(summation, 'id'), ); const submissionId = rawSubmissionId ? _.toString(rawSubmissionId) @@ -572,25 +568,25 @@ export function buildStatisticsData(reviewSummations = []) { const updatedSubmission = existingSubmission ? updateStatisticsSubmission(existingSubmission, { - timestamp, - timestampValue, - score, - }) + timestamp, + timestampValue, + score, + }) : createStatisticsSubmission({ - submissionId, - timestamp, - timestampValue, - score, - }); + submissionId, + timestamp, + timestampValue, + score, + }); entry.submissionsMap.set(submissionId, updatedSubmission); }); - return Array.from(grouped.values()).map((entry) => ({ + return Array.from(grouped.values()).map(entry => ({ handle: entry.handle, rating: entry.rating, submissions: Array.from(entry.submissionsMap.values()) - .map((submission) => ({ + .map(submission => ({ submissionId: submission.submissionId, created: submission.created, createdAt: submission.createdAt, From ee758a45273e012c3fc9a7d19e08f0b975ab3f32 Mon Sep 17 00:00:00 2001 From: Justin Gasper Date: Tue, 25 Nov 2025 08:16:58 +1100 Subject: [PATCH 3/4] Only look at latest submission --- src/shared/utils/mm-review-summations.js | 28 ++++++++++-------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/shared/utils/mm-review-summations.js b/src/shared/utils/mm-review-summations.js index 03158bf11..dd9abe2dc 100644 --- a/src/shared/utils/mm-review-summations.js +++ b/src/shared/utils/mm-review-summations.js @@ -422,23 +422,19 @@ export function buildMmSubmissionData(reviewSummations = []) { const candidates = latestSubmissions.length ? latestSubmissions : submissions; - const bestProvisionalScore = _.chain(candidates) - .map(s => (_.isNil(s.provisionalScore) ? null : s.provisionalScore)) - .filter(s => !_.isNil(s)) - .max() - .value() || null; - const bestProvisionalTimestampValue = _.min( - _.chain(candidates) - .filter( - s => !_.isNil(s.provisionalScore) - && s.provisionalScore === bestProvisionalScore, - ) - .map(s => toTimestampValue(s.submissionTime)) - .filter(value => value > 0) - .value(), + const latestSubmissionForRanking = (latestSubmissions.length + ? latestSubmissions + : submissions)[0] || null; + // Provisional ranks should be based solely on the most recent submission, + // not the best historical one. + const bestProvisionalScore = normalizeScoreValue( + _.get(latestSubmissionForRanking, 'provisionalScore'), + ); + const latestProvisionalTimestampValue = toTimestampValue( + _.get(latestSubmissionForRanking, 'submissionTime'), ); - const bestProvisionalTimestamp = _.isFinite(bestProvisionalTimestampValue) - ? bestProvisionalTimestampValue + const bestProvisionalTimestamp = _.isFinite(latestProvisionalTimestampValue) + ? latestProvisionalTimestampValue : null; const bestFinalScore = _.chain(candidates) .map(s => (_.isNil(s.finalScore) ? null : s.finalScore)) From 9fc9d9cfd2e970378d651bf19cf4950e5ad33ec9 Mon Sep 17 00:00:00 2001 From: Justin Gasper Date: Tue, 25 Nov 2025 09:54:32 +1100 Subject: [PATCH 4/4] Potential fix for ` vs. ``` in markdown rendering --- .../Specification/styles.scss | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/shared/components/challenge-detail/Specification/styles.scss b/src/shared/components/challenge-detail/Specification/styles.scss index 13db0603c..8de28d4cf 100644 --- a/src/shared/components/challenge-detail/Specification/styles.scss +++ b/src/shared/components/challenge-detail/Specification/styles.scss @@ -143,15 +143,23 @@ $tc-link-visited: #0c4e98; font-size: 13px; color: $tc-black; line-height: 20px; - margin: (2 * $base-unit) 0 (3 * $base-unit); - padding: 3 * $base-unit; - display: block; + margin: 0; + padding: 0 6px; + display: inline; } pre { + margin: (2 * $base-unit) 0 (3 * $base-unit); overflow-x: scroll; } + pre code { + display: block; + margin: 0; + padding: 3 * $base-unit; + white-space: pre; + } + ol { @include roboto-regular; @@ -385,8 +393,6 @@ $tc-link-visited: #0c4e98; } code { - white-space: pre; - margin: 10px 0 15px; background: $tc-gray-neutral-light; border: 1px solid silver; border-radius: 6px; @@ -395,8 +401,22 @@ $tc-link-visited: #0c4e98; font-size: 13px; color: $tc-black; line-height: 20px; - padding: 15px; + padding: 0 6px; + margin: 0; + display: inline; + white-space: pre-wrap; + } + + pre { + margin: 10px 0 15px; + overflow-x: scroll; + } + + pre code { display: block; + padding: 15px; + margin: 0; + white-space: pre; } sub { @@ -422,10 +442,6 @@ $tc-link-visited: #0c4e98; color: $tc-gray-90; line-height: 25px; } - - pre { - overflow-x: scroll; - } } }