Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 154
ImportAction
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 7
1406.00
0.00% covered (danger)
0.00%
0 / 154
 run
0.00% covered (danger)
0.00%
0 / 1
506.00
0.00% covered (danger)
0.00%
0 / 105
 findQuestion
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 7
 findOption
0.00% covered (danger)
0.00%
0 / 1
42.00
0.00% covered (danger)
0.00%
0 / 14
 startsWith
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 7
 endsWith
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 7
 isAnswered
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 6
 findModel
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 8
<?php
namespace backend\controllers\survey;
use Yii;
use common\models\Survey;
use common\models\SurveyAnswer;
use common\models\SurveyLock;
use common\models\SurveyOpinion;
use common\models\SurveyQuestion;
use backend\models\SurveyImportAnswersForm;
use yii\base\Action;
use yii\web\UploadedFile;
use yii\web\BadRequestHttpException;
use yii\web\ForbiddenHttpException;
use yii\web\NotFoundHttpException;
/**
 * Creates a new Meeting model.
 * If creation is successful, the browser will be redirected to the 'view' page.
 * @param integer $participant
 * @return mixed
 */
class ImportAction extends Action
{
    public $surveyClassName = 'common\models\Survey';
    public $surveyLockClassName = 'common\models\SurveyLock';
    public function run($id)
    {
        $genericSurvey = $this->findModel($id);
        $survey = $genericSurvey;
        if (!($survey instanceof Survey)) {
            $survey = $genericSurvey->survey;
        }
        $surveyQuestions = $survey->getSurveyQuestions()->orderBy('on')->all();
        $model = new SurveyImportAnswersForm();
        $questions = [];
        $options = [];
        $maxOptions = -1;
        foreach ($surveyQuestions as $i => $surveyQuestion) {
            $questions[] = $surveyQuestion;
            $options[$i] = $surveyQuestion->getSurveyOptions()->orderBy('value')->all();
            $maxOptions = max($maxOptions, count($options[$i]));
        }
        if ($survey->answered_quantity > 0) {
            Yii::$app->session->setFlash('warning', Yii::t('app', 'There are user answers. You can import answers only for survey that does not have any.'));
        } else {
            if (Yii::$app->request->isPost) {
                $model->answers_file = UploadedFile::getInstance($model, 'answers_file');
                $content = trim($model->getContent());
                
                $rows = explode("\n", $content);
                foreach ($rows as $i => $row) {
                    $rows[$i] = str_getcsv($row);
                }
                $nonQuestionColumns = 3;
                $surveyLockClassName = $this->surveyLockClassName;
                $header = array_shift($rows);
                $questionCount = count($header) - $nonQuestionColumns;
                $transaction = Yii::$app->db->beginTransaction();
                try {
                    foreach ($rows as $ir => $row) {
                        $userId = array_shift($row);
                        $lockId = explode(':', $userId);
                        $date = array_shift($row);
                        $time = array_shift($row);
                        $timestamp = strtotime($time, strtotime($date)); //TODO: AVOID strtotime
                        $surveyOpinion = new SurveyOpinion();
                        $surveyOpinion->survey_id = $survey->id;
                        $surveyOpinion->created_at = $timestamp;
                        $surveyOpinion->save();
                        foreach ($row as $i => $answer) {
                            if ($i < $questionCount) {
                                $question = $this->findQuestion($i, $header[$i + $nonQuestionColumns], $questions);
                                $option = $this->findOption($answer, $question, $options[$i]);
                                $surveyAnswer = new SurveyAnswer();
                                $surveyAnswer->survey_question_id = $question->id;
                                $surveyAnswer->survey_opinion_id = $surveyOpinion->id;
                                $surveyAnswer->content = is_string($option) ? $option : $option->content;
                                $surveyAnswer->value = is_string($option) ? 0 : $option->value;
                                $surveyAnswer->created_at = $timestamp;
                                if (!$surveyAnswer->save()) {
                                    $errors = array_values($surveyAnswer->firstErrors);
                                    throw new BadRequestHttpException("Record " . ($ir + 1) . " can not store answer " . ($i) . "[$answer]. Validation error: " . array_shift($errors), 1);
                                }
                            }
                        }
                        $surveyLock = new $surveyLockClassName();
                        $surveyLock->created_at = $timestamp;
                        $emptyFields = 0;
                        foreach ($surveyLock->attributes as $key => $value) {
                            if ($key == 'survey_opinion_id') {
                                $surveyLock->survey_opinion_id = $surveyOpinion->id;
                            } else if (is_null($value) && $surveyLock->isAttributeActive($key)) {
                                if ($this->endsWith($key, 'survey_id')) {
                                    $surveyLock->$key = $genericSurvey->id;
                                } else if ($this->endsWith($key, '_id')) {
                                    $surveyLock->$key = array_shift($lockId);
                                } else {
                                    $emptyFields++;
                                }
                            }
                        }
                        if (!$surveyLock->save()) {
                            $errors = array_values($surveyLock->firstErrors);
                            if (count($errors) > 0)
                                throw new BadRequestHttpException("Record " . ($ir + 1) . " can not store surveyLock. Validation error: " . array_shift($errors), 1);
                        }
                        $survey->answered_quantity = $survey->getSurveyOpinions()->count();
                        if (!$survey->save()) {
                            $errors = array_values($surveyLock->firstErrors);
                            throw new BadRequestHttpException("Survey " . ($survey->id) . " can not be updated. Validation error: " . array_shift($errors), 1);
                        }
                    }
                    $transaction->commit();
                } catch(\Exception $e) {
                    $transaction->rollBack();
                    throw new BadRequestHttpException($e->getMessage(), 1, $e);
                }
                Yii::$app->session->setFlash('success', Yii::t('app', 'There was {n} answers imported.', [
                    'n' => count($rows)
                ]));
                return $this->controller->redirect(['view', 'id' => $genericSurvey->id]);
            }
        }
        return $this->controller->render('..\survey\import', [
            'genericSurvey' => $genericSurvey,
            'survey' => $survey,
            'model' => $model,
            'questions' => $questions,
            'options' => $options,
            'maxOptions' => $maxOptions +1,
        ]);
    }
    protected function findQuestion($on, $content, $questions)
    {
        if (count($questions) <= $on) {
            throw new BadRequestHttpException("Question number " . ($on + 1) . " [$content] not found", 1);
        }
        $question = $questions[$on];
        return $question;
    }
    protected function findOption($content, $question, $options)
    {
        $userOption = $content;
        if ($question->type != SurveyQuestion::TYPE_TEXT) {
            foreach ($options as $option) {
                if ($option->content == $option || $option->value == $content) {
                    $userOption = $option;
                }
            }
            if (is_string($userOption)) {
                throw new BadRequestHttpException("Option [$content] not found for question [$question->content]", 1);
            }
        }
        return $userOption;
    }
    protected function startsWith($haystack, $needle)
    {
        $length = strlen($needle);
        if ($length == 0) {
            return true;
        }
        return (substr($haystack, 0, $length) === $needle);
    }
    protected function endsWith($haystack, $needle)
    {
        $length = strlen($needle);
        if ($length == 0) {
            return true;
        }
        return (substr($haystack, -$length) === $needle);
    }
    protected function isAnswered($survey, $user)
    {
        return $survey
            ->getSurveyLocks()
            ->where(['user_id' => $user->id])
            ->count() != 0;
    }
    /**
     * Finds the Survey model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param string $id
     * @return Survey the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($id)
    {
        $className = $this->surveyClassName;
        if (($model = $className::findOne($id)) !== null) {
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }
}