M
M
macho9922019-06-09 10:47:45
Yii
macho992, 2019-06-09 10:47:45

How to make the correct calculation in the test?

There is a test that does not calculate points correctly. Created a quiz with two questions. Each question has 4 possible answers, and each has one correct answer. If you choose the correct answer in one question, and one incorrect answer in the other, then the result is 75 points. If you choose the correct answer in one question, and two incorrect answers in the other, then the result is 50 points. Well, respectively, when choosing the correct answers in one and the other question, the result is 100 points.
The question is, why, if you choose the correct answer in one question and the wrong one in the other, then the result is 75 points?

<?php


namespace frontend\controllers;


use common\models\Question;
use common\models\Settings;
use common\models\Test;
use common\models\TestResult;
use common\models\TestResultQuestion;
use common\models\Theme;
use yii\data\Pagination;
use yii\db\ActiveQuery;
use yii\filters\AccessControl;
use yii\helpers\ArrayHelper;
use yii\web\Controller;
use yii\web\NotFoundHttpException;

class TestController extends Controller
{
    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    [
                        'actions' => ['index', 'index-theme', 'test', 'result', 'show-result'],
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                ],
            ]
        ];
    }

    public function beforeAction($action) 
    { 
        $this->enableCsrfValidation = false; 
        return parent::beforeAction($action); 
    }

    public function actionIndexTheme($theme_id)
    {
        $theme = Theme::findOne(['id' => $theme_id]);
        $query = Test::find()
            ->innerJoinWith('testQuestions')
            ->where(['theme_id' => $theme_id])
        ->groupBy('test.id');
        $countQuery = clone $query;
        $pages = new Pagination(['totalCount' => $countQuery->count()]);
        $pages->pageSize = 5;
        $models = $query->offset($pages->offset)
            ->limit($pages->limit)
            ->all();

        return $this->render('index-theme', [
            'models' => $models,
            'pages' => $pages,
            'theme' => $theme,
        ]);
    }

    public function actionIndex()
    {
        $query = Test::find()
            ->innerJoinWith('testQuestions')
            ->groupBy('test.id');
        $countQuery = clone $query;
        $pages = new Pagination(['totalCount' => $countQuery->count()]);
        $pages->pageSize = 5;
        $models = $query->offset($pages->offset)
            ->limit($pages->limit)
            ->all();

        return $this->render('index', [
            'models' => $models,
            'pages' => $pages,
        ]);
    }

    public function actionTest($id)
    {
        $time = 300;
        /** @var Settings $settings */
        $settings = Settings::find()->one();
        if ($settings && $settings->time) {
            $time = $settings->time;
        }
        /** @var Test $test */
        $test = Test::find()
            ->where(['test.id' => $id])
            ->innerJoinWith('testQuestions.question.answers')
            ->groupBy('test.id')
            ->one();
        if (!$test) throw new NotFoundHttpException();

        return $this->render('test', [
            'test' => $test,
            'time' => $time,
        ]);
    }

    public function actionResult($test_id)
    {
        $points = 100;
        $answers = \Yii::$app->request->post();

        $answers_id = [];
        $questions = Question::find()
            ->select('question.*')
            ->innerJoinWith(['testQuestions', 'answers' => function (ActiveQuery $query) {
                return $query->where(['answer.is_true' => 1]);
            }])
            ->where(['test_id' => $test_id])
            ->groupBy('question.id')
            ->all();

        $points_question = $points / count($questions);
        $points_result = 0;
        foreach ($questions as $key => $question) {
            $points_for_question = 0;
            if (array_key_exists($question->id, $answers)) {
                $answers_id = array_merge($answers_id, $answers[$question->id]);
                $true_answers = ArrayHelper::getColumn($question->answers, 'id');
                if (count($true_answers) >= count($answers[$question->id])) {
                    $diff = array_diff($true_answers, $answers[$question->id]);
                    if (count($diff) > 0) {
                        $points_for_question = $points_question / 2;
                    } else {
                        $points_for_question = $points_question;
                    }
                } else {
                    $diff = array_diff($answers[$question->id], $true_answers);
                    if (count($diff) < count($answers[$question->id])) {
                        $points_for_question = $points_question / 2;
                    }
                }
            }
            $points_result += $points_for_question;
        }

        $test_result = new TestResult();
        $test_result->result = round($points_result);
        $test_result->test_id = $test_id;
        $test_result->user_id = \Yii::$app->getUser()->getId();
        $test_result->save();

        foreach ($answers_id as $answer) {
            $testrq = new TestResultQuestion();
            $testrq->test_result_id = $test_result->id;
            $testrq->answer_id = $answer;
            $testrq->save();
        }
        return $this->redirect(['show-result','test_result_id' =>$test_result->id ]);
    }

    public function actionShowResult($test_result_id)
    {
        /** @var TestResult $test_result */
        $test_result =  TestResult::find()
            ->joinWith('testResultQuestions')
            ->where(['test_result.id' => $test_result_id])->one();

        $answers_pick = ($test_result->testResultQuestions)
                ? ArrayHelper::getColumn($test_result->testResultQuestions,'answer_id')
                : [];

        $test = Test::find()
            ->where(['test.id' => $test_result->test_id])
            ->innerJoinWith('testQuestions.question.answers')
            ->groupBy('test.id')
            ->one();

        return $this->render('result', [
            'answers_pick' => $answers_pick,
            'test' => $test,
            'test_result' => $test_result,
        ]);
    }
}

Answer the question

In order to leave comments, you need to log in

1 answer(s)
M
Maxim, 2019-06-09
@macho992

Such code, where there are a lot of if, else, is very difficult to test and it is difficult to read. Take out all the logic code in separate classes, with methods and test them. For example, the class might be the QuestionPoint class . There are methods and the like in it .. You will get such a code, if it is completely primitive

$question = 1;
foreach ($questions as $key => $question) {
$questionPoint = new  QuestionPoint($question);
$questionPoint->isAnswerExists($question, $answer)
}

Therefore, make code according to OOP or DDD and then you will be happy. In your case, there will be hemorrhoids. Especially if the code is in the controller. Testing it will be extremely difficult and time consuming. No one wants to run such tests. You will have to raise the application and the database in tests. Although in your case it is generally superfluous!
In general, take the code out of the controller and get rid of the noodles. Create objects.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question