55use App \DataTransferObject \SubmissionRestriction ;
66use App \Entity \Contest ;
77use App \Entity \ContestProblem ;
8+ use App \Entity \Submission ;
89use App \Entity \Team ;
910use App \Entity \TeamCategory ;
1011use App \Service \ConfigurationService ;
1314use App \Service \ScoreboardService ;
1415use App \Service \StatisticsService ;
1516use App \Service \SubmissionService ;
17+ use App \Twig \TwigExtension ;
1618use Doctrine \ORM \EntityManagerInterface ;
1719use Doctrine \ORM \NonUniqueResultException ;
20+ use Symfony \Component \HttpFoundation \JsonResponse ;
1821use Symfony \Component \HttpFoundation \RedirectResponse ;
1922use Symfony \Component \HttpFoundation \Request ;
2023use Symfony \Component \HttpFoundation \RequestStack ;
@@ -36,6 +39,7 @@ public function __construct(
3639 protected readonly ScoreboardService $ scoreboardService ,
3740 protected readonly StatisticsService $ stats ,
3841 protected readonly SubmissionService $ submissionService ,
42+ protected readonly TwigExtension $ twigExtension ,
3943 EntityManagerInterface $ em ,
4044 EventLogService $ eventLog ,
4145 KernelInterface $ kernel ,
@@ -283,8 +287,8 @@ protected function getBinaryFile(int $probId, callable $response): StreamedRespo
283287 return $ response ($ probId , $ contest , $ contestProblem );
284288 }
285289
286- #[Route(path: '/submissions/team/{teamId<\d+> }/problem/{problemId<\d+> } ' , name: 'public_submissions ' )]
287- public function submissionsAction (Request $ request , int $ teamId , int $ problemId ): Response
290+ #[Route(path: '/submissions/team/{teamId}/problem/{problemId} ' , name: 'public_submissions ' )]
291+ public function submissionsAction (Request $ request , string $ teamId , string $ problemId ): Response
288292 {
289293 $ contest = $ this ->dj ->getCurrentContest (onlyPublic: true );
290294
@@ -293,7 +297,7 @@ public function submissionsAction(Request $request, int $teamId, int $problemId)
293297 }
294298
295299 /** @var Team|null $team */
296- $ team = $ this ->em ->getRepository (Team::class)->find ( $ teamId );
300+ $ team = $ this ->em ->getRepository (Team::class)->findOneBy ([ ' externalid ' => $ teamId] );
297301 if ($ team && $ team ->getCategory () && !$ team ->getCategory ()->getVisible ()) {
298302 $ team = null ;
299303 }
@@ -303,32 +307,108 @@ public function submissionsAction(Request $request, int $teamId, int $problemId)
303307 }
304308
305309 /** @var ContestProblem|null $problem */
306- $ problem = $ this ->em ->getRepository (ContestProblem::class)->find ([
307- 'problem ' => $ problemId ,
308- 'contest ' => $ contest ,
309- ]);
310+ $ problem = $ this ->em ->createQueryBuilder ()
311+ ->from (ContestProblem::class, 'cp ' )
312+ ->select ('cp ' )
313+ ->innerJoin ('cp.problem ' , 'p ' )
314+ ->andWhere ('cp.contest = :contest ' )
315+ ->andWhere ('p.externalid = :problem ' )
316+ ->setParameter ('contest ' , $ contest )
317+ ->setParameter ('problem ' , $ problemId )
318+ ->getQuery ()
319+ ->getOneOrNullResult ();
310320
311321 if (!$ problem ) {
312322 throw $ this ->createNotFoundException ('Problem not found ' );
313323 }
314324
315- $ submissions = $ this ->submissionService ->getSubmissionList (
316- [$ contest ->getCid () => $ contest ],
317- new SubmissionRestriction (teamId: $ teamId , problemId: $ problemId , valid: true ),
318- paginated: false
319- )[0 ];
320-
321325 $ data = [
322326 'contest ' => $ contest ,
323327 'problem ' => $ problem ,
324328 'team ' => $ team ,
325- 'submissions ' => $ submissions ,
326- 'verificationRequired ' => $ this ->config ->get ('verification_required ' ),
327329 ];
328330
329- if ($ request ->isXmlHttpRequest ()) {
330- return $ this ->render ('public/team_submissions_modal.html.twig ' , $ data );
331- }
332331 return $ this ->render ('public/team_submissions.html.twig ' , $ data );
333332 }
333+
334+ #[Route(path: '/submissions-data.json ' , name: 'public_submissions_data ' )]
335+ #[Route(path: '/submissions-data/team/{teamId}/problem/{problemId}.json ' , name: 'public_submissions_data_cell ' )]
336+ public function submissionsDataAction (Request $ request , ?string $ teamId , ?string $ problemId ): JsonResponse
337+ {
338+ $ contest = $ this ->dj ->getCurrentContest (onlyPublic: true );
339+
340+ if (!$ contest ) {
341+ throw $ this ->createNotFoundException ('No active contest found ' );
342+ }
343+
344+ $ scoreboard = $ this ->scoreboardService ->getScoreboard ($ contest );
345+
346+ /** @var Submission[] $submissions */
347+ $ submissions = $ this ->submissionService ->getSubmissionList (
348+ [$ contest ->getCid () => $ contest ],
349+ restrictions: new SubmissionRestriction (valid: true ),
350+ paginated: false
351+ )[0 ];
352+
353+ $ submissionData = [];
354+
355+ // We prepend IDs with team- and problem- to make sure they are not
356+ // consecutive integers
357+ foreach ($ scoreboard ->getTeamsInDescendingOrder () as $ team ) {
358+ if ($ teamId && $ teamId !== $ team ->getExternalid ()) {
359+ continue ;
360+ }
361+ $ teamKey = 'team- ' . $ team ->getExternalid ();
362+ $ submissionData [$ teamKey ] = [];
363+ foreach ($ scoreboard ->getProblems () as $ problem ) {
364+ if ($ problemId && $ problemId !== $ problem ->getExternalId ()) {
365+ continue ;
366+ }
367+ $ problemKey = 'problem- ' . $ problem ->getExternalId ();
368+ $ submissionData [$ teamKey ][$ problemKey ] = [];
369+ }
370+ }
371+
372+ $ verificationRequired = $ this ->config ->get ('verification_required ' );
373+
374+ foreach ($ submissions as $ submission ) {
375+ $ teamKey = 'team- ' . $ submission ->getTeam ()->getExternalid ();
376+ $ problemKey = 'problem- ' . $ submission ->getProblem ()->getExternalid ();
377+ if ($ teamId && $ teamId !== $ submission ->getTeam ()->getExternalid ()) {
378+ continue ;
379+ }
380+ if ($ problemId && $ problemId !== $ submission ->getProblem ()->getExternalid ()) {
381+ continue ;
382+ }
383+ $ submissionData [$ teamKey ][$ problemKey ][] = [
384+ 'time ' => $ this ->twigExtension ->printtime ($ submission ->getSubmittime (), contest: $ contest ),
385+ 'language ' => $ submission ->getLanguageId (),
386+ 'verdict ' => $ this ->submissionVerdict ($ submission , $ contest , $ verificationRequired ),
387+ ];
388+ }
389+
390+ return new JsonResponse ([
391+ 'submissions ' => $ submissionData ,
392+ ]);
393+ }
394+
395+ protected function submissionVerdict (
396+ Submission $ submission ,
397+ Contest $ contest ,
398+ bool $ verificationRequired
399+ ): string {
400+ if ($ submission ->getSubmittime () >= $ contest ->getEndtime ()) {
401+ return $ this ->twigExtension ->printResult ('too-late ' );
402+ }
403+ if ($ contest ->getFreezetime () && $ submission ->getSubmittime () >= $ contest ->getFreezetime () && !$ contest ->getFreezeData ()->showFinal ()) {
404+ return $ this ->twigExtension ->printResult ('' );
405+ }
406+ if (!$ submission ->getJudgings ()->first () || !$ submission ->getJudgings ()->first ()->getResult ()) {
407+ return $ this ->twigExtension ->printResult ('' );
408+ }
409+ if ($ verificationRequired && !$ submission ->getJudgings ()->first ()->getVerified ()) {
410+ return $ this ->twigExtension ->printResult ('' );
411+ }
412+ return $ this ->twigExtension ->printResult ($ submission ->getJudgings ()->first ()->getResult (), onlyRejectedForIncorrect: true );
413+ }
334414}
0 commit comments