mirror of
https://github.com/MarijnDoeve/TijdVoorDeTest.git
synced 2026-03-05 20:44:19 +01:00
Refactor code for improved readability and consistency; add flash message handling and enhance quiz functionality
This commit is contained in:
@@ -1,17 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
use PhpCsFixer\Config;
|
||||
use PhpCsFixer\Finder;
|
||||
|
||||
$finder = (new PhpCsFixer\Finder())
|
||||
$finder = (new Finder())
|
||||
->in(__DIR__)
|
||||
->exclude('var')
|
||||
;
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
return (new Config())
|
||||
->setRules([
|
||||
'@Symfony' => true,
|
||||
'@Symfony:risky' => true,
|
||||
'declare_strict_types' => true,
|
||||
'fully_qualified_strict_types' => ['import_symbols' => true],
|
||||
'linebreak_after_opening_tag' => true,
|
||||
'mb_str_functions' => true,
|
||||
'no_php4_constructor' => true,
|
||||
@@ -19,11 +22,11 @@ return (new PhpCsFixer\Config())
|
||||
'no_useless_else' => true,
|
||||
'no_useless_return' => true,
|
||||
'php_unit_strict' => true,
|
||||
'phpdoc_line_span' => ['const' => 'single', 'method' => 'single', 'property' => 'single'],
|
||||
'phpdoc_order' => true,
|
||||
'single_line_empty_body' => true,
|
||||
'strict_comparison' => true,
|
||||
'strict_param' => true,
|
||||
'blank_line_between_import_groups' => false,
|
||||
'phpdoc_line_span' => ['const' => 'single', 'method' => 'single', 'property' => 'single'],
|
||||
])
|
||||
->setRiskyAllowed(true)
|
||||
->setFinder($finder)
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle;
|
||||
use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle;
|
||||
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
||||
use Symfony\Bundle\MakerBundle\MakerBundle;
|
||||
use Symfony\Bundle\SecurityBundle\SecurityBundle;
|
||||
use Symfony\Bundle\TwigBundle\TwigBundle;
|
||||
use Symfony\Bundle\WebProfilerBundle\WebProfilerBundle;
|
||||
use Symfony\UX\TwigComponent\TwigComponentBundle;
|
||||
use Twig\Extra\TwigExtraBundle\TwigExtraBundle;
|
||||
|
||||
return [
|
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
||||
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
|
||||
EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
||||
FrameworkBundle::class => ['all' => true],
|
||||
DoctrineBundle::class => ['all' => true],
|
||||
DoctrineMigrationsBundle::class => ['all' => true],
|
||||
MakerBundle::class => ['dev' => true],
|
||||
TwigBundle::class => ['all' => true],
|
||||
SecurityBundle::class => ['all' => true],
|
||||
WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||
TwigExtraBundle::class => ['all' => true],
|
||||
TwigComponentBundle::class => ['all' => true],
|
||||
EasyAdminBundle::class => ['all' => true],
|
||||
DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
||||
];
|
||||
|
||||
@@ -7,8 +7,8 @@ use App\Kernel;
|
||||
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
|
||||
|
||||
return static function (array $context): Kernel {
|
||||
$appEnv = !empty($context['APP_ENV']) ? (string) $context['APP_ENV'] : 'prod';
|
||||
$appDebug = !empty($context['APP_DEBUG']) ? filter_var($context['APP_DEBUG'], \FILTER_VALIDATE_BOOL) : 'prod' !== $appEnv;
|
||||
$appEnv = empty($context['APP_ENV']) ? 'prod' : (string) $context['APP_ENV'];
|
||||
$appDebug = empty($context['APP_DEBUG']) ? 'prod' !== $appEnv : filter_var($context['APP_DEBUG'], \FILTER_VALIDATE_BOOL);
|
||||
|
||||
return new Kernel($appEnv, $appDebug);
|
||||
};
|
||||
|
||||
34
src/Command/TestCommand.php
Normal file
34
src/Command/TestCommand.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Repository\CandidateRepository;
|
||||
use App\Repository\QuizRepository;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:test-command',
|
||||
description: 'Add a short description for your command',
|
||||
)]
|
||||
class TestCommand extends Command
|
||||
{
|
||||
public function __construct(private readonly CandidateRepository $candidateRepository, private readonly QuizRepository $quizRepository)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
new SymfonyStyle($input, $output);
|
||||
|
||||
dd($this->candidateRepository->getScores($this->quizRepository->find('1effa06a-8aca-6c52-b52b-3974eda7eed7')));
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,13 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as AbstractBase
|
||||
|
||||
abstract class AbstractController extends AbstractBaseController
|
||||
{
|
||||
#[\Override]
|
||||
protected function addFlash(FlashType|string $type, mixed $message): void
|
||||
{
|
||||
if ($type instanceof FlashType) {
|
||||
$type = $type->value;
|
||||
}
|
||||
|
||||
parent::addFlash($type, $message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
class DashboardController extends AbstractDashboardController
|
||||
{
|
||||
#[Route('/admin', name: 'admin')]
|
||||
#[\Override]
|
||||
public function index(): Response
|
||||
{
|
||||
// return parent::index();
|
||||
@@ -44,12 +45,14 @@ class DashboardController extends AbstractDashboardController
|
||||
// return $this->render('some/path/my-dashboard.html.twig');
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function configureDashboard(): Dashboard
|
||||
{
|
||||
return Dashboard::new()
|
||||
->setTitle('TijdVoorDeTest');
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function configureMenuItems(): iterable
|
||||
{
|
||||
yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home');
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace App\Controller;
|
||||
|
||||
use App\Entity\Quiz;
|
||||
use App\Entity\Season;
|
||||
use App\Repository\CandidateRepository;
|
||||
use App\Repository\SeasonRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -13,14 +14,14 @@ use Symfony\Component\HttpKernel\Attribute\AsController;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
#[AsController]
|
||||
#[Route('/backoffice', name: 'backoffice_')]
|
||||
final class BackofficeController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly SeasonRepository $seasonRepository)
|
||||
{
|
||||
}
|
||||
public function __construct(
|
||||
private readonly SeasonRepository $seasonRepository,
|
||||
private readonly CandidateRepository $candidateRepository,
|
||||
) {}
|
||||
|
||||
#[Route('/', name: 'index')]
|
||||
#[Route('/backoffice/', name: 'index')]
|
||||
public function index(): Response
|
||||
{
|
||||
$seasons = $this->seasonRepository->findAll();
|
||||
@@ -30,7 +31,7 @@ final class BackofficeController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/{seasonCode}', name: 'season')]
|
||||
#[Route('/backoffice/{seasonCode}', name: 'season')]
|
||||
public function season(Season $season): Response
|
||||
{
|
||||
return $this->render('backoffice/season.html.twig', [
|
||||
@@ -38,12 +39,13 @@ final class BackofficeController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/{seasonCode}/{quiz}', name: 'quiz')]
|
||||
#[Route('/backoffice/{seasonCode}/{quiz}', name: 'quiz')]
|
||||
public function quiz(Season $season, Quiz $quiz): Response
|
||||
{
|
||||
return $this->render('backoffice/quiz.html.twig', [
|
||||
'season' => $season,
|
||||
'quiz' => $quiz,
|
||||
'result' => $this->candidateRepository->getScores($quiz),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
final class QuizController extends AbstractController
|
||||
{
|
||||
public const string SEASON_CODE_REGEX = '[A-Za-z\d]{5}';
|
||||
|
||||
private const string CANDIDATE_HASH_REGEX = '[\w\-=]+';
|
||||
|
||||
#[Route(path: '/', name: 'select_season', methods: ['GET', 'POST'])]
|
||||
@@ -83,7 +84,7 @@ final class QuizController extends AbstractController
|
||||
$candidate = $candidateRepository->getCandidateByHash($season, $nameHash);
|
||||
|
||||
if (!$candidate instanceof Candidate) {
|
||||
if (true === $season->isPreregisterCandidates()) {
|
||||
if ($season->isPreregisterCandidates()) {
|
||||
$this->addFlash(FlashType::Danger, 'Candidate not found');
|
||||
|
||||
return $this->redirectToRoute('enter_name', ['seasonCode' => $season->getSeasonCode()]);
|
||||
|
||||
42
src/Entity/Elimination.php
Normal file
42
src/Entity/Elimination.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\EliminationRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
||||
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
#[ORM\Entity(repositoryClass: EliminationRepository::class)]
|
||||
class Elimination
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: UuidType::NAME, unique: true)]
|
||||
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
|
||||
private ?Uuid $id = null;
|
||||
|
||||
#[ORM\Column(type: Types::JSON)]
|
||||
private array $data = [];
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setData(array $data): static
|
||||
{
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -96,4 +96,23 @@ class Question
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getErrors(): ?string
|
||||
{
|
||||
if (0 === \count($this->answers)) {
|
||||
return 'This question has no answers';
|
||||
}
|
||||
|
||||
$correctAnswers = $this->answers->filter(static fn (Answer $answer): ?bool => $answer->isRightAnswer())->count();
|
||||
|
||||
if (0 === $correctAnswers) {
|
||||
return 'This question has no correct answers';
|
||||
}
|
||||
|
||||
if ($correctAnswers > 1) {
|
||||
return 'This question has multiple correct answers';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class EnterNameType extends AbstractType
|
||||
{
|
||||
public function __construct(private TranslatorInterface $translator)
|
||||
{
|
||||
}
|
||||
public function __construct(private readonly TranslatorInterface $translator) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
|
||||
@@ -8,9 +8,7 @@ use Safe\Exceptions\UrlException;
|
||||
|
||||
class Base64
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
private function __construct() {}
|
||||
|
||||
public static function base64_url_encode(string $input): string
|
||||
{
|
||||
|
||||
@@ -5,14 +5,20 @@ declare(strict_types=1);
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Candidate;
|
||||
use App\Entity\Correction;
|
||||
use App\Entity\Quiz;
|
||||
use App\Entity\Season;
|
||||
use App\Helpers\Base64;
|
||||
use DateInterval;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Safe\Exceptions\UrlException;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Candidate>
|
||||
*
|
||||
* @phpstan-type ResultArray array<string, array{0: Candidate, correct: int, time: DateInterval, corrections?: float, score: float}>
|
||||
*/
|
||||
class CandidateRepository extends ServiceEntityRepository
|
||||
{
|
||||
@@ -41,8 +47,54 @@ class CandidateRepository extends ServiceEntityRepository
|
||||
{
|
||||
$this->getEntityManager()->persist($candidate);
|
||||
|
||||
if (true === $flush) {
|
||||
if ($flush) {
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/** @return ResultArray */
|
||||
public function getScores(Quiz $quiz): array
|
||||
{
|
||||
$scoreTimeQb = $this->createQueryBuilder('c', 'c.id')
|
||||
->select('c', 'sum(case when a.isRightAnswer = true then 1 else 0 end) as correct', 'max(ga.created) - min(ga.created) as time')
|
||||
->join('c.givenAnswers', 'ga')
|
||||
->join('ga.answer', 'a')
|
||||
->where('ga.quiz = :quiz')
|
||||
->groupBy('c.id')
|
||||
->setParameter('quiz', $quiz);
|
||||
|
||||
$correctionsQb = $this->createQueryBuilder('c', 'c.id')
|
||||
->select('c', 'cor.amount as corrections')
|
||||
->innerJoin(Correction::class, 'cor', Join::WITH, 'cor.candidate = c and cor.quiz = :quiz')
|
||||
->setParameter('quiz', $quiz);
|
||||
|
||||
$merged = array_merge_recursive($scoreTimeQb->getQuery()->getArrayResult(), $correctionsQb->getQuery()->getArrayResult());
|
||||
|
||||
return $this->sortResults($this->calculateScore($merged));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array{0: Candidate, correct: int, time: \DateInterval, corrections?: float}> $in
|
||||
*
|
||||
* @return ResultArray
|
||||
*/
|
||||
private function calculateScore(array $in): array
|
||||
{
|
||||
return array_map(static fn ($candidate): array => [
|
||||
...$candidate,
|
||||
'score' => $candidate['correct'] + ($candidate['corrections'] ?? 0.0),
|
||||
], $in);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ResultArray $results
|
||||
*
|
||||
* @return ResultArray
|
||||
*/
|
||||
private function sortResults(array $results): array
|
||||
{
|
||||
usort($results, static fn ($a, $b): int => $b['score'] <=> $a['score']);
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
||||
45
src/Repository/EliminationRepository.php
Normal file
45
src/Repository/EliminationRepository.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Elimination;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Elimination>
|
||||
*/
|
||||
class EliminationRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Elimination::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return Elimination[] Returns an array of Elimination objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('e')
|
||||
// ->andWhere('e.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('e.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?Elimination
|
||||
// {
|
||||
// return $this->createQueryBuilder('e')
|
||||
// ->andWhere('e.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
@@ -22,7 +22,7 @@ class GivenAnswerRepository extends ServiceEntityRepository
|
||||
{
|
||||
$this->getEntityManager()->persist($givenAnswer);
|
||||
|
||||
if (true === $flush) {
|
||||
if ($flush) {
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,5 @@ class QuizRepository extends ServiceEntityRepository
|
||||
parent::__construct($registry, Quiz::class);
|
||||
}
|
||||
|
||||
public function quizReault(Quiz $quiz): array
|
||||
{
|
||||
}
|
||||
public function quizReault(Quiz $quiz): array {}
|
||||
}
|
||||
|
||||
16
src/Service/EliminationService.php
Normal file
16
src/Service/EliminationService.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Repository\CandidateRepository;
|
||||
|
||||
/**
|
||||
* @phpstan-import-type ResultArray from CandidateRepository
|
||||
*/
|
||||
class EliminationService
|
||||
{
|
||||
/** @phpstan-param ResultArray $result */
|
||||
public function createEliminationFromResult(array $result): void {}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if 'backoffice_index' == app.current_route() %} active{% endif %}"
|
||||
href="{{ path('backoffice_index') }}">{{ t('Seasons') }}</a>
|
||||
href="{{ path('backoffice_index') }}">{{ 'Seasons'|trans }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
{% block body %}
|
||||
<p>
|
||||
<h2>{{ t('Quiz') }}: {{ quiz.season.name }} - {{ quiz.name }}</h2>
|
||||
<h2>{{ 'Quiz'|trans }}: {{ quiz.season.name }} - {{ quiz.name }}</h2>
|
||||
</p>
|
||||
<div id="questions">
|
||||
<p>
|
||||
<h4>{{ t('Questions') }}</h4>
|
||||
<h4>{{ 'Questions'|trans }}</h4>
|
||||
</p>
|
||||
<div class="accordion">
|
||||
{% for question in quiz.questions %}
|
||||
@@ -17,24 +17,25 @@
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#question-{{ loop.index0 }}"
|
||||
aria-controls="question-{{ loop.index0 }}">
|
||||
{# {% with question_error = question.errors %} #}
|
||||
{# {% if question_error %} #}
|
||||
{# <span data-bs-toggle="tooltip" #}
|
||||
{# title="{{ question_error }}" #}
|
||||
{# class="badge text-bg-danger rounded-pill me-2">!</span> #}
|
||||
{# {% endif %} #}
|
||||
{# {% endwith %} #}
|
||||
{% set questionErrors = question.getErrors %}
|
||||
{% if questionErrors %}
|
||||
<span data-bs-toggle="tooltip"
|
||||
title="{{ questionErrors }}"
|
||||
class="badge text-bg-danger rounded-pill me-2">!</span>
|
||||
{% endif %}
|
||||
{{ loop.index }}. {{ question.question }}
|
||||
</button>
|
||||
</h2>
|
||||
<div id="question-{{ loop.index0 }}"
|
||||
class="accordion-collapse collapse">
|
||||
<div class="accordion-body">
|
||||
<ul>
|
||||
{% for answer in question.answers %}
|
||||
<li {% if answer.isRightAnswer %}class="text-decoration-underline"{% endif %}>{{ answer.text }}</li>
|
||||
{% else %}
|
||||
{{ t('There are no answers for this question') }}
|
||||
{{ 'There are no answers for this question'|trans }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,51 +46,54 @@
|
||||
</div>
|
||||
<div class="scores">
|
||||
<p>
|
||||
<h4>{{ t('Score') }}</h4>
|
||||
<h4>{{ 'Score'|trans }}</h4>
|
||||
</p>
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group btn-group-lg me-2">
|
||||
<a class="btn btn-primary">{{ t('Start Elimination') }}</a>
|
||||
<a class="btn btn-primary">{{ 'Start Elimination'|trans }}</a>
|
||||
</div>
|
||||
<div class="btn-group btn-group-lg">
|
||||
<a class="btn btn-secondary">{{ t('Prepare Custom Elimination') }}</a>
|
||||
<a class="btn btn-secondary">{{ t('Load Prepared Elimination') }}</a>
|
||||
<a class="btn btn-secondary">{{ 'Prepare Custom Elimination'|trans }}</a>
|
||||
<a class="btn btn-secondary">{{ 'Load Prepared Elimination'|trans }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{ t('Number of dropouts:') }} {{ quiz.dropouts }} </p>
|
||||
<p>{{ 'Number of dropouts:'|trans }} {{ quiz.dropouts }} </p>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{ t('Candidate') }}</th>
|
||||
<th scope="col">{{ t('Correct Answers') }}</th>
|
||||
<th scope="col">{{ t('Corrections') }}</th>
|
||||
<th scope="col">{{ t('Score') }}</th>
|
||||
<th scope="col">{{ t('Time') }}</th>
|
||||
<th scope="col">{{ 'Candidate'|trans }}</th>
|
||||
<th scope="col">{{ 'Correct Answers'|trans }}</th>
|
||||
<th scope="col">{{ 'Corrections'|trans }}</th>
|
||||
<th scope="col">{{ 'Score'|trans }}</th>
|
||||
<th scope="col">{{ 'Time'|trans }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{# {% with result = quiz.get_score %} #}
|
||||
{# {% for candidate in result %} #}
|
||||
{# <tr class="table-{% if forloop.revcounter > quiz.dropouts %}success{% else %}danger{% endif %}"> #}
|
||||
{# <td>{{ candidate.name }}</td> #}
|
||||
{# <td>{{ candidate.correct }}</td> #}
|
||||
{# <td>{{ candidate.corrections }}</td> #}
|
||||
{# <td>{{ candidate.score }}</td> #}
|
||||
{# <td>{{ candidate.time }}</td> #}
|
||||
{# </tr> #}
|
||||
{# {% empty %} #}
|
||||
{# {% endfor %} #}
|
||||
{% for candidate in result %}
|
||||
<tr class="table-{% if loop.revindex > quiz.dropouts %}success{% else %}danger{% endif %}">
|
||||
<td>{{ candidate.0.name }}</td>
|
||||
<td>{{ candidate.correct|default('0') }}</td>
|
||||
<td>{{ candidate.corrections|default('0') }}</td>
|
||||
<td>{{ candidate.score|default('x') }}</td>
|
||||
<td>{{ candidate.time }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5">{{ 'No results'|trans }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{# {% endwith %} #}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block script %}
|
||||
{% block javascripts %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
||||
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
|
||||
});
|
||||
</script>
|
||||
{% endblock script %}
|
||||
{% endblock javascripts %}
|
||||
{% block title %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{% extends 'backoffice/base.html.twig' %}
|
||||
{% block body %}
|
||||
<p>
|
||||
<h2>{{ t('Season') }}: {{ season.name }}</h2>
|
||||
<h2>{{ 'Season'|trans }}: {{ season.name }}</h2>
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12">
|
||||
<h4>{{ t('Quizzes') }}</h4>
|
||||
<h4>{{ 'Quizzes'|trans }}</h4>
|
||||
<div class="list-group">
|
||||
{% for quiz in season.quizzes %}
|
||||
<a class="list-group-item list-group-item-action{% if season.activeQuiz == quiz %} active{% endif %}"
|
||||
@@ -16,7 +16,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<h4>{{ t('Candidates') }}</h4>
|
||||
<h4>{{ 'Candidates'|trans }}</h4>
|
||||
<ul>
|
||||
{% for candidate in season.candidates %}
|
||||
<li>{{ candidate.name }}</li>{% endfor %}
|
||||
|
||||
@@ -8,6 +8,7 @@ Corrections: Jokers
|
||||
Manage: Beheren
|
||||
Name: Naam
|
||||
'No active quiz': 'Geen actieve test'
|
||||
'No results': 'Geen resultaten'
|
||||
'Number of dropouts:': 'Aantal afvallers:'
|
||||
'Prepare Custom Elimination': 'Bereid aangepaste eliminatie voor'
|
||||
'Preregister?': 'Voorregistreren?'
|
||||
|
||||
Reference in New Issue
Block a user