5
5
use App \DataTransferObject \SubmissionRestriction ;
6
6
use App \Entity \Contest ;
7
7
use App \Entity \ContestProblem ;
8
+ use App \Entity \Submission ;
8
9
use App \Entity \Team ;
9
10
use App \Entity \TeamCategory ;
10
11
use App \Service \ConfigurationService ;
13
14
use App \Service \ScoreboardService ;
14
15
use App \Service \StatisticsService ;
15
16
use App \Service \SubmissionService ;
17
+ use App \Twig \TwigExtension ;
16
18
use Doctrine \ORM \EntityManagerInterface ;
17
19
use Doctrine \ORM \NonUniqueResultException ;
20
+ use Symfony \Component \HttpFoundation \JsonResponse ;
18
21
use Symfony \Component \HttpFoundation \RedirectResponse ;
19
22
use Symfony \Component \HttpFoundation \Request ;
20
23
use Symfony \Component \HttpFoundation \RequestStack ;
@@ -36,6 +39,7 @@ public function __construct(
36
39
protected readonly ScoreboardService $ scoreboardService ,
37
40
protected readonly StatisticsService $ stats ,
38
41
protected readonly SubmissionService $ submissionService ,
42
+ protected readonly TwigExtension $ twigExtension ,
39
43
EntityManagerInterface $ em ,
40
44
EventLogService $ eventLog ,
41
45
KernelInterface $ kernel ,
@@ -283,8 +287,8 @@ protected function getBinaryFile(int $probId, callable $response): StreamedRespo
283
287
return $ response ($ probId , $ contest , $ contestProblem );
284
288
}
285
289
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
288
292
{
289
293
$ contest = $ this ->dj ->getCurrentContest (onlyPublic: true );
290
294
@@ -293,7 +297,7 @@ public function submissionsAction(Request $request, int $teamId, int $problemId)
293
297
}
294
298
295
299
/** @var Team|null $team */
296
- $ team = $ this ->em ->getRepository (Team::class)->find ( $ teamId );
300
+ $ team = $ this ->em ->getRepository (Team::class)->findOneBy ([ ' externalid ' => $ teamId] );
297
301
if ($ team && $ team ->getCategory () && !$ team ->getCategory ()->getVisible ()) {
298
302
$ team = null ;
299
303
}
@@ -303,32 +307,108 @@ public function submissionsAction(Request $request, int $teamId, int $problemId)
303
307
}
304
308
305
309
/** @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 ();
310
320
311
321
if (!$ problem ) {
312
322
throw $ this ->createNotFoundException ('Problem not found ' );
313
323
}
314
324
315
- $ submissions = $ this ->submissionService ->getSubmissionList (
316
- [$ contest ->getCid () => $ contest ],
317
- new SubmissionRestriction (teamId: $ teamId , problemId: $ problemId , valid: true ),
318
- paginated: false
319
- )[0 ];
320
-
321
325
$ data = [
322
326
'contest ' => $ contest ,
323
327
'problem ' => $ problem ,
324
328
'team ' => $ team ,
325
- 'submissions ' => $ submissions ,
326
- 'verificationRequired ' => $ this ->config ->get ('verification_required ' ),
327
329
];
328
330
329
- if ($ request ->isXmlHttpRequest ()) {
330
- return $ this ->render ('public/team_submissions_modal.html.twig ' , $ data );
331
- }
332
331
return $ this ->render ('public/team_submissions.html.twig ' , $ data );
333
332
}
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
+ }
334
414
}
0 commit comments