ParserController.php 12.8 KB
<?php
namespace backend\controllers;

use backend\models\Details;
use common\components\exceptions\CrossParsingException;
use common\components\exceptions\OrdinaryActiveRecordException;
use common\components\exceptions\PriceParsingException;
use common\components\exceptions\RgParsingException;
use common\components\ModelArrayValidator;
use common\components\parsers\MailParser;
use common\components\parsers\Parser;
use Yii;
use yii\data\ActiveDataProvider;
use yii\filters\AccessControl;
use backend\components\base\BaseController;
use yii\filters\VerbFilter;
use backend\models\UploadFileParsingForm;
use yii\web\UploadedFile;
use yii\data\ArrayDataProvider;
use common\components\parsers\DynamicFormHelper;
use backend\models\ImportersFiles;
use backend\models\Importers;
use yii\base\ErrorException;
use common\components\PriceWriter;
use common\components\CustomArrayHelper;
use backend\components\traits\ParserTrait;

/**
 * Parser controller
 */
class ParserController extends BaseController
{
    use ParserTrait;
    public $layout = "/column";

    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                ],
            ],
//            'verbs' => [
//                'class' => VerbFilter::className(),
//                'actions' => [
//                    'logout' => ['post'],
//                ],
//            ],
        ];
    }


    public function actionIndex($mode = 0)
    {
        $model = new UploadFileParsingForm();
        // установим режим, 0 - ручная загрузка, 1 - автозагрузка
        $model->mode = $mode;
        return $this->render('index', ['model' => $model]);
    }

    public function actionError()
    {
        $exception = Yii::$app->errorHandler->exception;
        $action_name = '';

        if ( $exception instanceof CrossParsingException ) {
            $action_name = ['crossing-upload/result'];
        }elseif ( $exception instanceof PriceParsingException ) {
            $action_name = ['results'];
        }elseif ( $exception instanceof RgParsingException ) {
            $action_name = ['rg-grup/results'];
        }elseif ( $exception instanceof OrdinaryActiveRecordException ) {
            // для обычной модели возвращаемся в index в качестве контроллера используем имя модели
            $action_name = strtolower($exception->active_record_name);
            $action_name = ["$action_name/index"];
        }

        if ( $exception !== null ) {
           $msg = $exception->getMessage();
            return $this->render( 'error', [ 'message' => $msg, 'action_name' => $action_name  ] );
        }
    }

    public function actionResults($mode = 0)
    {
        set_time_limit(600);
        $model = new UploadFileParsingForm( ['mode' => $mode] );
        $data = [];
        if ( $model->load(Yii::$app->request->post()) ) {
            $model->file = UploadedFile::getInstance( $model, 'file' );
            // первый проход - валидируем, сохраняем файл,
            // ложим в кеш (для ручной загрузки) отпарсенные данные и параметры модели
            if ( $model->validate() ) {
                // сохраним файл и создадим модель - ImportersFiles
                $files_model = $this->saveParserFile($model);
                // для авто загрузки, обработка завершена
                if ( $model->mode ) {
                    $model->success = true;
                    return $this->render('index', ['model' => $model]);
                }
                // === ручная загрузка ===========
                //запускаем парсинг
               $data = $this->parseDataFromFile( $files_model, $model );

            } else {
                // не прошла валидация форма загрузки файлов
                $this->throwStringErrorException( $model , 'common\components\exceptions\PriceParsingException' );
            }
            // листаем пагинатором, или повторно вызываем - считываем из кеша отпрасенные данные
        } else if ( Yii::$app->getCache()->get('parser_data') ) {
            $data = json_decode(Yii::$app->getCache()->get('parser_data'), true);
        }
        return $this->renderResultView( $data );
    }

    public function actionWrite()
    {
        //получим колонки которые выбрал пользователь
        $arr_attributes = Yii::$app->request->post()['DynamicModel'];
        //соберем модель по полученным данным
        $model = DynamicFormHelper::CreateDynamicModel($arr_attributes);
        $details_model = new Details();
        //добавим правила валидации (колонки должны быть те что указаны в конфиге)
        foreach ($arr_attributes as $key => $value) {
            $model->addRule($key, 'in', ['range' => array_keys(Yii::$app->multiparser->getConfiguration('csv', 'basic_column'))]);
        }

        // установим режим проверки обязательных полей
        $details_model->setScenario('form_upload_validation');
        $model_validator = new ModelArrayValidator( $details_model );
        // провалидируем выбранные колонки
        if ( $model->validate() &&  $model_validator->validateRow( array_flip( $arr_attributes ) ) ) {

            // валидация успешна у нас есть соответсвие колонок, преобразуем в массив данное соответсвие для дальнейшей работы
            $arr = $model->toArray();

            // получим данные из кеша
            $this->parserCacheHandler( 0, $data, $configuration );

            // соотнесем отпарсенные данные с соответсивем полученным от пользователя
            // для этого преобразуем массив отпарсенных данных - назначим ключи согласно соответствию
            $data = CustomArrayHelper::createAssocArray( $data, $arr , 'attr_' );

            // запустим специальный класс который запишет данные в таблицы связанные с прайсами
            $writer = new PriceWriter();
            $writer->setConfiguration( $configuration );
            $writer->setData($data);
            $writer->setMode(0); //web-режим
            if ( $writer->writePriceToDB() ) {

                $this->parserCacheHandler( 2 );

                if( file_exists($configuration['file_path']) )
                    unlink($configuration['file_path']);
                $validated_type_msg =  $writer->hasValidationError() ?  'warning' : 'success';
                Yii::$app->session->setFlash( $validated_type_msg, $writer->getValidatedMsg() );
               return $this->render('index', ['model' => $configuration]);

            };

        } else {
            // не прошла валидация формы загрузки файлов
            $errors_str = "Ошибка валидации формы загрузки файлов. ";
            $this->throwStringErrorException( $details_model , 'common\components\exceptions\PriceParsingException', $errors_str );
        }

    }

    public function actionAutoUpload()
    {
        $query = Importers::find()->where(['active' => true])->orderBy(['price_date_update' => SORT_DESC]);

        $provider = new ActiveDataProvider([
            'query' => $query,
            'pagination' => [
                'pageSize' => 10,
            ],
        ]);
        return $this->render('check_price',
            [
                'dataProvider' => $provider]);
    }

    public function actionServerFiles ()
    {
            $arr_id_files = [];
            $arr_supported_extension = Parser::supportedExtension();

        // получим список файлов которые ожидают к загрузке
        foreach ($arr_supported_extension as $ext) {
            foreach ( glob( Yii::getAlias('@temp_upload') . '/*.' . $ext ) as $server_file ) {
                $file_id = basename($server_file, ".{$ext}");
                $arr_id_files[] = (int)$file_id;
            }
        }

        $query = ImportersFiles::find()->where(['in', 'id', $arr_id_files])->orderBy( ['upload_time' => SORT_DESC] );

        $provider = new ActiveDataProvider([
            'query' => $query,
            'pagination' => [
                'pageSize' => 10,
            ],
        ]);
        return $this->render('server-files',
            [
                'dataProvider' => $provider]);
    }

    public function actionDelete ()
    {
        if ( Yii::$app->request->isAjax ) {

            $files_model = new ImportersFiles();
            if ( isset(Yii::$app->request->post()['id'] )) {
                $id = Yii::$app->request->post()['id'];
                try {
                    $files_model->delete($id);
                    $arr_supported_extension = Parser::supportedExtension();
                    // получим список файлов которые ожидают к загрузке
                    foreach ($arr_supported_extension as $ext) {
                        if (file_exists(Yii::getAlias('@temp_upload') . '/' . $id . ".{$ext}")) {
                            unlink(Yii::getAlias('@temp_upload') . '/' . $id . ".{$ext}");
                        }
                    }
                    // сообщим скрипту что все ОК
                    echo 1;
                } catch (ErrorException  $e) {

                    throw $e;

                }
            }
        }

    }

    public function actionLaunchCroneUploads ()
    {
        $arr_supported_extension = Parser::supportedExtension();
        // получим список файлов которые ожидают к загрузке
        foreach ($arr_supported_extension as $ext) {
            foreach ( glob( Yii::getAlias('@temp_upload') . '/*.' . $ext ) as $server_file ) {

                $file_name = basename($server_file, ".{$ext}");
                copy($server_file, Yii::getAlias('@auto_upload') . '/' . $file_name . ".{$ext}");

            }
        }
        $controller = new \console\controllers\ParserController( 'parse-prices', $this->module );
        //$controller->actionSaveMailAttachments();
        $controller->actionParsePrices();

        Yii::$app->session->setFlash( 'server-files', 'Файл успешно загружен' );
        $this->redirect('server-files');
    }

    /**
     * сохраняет файл на диск и регистрирует в ImportersFiles
     * @param $model - модель с настройками
     * @return ImportersFiles
     * @throws ErrorException
     * @throws \Exception
     */
    protected function saveParserFile ($model)
    {
        $files_model = new ImportersFiles();
        // id поставщика получим из конфигурации
        $files_model->load(['ImportersFiles' => $model->toArray()]);
        try {
            $files_model->save();
        } catch (ErrorException  $e) {
            throw $e;
        }
        // получим id только что записанной записи - его запишем в название файла
        $model->record_id = $files_model->id;

        $file_name = $model->record_id . '.' . $model->file->extension;

        if ($model->mode) {
            $model->file_path = Yii::getAlias('@temp_upload') . '/' . $file_name;
        } else {
            $model->file_path = Yii::getAlias('@manual_upload') . '/' . $file_name;
        }

        $model->file->saveAs($model->file_path);

        return $files_model;
    }
    protected function parseDataFromFile ( $files_model, $model )
    {
        // доп. опции для парсера
        $options = ['converter_conf' =>
            ['importer_id' => $files_model->importer_id]
        ];

        if( ! $model->action ) // обработка с кастомным разделителем
            $options['$delimiter'] = $model->delimiter;

        $data = $model->readFile( $options );
        // сохраняем в кеш отпарсенные даные
        $this->parserCacheHandler( 1, $data, $model );

        return $data;
    }

    protected function getBasicColumns()
    {
        return Yii::$app->multiparser->getConfiguration('csv', 'basic_column');

    }
}