From d07e20a1d5c6a4790cea7361f4ccfbfcb9d9d516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20P=C5=82aczek?= Date: Thu, 24 Oct 2024 16:23:43 +0200 Subject: [PATCH] Add a way of validating answers and render whole group of questions not just one file at a time. Add a cs-fixer package --- .gitignore | 5 + .php-cs-fixer.dist.php | 13 +++ composer.json | 1 + composer.lock | 54 ++++++++++- src/Controller/ChallengeController.php | 39 ++++++-- src/Controller/HomeController.php | 5 +- src/Service/QuestionsService.php | 73 ++++++++++++-- symfony.lock | 12 +++ templates/base.html.twig | 10 +- templates/challenge/list.html.twig | 10 +- templates/challenge/start.html.twig | 113 +++++++--------------- templates/challenge/start_group.html.twig | 30 ++++++ templates/challenge/submit.html.twig | 34 +++++++ 13 files changed, 289 insertions(+), 110 deletions(-) create mode 100644 .php-cs-fixer.dist.php create mode 100644 templates/challenge/start_group.html.twig create mode 100644 templates/challenge/submit.html.twig diff --git a/.gitignore b/.gitignore index 870bd29..ae33ad4 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,8 @@ /public/assets/ /assets/vendor/ ###< symfony/asset-mapper ### + +###> php-cs-fixer/shim ### +/.php-cs-fixer.php +/.php-cs-fixer.cache +###< php-cs-fixer/shim ### diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..3788194 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,13 @@ +in(__DIR__) + ->exclude('var') +; + +return (new PhpCsFixer\Config()) + ->setRules([ + '@Symfony' => true, + ]) + ->setFinder($finder) +; diff --git a/composer.json b/composer.json index fcd2bdf..34dfd99 100644 --- a/composer.json +++ b/composer.json @@ -8,6 +8,7 @@ "ext-ctype": "*", "ext-iconv": "*", "efficience-it/certification-symfony": "^1.0", + "php-cs-fixer/shim": "^3.64", "symfony/asset": "7.1.*", "symfony/asset-mapper": "7.1.*", "symfony/console": "7.1.*", diff --git a/composer.lock b/composer.lock index 48a8988..697bf7a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "37fda8896febb7725269604fcf26625b", + "content-hash": "4cecbae9a6775775e8fcd1192a4860ae", "packages": [ { "name": "composer/semver", @@ -97,6 +97,58 @@ }, "type": "library" }, + { + "name": "php-cs-fixer/shim", + "version": "v3.64.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/shim.git", + "reference": "81ccfd24baf3a10810dab1152c403981a790b837" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/81ccfd24baf3a10810dab1152c403981a790b837", + "reference": "81ccfd24baf3a10810dab1152c403981a790b837", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "replace": { + "friendsofphp/php-cs-fixer": "self.version" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer", + "php-cs-fixer.phar" + ], + "type": "application", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz RumiƄski", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/PHP-CS-Fixer/shim/issues", + "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.64.0" + }, + "time": "2024-08-30T23:10:11+00:00" + }, { "name": "psr/cache", "version": "3.0.0", diff --git a/src/Controller/ChallengeController.php b/src/Controller/ChallengeController.php index f923a57..0455613 100644 --- a/src/Controller/ChallengeController.php +++ b/src/Controller/ChallengeController.php @@ -4,29 +4,56 @@ namespace App\Controller; use App\Service\QuestionsService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; class ChallengeController extends AbstractController { - - #[Route('/challenge', name: 'app_challenge')] + #[Route('/challenge', name: 'app_challenges')] public function randomChallenge(): Response { return $this->render('challenge/start.html.twig'); } - #[Route('/list', name: 'app_list')] + #[Route('/', name: 'app_home')] public function list(QuestionsService $questionsService): Response { $listOfChallenges = $questionsService->getList(); + return $this->render('challenge/list.html.twig', ['list' => $listOfChallenges]); } - #[Route('/challenge/{challenge<.*>}', name: 'app_challenge')] + #[Route('/challenge/{challenge<.*>}', name: 'app_challenge', methods: ['GET'])] public function challenge(QuestionsService $questionsService, string $challenge): Response { $questions = $questionsService->getQuestions($challenge); - return $this->render('challenge/list.html.twig', ['list' => $questions, 'challenge' => $challenge]); + + return $this->render('challenge/start.html.twig', ['questions' => $questions, 'challenge' => $challenge]); } -} \ No newline at end of file + + + #[Route('/challenge/{challenge<.*>}', name: 'app_challenge2', methods: ['POST'])] + public function challengeSubmit(Request $request, QuestionsService $questionsService, string $challenge): Response + { + $answers = $request->getPayload()->all('answer'); + $questions = $questionsService->questionsById($request->getPayload()->all('question_id')); + + //dd($answers); + //dd($questionsService->questionsById($request->getPayload()->all('question_id'))); + //$x = $request->getPayload()->get('question_id'); + //dd($questionsService->questionsById($request->getPayload()->all('question_id'))); + //dd($request->getPayload()->all()); + + //$questions = $questionsService->getQuestions($challenge); + return $this->render('challenge/submit.html.twig', ['questions' => $questions, 'challenge' => $challenge, 'answers' => $answers]); + } + + #[Route('/challenge_group/{group<.*>}', name: 'app_challenge_group')] + public function randomChallengeGroup(QuestionsService $questionsService, string $group): Response + { + $questions = $questionsService->getQuestionsGroup($group); + + return $this->render('challenge/start_group.html.twig', ['questions' => $questions, 'group' => $group]); + } +} diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index dcf13c5..18bf365 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -9,12 +9,11 @@ use Symfony\Component\Routing\Attribute\Route; class HomeController extends AbstractController { - #[Route('/', name: 'app_home')] + #[Route('/home')] public function home(QuestionsService $questionsService): Response { $questions = $questionsService->getQuestions(); return $this->render('home/index.html.twig', ['questions' => $questions]); } - -} \ No newline at end of file +} diff --git a/src/Service/QuestionsService.php b/src/Service/QuestionsService.php index 20bfa41..6ddb924 100644 --- a/src/Service/QuestionsService.php +++ b/src/Service/QuestionsService.php @@ -12,27 +12,66 @@ class QuestionsService { } - public function getQuestions(?string $file): array + public function getQuestions(?string $file = null, bool $shuffle = true): array { $questions = []; - foreach ($this->getQuestionFiles($file) as $file) { + foreach ($this->getQuestionFiles(file: $file) as $file) { $yamlFile = Yaml::parseFile($file->getRealPath()); $questions = array_merge($questions, $yamlFile['questions']); } + if ($shuffle) { + shuffle($questions); + array_walk($questions, static function (array &$value): void { + shuffle($value['answers']); + $answers = 0; + foreach ($value['answers'] as $answer) { + $answers += $answer['correct']; + } + $value['type'] = $answers > 1 ? 'multiple' : 'single'; + $value['count'] = $answers; + }); + } return $questions; } - private function getQuestionFiles(?string $file = null): array + public function questionsById(array $questionIds): array { - $finder = new Finder(); - if ($file) { - $finder->name($file); - } else { - $finder->name('*.yaml'); + $questions = []; + foreach ($this->getQuestionFiles() as $file) { + $yamlFile = Yaml::parseFile($file->getRealPath()); + if (isset($yamlFile['questions'])) { + //dd(array_column($yamlFile['questions'], 'uuid')); + //dd($yamlFile['questions']); + $questions += array_combine(array_column($yamlFile['questions'], 'uuid'), $yamlFile['questions']); + //$questions = array_merge($questions, $yamlFile['questions']); + } } - return iterator_to_array($finder->files()->in($this->paths)); + return array_filter($questions, fn(array $value): bool => in_array($value['uuid'], $questionIds, true)); + } + + private function getQuestionFiles(?string $file = null, ?string $group = null): array + { + $finder = new Finder(); + if (null !== $file) { + if ($file) { + $finder->name($file); + } else { + $finder->name('*.yaml'); + } + $finder->in($this->paths); + } + if (null !== $group) { + $paths = array_filter(array_map(fn(string $path): string => $path . DIRECTORY_SEPARATOR . $group, $this->paths), 'is_dir'); + $finder->in($paths); + $finder->name('*.yaml'); + } + if (null === $group and null === $file) { + $finder->in($this->paths); + } + + return iterator_to_array($finder->files()); } /** @@ -43,4 +82,18 @@ class QuestionsService return $this->getQuestionFiles(); } -} \ No newline at end of file + public function getQuestionsGroup(string $group, bool $shuffle = true): array + { + $questions = []; + foreach ($this->getQuestionFiles(group: $group) as $file) { + $yamlFile = Yaml::parseFile($file->getRealPath()); + $questions = array_merge($questions, $yamlFile['questions']); + } + if ($shuffle) { + shuffle($questions); + array_walk($questions, static fn(array &$value): true => shuffle($value['answers'])); + } + + return $questions; + } +} diff --git a/symfony.lock b/symfony.lock index aa66494..88cdaf6 100644 --- a/symfony.lock +++ b/symfony.lock @@ -1,4 +1,16 @@ { + "php-cs-fixer/shim": { + "version": "3.64", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "3.0", + "ref": "16422bf8eac6c3be42afe07d37e2abc89d2bdf6b" + }, + "files": [ + ".php-cs-fixer.dist.php" + ] + }, "symfony/asset-mapper": { "version": "7.1", "recipe": { diff --git a/templates/base.html.twig b/templates/base.html.twig index a8bbc09..64e18d7 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -45,7 +45,7 @@
{# Your Company #} - + logo @@ -53,10 +53,10 @@
diff --git a/templates/challenge/list.html.twig b/templates/challenge/list.html.twig index 5be80b4..017d5a4 100644 --- a/templates/challenge/list.html.twig +++ b/templates/challenge/list.html.twig @@ -3,16 +3,16 @@ {% block body %}
- {% set test = '' %} + {% set currentGroup = '' %} {% for challenge in list %} - {% if test != challenge.getRelativePath %} + {% if currentGroup != challenge.getRelativePath %}

- {{ challenge.getRelativePath|replace({'_': ' '})|capitalize }} / All + {{ challenge.getRelativePath|replace({'_': ' '})|capitalize }} / All

{% endif %} -

+

{{ challenge.getFilenameWithoutExtension|replace({'_': ' '})|capitalize }}

- {% set test = challenge.getRelativePath %} + {% set currentGroup = challenge.getRelativePath %} {% endfor %}
diff --git a/templates/challenge/start.html.twig b/templates/challenge/start.html.twig index 567dc06..6aacdc2 100644 --- a/templates/challenge/start.html.twig +++ b/templates/challenge/start.html.twig @@ -1,99 +1,52 @@ {% extends 'base.html.twig' %} {% block body %} -
+
-
-
-

Back End Developer

-
-
- - Full-time -
-
- - Remote -
-
- - $120k – $140k -
-
- - Closing on January 9, 2020 -
-
-
-
- +

{{ challenge|replace({'.yaml': '', '_': ' '})|capitalize }}

- +
+
+ {% for question in questions %} + +
+

{{ loop.index }}. {{ question.question }}

+ {% for answer in question.answers %} +
+ +
+ {% endfor %} +
+ {% endfor %} - + - -
- + + + - -
-
+ +
{% endblock %} \ No newline at end of file diff --git a/templates/challenge/start_group.html.twig b/templates/challenge/start_group.html.twig new file mode 100644 index 0000000..5c992e3 --- /dev/null +++ b/templates/challenge/start_group.html.twig @@ -0,0 +1,30 @@ +{% extends 'base.html.twig' %} + +{% block body %} +
+ {% for question in questions %} +
+ +

{{ loop.index }}. {{ question.question }}

+ {% for answer in question.answers %} +
+ +
+ {% endfor %} +
+ {% endfor %} + + + +
+{% endblock %} \ No newline at end of file diff --git a/templates/challenge/submit.html.twig b/templates/challenge/submit.html.twig new file mode 100644 index 0000000..fe65443 --- /dev/null +++ b/templates/challenge/submit.html.twig @@ -0,0 +1,34 @@ +{% extends 'base.html.twig' %} + +{% block body %} +
+

{{ challenge|replace({'.yaml': '', '_': ' '})|capitalize }}

+
+ {% for question in questions %} +
+

{{ loop.index }}. {{ question.question }}

+ {% for questionanswer in question.answers %} +
+
+ {% if answers[question.uuid] is defined %} + {% if questionanswer.value in answers[question.uuid] and questionanswer.correct == true %} + {{ questionanswer.value }} + {% elseif questionanswer.value in answers[question.uuid] and questionanswer.correct == false %} + {{ questionanswer.value }} + {% else %} + {{ questionanswer.value }} + {% endif %} + {% else %} + {{ questionanswer.value }} + {% endif %} +
+
+ {% endfor %} + +
+ {% endfor %} +
+
+{% endblock %} \ No newline at end of file