Skip to content

Commit

Permalink
Merge pull request #33 from harishdurga/debug_quizattempt
Browse files Browse the repository at this point in the history
validate and other render methods fixed
  • Loading branch information
harishdurga authored Feb 2, 2023
2 parents c58465c + 900f841 commit 69b6e3a
Show file tree
Hide file tree
Showing 3 changed files with 347 additions and 197 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,14 +294,17 @@ public function correct_options(): Collection
Please refer unit and features tests for more understanding.

### Validate A Quiz Question
Instead of getting total score for the quiz attempt, you can use `QuizAttempt` model's `validate()` method. This method will return an array with a QuizQuestion model's

Instead of getting total score for the quiz attempt, you can use `QuizAttempt` model's `validate()` method. This method will return an array with a QuizQuestion model's
`id` as the key for the assoc array that will be returned.
**Example:**

```injectablephp
$quizAttempt->validate($quizQuestion->id); //For a particular question
$quizAttempt->validate(); //For all the questions in the quiz attempt
$quizAttempt->validate($quizQuestion->id,$data); //$data can any type
```

```php
[
1 => [
Expand All @@ -318,25 +321,29 @@ $quizAttempt->validate($quizQuestion->id,$data); //$data can any type
]
]
```

To be able to render the user answer and correct answer for different types of question types other than the 3 types supported by the package, a new config option has been added.

```php
'render_answers_responses' => [
1 => '\Harishdurga\LaravelQuiz\Models\QuizAttempt::renderQuestionType1Answers',
2 => '\Harishdurga\LaravelQuiz\Models\QuizAttempt::renderQuestionType2Answers',
3 => '\Harishdurga\LaravelQuiz\Models\QuizAttempt::renderQuestionType3Answers',
]
```
By keeping the question type id as the key, you can put the path to your custom function to handle the question type. This custom method will be called from inside the

By keeping the question type id as the key, you can put the path to your custom function to handle the question type. This custom method will be called from inside the
`validate()` method by passing the `QuizQuestion` object as the argument for your custom method as defined in the config.
**Example:**

```php
public static function renderQuestionType1Answers(QuizQuestion $quizQuestion, mixed $data=null)
public static function renderQuestionType1Answers(QuizQuestion $quizQuestion,QuizAttempt $quizAttempt,mixed $data=null)
{
/**
* @var Question $actualQuestion
*/
$actualQuestion = $quizQuestion->question;
$answers = $quizQuestion->answers;
$answers = $quizQuestion->answers->where('quiz_attempt_id', $quizAttempt->id);
$questionOptions = $actualQuestion->options;
$correctAnswer = $actualQuestion->correct_options()->first()?->option;
$givenAnswer = $answers->first()?->question_option_id;
Expand All @@ -349,6 +356,7 @@ public static function renderQuestionType1Answers(QuizQuestion $quizQuestion, mi
return [$correctAnswer, $givenAnswer];
}
```

As shown in the example you customer method should return an array with two elements the first one being the correct answer and the second element being the user's answer for the question.
And whatever the `$data` you send to the `validate()` will be sent to these custom methods so that you can send additional data for rendering the answers.

Expand Down
52 changes: 28 additions & 24 deletions src/Models/QuizAttempt.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public function calculate_score($data = null): float
/**
* @param QuizAttemptAnswer[] $quizQuestionAnswers All the answers of the quiz question
*/
public static function get_score_for_type_1_question(QuizAttempt $quizAttempt, QuizQuestion $quizQuestion, array|Collection $quizQuestionAnswers, $data = null): float
public static function get_score_for_type_1_question(QuizAttempt $quizAttempt, QuizQuestion $quizQuestion, array | Collection $quizQuestionAnswers, $data = null): float
{
$quiz = $quizAttempt->quiz;
$question = $quizQuestion->question;
Expand All @@ -81,13 +81,13 @@ public static function get_score_for_type_1_question(QuizAttempt $quizAttempt, Q
}
return $quizQuestion->is_optional ? 0 : -$negative_marks; // If the question is optional, then the negative marks will be 0
}
return count($quizQuestionAnswers) ? (float)$quizQuestion->marks : 0; // Incase of no correct answer, if there is any answer then give full marks
return count($quizQuestionAnswers) ? (float) $quizQuestion->marks : 0; // Incase of no correct answer, if there is any answer then give full marks
}

/**
* @param QuizAttemptAnswer[] $quizQuestionAnswers All the answers of the quiz question
*/
public static function get_score_for_type_2_question(QuizAttempt $quizAttempt, QuizQuestion $quizQuestion, array|Collection $quizQuestionAnswers, $data = null): float
public static function get_score_for_type_2_question(QuizAttempt $quizAttempt, QuizQuestion $quizQuestion, array | Collection $quizQuestionAnswers, $data = null): float
{
$quiz = $quizAttempt->quiz;
$question = $quizQuestion->question;
Expand All @@ -103,60 +103,64 @@ public static function get_score_for_type_2_question(QuizAttempt $quizAttempt, Q
}
return $quizQuestion->is_optional ? 0 : -$negative_marks; // If the question is optional, then the negative marks will be 0
}
return count($quizQuestionAnswers) ? (float)$quizQuestion->marks : 0; // Incase of no correct answer, if there is any answer then give full marks
return count($quizQuestionAnswers) ? (float) $quizQuestion->marks : 0; // Incase of no correct answer, if there is any answer then give full marks
}

/**
* @param QuizAttemptAnswer[] $quizQuestionAnswers All the answers of the quiz question
*/
public static function get_score_for_type_3_question(QuizAttempt $quizAttempt, QuizQuestion $quizQuestion, array|Collection $quizQuestionAnswers, $data = null): float
public static function get_score_for_type_3_question(QuizAttempt $quizAttempt, QuizQuestion $quizQuestion, array | Collection $quizQuestionAnswers, $data = null): float
{
$quiz = $quizAttempt->quiz;
$question = $quizQuestion->question;
$correct_answer = ($question->correct_options())->first()->option;
$negative_marks = self::get_negative_marks_for_question($quiz, $quizQuestion);
if (!empty($correct_answer)) {

if (count($quizQuestionAnswers)) {
return $quizQuestionAnswers[0]->answer == $correct_answer ? $quizQuestion->marks : -($negative_marks); // Return marks in case of correct answer else negative marks
if (is_array($quizQuestionAnswers)) {
return $quizQuestionAnswers[0]->answer == $correct_answer ? $quizQuestion->marks : -($negative_marks); // Return marks in case of correct answer else negative marks
}
return $quizQuestionAnswers->first()->answer == $correct_answer ? $quizQuestion->marks : -($negative_marks); // Return marks in case of correct answer else negative marks
}
return $quizQuestion->is_optional ? 0 : -$negative_marks; // If the question is optional, then the negative marks will be 0
}
return count($quizQuestionAnswers) ? (float)$quizQuestion->marks : 0; // Incase of no correct answer, if there is any answer then give full marks
return count($quizQuestionAnswers) ? (float) $quizQuestion->marks : 0; // Incase of no correct answer, if there is any answer then give full marks
}

public static function get_negative_marks_for_question(Quiz $quiz, QuizQuestion $quizQuestion): float
{
$negative_marking_settings = $quiz->negative_marking_settings ?? [
'enable_negative_marks' => true,
'negative_marking_type' => 'fixed',
'negative_mark_value' => 0,
'negative_mark_value' => 0,
];
if (!$negative_marking_settings['enable_negative_marks']) { // If negative marking is disabled
return 0;
}
if (!empty($quizQuestion->negative_marks)) {
return $negative_marking_settings['negative_marking_type'] == 'fixed' ?
($quizQuestion->negative_marks < 0 ? -$quizQuestion->negative_marks : $quizQuestion->negative_marks) : ($quizQuestion->marks * (($quizQuestion->negative_marks < 0 ? -$quizQuestion->negative_marks : $quizQuestion->negative_marks) / 100));
($quizQuestion->negative_marks < 0 ? -$quizQuestion->negative_marks : $quizQuestion->negative_marks) : ($quizQuestion->marks * (($quizQuestion->negative_marks < 0 ? -$quizQuestion->negative_marks : $quizQuestion->negative_marks) / 100));
}
return $negative_marking_settings['negative_marking_type'] == 'fixed' ? ($negative_marking_settings['negative_mark_value'] < 0 ? -$negative_marking_settings['negative_mark_value'] : $negative_marking_settings['negative_mark_value']) : ($quizQuestion->marks * (($negative_marking_settings['negative_mark_value'] < 0 ? -$negative_marking_settings['negative_mark_value'] : $negative_marking_settings['negative_mark_value']) / 100));
}

private function validateQuizQuestion(QuizQuestion $quizQuestion, mixed $data = null): array
private function validateQuizQuestion(QuizAttempt $quizAttempt, QuizQuestion $quizQuestion, mixed $data = null): array
{
$isCorrect = true;
$actualQuestion = $quizQuestion->question;
$answers = $quizQuestion->answers;
$answers = $quizQuestion->answers->where('quiz_attempt_id', $quizAttempt->id);
$questionType = $actualQuestion->question_type;
$score = call_user_func_array(config('laravel-quiz.get_score_for_question_type')[$questionType->id], [$this, $quizQuestion, $answers ?? [], $data]);
if ($score <= 0) {
$isCorrect = false;
}
list($correctAnswer, $userAnswer) = config('laravel-quiz.render_answers_responses')[$questionType->id]($quizQuestion, $data);
list($correctAnswer, $userAnswer) = config('laravel-quiz.render_answers_responses')[$questionType->id]($quizQuestion, $quizAttempt, $data);
return [
'score' => $score,
'is_correct' => $isCorrect,
'score' => $score,
'is_correct' => $isCorrect,
'correct_answer' => $correctAnswer,
'user_answer' => $userAnswer
'user_answer' => $userAnswer,
];
}

Expand All @@ -165,15 +169,15 @@ private function validateQuizQuestion(QuizQuestion $quizQuestion, mixed $data =
* @param $data mixed|null data to be passed to the user defined function to evaluate different question types
* @return array|null [1=>['score'=>10,'is_correct'=>true,'correct_answer'=>'a','user_answer'=>'a']]
*/
public function validate(int|null $quizQuestionId = null, mixed $data = null): array|null
public function validate(int | null $quizQuestionId = null, mixed $data = null): array | null
{
if ($quizQuestionId) {
$quizQuestion = QuizQuestion::where(['quiz_id' => $this->quiz_id, 'id' => $quizQuestionId])
->with('question')
->with('answers')
->with('question.options')->first();
if ($quizQuestion) {
return [$quizQuestionId => $this->validateQuizQuestion($quizQuestion, $data)];
return [$quizQuestionId => $this->validateQuizQuestion($this, $quizQuestion, $data)];
}
return null; //QuizQuestion is empty
}
Expand All @@ -183,18 +187,18 @@ public function validate(int|null $quizQuestionId = null, mixed $data = null): a
}
$result = [];
foreach ($quizQuestions as $quizQuestion) {
$result[$quizQuestion->id] = $this->validateQuizQuestion($quizQuestion);
$result[$quizQuestion->id] = $this->validateQuizQuestion($this, $quizQuestion);
}
return $result;
}

public static function renderQuestionType1Answers(QuizQuestion $quizQuestion, mixed $data = null)
public static function renderQuestionType1Answers(QuizQuestion $quizQuestion, QuizAttempt $quizAttempt, mixed $data = null)
{
/**
* @var Question $actualQuestion
*/
$actualQuestion = $quizQuestion->question;
$answers = $quizQuestion->answers;
$answers = $quizQuestion->answers->where('quiz_attempt_id', $quizAttempt->id);
$questionOptions = $actualQuestion->options;
$correctAnswer = $actualQuestion->correct_options()->first()?->option;
$givenAnswer = $answers->first()?->question_option_id;
Expand All @@ -207,10 +211,10 @@ public static function renderQuestionType1Answers(QuizQuestion $quizQuestion, mi
return [$correctAnswer, $givenAnswer];
}

public static function renderQuestionType2Answers(QuizQuestion $quizQuestion, mixed $data = null)
public static function renderQuestionType2Answers(QuizQuestion $quizQuestion, QuizAttempt $quizAttempt, mixed $data = null)
{
$actualQuestion = $quizQuestion->question;
$userAnswersCollection = $quizQuestion->answers;
$userAnswersCollection = $quizQuestion->answers->where('quiz_attempt_id', $quizAttempt->id);
$correctAnswersCollection = $actualQuestion->correct_options();
$correctAnswers = $userAnswers = [];
foreach ($correctAnswersCollection as $correctAnswer) {
Expand All @@ -222,10 +226,10 @@ public static function renderQuestionType2Answers(QuizQuestion $quizQuestion, mi
return [$correctAnswers, $userAnswers];
}

public static function renderQuestionType3Answers(QuizQuestion $quizQuestion, mixed $data = null)
public static function renderQuestionType3Answers(QuizQuestion $quizQuestion, QuizAttempt $quizAttempt, mixed $data = null)
{
$actualQuestion = $quizQuestion->question;
$userAnswersCollection = $quizQuestion->answers;
$userAnswersCollection = $quizQuestion->answers->where('quiz_attempt_id', $quizAttempt->id);
$correctAnswersCollection = $actualQuestion->correct_options();
$userAnswer = $userAnswersCollection->first()?->answer;
$correctAnswer = $correctAnswersCollection->first()?->option;
Expand Down
Loading

0 comments on commit 69b6e3a

Please sign in to comment.