Skip to content

Commit b818eee

Browse files
Add category types
1 parent 0b864fe commit b818eee

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;
@@ -463,8 +465,8 @@ protected function getQueryBuilder(Request $request): QueryBuilder
463465
if (!$this->dj->checkrole('api_reader') &&
464466
!$this->dj->checkrole('judgehost')) {
465467
$queryBuilder
466-
// TODO: category type
467-
->join('t.categories', 'cat');
468+
->join('t.categories', 'cat', Join::WITH, 'BIT_AND(cat.types, :scoring) = :scoring')
469+
->setParameter('scoring', TeamCategory::TYPE_SCORING);
468470
if ($this->dj->checkrole('team')) {
469471
$queryBuilder
470472
->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
@@ -56,11 +56,12 @@ public function indexAction(): Response
5656
->groupBy('c.categoryid')
5757
->getQuery()->getResult();
5858
$table_fields = [
59-
'categoryid' => ['title' => 'ID', 'sort' => true],
59+
'categoryid' => ['title' => 'ID', 'sort' => true, 'default_sort' => true],
6060
'externalid' => ['title' => 'external ID', 'sort' => true],
6161
'icpcid' => ['title' => 'ICPC ID', 'sort' => true],
62-
'sortorder' => ['title' => 'sort', 'sort' => true, 'default_sort' => true],
62+
'sortorder' => ['title' => 'sort', 'sort' => true],
6363
'name' => ['title' => 'name', 'sort' => true],
64+
'types' => ['title' => 'types', 'sort' => false],
6465
'num_teams' => ['title' => '# teams', 'sort' => true],
6566
'visible' => ['title' => 'visible', 'sort' => true],
6667
'allow_self_registration' => ['title' => 'self-registration', 'sort' => true],
@@ -103,6 +104,7 @@ public function indexAction(): Response
103104
];
104105
}
105106

107+
$categorydata['types'] = ['value' => implode(', ', $teamCategory->getTypeHumanNames()) ?: '-'];
106108
$categorydata['num_teams'] = ['value' => $teamCategoryData['num_teams']];
107109
$categorydata['visible'] = ['value' => $teamCategory->getVisible() ? 'yes' : 'no'];
108110
$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
@@ -128,7 +128,7 @@ public function indexAction(): Response
128128
}
129129
}
130130

131-
$teamdata['category'] = ['value' => $t->getSortOrderCategory()];
131+
$teamdata['category'] = ['value' => $t->getScoringCategory()];
132132
$teamdata['num_categories'] = ['value' => $t->getCategories()->count()];
133133

134134
// Add some elements for the solved status.
@@ -221,9 +221,7 @@ public function indexAction(): Response
221221
'data' => $teamdata,
222222
'actions' => $teamactions,
223223
'link' => $this->generateUrl('jury_team', ['teamId' => $t->getTeamId()]),
224-
// TODO: category type
225-
'cssclass' => ($t->getCategories()->first() ? ("category" . $t->getCategories()->first()->getCategoryId()) : '') .
226-
($t->getEnabled() ? '' : ' disabled'),
224+
'cssclass' => ($t->getEnabled() ? '' : ' disabled'),
227225
];
228226
}
229227
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
@@ -174,7 +174,15 @@ public function changeContestAction(Request $request, RouterInterface $router, i
174174
public function teamAction(Request $request, int $teamId): Response
175175
{
176176
/** @var Team|null $team */
177-
$team = $this->em->getRepository(Team::class)->find($teamId);
177+
$team = $this->em->createQueryBuilder()
178+
->from(Team::class, 't')
179+
->innerJoin('t.categories', 'tc')
180+
->select('t, tc')
181+
->andWhere('tc.visible = 1')
182+
->andWhere('t.teamid = :teamId')
183+
->setParameter('teamId', $teamId)
184+
->getQuery()
185+
->getOneOrNullResult();
178186
if ($team?->getHidden()) {
179187
$team = null;
180188
}
@@ -286,7 +294,7 @@ public function submissionsAction(Request $request, string $teamId, string $prob
286294

287295
/** @var Team|null $team */
288296
$team = $this->em->getRepository(Team::class)->findOneBy(['externalid' => $teamId]);
289-
if ($team && $team->getCategory() && !$team->getCategory()->getVisible()) {
297+
if ($team && $team->getScoringCategory() && !$team->getScoringCategory()->getVisible()) {
290298
$team = null;
291299
}
292300

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)