@@ -62,6 +62,61 @@ async function hasPendingScorecardsForPhase(challengePhaseId) {
6262 return Number ( count ) > 0 ;
6363}
6464
65+ async function hasCompletedReviewsForPhase ( challengePhaseIdOrIds ) {
66+ if ( ! config . REVIEW_DB_URL ) {
67+ logger . debug (
68+ `Skipping completed review check for phase ${ challengePhaseIdOrIds } because REVIEW_DB_URL is not configured`
69+ ) ;
70+ return false ;
71+ }
72+
73+ const phaseIds = Array . isArray ( challengePhaseIdOrIds )
74+ ? challengePhaseIdOrIds . filter ( ( id ) => ! _ . isNil ( id ) )
75+ : [ challengePhaseIdOrIds ] ;
76+
77+ if ( phaseIds . length === 0 ) {
78+ return false ;
79+ }
80+
81+ const reviewPrisma = getReviewClient ( ) ;
82+ const reviewSchema = config . REVIEW_DB_SCHEMA ;
83+ const reviewTable = Prisma . raw ( `"${ reviewSchema } "."review"` ) ;
84+ const statusText = Prisma . raw ( `"status"::text` ) ;
85+ const completedStatuses = [ "IN_PROGRESS" , "COMPLETED" ] ;
86+
87+ const phaseIdClause =
88+ phaseIds . length === 1
89+ ? Prisma . sql `"phaseId" = ${ phaseIds [ 0 ] } `
90+ : Prisma . sql `"phaseId" IN (${ Prisma . join (
91+ phaseIds . map ( ( phaseId ) => Prisma . sql `${ phaseId } ` )
92+ ) } )`;
93+
94+ const completedStatusClause = Prisma . sql `${ statusText } IN (${ Prisma . join (
95+ completedStatuses . map ( ( status ) => Prisma . sql `${ status } ` )
96+ ) } )`;
97+
98+ let rows ;
99+ try {
100+ rows = await reviewPrisma . $queryRaw (
101+ Prisma . sql `
102+ SELECT COUNT(*)::int AS count
103+ FROM ${ reviewTable }
104+ WHERE ${ phaseIdClause }
105+ AND ${ completedStatusClause }
106+ `
107+ ) ;
108+ } catch ( err ) {
109+ logger . error (
110+ `Failed to check completed reviews for phase(s) ${ phaseIds . join ( ", " ) } : ${ err . message } ` ,
111+ err
112+ ) ;
113+ throw err ;
114+ }
115+
116+ const [ { count = 0 } = { } ] = rows || [ ] ;
117+ return Number ( count ) > 0 ;
118+ }
119+
65120async function checkChallengeExists ( challengeId ) {
66121 const challenge = await prisma . challenge . findUnique ( { where : { id : challengeId } } ) ;
67122 if ( ! challenge ) {
@@ -230,8 +285,47 @@ async function partiallyUpdateChallengePhase(currentUser, challengeId, id, data)
230285 `Cannot close ${ phaseName } because there are still pending scorecards`
231286 ) ;
232287 }
288+ if ( ! ( "actualEndDate" in data ) || _ . isNil ( data . actualEndDate ) ) {
289+ data . actualEndDate = new Date ( ) ;
290+ }
233291 }
234292 if ( isReopeningPhase ) {
293+ const phaseName = challengePhase . name ;
294+ if ( phaseName === "Submission" || phaseName === "Registration" ) {
295+ const hasCompletedReviews = await hasCompletedReviewsForPhase ( challengePhase . id ) ;
296+ if ( hasCompletedReviews ) {
297+ throw new errors . ForbiddenError (
298+ "Cannot reopen Submission/Registration phase because reviews are already in progress or completed"
299+ ) ;
300+ }
301+ }
302+
303+ if ( phaseName === "Checkpoint Submission" ) {
304+ const checkpointPhases = await prisma . challengePhase . findMany ( {
305+ where : {
306+ challengeId : challengePhase . challengeId ,
307+ name : { in : [ "Checkpoint Screening" , "Checkpoint Review" ] } ,
308+ } ,
309+ select : { id : true } ,
310+ } ) ;
311+ const checkpointPhaseIds = checkpointPhases . map ( ( cp ) => cp . id ) ;
312+ if ( checkpointPhaseIds . length > 0 ) {
313+ const hasCheckpointReviews = await hasCompletedReviewsForPhase ( checkpointPhaseIds ) ;
314+ if ( hasCheckpointReviews ) {
315+ throw new errors . ForbiddenError (
316+ "Cannot reopen Checkpoint Submission phase because Checkpoint Screening or Checkpoint Review reviews are already in progress or completed"
317+ ) ;
318+ }
319+ }
320+ }
321+
322+ const hasActualStartDate = ! _ . isNil ( challengePhase . actualStartDate ) ;
323+ const hasActualEndDate = ! _ . isNil ( challengePhase . actualEndDate ) ;
324+ if ( hasActualStartDate ) {
325+ data . actualStartDate = challengePhase . actualStartDate ;
326+ } else if ( ! ( "actualStartDate" in data ) || _ . isNil ( data . actualStartDate ) ) {
327+ data . actualStartDate = new Date ( ) ;
328+ }
235329 data . actualEndDate = null ;
236330 }
237331
0 commit comments