Skip to content

Commit

Permalink
POC for #118 (#149)
Browse files Browse the repository at this point in the history
  • Loading branch information
tabuna authored May 2, 2024
1 parent 36023b2 commit 44f99cb
Show file tree
Hide file tree
Showing 15 changed files with 823 additions and 9 deletions.
151 changes: 151 additions & 0 deletions app/Http/Controllers/ReviewController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

namespace App\Http\Controllers;

use App\Quiz\Question;
use App\Quiz\QuizState;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class ReviewController extends Controller
{
/**
* @var QuizState
*/
public QuizState $quiz;

/**
* Display the welcome page.
*
* @throws \Exception
*/
public function index()
{
return view('review.index');
}

/**
* Set collection of questions and start the quiz.
*
* @throws \Exception
*
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function start()
{
$this->quiz = new QuizState($this->questions());

return $this->show();
}

/**
* Move to the next question.
*
* @param \Illuminate\Http\Request $request
*
* @throws \Exception
*
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function next(Request $request)
{
$this->quiz ??= unserialize($request->session()->get('review'));

$this->quiz->next();

return $this->show();
}

/**
* Process user's answer.
*
* @param Request $request
*
* @throws \Exception
*
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|void
*/
public function answer(Request $request)
{
$userAnswer = $request->get('answer');
$this->quiz = unserialize($request->session()->get('review'));

$this->quiz->applyAnswer($userAnswer);

return $this->show();
}

/**
* Display the quiz page.
*
* @throws \Exception
*/
protected function show()
{
session()->put('review', serialize($this->quiz));

return turbo_stream_view(view('review.quiz', [
'quiz' => $this->quiz,
'currentQuestion' => $this->quiz->question(),
]));
}

/**
* Generate questions for the quiz.
*
* Randomly choose one situation for each emotion.
*
* @throws \Exception
*
* @return Collection
*/
public function questions(): Collection
{
$storage = Storage::disk('review');

return collect($storage->allFiles('beginning'))
->map(fn ($path) => $storage->get($path))
->map(function ($content) {
$question = Str::of($content)
->between('<question>', '</question>')
->trim()
->toString();

$answers = Str::of($content)
->matchAll("/<answer>(.*?)<\/answer>/s")
->map(fn ($answer) => trim($answer));

$correct = Str::of($content)
->between('<correct>', '</correct>')
->trim()
->toString();

return Question::make($question)
->answers($answers->push($correct)->toArray())
->setCorrectAnswer($correct);
});

/*
* Example of questions for the quiz.
$example = [
Question::make(['Какой компонент Laravel позволяет управлять операциями с базой данных упрощенным способом?', 'Какая функция в Laravel упрощает взаимодействие с базой данных?'])
->answers(['Eloquent ORM', 'Lumen', 'Blade', 'Artisan', 'Eloquent'])
->setCorrectAnswer('Eloquent ORM'),
Question::make(['Какая функция Laravel предоставляет удобный способ определения логики авторизации в приложении?', 'Какой компонент Laravel упрощает задачи авторизации пользователей?'])
->answers(['Blade', 'Middleware', 'Eloquent ORM', 'Authentication', 'Policies'])
->setCorrectAnswer('Policies'),
Question::make(['Какой инструмент командной строки в Laravel помогает с повторяющимися задачами, такими как миграции базы данных и их наполнение данными?', 'Какой компонент Laravel используется для задач в командной строке?'])
->answers(['Composer', 'Eloquent ORM', 'Blade', 'Artisan', 'Artisan'])
->setCorrectAnswer('Artisan'),
Question::make(['Какая функция Laravel позволяет разработчикам создавать модульный, многократно используемый код?', 'Какое понятие Laravel способствует повторному использованию кода и его поддержке?'])
->answers(['Routing', 'Middleware', 'Blade', 'Controllers', 'Middleware'])
->setCorrectAnswer('Middleware'),
];
*/
}
}
143 changes: 143 additions & 0 deletions app/Quiz/Question.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<?php

declare(strict_types=1);

namespace App\Quiz;

use Illuminate\Support\Str;

class Question
{
/**
* @var array
*/
public array $answers = [];

/**
* @var string
*/
public string $title = '';
/**
* @var string
*/
public string $correctAnswer;

/**
* @param string $title
*/
public function __construct(string $title)
{
$this->title = $title;
}

/**
* @param string $title
*
* @return \App\Quiz\Question
*/
public static function make(mixed $title): Question
{
// если передан массив вопросов(ситуаций) - нужно выбрать один вопрос
if (is_array($title)) {
$key = array_rand($title, 1);

Check failure on line 42 in app/Quiz/Question.php

View workflow job for this annotation

GitHub Actions / static-analysis

NoValue

app/Quiz/Question.php:42:31: NoValue: All possible types for this argument were invalidated - This may be dead code (see https://psalm.dev/179)

return new self($title[$key]);
}

return new self($title);
}

/**
* @param array $answers
* @param int $count
*
* @return $this
*/
public function answers(array $answers, int $count = 4): Question
{
$this->answers = $count !== 0 && $count < count($answers) ? $this->randomValues($answers, $count) : $answers;

return $this;
}

/**
* @param string $correctAnswer
*
* @throws \Exception
*
* @return $this
*/
public function setCorrectAnswer(string $correctAnswer)
{
$this->correctAnswer = $correctAnswer;
if (! in_array($correctAnswer, $this->answers)) {
// нужно не только задать правильный ответ, но и добавить его в массив с вопросами
$number = random_int(0, count($this->answers)); // правильный ответ положим в массив ответов на место $number
array_splice($this->answers, $number, 0, $correctAnswer);
}

return $this;
}

/**
* @return string
*/
public function getCorrectAnswer()
{
return $this->correctAnswer;
}

/**
* @param string $ansver
*
* @return bool
*/
public function isCorrect(string $ansver): bool
{
return $ansver === $this->correctAnswer;
}

public function getTitle()
{
return Str::of($this->title)->markdown();
}

public function getAnswers()
{
return $this->answers;
}

public function toArray()
{
return get_object_vars($this);
}

/**
* @param $value
*
* @throws \Exception
*
* @return \App\Quiz\Question
*/
public static function recovery($value)
{
return (new self($value['title']))
->answers($value['answers'], count($value['answers']))
->setCorrectAnswer($value['correctAnswer']);
}

protected function randomValues(array $arr, $count = 2): array
{
$result = [];
$arrKeys = array_rand($arr, $count);
if (is_array($arrKeys)) {
foreach ($arrKeys as $key) {
$result[] = $arr[$key];
}

return $result;
}

return $arr;
}
}
Loading

0 comments on commit 44f99cb

Please sign in to comment.