tods-competition-factory 7.1.0 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/assemblies/generators/mocks/applyScenarioProfile.mjs +11 -11
- package/dist/esm/mutate/drawDefinitions/matchUpGovernor/removeDoubleExit.mjs +21 -0
- package/dist/esm/mutate/drawDefinitions/positionGovernor/doubleExitAdvancement.mjs +53 -4
- package/dist/esm/mutate/drawDefinitions/resetDrawDefinition.mjs +7 -0
- package/dist/esm/mutate/matchUps/drawPositions/assignDrawPositionBye.mjs +1 -1
- package/dist/esm/mutate/matchUps/drawPositions/drawPositionPlacement.mjs +21 -5
- package/dist/esm/mutate/matchUps/drawPositions/propagateUnfillableLoserBye.mjs +63 -0
- package/dist/esm/mutate/matchUps/matchUpStatus/sideExitProvenance.mjs +19 -1
- package/dist/esm/mutate/matchUps/schedule/schedulers/proScheduler/proAutoSchedule.mjs +46 -2
- package/dist/esm/mutate/matchUps/score/modifyMatchUpScore.mjs +5 -3
- package/dist/esm/mutate/timeItems/publishTournamentInfo.mjs +1 -1
- package/dist/esm/package.json.mjs +1 -1
- package/dist/esm/query/drawDefinition/getDrawInconsistencies.mjs +5 -4
- package/dist/esm/query/drawDefinition/getStructureInconsistencies.mjs +42 -9
- package/dist/esm/query/drawDefinition/isActiveDownstream.mjs +2 -2
- package/dist/esm/query/event/getEventInconsistencies.mjs +2 -2
- package/dist/esm/query/integrity/inconsistency.mjs +4 -1
- package/dist/esm/query/matchUps/scheduling/getMatchUpReadiness.mjs +8 -0
- package/dist/esm/query/tournaments/getTournamentInconsistencies.mjs +2 -2
- package/dist/tods-competition-factory.development.cjs.js +291 -40
- package/dist/tods-competition-factory.development.cjs.js.map +1 -1
- package/dist/tods-competition-factory.production.cjs.min.js +1 -1
- package/dist/tods-competition-factory.production.cjs.min.js.map +1 -1
- package/package.json +3 -2
|
@@ -35,17 +35,6 @@ function applyScenarioProfile({ tournamentRecord, scenarioProfile }) {
|
|
|
35
35
|
const anchorDate = anchor === 'NOW' ? new Date() : new Date(anchor);
|
|
36
36
|
if (isNaN(anchorDate.getTime()))
|
|
37
37
|
return { error: INVALID_VALUES };
|
|
38
|
-
if (assignCourts) {
|
|
39
|
-
const tournamentRecords = { [tournamentRecord.tournamentId]: tournamentRecord };
|
|
40
|
-
const scheduleDates = [
|
|
41
|
-
...new Set((allTournamentMatchUps({ tournamentRecord }).matchUps ?? [])
|
|
42
|
-
.map((m) => m.schedule?.scheduledDate)
|
|
43
|
-
.filter(Boolean)
|
|
44
|
-
.map((d) => String(d).split('T')[0])),
|
|
45
|
-
];
|
|
46
|
-
if (scheduleDates.length)
|
|
47
|
-
scheduleProfileGrid({ tournamentRecords, scheduleDates });
|
|
48
|
-
}
|
|
49
38
|
const matchUps = allTournamentMatchUps({ tournamentRecord }).matchUps ?? [];
|
|
50
39
|
const scheduled = matchUps
|
|
51
40
|
.map((matchUp) => ({ matchUp, instant: scheduledInstant(matchUp) }))
|
|
@@ -104,6 +93,17 @@ function applyScenarioProfile({ tournamentRecord, scenarioProfile }) {
|
|
|
104
93
|
if (!result?.error)
|
|
105
94
|
shiftedCount += 1;
|
|
106
95
|
}
|
|
96
|
+
if (assignCourts) {
|
|
97
|
+
const tournamentRecords = { [tournamentRecord.tournamentId]: tournamentRecord };
|
|
98
|
+
const scheduleDates = [
|
|
99
|
+
...new Set((allTournamentMatchUps({ tournamentRecord }).matchUps ?? [])
|
|
100
|
+
.map((m) => m.schedule?.scheduledDate)
|
|
101
|
+
.filter(Boolean)
|
|
102
|
+
.map((d) => String(d).split('T')[0])),
|
|
103
|
+
];
|
|
104
|
+
if (scheduleDates.length)
|
|
105
|
+
scheduleProfileGrid({ tournamentRecords, scheduleDates });
|
|
106
|
+
}
|
|
107
107
|
return {
|
|
108
108
|
...SUCCESS,
|
|
109
109
|
anchoredTo: anchorDate.toISOString(),
|
|
@@ -150,6 +150,10 @@ function withdrawExitFromByeChain({ inContextDrawMatchUps, withdrawnSourceIds, d
|
|
|
150
150
|
if (!noContextTargetMatchUp || !carriesWithdrawnOrigin(noContextTargetMatchUp, withdrawnSourceIds)) {
|
|
151
151
|
return { ...SUCCESS };
|
|
152
152
|
}
|
|
153
|
+
const priorWinningSide = noContextTargetMatchUp.winningSide;
|
|
154
|
+
const advancedDrawPosition = priorWinningSide
|
|
155
|
+
? (noContextTargetMatchUp.drawPositions ?? [])[priorWinningSide - 1]
|
|
156
|
+
: undefined;
|
|
153
157
|
const unwound = getUnwoundState({
|
|
154
158
|
pairedPreviousDoubleExit: false,
|
|
155
159
|
noContextTargetMatchUp,
|
|
@@ -191,6 +195,23 @@ function withdrawExitFromByeChain({ inContextDrawMatchUps, withdrawnSourceIds, d
|
|
|
191
195
|
const nextWinnerMatchUp = targetMatchUps?.winnerMatchUp;
|
|
192
196
|
if (!nextWinnerMatchUp?.matchUpId)
|
|
193
197
|
return { ...SUCCESS };
|
|
198
|
+
if (advancedDrawPosition && nextWinnerMatchUp.structureId === fromMatchUp.structureId) {
|
|
199
|
+
pushGlobalLog({
|
|
200
|
+
method: 'withdrawExitFromByeChain',
|
|
201
|
+
decision: 'take_back_advanced_winner',
|
|
202
|
+
nextWinnerMatchUpId: nextWinnerMatchUp.matchUpId,
|
|
203
|
+
advancedDrawPosition,
|
|
204
|
+
color: 'brightcyan',
|
|
205
|
+
keyColors,
|
|
206
|
+
});
|
|
207
|
+
removeDirectedWinner({
|
|
208
|
+
winningDrawPosition: advancedDrawPosition,
|
|
209
|
+
winnerMatchUp: nextWinnerMatchUp,
|
|
210
|
+
inContextDrawMatchUps,
|
|
211
|
+
drawDefinition,
|
|
212
|
+
matchUpsMap,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
194
215
|
return withdrawExitFromByeChain({
|
|
195
216
|
fromMatchUp: nextWinnerMatchUp,
|
|
196
217
|
inContextDrawMatchUps,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { propagateUnfillableLoserBye } from '../../matchUps/drawPositions/propagateUnfillableLoserBye.mjs';
|
|
1
2
|
import { assignDrawPositionBye, advanceDrawPosition } from '../../matchUps/drawPositions/assignDrawPositionBye.mjs';
|
|
2
3
|
import { getPairedPreviousMatchUpIsDoubleExit } from '../../../query/matchUps/getPairedPreviousMatchUpIsDoubleExit.mjs';
|
|
3
4
|
import { assignMatchUpDrawPosition, directWinner } from '../../matchUps/drawPositions/drawPositionPlacement.mjs';
|
|
@@ -638,7 +639,12 @@ function advanceByeAdvancedDrawPosition({ nextWinnerMatchUpDrawPositions, nextWi
|
|
|
638
639
|
const advancingParticipantId = inContextDrawMatchUps
|
|
639
640
|
.find((candidate) => candidate.matchUpId === noContextNextWinnerMatchUp.matchUpId)
|
|
640
641
|
?.sides?.find((side) => side.drawPosition === nextDrawPositionToAdvance)?.participantId;
|
|
641
|
-
const
|
|
642
|
+
const arrivalSideNumber = getExitArrivalSideNumber({
|
|
643
|
+
inContextDrawMatchUps,
|
|
644
|
+
nextWinnerMatchUp,
|
|
645
|
+
sourceMatchUp: targetMatchUp,
|
|
646
|
+
});
|
|
647
|
+
const winningSide = advancingParticipantId || !occupiedSide ? occupiedSide : 3 - (arrivalSideNumber ?? occupiedSide);
|
|
642
648
|
const exitingSideNumber = isExit(EXIT) && winningSide ? 3 - winningSide : undefined;
|
|
643
649
|
const provenance = {
|
|
644
650
|
...getSideExitProvenance({ matchUp: noContextNextWinnerMatchUp }),
|
|
@@ -728,7 +734,9 @@ function opponentFeederCanDeliver({ inContextDrawMatchUps, nextWinnerMatchUp, so
|
|
|
728
734
|
const holdsParticipant = feeder.sides?.some((side) => side.participantId);
|
|
729
735
|
if (holdsParticipant)
|
|
730
736
|
return true;
|
|
731
|
-
|
|
737
|
+
if (feeder.matchUpStatus === BYE)
|
|
738
|
+
return !!feeder.sides?.some((side) => side && !side.participantId && !side.bye);
|
|
739
|
+
return !isAnyExit(feeder.matchUpStatus);
|
|
732
740
|
});
|
|
733
741
|
}
|
|
734
742
|
function matchUpHoldsBye({ drawDefinition, matchUp }) {
|
|
@@ -760,7 +768,15 @@ function carryExitOnward({ inContextDrawMatchUps, drawDefinition, matchUpsMap, o
|
|
|
760
768
|
});
|
|
761
769
|
if (!arrivalSideNumber)
|
|
762
770
|
return decorateResult({ result: { ...SUCCESS }, stack });
|
|
763
|
-
|
|
771
|
+
const arrivingSideOccupied = !!nextWinnerMatchUp.sides?.some((side) => side?.sideNumber === arrivalSideNumber && side?.participantId);
|
|
772
|
+
if (arrivingSideOccupied || isAnyExit(nextWinnerMatchUp.matchUpStatus)) {
|
|
773
|
+
logAdvancement(stack, {
|
|
774
|
+
color: 'brightyellow',
|
|
775
|
+
decision: arrivingSideOccupied ? 'exit_not_carried_arriving_side_occupied' : 'exit_not_carried_convergence',
|
|
776
|
+
nextWinnerMatchUpId: nextWinnerMatchUp.matchUpId,
|
|
777
|
+
fromMatchUpId: fromMatchUp.matchUpId,
|
|
778
|
+
arrivalSideNumber,
|
|
779
|
+
});
|
|
764
780
|
return decorateResult({ result: { ...SUCCESS }, stack });
|
|
765
781
|
}
|
|
766
782
|
const noContextNextWinnerMatchUp = matchUpsMap.drawMatchUps.find((matchUp) => matchUp.matchUpId === nextWinnerMatchUp.matchUpId);
|
|
@@ -777,6 +793,9 @@ function carryExitOnward({ inContextDrawMatchUps, drawDefinition, matchUpsMap, o
|
|
|
777
793
|
});
|
|
778
794
|
return decorateResult({ result: { ...SUCCESS }, stack });
|
|
779
795
|
}
|
|
796
|
+
const opponentSideNumber = arrivalSideNumber === 1 ? 2 : 1;
|
|
797
|
+
const settledOpponentSide = nextWinnerMatchUp.sides?.find((side) => side?.sideNumber === opponentSideNumber && side?.participantId && !side?.bye);
|
|
798
|
+
const settledOpponentSideNumber = settledOpponentSide ? opponentSideNumber : undefined;
|
|
780
799
|
const provenance = {
|
|
781
800
|
...getSideExitProvenance({ matchUp: noContextNextWinnerMatchUp }),
|
|
782
801
|
...buildCarriedExitProvenance({
|
|
@@ -800,7 +819,7 @@ function carryExitOnward({ inContextDrawMatchUps, drawDefinition, matchUpsMap, o
|
|
|
800
819
|
matchUpId: noContextNextWinnerMatchUp.matchUpId,
|
|
801
820
|
appliedPolicies: params.appliedPolicies,
|
|
802
821
|
matchUp: noContextNextWinnerMatchUp,
|
|
803
|
-
winningSide: undefined,
|
|
822
|
+
winningSide: !holdsBye ? settledOpponentSideNumber : undefined,
|
|
804
823
|
matchUpStatus: holdsBye ? BYE : EXIT,
|
|
805
824
|
removeScore: true,
|
|
806
825
|
drawDefinition,
|
|
@@ -808,6 +827,36 @@ function carryExitOnward({ inContextDrawMatchUps, drawDefinition, matchUpsMap, o
|
|
|
808
827
|
if (result.error)
|
|
809
828
|
return decorateResult({ result, stack });
|
|
810
829
|
mergeSideExitProvenance({ matchUp: noContextNextWinnerMatchUp, provenance });
|
|
830
|
+
if (!holdsBye && settledOpponentSideNumber && settledOpponentSide?.drawPosition) {
|
|
831
|
+
logAdvancement(stack, {
|
|
832
|
+
color: 'brightcyan',
|
|
833
|
+
keyColors: { decision: 'brightgreen' },
|
|
834
|
+
decision: 'carried_exit_advances_settled_opponent',
|
|
835
|
+
nextWinnerMatchUpId: nextWinnerMatchUp.matchUpId,
|
|
836
|
+
drawPositionToAdvance: settledOpponentSide.drawPosition,
|
|
837
|
+
winningSide: settledOpponentSideNumber,
|
|
838
|
+
});
|
|
839
|
+
const advanced = advanceDrawPosition({
|
|
840
|
+
inContextDrawMatchUps: getAllDrawMatchUps({ inContext: true, drawDefinition, matchUpsMap })?.matchUps ?? [],
|
|
841
|
+
drawPositionToAdvance: settledOpponentSide.drawPosition,
|
|
842
|
+
matchUpId: noContextNextWinnerMatchUp.matchUpId,
|
|
843
|
+
tournamentRecord: params.tournamentRecord,
|
|
844
|
+
event: params.event,
|
|
845
|
+
drawDefinition,
|
|
846
|
+
matchUpsMap,
|
|
847
|
+
});
|
|
848
|
+
if (advanced?.error)
|
|
849
|
+
return decorateResult({ result: advanced, stack });
|
|
850
|
+
const unfillable = propagateUnfillableLoserBye({
|
|
851
|
+
matchUpId: noContextNextWinnerMatchUp.matchUpId,
|
|
852
|
+
tournamentRecord: params.tournamentRecord,
|
|
853
|
+
event: params.event,
|
|
854
|
+
drawDefinition,
|
|
855
|
+
matchUpsMap,
|
|
856
|
+
});
|
|
857
|
+
if (unfillable?.error)
|
|
858
|
+
return decorateResult({ result: unfillable, stack });
|
|
859
|
+
}
|
|
811
860
|
if (!holdsBye)
|
|
812
861
|
return decorateResult({ result: { ...SUCCESS }, stack });
|
|
813
862
|
const refreshed = getAllDrawMatchUps({ inContext: true, drawDefinition, matchUpsMap })?.matchUps ?? inContextDrawMatchUps;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { modifyDrawNotice, modifyMatchUpNotice } from '../notifications/drawNotifications.mjs';
|
|
2
2
|
import { normalizeDrawPositions } from '../matchUps/drawPositions/normalizeDrawPositions.mjs';
|
|
3
|
+
import { clearSideExitProvenance } from '../matchUps/matchUpStatus/sideExitProvenance.mjs';
|
|
3
4
|
import { getAllStructureMatchUps } from '../../query/matchUps/getAllStructureMatchUps.mjs';
|
|
4
5
|
import { isLuckyBasedDraw } from '../../query/drawDefinition/isLuckyBasedDraw.mjs';
|
|
5
6
|
import { removeExtension } from '../extensions/removeExtension.mjs';
|
|
@@ -144,7 +145,13 @@ function resetStructureMatchUps({ removeScheduling, tournamentRecord, removeAssi
|
|
|
144
145
|
});
|
|
145
146
|
}
|
|
146
147
|
}
|
|
148
|
+
function clearExitResidue(matchUp) {
|
|
149
|
+
if (matchUp.matchUpStatusCodes?.length)
|
|
150
|
+
matchUp.matchUpStatusCodes = [];
|
|
151
|
+
clearSideExitProvenance(matchUp);
|
|
152
|
+
}
|
|
147
153
|
function resetMatchUpScore({ matchUp, isLuckyDraw, removeAssignments, roundNumber, isRoundRobin, playedPositions }) {
|
|
154
|
+
clearExitResidue(matchUp);
|
|
148
155
|
if (isLuckyDraw) {
|
|
149
156
|
if (!removeAssignments && matchUp.matchUpStatus === BYE) ;
|
|
150
157
|
else {
|
|
@@ -6,7 +6,7 @@ import { getPairedPreviousMatchUpIsDoubleExit } from '../../../query/matchUps/ge
|
|
|
6
6
|
import { getUpdatedDrawPositions } from '../../drawDefinitions/matchUpGovernor/getUpdatedDrawPositions.mjs';
|
|
7
7
|
import { updateMatchUpStatusCodes } from '../../drawDefinitions/matchUpGovernor/matchUpStatusCodes.mjs';
|
|
8
8
|
import { getStructureDrawPositionProfiles } from '../../../query/structure/getStructureDrawPositionProfiles.mjs';
|
|
9
|
-
import { isPropagatedExit, clearResolvedSideExitProvenance } from '../matchUpStatus/sideExitProvenance.mjs';
|
|
9
|
+
import { isPropagatedExit, isProjectedExitCode, clearResolvedSideExitProvenance } from '../matchUpStatus/sideExitProvenance.mjs';
|
|
10
10
|
import { getExitWinningSide } from '../../drawDefinitions/matchUpGovernor/getExitWinningSide.mjs';
|
|
11
11
|
import { removeLineUpSubstitutions } from '../../drawDefinitions/removeLineUpSubstitutions.mjs';
|
|
12
12
|
import { getMatchUpsMap, getMappedStructureMatchUps } from '../../../query/matchUps/getMatchUpsMap.mjs';
|
|
@@ -26,6 +26,7 @@ import { getRoundMatchUps } from '../../../query/matchUps/getRoundMatchUps.mjs';
|
|
|
26
26
|
import { decorateResult } from '../../../functions/global/decorateResult.mjs';
|
|
27
27
|
import { getAllDrawMatchUps } from '../../../query/matchUps/drawMatchUps.mjs';
|
|
28
28
|
import { positionTargets } from '../../../query/matchUp/positionTargets.mjs';
|
|
29
|
+
import { propagateUnfillableLoserBye } from './propagateUnfillableLoserBye.mjs';
|
|
29
30
|
import { assignDrawPositionBye } from './assignDrawPositionBye.mjs';
|
|
30
31
|
import { getParticipantId } from '../../../functions/global/extractors.mjs';
|
|
31
32
|
import { pushGlobalLog } from '../../../functions/global/globalLog.mjs';
|
|
@@ -106,7 +107,8 @@ function assignMatchUpDrawPosition({ inContextDrawMatchUps, sourceMatchUpStatus,
|
|
|
106
107
|
!!matchUp?.roundNumber &&
|
|
107
108
|
!!initialRoundNumber &&
|
|
108
109
|
matchUp.roundNumber > initialRoundNumber;
|
|
109
|
-
|
|
110
|
+
const resolvesPendingExit = !positionAdded && isPropagatedExit$1 && slotNewlyOccupied;
|
|
111
|
+
if (matchUp && (positionAdded || resolvesPendingExit)) {
|
|
110
112
|
applyPositionToMatchUp({
|
|
111
113
|
updatedDrawPositions,
|
|
112
114
|
holdsPropagatedExit,
|
|
@@ -189,6 +191,15 @@ function assignMatchUpDrawPosition({ inContextDrawMatchUps, sourceMatchUpStatus,
|
|
|
189
191
|
});
|
|
190
192
|
if (byeResult?.error)
|
|
191
193
|
return byeResult;
|
|
194
|
+
const unfillableResult = propagateUnfillableLoserBye({
|
|
195
|
+
matchUpId: matchUp?.matchUpId,
|
|
196
|
+
tournamentRecord,
|
|
197
|
+
drawDefinition,
|
|
198
|
+
matchUpsMap,
|
|
199
|
+
event,
|
|
200
|
+
});
|
|
201
|
+
if (unfillableResult?.error)
|
|
202
|
+
return unfillableResult;
|
|
192
203
|
return { ...SUCCESS };
|
|
193
204
|
}
|
|
194
205
|
function resolveMatchUpStatus({ isByeMatchUp, matchUpStatus, isDoubleExitExit, matchUp }) {
|
|
@@ -200,10 +211,12 @@ function resolveMatchUpStatus({ isByeMatchUp, matchUpStatus, isDoubleExitExit, m
|
|
|
200
211
|
matchUp.matchUpStatus) ||
|
|
201
212
|
TO_BE_PLAYED);
|
|
202
213
|
}
|
|
203
|
-
function
|
|
214
|
+
function policyCodeString(code) {
|
|
204
215
|
if (typeof code === 'string')
|
|
205
216
|
return code || undefined;
|
|
206
|
-
|
|
217
|
+
if (isProjectedExitCode(code))
|
|
218
|
+
return undefined;
|
|
219
|
+
return code?.matchUpStatusCode ?? code?.code ?? undefined;
|
|
207
220
|
}
|
|
208
221
|
function applyPositionToMatchUp({ updatedDrawPositions, sourceMatchUpStatus, holdsPropagatedExit, isPropagatedExit, isDoubleExitExit, tournamentRecord, inContextMatchUp, sourceMatchUpId, drawDefinition, matchUpStatus, drawPosition, matchUpsMap, matchUpId, matchUp, stack, event, }) {
|
|
209
222
|
const refreshedMatchUps = getAllDrawMatchUps({
|
|
@@ -222,7 +235,10 @@ function applyPositionToMatchUp({ updatedDrawPositions, sourceMatchUpStatus, hol
|
|
|
222
235
|
undefined;
|
|
223
236
|
if (advancedExitWinningSide && !isDoubleExitExit) {
|
|
224
237
|
const exitSideNumber = advancedExitWinningSide === 1 ? 2 : 1;
|
|
225
|
-
const carriedCode = (matchUp.matchUpStatusCodes ?? [])
|
|
238
|
+
const carriedCode = (matchUp.matchUpStatusCodes ?? [])
|
|
239
|
+
.filter((code) => !isProjectedExitCode(code))
|
|
240
|
+
.map(policyCodeString)
|
|
241
|
+
.find(Boolean);
|
|
226
242
|
const matchUpStatusCodes = [];
|
|
227
243
|
if (carriedCode) {
|
|
228
244
|
for (let i = 0; i < exitSideNumber - 1; i++)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { getSideExitProvenance, carriedExitStatus, recordByeClaim } from '../matchUpStatus/sideExitProvenance.mjs';
|
|
2
|
+
import { getPositionAssignments } from '../../../query/drawDefinition/positionsGetter.mjs';
|
|
3
|
+
import { assignDrawPositionBye, assignFedDrawPositionBye } from './assignDrawPositionBye.mjs';
|
|
4
|
+
import { getAllDrawMatchUps } from '../../../query/matchUps/drawMatchUps.mjs';
|
|
5
|
+
import { positionTargets } from '../../../query/matchUp/positionTargets.mjs';
|
|
6
|
+
import { isExit } from '../../../validators/isExit.mjs';
|
|
7
|
+
|
|
8
|
+
function propagateUnfillableLoserBye({ tournamentRecord, drawDefinition, matchUpsMap, matchUpId, event, }) {
|
|
9
|
+
if (!matchUpId || !drawDefinition || !matchUpsMap)
|
|
10
|
+
return undefined;
|
|
11
|
+
const matchUp = matchUpsMap.drawMatchUps?.find((candidate) => candidate.matchUpId === matchUpId);
|
|
12
|
+
if (!matchUp?.winningSide || !isExit(matchUp.matchUpStatus))
|
|
13
|
+
return undefined;
|
|
14
|
+
const provenance = getSideExitProvenance({ matchUp }) ?? {};
|
|
15
|
+
const exitingSides = [1, 2].filter((sideNumber) => carriedExitStatus(provenance[sideNumber]));
|
|
16
|
+
if (exitingSides.length !== 1)
|
|
17
|
+
return undefined;
|
|
18
|
+
if (matchUp.winningSide === exitingSides[0])
|
|
19
|
+
return undefined;
|
|
20
|
+
const inContextDrawMatchUps = getAllDrawMatchUps({ inContext: true, drawDefinition, matchUpsMap })?.matchUps ?? [];
|
|
21
|
+
const inContextMatchUp = inContextDrawMatchUps.find((candidate) => candidate.matchUpId === matchUpId);
|
|
22
|
+
const { targetLinks: { loserTargetLink }, targetMatchUps: { loserMatchUp, loserTargetDrawPosition }, } = positionTargets({ inContextDrawMatchUps, drawDefinition, matchUpId });
|
|
23
|
+
if (!loserMatchUp || !loserTargetDrawPosition || !loserTargetLink)
|
|
24
|
+
return undefined;
|
|
25
|
+
if (loserMatchUp.structureId === inContextMatchUp?.structureId)
|
|
26
|
+
return undefined;
|
|
27
|
+
const { positionAssignments } = getPositionAssignments({ drawDefinition, structureId: loserMatchUp.structureId });
|
|
28
|
+
const targetAssignment = positionAssignments?.find((assignment) => assignment.drawPosition === loserTargetDrawPosition);
|
|
29
|
+
if (targetAssignment?.participantId || targetAssignment?.bye)
|
|
30
|
+
return undefined;
|
|
31
|
+
const result = loserMatchUp.roundNumber === 1
|
|
32
|
+
? assignDrawPositionBye({
|
|
33
|
+
structureId: loserMatchUp.structureId,
|
|
34
|
+
drawPosition: loserTargetDrawPosition,
|
|
35
|
+
byeFromPropagation: true,
|
|
36
|
+
tournamentRecord,
|
|
37
|
+
drawDefinition,
|
|
38
|
+
matchUpsMap,
|
|
39
|
+
event,
|
|
40
|
+
})
|
|
41
|
+
: assignFedDrawPositionBye({
|
|
42
|
+
loserTargetDrawPosition,
|
|
43
|
+
byeFromPropagation: true,
|
|
44
|
+
loserTargetLink,
|
|
45
|
+
tournamentRecord,
|
|
46
|
+
drawDefinition,
|
|
47
|
+
loserMatchUp,
|
|
48
|
+
matchUpsMap,
|
|
49
|
+
event,
|
|
50
|
+
});
|
|
51
|
+
if (result?.error)
|
|
52
|
+
return result;
|
|
53
|
+
const refreshed = getAllDrawMatchUps({ inContext: true, drawDefinition, matchUpsMap })?.matchUps ?? [];
|
|
54
|
+
const inContextTarget = refreshed.find((candidate) => candidate.matchUpId === loserMatchUp.matchUpId);
|
|
55
|
+
const claimSideNumber = (inContextTarget?.sides ?? []).find((side) => side?.drawPosition === loserTargetDrawPosition)?.sideNumber;
|
|
56
|
+
if (claimSideNumber === 1 || claimSideNumber === 2) {
|
|
57
|
+
const rawLoserMatchUp = matchUpsMap.drawMatchUps?.find((candidate) => candidate.matchUpId === loserMatchUp.matchUpId);
|
|
58
|
+
recordByeClaim({ claimantMatchUpId: matchUpId, sideNumber: claimSideNumber, matchUp: rawLoserMatchUp });
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { propagateUnfillableLoserBye };
|
|
@@ -81,6 +81,17 @@ function clearSideExitProvenance(matchUp) {
|
|
|
81
81
|
if (matchUp)
|
|
82
82
|
delete matchUp.sideExitProvenance;
|
|
83
83
|
}
|
|
84
|
+
function retainByeClaimsOnly(provenance) {
|
|
85
|
+
if (!provenance)
|
|
86
|
+
return undefined;
|
|
87
|
+
const retained = {};
|
|
88
|
+
for (const sideNumber of [1, 2]) {
|
|
89
|
+
const claims = provenance[sideNumber]?.byeClaims;
|
|
90
|
+
if (claims?.length)
|
|
91
|
+
retained[sideNumber] = { byeClaims: [...claims] };
|
|
92
|
+
}
|
|
93
|
+
return Object.keys(retained).length ? retained : undefined;
|
|
94
|
+
}
|
|
84
95
|
function clearResolvedSideExitProvenance(matchUp) {
|
|
85
96
|
if (!matchUp || isAnyExit(matchUp.matchUpStatus) || matchUp.matchUpStatus === BYE)
|
|
86
97
|
return;
|
|
@@ -218,6 +229,13 @@ function projectExitStatusCodes(provenance) {
|
|
|
218
229
|
sideNumber,
|
|
219
230
|
}));
|
|
220
231
|
}
|
|
232
|
+
function isProjectedExitCode(code) {
|
|
233
|
+
if (!code || typeof code !== 'object')
|
|
234
|
+
return false;
|
|
235
|
+
if (code.matchUpStatusCode || code.code)
|
|
236
|
+
return false;
|
|
237
|
+
return !!(code.matchUpStatus || code.previousMatchUpStatus || code.sideNumber);
|
|
238
|
+
}
|
|
221
239
|
function isPropagatedExit({ matchUp }) {
|
|
222
240
|
return !!getSideExitProvenance({ matchUp });
|
|
223
241
|
}
|
|
@@ -304,4 +322,4 @@ function withdrawProducedExits({ mappedMatchUps, sourceMatchUpId, }) {
|
|
|
304
322
|
return withdrawn;
|
|
305
323
|
}
|
|
306
324
|
|
|
307
|
-
export { buildCarriedExitProvenance, buildSideExitProvenance, byeClaimSurvives, carriedExitStatus, clearResolvedSideExitProvenance, clearSideExitProvenance, collapseDoubleExitStatus, deriveExitStateFromProvenance, exitOutcomeCode, exitProducedBy, getNativeSideExitProvenance, getSideExitProvenance, isPropagatedExit, mergeSideExitProvenance, producedExitStatus, projectExitStatusCodes, recordByeClaim, retainForeignProvenance, setSideExitProvenance, withdrawByeClaim, withdrawByeClaimsFrom, withdrawProducedExits };
|
|
325
|
+
export { buildCarriedExitProvenance, buildSideExitProvenance, byeClaimSurvives, carriedExitStatus, clearResolvedSideExitProvenance, clearSideExitProvenance, collapseDoubleExitStatus, deriveExitStateFromProvenance, exitOutcomeCode, exitProducedBy, getNativeSideExitProvenance, getSideExitProvenance, isProjectedExitCode, isPropagatedExit, mergeSideExitProvenance, producedExitStatus, projectExitStatusCodes, recordByeClaim, retainByeClaimsOnly, retainForeignProvenance, setSideExitProvenance, withdrawByeClaim, withdrawByeClaimsFrom, withdrawProducedExits };
|
|
@@ -5,10 +5,51 @@ import { competitionScheduleMatchUps } from '../../../../../query/matchUps/compe
|
|
|
5
5
|
import { matchUpChronologicalSort } from '../../../../../functions/sorters/matchUpChronologicalSort.mjs';
|
|
6
6
|
import { bulkScheduleMatchUps } from '../../bulkScheduleMatchUps.mjs';
|
|
7
7
|
import { getMatchUpDependencies } from '../../../../../query/matchUps/getMatchUpDependencies.mjs';
|
|
8
|
+
import { getVenuesAndCourts } from '../../../../../query/venues/venuesAndCourtsGetter.mjs';
|
|
9
|
+
import { getGridBookings } from '../../../../../query/venues/getGridBookings.mjs';
|
|
8
10
|
import { validMatchUps } from '../../../../../validators/validMatchUp.mjs';
|
|
9
11
|
import { isObject } from '../../../../../tools/objects.mjs';
|
|
10
12
|
import { INVALID_VALUES, MISSING_CONTEXT } from '../../../../../constants/errorConditionConstants.mjs';
|
|
11
13
|
|
|
14
|
+
function timeToMinutes(value) {
|
|
15
|
+
if (typeof value !== 'string' || !value)
|
|
16
|
+
return undefined;
|
|
17
|
+
const time = value.includes('T') ? value.split('T')[1] : value;
|
|
18
|
+
const [hours, minutes] = time.split(':').map(Number);
|
|
19
|
+
if (!Number.isFinite(hours) || !Number.isFinite(minutes))
|
|
20
|
+
return undefined;
|
|
21
|
+
return hours * 60 + minutes;
|
|
22
|
+
}
|
|
23
|
+
function buildCourtClosures({ tournamentRecords, scheduledDate }) {
|
|
24
|
+
const closures = {};
|
|
25
|
+
for (const court of getVenuesAndCourts({ tournamentRecords }).courts ?? []) {
|
|
26
|
+
const { timeBookings } = getGridBookings({ court, date: scheduledDate });
|
|
27
|
+
const windows = [];
|
|
28
|
+
for (const booking of timeBookings) {
|
|
29
|
+
const start = timeToMinutes(booking.startTime);
|
|
30
|
+
const end = timeToMinutes(booking.endTime);
|
|
31
|
+
if (start !== undefined && end !== undefined)
|
|
32
|
+
windows.push({ start, end });
|
|
33
|
+
}
|
|
34
|
+
if (windows.length)
|
|
35
|
+
closures[court.courtId] = windows;
|
|
36
|
+
}
|
|
37
|
+
return closures;
|
|
38
|
+
}
|
|
39
|
+
function isClosedAt(windows, scheduledTime) {
|
|
40
|
+
if (!windows?.length)
|
|
41
|
+
return false;
|
|
42
|
+
const startsAt = timeToMinutes(scheduledTime);
|
|
43
|
+
if (startsAt === undefined)
|
|
44
|
+
return false;
|
|
45
|
+
return windows.some((window) => startsAt >= window.start && startsAt < window.end);
|
|
46
|
+
}
|
|
47
|
+
function findOpenCourtIndex(availableCourts, matchUp, hasClosures, courtClosures) {
|
|
48
|
+
if (!hasClosures)
|
|
49
|
+
return 0;
|
|
50
|
+
const scheduledTime = matchUp?.schedule?.scheduledTime;
|
|
51
|
+
return availableCourts.findIndex((court) => !isClosedAt(courtClosures[court?.schedule?.courtId], scheduledTime));
|
|
52
|
+
}
|
|
12
53
|
function proAutoSchedule({ matchUpDailyLimits, minCourtGridRows = 10, tournamentRecords, scheduledDate, courtIds, matchUps, }) {
|
|
13
54
|
if (!validMatchUps(matchUps))
|
|
14
55
|
return { error: INVALID_VALUES };
|
|
@@ -74,6 +115,8 @@ function proAutoSchedule({ matchUpDailyLimits, minCourtGridRows = 10, tournament
|
|
|
74
115
|
processNextMatchUps({ matchUpPotentialParticipantIds, matchUpNotBeforeTimes: {}, matchUp: m });
|
|
75
116
|
}
|
|
76
117
|
}
|
|
118
|
+
const courtClosures = buildCourtClosures({ tournamentRecords, scheduledDate });
|
|
119
|
+
const hasClosures = Object.keys(courtClosures).length > 0;
|
|
77
120
|
const scheduled = [];
|
|
78
121
|
const previousRowMatchUpIds = [];
|
|
79
122
|
while (matchUps.length && gridRows.length) {
|
|
@@ -82,6 +125,7 @@ function proAutoSchedule({ matchUpDailyLimits, minCourtGridRows = 10, tournament
|
|
|
82
125
|
while (matchUps.length && row.availableCourts.length) {
|
|
83
126
|
const unscheduledMatchUpIds = matchUps.concat(unscheduledMatchUps).map((m) => m.matchUpId);
|
|
84
127
|
const matchUp = matchUps.shift();
|
|
128
|
+
const openCourtIndex = findOpenCourtIndex(row.availableCourts, matchUp, hasClosures, courtClosures);
|
|
85
129
|
const verdict = evaluatePlacement({
|
|
86
130
|
matchUp,
|
|
87
131
|
row,
|
|
@@ -94,8 +138,8 @@ function proAutoSchedule({ matchUpDailyLimits, minCourtGridRows = 10, tournament
|
|
|
94
138
|
individualParticipantProfiles,
|
|
95
139
|
matchUpPotentialParticipantIds,
|
|
96
140
|
});
|
|
97
|
-
if (verdict.canPlace && matchUp) {
|
|
98
|
-
const court = row.availableCourts.
|
|
141
|
+
if (verdict.canPlace && matchUp && openCourtIndex !== -1) {
|
|
142
|
+
const court = row.availableCourts.splice(openCourtIndex, 1)[0];
|
|
99
143
|
matchUp.schedule ??= {};
|
|
100
144
|
Object.assign(matchUp.schedule, court.schedule);
|
|
101
145
|
Object.assign(court, matchUp);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { updateAssignmentParticipantResults } from '../../drawDefinitions/matchUpGovernor/updateAssignmentParticipantResults.mjs';
|
|
2
2
|
import { processCompetitionMatchUp } from '../../drawDefinitions/competition/processCompetitionMatchUp.mjs';
|
|
3
3
|
import { updateInContextMatchUp, modifyMatchUpNotice } from '../../notifications/drawNotifications.mjs';
|
|
4
|
-
import { clearResolvedSideExitProvenance } from '../matchUpStatus/sideExitProvenance.mjs';
|
|
4
|
+
import { retainByeClaimsOnly, clearResolvedSideExitProvenance } from '../matchUpStatus/sideExitProvenance.mjs';
|
|
5
5
|
import { getCompetitionPolicy } from '../../../query/drawDefinition/competition/getCompetitionPolicy.mjs';
|
|
6
6
|
import { getAllStructureMatchUps } from '../../../query/matchUps/getAllStructureMatchUps.mjs';
|
|
7
7
|
import { getAppliedPolicies } from '../../../query/extensions/getAppliedPolicies.mjs';
|
|
@@ -24,7 +24,7 @@ import { CONTAINER } from '../../../constants/drawDefinitionConstants.mjs';
|
|
|
24
24
|
import { SUCCESS } from '../../../constants/resultConstants.mjs';
|
|
25
25
|
import { TEAM } from '../../../constants/matchUpTypes.mjs';
|
|
26
26
|
import { completedMatchUpStatuses } from '../../../constants/matchUpStatusConstants.mjs';
|
|
27
|
-
import { DEFAULTED, IN_PROGRESS, WALKOVER, DOUBLE_WALKOVER, AWAITING_RESULT, SUSPENDED, INCOMPLETE } from '../../../constants/matchUpStatusValues.mjs';
|
|
27
|
+
import { DEFAULTED, BYE, IN_PROGRESS, WALKOVER, DOUBLE_WALKOVER, AWAITING_RESULT, SUSPENDED, INCOMPLETE } from '../../../constants/matchUpStatusValues.mjs';
|
|
28
28
|
|
|
29
29
|
function modifyMatchUpScore(params) {
|
|
30
30
|
const stack = 'modifyMatchUpScore';
|
|
@@ -158,7 +158,9 @@ function resolveDualMatchUpTarget({ isDualMatchUp, drawDefinition, matchUpId, ma
|
|
|
158
158
|
function applyScoreAndStatus({ matchUpStatusCodes, removeWinningSide, matchUpStatus, matchUpFormat, removeScore, winningSide, matchUp, score, }) {
|
|
159
159
|
const walkoverStatuses = new Set([WALKOVER, DOUBLE_WALKOVER]);
|
|
160
160
|
if ((matchUpStatus && walkoverStatuses.has(matchUpStatus)) || removeScore) {
|
|
161
|
-
const survivingProvenance = isAnyExit(matchUpStatus)
|
|
161
|
+
const survivingProvenance = isAnyExit(matchUpStatus)
|
|
162
|
+
? matchUp.sideExitProvenance
|
|
163
|
+
: retainByeClaimsOnly(matchUpStatus === BYE ? matchUp.sideExitProvenance : undefined);
|
|
162
164
|
Object.assign(matchUp, { ...toBePlayed });
|
|
163
165
|
if (survivingProvenance)
|
|
164
166
|
matchUp.sideExitProvenance = survivingProvenance;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { resolveTournamentRecords } from '../../helpers/parameters/resolveTournamentRecords.mjs';
|
|
2
2
|
import { addNotice } from '../../global/state/globalState.mjs';
|
|
3
3
|
import { getTimeItem } from '../../query/base/timeItems.mjs';
|
|
4
|
-
import { addTimeItem } from './addTimeItem.mjs';
|
|
5
4
|
import { isISODateString } from '../../tools/plainDate.mjs';
|
|
5
|
+
import { addTimeItem } from './addTimeItem.mjs';
|
|
6
6
|
import { isString } from '../../tools/objects.mjs';
|
|
7
7
|
import { PUBLIC, PUBLISH, STATUS } from '../../constants/timeItemConstants.mjs';
|
|
8
8
|
import { PUBLISH_TOURNAMENT_INFO } from '../../constants/topicConstants.mjs';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isPropagatedExit } from '../../mutate/matchUps/matchUpStatus/sideExitProvenance.mjs';
|
|
2
|
+
import { getStructureInconsistencies } from './getStructureInconsistencies.mjs';
|
|
3
|
+
import { finalize, hasErrorSeverity } from '../integrity/inconsistency.mjs';
|
|
3
4
|
import { isFedLoserEligible } from '../matchUp/isFedLoserEligible.mjs';
|
|
4
5
|
import { getAllDrawMatchUps } from '../matchUps/drawMatchUps.mjs';
|
|
5
6
|
import { WINNER, LOSER, QUALIFYING } from '../../constants/drawDefinitionConstants.mjs';
|
|
@@ -80,7 +81,7 @@ function droppedProgressionForLink(link, sourceStructure, sourceStructureMatchUp
|
|
|
80
81
|
(!link.source?.roundNumber || matchUp.roundNumber === link.source.roundNumber));
|
|
81
82
|
const inconsistencies = [];
|
|
82
83
|
for (const matchUp of roundMatchUps) {
|
|
83
|
-
if (isPropagatedExit(matchUp))
|
|
84
|
+
if (isPropagatedExit({ matchUp }))
|
|
84
85
|
continue;
|
|
85
86
|
const loserSideNumber = matchUp.winningSide === 1 ? 2 : 1;
|
|
86
87
|
const sideNumber = isLoserLink ? loserSideNumber : matchUp.winningSide;
|
|
@@ -152,7 +153,7 @@ function getDrawInconsistencies(params) {
|
|
|
152
153
|
const progressionInconsistencies = getProgressionInconsistencies(drawDefinition, inContextDrawMatchUps, structureById);
|
|
153
154
|
const combined = [...structureInconsistencies, ...linkInconsistencies, ...progressionInconsistencies];
|
|
154
155
|
const finalized = finalize(combined, { scope: 'DRAW', drawId });
|
|
155
|
-
return { ...SUCCESS, valid: finalized
|
|
156
|
+
return { ...SUCCESS, valid: !hasErrorSeverity(finalized), inconsistencies: finalized };
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
export { DANGLING_LINK, DROPPED_PROGRESSION, INELIGIBLE_PROGRESSION, LINK_MISSING_SOURCE_ROUND, SCAN_ERROR, getDrawInconsistencies };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { finalize } from '../integrity/inconsistency.mjs';
|
|
1
|
+
import { isPropagatedExit, getNativeSideExitProvenance } from '../../mutate/matchUps/matchUpStatus/sideExitProvenance.mjs';
|
|
2
|
+
import { finalize, hasErrorSeverity } from '../integrity/inconsistency.mjs';
|
|
3
3
|
import { getAllDrawMatchUps } from '../matchUps/drawMatchUps.mjs';
|
|
4
4
|
import { isExit, isAnyExit } from '../../validators/isExit.mjs';
|
|
5
|
-
import { BYE, DOUBLE_WALKOVER, DOUBLE_DEFAULT } from '../../constants/matchUpStatusValues.mjs';
|
|
5
|
+
import { BYE, DOUBLE_WALKOVER, DOUBLE_DEFAULT, TO_BE_PLAYED } from '../../constants/matchUpStatusValues.mjs';
|
|
6
6
|
import { MISSING_DRAW_DEFINITION } from '../../constants/errorConditionConstants.mjs';
|
|
7
7
|
import { CONTAINER } from '../../constants/drawDefinitionConstants.mjs';
|
|
8
8
|
import { SUCCESS } from '../../constants/resultConstants.mjs';
|
|
@@ -16,13 +16,11 @@ const DRAW_POSITIONS_NOT_SORTED = 'DRAW_POSITIONS_NOT_SORTED';
|
|
|
16
16
|
const EXIT_CODE_ON_WINNER_SIDE = 'EXIT_CODE_ON_WINNER_SIDE';
|
|
17
17
|
const EXIT_WITHOUT_LOSER = 'EXIT_WITHOUT_LOSER';
|
|
18
18
|
const PROPAGATED_EXIT_LOST = 'PROPAGATED_EXIT_LOST';
|
|
19
|
+
const STALLED_POSITION = 'STALLED_POSITION';
|
|
19
20
|
function codeString(code) {
|
|
20
21
|
const value = typeof code === 'string' ? code : code?.code;
|
|
21
22
|
return value || undefined;
|
|
22
23
|
}
|
|
23
|
-
function isPropagatedExit(matchUp) {
|
|
24
|
-
return isPropagatedExit$1({ matchUp });
|
|
25
|
-
}
|
|
26
24
|
function assignmentOccupied(assignment) {
|
|
27
25
|
return !!(assignment && (assignment.participantId || assignment.bye || assignment.qualifier));
|
|
28
26
|
}
|
|
@@ -150,6 +148,40 @@ function getLostPropagatedExitInconsistency(matchUp) {
|
|
|
150
148
|
matchUpStatus,
|
|
151
149
|
};
|
|
152
150
|
}
|
|
151
|
+
function getStalledPositionInconsistencies(scoped, allDrawMatchUps, roundRobinGroupStructureIds) {
|
|
152
|
+
const playableShape = (matchUp) => !matchUp.winningSide && (!matchUp.matchUpStatus || matchUp.matchUpStatus === TO_BE_PLAYED);
|
|
153
|
+
const occupants = (matchUp) => (matchUp.sides ?? []).filter((side) => side?.participantId && !side?.bye);
|
|
154
|
+
const drawMatchUps = allDrawMatchUps.filter((matchUp) => !matchUp.collectionId);
|
|
155
|
+
const anythingPlayable = drawMatchUps.some((matchUp) => playableShape(matchUp) && occupants(matchUp).length === 2);
|
|
156
|
+
if (anythingPlayable)
|
|
157
|
+
return [];
|
|
158
|
+
const hasStarted = drawMatchUps.some((matchUp) => matchUp.winningSide);
|
|
159
|
+
if (!hasStarted)
|
|
160
|
+
return [];
|
|
161
|
+
const inconsistencies = [];
|
|
162
|
+
for (const matchUp of scoped) {
|
|
163
|
+
if (roundRobinGroupStructureIds.has(matchUp.structureId))
|
|
164
|
+
continue;
|
|
165
|
+
if (matchUp.winningSide)
|
|
166
|
+
continue;
|
|
167
|
+
if ((matchUp.sides ?? []).some((side) => side?.bye))
|
|
168
|
+
continue;
|
|
169
|
+
const present = occupants(matchUp);
|
|
170
|
+
if (present.length !== 1)
|
|
171
|
+
continue;
|
|
172
|
+
inconsistencies.push({
|
|
173
|
+
matchUpId: matchUp.matchUpId,
|
|
174
|
+
structureId: matchUp.structureId,
|
|
175
|
+
issueType: STALLED_POSITION,
|
|
176
|
+
severity: 'warning',
|
|
177
|
+
message: `side ${present[0].sideNumber} holds a participant whose opponent can never arrive — ` +
|
|
178
|
+
`no matchUp in the draw is playable`,
|
|
179
|
+
sideNumber: present[0].sideNumber,
|
|
180
|
+
matchUpStatus: matchUp.matchUpStatus,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return inconsistencies;
|
|
184
|
+
}
|
|
153
185
|
function getStructureInconsistencies(params) {
|
|
154
186
|
const { drawDefinition, structureId, matchUpsMap } = params;
|
|
155
187
|
if (!drawDefinition)
|
|
@@ -160,6 +192,7 @@ function getStructureInconsistencies(params) {
|
|
|
160
192
|
const inconsistencies = getPhantomPositionInconsistencies(drawDefinition, structureId);
|
|
161
193
|
const roundRobinGroupStructureIds = new Set();
|
|
162
194
|
collectRoundRobinGroupStructureIds(drawDefinition.structures, roundRobinGroupStructureIds);
|
|
195
|
+
inconsistencies.push(...getStalledPositionInconsistencies(scoped, inContextDrawMatchUps, roundRobinGroupStructureIds));
|
|
163
196
|
for (const matchUp of scoped) {
|
|
164
197
|
const { winningSide, matchUpStatus, matchUpStatusCodes, sides, matchUpId, drawPositions } = matchUp;
|
|
165
198
|
const filledPositions = (drawPositions ?? []).filter((drawPosition) => typeof drawPosition === 'number');
|
|
@@ -205,7 +238,7 @@ function getStructureInconsistencies(params) {
|
|
|
205
238
|
loserSide?.drawPosition &&
|
|
206
239
|
!loserSide.participantId &&
|
|
207
240
|
!loserSide.bye &&
|
|
208
|
-
!isPropagatedExit(matchUp)) {
|
|
241
|
+
!isPropagatedExit({ matchUp })) {
|
|
209
242
|
inconsistencies.push({
|
|
210
243
|
...base,
|
|
211
244
|
issueType: EXIT_WITHOUT_LOSER,
|
|
@@ -217,7 +250,7 @@ function getStructureInconsistencies(params) {
|
|
|
217
250
|
inconsistencies.push(advancement);
|
|
218
251
|
}
|
|
219
252
|
const finalized = finalize(inconsistencies, { scope: 'STRUCTURE' });
|
|
220
|
-
return { ...SUCCESS, valid: finalized
|
|
253
|
+
return { ...SUCCESS, valid: !hasErrorSeverity(finalized), inconsistencies: finalized };
|
|
221
254
|
}
|
|
222
255
|
|
|
223
|
-
export { BYE_ADVANCEMENT_MISSING, DRAW_POSITIONS_NOT_SORTED, DRAW_POSITION_UNASSIGNED, EXIT_CODE_ON_WINNER_SIDE, EXIT_WITHOUT_LOSER, PROPAGATED_EXIT_LOST, WINNER_NOT_ADVANCED, WINNING_SIDE_ADVANCEMENT_MISMATCH, WINNING_SIDE_WITHOUT_PARTICIPANT, getStructureInconsistencies
|
|
256
|
+
export { BYE_ADVANCEMENT_MISSING, DRAW_POSITIONS_NOT_SORTED, DRAW_POSITION_UNASSIGNED, EXIT_CODE_ON_WINNER_SIDE, EXIT_WITHOUT_LOSER, PROPAGATED_EXIT_LOST, STALLED_POSITION, WINNER_NOT_ADVANCED, WINNING_SIDE_ADVANCEMENT_MISMATCH, WINNING_SIDE_WITHOUT_PARTICIPANT, getStructureInconsistencies };
|
|
@@ -41,8 +41,8 @@ function isActiveDownstream(params) {
|
|
|
41
41
|
const provenance = getSideExitProvenance({ matchUp: candidate });
|
|
42
42
|
return !occupiedSides.every((side) => provenance?.[side.sideNumber]);
|
|
43
43
|
};
|
|
44
|
-
const winnerSideResolved = !!winnerMatchUp?.sides?.find((s) => s?.sideNumber === winnerMatchUp.winningSide)
|
|
45
|
-
|
|
44
|
+
const winnerSideResolved = !!winnerMatchUp?.sides?.find((s) => s?.sideNumber === winnerMatchUp.winningSide)?.participant &&
|
|
45
|
+
!isPropagatedExit({ matchUp: winnerMatchUp });
|
|
46
46
|
if (contestedDoubleExit(loserMatchUp) || contestedDoubleExit(winnerMatchUp)) {
|
|
47
47
|
return true;
|
|
48
48
|
}
|