Skip to content

Commit 878201d

Browse files
Add category types
1 parent d63fc20 commit 878201d

30 files changed

+681
-136
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DoctrineMigrations;
6+
7+
use Doctrine\DBAL\Schema\Schema;
8+
use Doctrine\Migrations\AbstractMigration;
9+
10+
/**
11+
* Auto-generated Migration: Please modify to your needs!
12+
*/
13+
final class Version20250801124024 extends AbstractMigration
14+
{
15+
public function getDescription(): string
16+
{
17+
return 'Add category types (bitset), css_class field, and make sortorder nullable for TeamCategory';
18+
}
19+
20+
public function up(Schema $schema): void
21+
{
22+
// this up() migration is auto-generated, please modify it to your needs
23+
$this->addSql(<<<SQL
24+
ALTER TABLE team_category
25+
ADD types INT NOT NULL DEFAULT 1 COMMENT 'Bitmask of category types, default is scoring.' AFTER name,
26+
CHANGE sortorder sortorder TINYINT UNSIGNED DEFAULT NULL COMMENT 'Where to sort this category on the scoreboard',
27+
ADD css_class VARCHAR(255) DEFAULT NULL COMMENT 'CSS class to apply to scoreboard rows (only for TYPE_CSS_CLASS)' AFTER allow_self_registration
28+
SQL);
29+
}
30+
31+
public function down(Schema $schema): void
32+
{
33+
// this down() migration is auto-generated, please modify it to your needs
34+
$this->addSql(<<<SQL
35+
ALTER TABLE team_category
36+
DROP types,
37+
DROP css_class,
38+
CHANGE sortorder sortorder TINYINT UNSIGNED DEFAULT 0 NOT NULL COMMENT 'Where to sort this category on the scoreboard'
39+
SQL);
40+
}
41+
42+
public function isTransactional(): bool
43+
{
44+
return false;
45+
}
46+
}

webapp/public/style_domjudge.css

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -665,8 +665,12 @@ blockquote {
665665
color: darkgrey;
666666
}
667667

668-
.category-best {
669-
margin-right: 2em;
668+
.category-badges {
669+
margin-right: 1.5em;
670+
}
671+
672+
.category-best, .category-badge {
673+
margin-right: 0.5em;
670674
font-weight: normal;
671675
}
672676

webapp/src/Controller/API/MetricsController.php

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
use App\Entity\QueueTask;
88
use App\Entity\Submission;
99
use App\Entity\Team;
10+
use App\Entity\TeamCategory;
1011
use App\Entity\User;
1112
use App\Service\DOMJudgeService;
1213
use App\Service\SubmissionService;
1314
use App\Utils\Utils;
1415
use Doctrine\ORM\EntityManagerInterface;
16+
use Doctrine\ORM\Query\Expr\Join;
1517
use FOS\RestBundle\Controller\AbstractFOSRestController;
1618
use FOS\RestBundle\Controller\Annotations as Rest;
1719
use OpenApi\Attributes as OA;
@@ -79,9 +81,9 @@ public function prometheusAction(): Response
7981
->select('t', 'u')
8082
->from(Team::class, 't')
8183
->leftJoin('t.users', 'u')
82-
// TODO: category type
83-
->join('t.categories', 'cat')
84+
->join('t.categories', 'cat', Join::WITH, 'BIT_AND(cat.types, :scoring) = :scoring')
8485
->andWhere('cat.visible = true')
86+
->setParameter('scoring', TeamCategory::TYPE_SCORING)
8587
->getQuery()
8688
->getResult();
8789

@@ -90,9 +92,9 @@ public function prometheusAction(): Response
9092
->select('u')
9193
->from(User::class, 'u')
9294
->leftJoin('u.team', 't')
93-
// TODO: category type
94-
->join('t.categories', 'cat')
95+
->join('t.categories', 'cat', Join::WITH, 'BIT_AND(cat.types, :scoring) = :scoring')
9596
->andWhere('cat.visible = true')
97+
->setParameter('scoring', TeamCategory::TYPE_SCORING)
9698
->getQuery()
9799
->getResult();
98100

@@ -136,11 +138,11 @@ public function prometheusAction(): Response
136138
->from(Team::class, 't')
137139
->leftJoin('t.users', 'u')
138140
->leftJoin('t.contests', 'c')
139-
// TODO: category type
140-
->join('t.categories', 'cat')
141+
->join('t.categories', 'cat', Join::WITH, 'BIT_AND(cat.types, :scoring) = :scoring')
141142
->leftJoin('cat.contests', 'cc')
142143
->andWhere('c.cid = :cid OR cc.cid = :cid')
143144
->andWhere('cat.visible = true')
145+
->setParameter('scoring', TeamCategory::TYPE_SCORING)
144146
->setParameter('cid', $contest->getCid())
145147
->getQuery()
146148
->getResult();
@@ -157,11 +159,11 @@ public function prometheusAction(): Response
157159
->from(User::class, 'u')
158160
->leftJoin('u.team', 't')
159161
->leftJoin('t.contests', 'c')
160-
// TODO: category type
161-
->join('t.categories', 'cat')
162+
->join('t.categories', 'cat', Join::WITH, 'BIT_AND(cat.types, :scoring) = :scoring')
162163
->leftJoin('cat.contests', 'cc')
163164
->andWhere('c.cid = :cid OR cc.cid = :cid')
164165
->andWhere('cat.visible = true')
166+
->setParameter('scoring', TeamCategory::TYPE_SCORING)
165167
->setParameter('cid', $contest->getCid())
166168
->getQuery()
167169
->getResult();
@@ -231,11 +233,11 @@ public function prometheusAction(): Response
231233
->join('b.submission', 's')
232234
->join('s.contest', 'c')
233235
->join('s.team', 't')
234-
// TODO: category type
235-
->join('t.categories', 'cat')
236+
->join('t.categories', 'cat', Join::WITH, 'BIT_AND(cat.types, :scoring) = :scoring')
236237
->andWhere('b.done = false')
237238
->andWhere('c.cid = :cid')
238239
->andWhere('cat.visible = true')
240+
->setParameter('scoring', TeamCategory::TYPE_SCORING)
239241
->setParameter('cid', $contest->getCid())
240242
->getQuery()
241243
->getResult();

webapp/src/Controller/API/SubmissionController.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use App\Entity\SubmissionFile;
1212
use App\Entity\SubmissionSource;
1313
use App\Entity\Team;
14+
use App\Entity\TeamCategory;
1415
use App\Entity\User;
1516
use App\Service\ConfigurationService;
1617
use App\Service\DOMJudgeService;
@@ -19,6 +20,7 @@
1920
use App\Utils\Utils;
2021
use Doctrine\ORM\EntityManagerInterface;
2122
use Doctrine\ORM\NonUniqueResultException;
23+
use Doctrine\ORM\Query\Expr\Join;
2224
use Doctrine\ORM\QueryBuilder;
2325
use Exception;
2426
use FOS\RestBundle\Controller\Annotations as Rest;
@@ -462,8 +464,8 @@ protected function getQueryBuilder(Request $request): QueryBuilder
462464
if (!$this->dj->checkrole('api_reader') &&
463465
!$this->dj->checkrole('judgehost')) {
464466
$queryBuilder
465-
// TODO: category type
466-
->join('t.categories', 'cat');
467+
->join('t.categories', 'cat', Join::WITH, 'BIT_AND(cat.types, :scoring) = :scoring')
468+
->setParameter('scoring', TeamCategory::TYPE_SCORING);
467469
if ($this->dj->checkrole('team')) {
468470
$queryBuilder
469471
->andWhere('cat.visible = 1 OR s.team = :team')

webapp/src/Controller/API/TeamController.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
use App\DataTransferObject\AddTeam;
66
use App\Entity\Contest;
77
use App\Entity\Team;
8+
use App\Entity\TeamCategory;
89
use App\Service\AssetUpdateService;
910
use App\Service\ConfigurationService;
1011
use App\Service\DOMJudgeService;
1112
use App\Service\EventLogService;
1213
use App\Service\ImportExportService;
1314
use Doctrine\ORM\EntityManagerInterface;
1415
use Doctrine\ORM\NonUniqueResultException;
16+
use Doctrine\ORM\Query\Expr\Join;
1517
use Doctrine\ORM\QueryBuilder;
1618
use FOS\RestBundle\Controller\Annotations as Rest;
1719
use Nelmio\ApiDocBundle\Attribute\Model;
@@ -305,11 +307,12 @@ protected function getQueryBuilder(Request $request): QueryBuilder
305307
$queryBuilder = $this->em->createQueryBuilder()
306308
->from(Team::class, 't')
307309
->leftJoin('t.affiliation', 'ta')
308-
// TODO: category type
309310
->leftJoin('t.categories', 'tc')
311+
->leftJoin('t.categories', 'tcc', Join::WITH, 'BIT_AND(tcc.types, :scoring) = :scoring')
310312
->leftJoin('t.contests', 'c')
311313
->leftJoin('tc.contests', 'cc')
312-
->select('t, ta');
314+
->setParameter('scoring', TeamCategory::TYPE_SCORING)
315+
->select('t, ta, tc');
313316

314317
if ($request->query->has('category')) {
315318
$queryBuilder
@@ -325,6 +328,7 @@ protected function getQueryBuilder(Request $request): QueryBuilder
325328

326329
if (!$this->dj->checkrole('api_reader') || $request->query->getBoolean('public')) {
327330
$queryBuilder->andWhere('tc.visible = 1');
331+
$queryBuilder->andWhere('tcc.visible = 1');
328332
}
329333

330334
if ($request->attributes->has('cid')) {

webapp/src/Controller/Jury/TeamCategoryController.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ public function indexAction(): Response
5555
->groupBy('c.categoryid')
5656
->getQuery()->getResult();
5757
$table_fields = [
58-
'categoryid' => ['title' => 'ID', 'sort' => true],
58+
'categoryid' => ['title' => 'ID', 'sort' => true, 'default_sort' => true],
5959
'externalid' => ['title' => 'external ID', 'sort' => true],
6060
'icpcid' => ['title' => 'ICPC ID', 'sort' => true],
61-
'sortorder' => ['title' => 'sort', 'sort' => true, 'default_sort' => true],
61+
'sortorder' => ['title' => 'sort', 'sort' => true],
6262
'name' => ['title' => 'name', 'sort' => true],
63+
'types' => ['title' => 'types', 'sort' => false],
6364
'num_teams' => ['title' => '# teams', 'sort' => true],
6465
'visible' => ['title' => 'visible', 'sort' => true],
6566
'allow_self_registration' => ['title' => 'self-registration', 'sort' => true],
@@ -97,6 +98,7 @@ public function indexAction(): Response
9798
];
9899
}
99100

101+
$categorydata['types'] = ['value' => implode(', ', $teamCategory->getTypeHumanNames()) ?: '-'];
100102
$categorydata['num_teams'] = ['value' => $teamCategoryData['num_teams']];
101103
$categorydata['visible'] = ['value' => $teamCategory->getVisible() ? 'yes' : 'no'];
102104
$categorydata['allow_self_registration'] = ['value' => $teamCategory->getAllowSelfRegistration() ? 'yes' : 'no'];

webapp/src/Controller/Jury/TeamController.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public function indexAction(): Response
123123
}
124124
}
125125

126-
$teamdata['category'] = ['value' => $t->getSortOrderCategory()];
126+
$teamdata['category'] = ['value' => $t->getScoringCategory()];
127127
$teamdata['num_categories'] = ['value' => $t->getCategories()->count()];
128128

129129
// Add some elements for the solved status.
@@ -216,9 +216,7 @@ public function indexAction(): Response
216216
'data' => $teamdata,
217217
'actions' => $teamactions,
218218
'link' => $this->generateUrl('jury_team', ['teamId' => $t->getTeamId()]),
219-
// TODO: category type
220-
'cssclass' => ($t->getCategories()->first() ? ("category" . $t->getCategories()->first()->getCategoryId()) : '') .
221-
($t->getEnabled() ? '' : ' disabled'),
219+
'cssclass' => ($t->getEnabled() ? '' : ' disabled'),
222220
];
223221
}
224222
return $this->render('jury/teams.html.twig', [

webapp/src/Controller/PublicController.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,15 @@ public function changeContestAction(Request $request, RouterInterface $router, i
186186
public function teamAction(Request $request, int $teamId): Response
187187
{
188188
/** @var Team|null $team */
189-
$team = $this->em->getRepository(Team::class)->find($teamId);
189+
$team = $this->em->createQueryBuilder()
190+
->from(Team::class, 't')
191+
->innerJoin('t.categories', 'tc')
192+
->select('t, tc')
193+
->andWhere('tc.visible = 1')
194+
->andWhere('t.teamid = :teamId')
195+
->setParameter('teamId', $teamId)
196+
->getQuery()
197+
->getOneOrNullResult();
190198
if ($team?->getHidden()) {
191199
$team = null;
192200
}
@@ -298,7 +306,7 @@ public function submissionsAction(Request $request, string $teamId, string $prob
298306

299307
/** @var Team|null $team */
300308
$team = $this->em->getRepository(Team::class)->findOneBy(['externalid' => $teamId]);
301-
if ($team && $team->getCategory() && !$team->getCategory()->getVisible()) {
309+
if ($team && $team->getScoringCategory() && !$team->getScoringCategory()->getVisible()) {
302310
$team = null;
303311
}
304312

webapp/src/Controller/Team/ScoreboardController.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,15 @@ public function teamAction(Request $request, int $teamId): Response
6767
}
6868

6969
/** @var Team|null $team */
70-
$team = $this->em->getRepository(Team::class)->find($teamId);
70+
$team = $this->em->createQueryBuilder()
71+
->from(Team::class, 't')
72+
->innerJoin('t.categories', 'tc')
73+
->select('t, tc')
74+
->andWhere('tc.visible = 1')
75+
->andWhere('t.teamid = :teamId')
76+
->setParameter('teamId', $teamId)
77+
->getQuery()
78+
->getOneOrNullResult();
7179
if ($team?->getHidden() && $teamId !== $this->dj->getUser()->getTeamId()) {
7280
$team = null;
7381
}

webapp/src/DataFixtures/DefaultData/TeamCategoryFixture.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public function load(ObjectManager $manager): void
2626
if (!($category = $manager->getRepository(TeamCategory::class)->findOneBy(['externalid' => $item[4]]))) {
2727
$category = (new TeamCategory())
2828
->setName($item[0])
29+
->setTypes([TeamCategory::TYPE_SCORING, TeamCategory::TYPE_BADGE_TOP, TeamCategory::TYPE_BACKGROUND])
2930
->setSortorder($item[1])
3031
->setColor($item[2])
3132
->setVisible($item[3])

0 commit comments

Comments
 (0)