ParserController.php 13.5 KB
<?php
namespace console\controllers;

use backend\models\Log;
use common\components\archives\ArchiveCreator;
use common\components\CustomVarDamp;
use common\components\mail\ImapMailReader;
use common\components\mail\MailAttachmentsSaver;
use common\components\parsers\Parser;
use yii\console\Controller;
use yii\helpers\Console;
use common\components\PriceWriter;
use backend\models\ImportersFiles;
use backend\models\Importers;
use yii\base\ErrorException;
use yii\helpers\Html;

class ParserController extends Controller
{
    protected $start_time;

    public function actionParsePrices()
    {
        $this->start_time = date('Y-m-d H:i:s');

        $path_arr = [
            ['mode' => 1, 'path' => \Yii::getAlias('@auto_upload')],
            ['mode' => 2, 'path' => \Yii::getAlias('@mail_upload')],
        ];

        $arr_supported_extension = Parser::supportedExtension();
        foreach ($arr_supported_extension as $ext) {

            $this->parseFilesByExtension( $ext , $path_arr );
        }

    }

    public function actionTest()
    {
        Console::output('It is working');
        \Yii::info('2', 'parser');

    }

    public function actionSaveMailAttachments()
    {
        \Yii::info('Начало сохранения файлов почты', 'mail');

        // получим разделитель для файлов поставщика
        $importer_id_prefix = ImportersFiles::FILES_PREFIX;
        // подключимся к ящику
        $mail_reader = new ImapMailReader('{imap.gmail.com:993/imap/ssl/novalidate-cert}', 'tsurkanovm@gmail.com', 'Wtvr@2000');

        // 1. получим все вложения
        \Yii::info('Начало сохранения файлов почты', 'mail');
        $files = $this->getMailAttachments($mail_reader, $importer_id_prefix);

        if (!$files) {
            // нет файлов в ящиках (не было вложений в письмах)
            \Yii::warning('Вложений не найдено', 'mail');
            return;
        }

        // 2. если в вложениях есть архивы - распакуем их и дополним итоговый массив
        \Yii::info('Запуск распаковки архивов...', 'mail');
        $this->UnpackFiles($files);


        // 3. переименуем, зарегистрируем прайсы и перенесем извлеченные файлы
        // укажем папку куда нужно перенести все извлеченные вложения
        \Yii::info('Запуск перемещения и регистрации прайсов...', 'mail');
        $new_destination = \Yii::getAlias('@mail_upload') . '/';

        $this->registerAndReplaceFiles($files, $new_destination);
    }

    protected function parseFilesByExtension( $ext, $path_arr )
    {
        \Yii::info("Начало загрузки файлов прайсов {$ext}", 'parser');
        foreach ($path_arr as $path_config) {
            foreach ( glob( $path_config['path'] . "/*.{$ext}" ) as $file_path ) {
                $file_name = basename( $file_path, ".{$ext}" );
                \Yii::info( "Обработка файла - $file_path", 'parser' );

                $config = $this->getParsingConfiguration( $file_name, $path_config['mode'], $ext );

                if ($this->parseFile($file_path, $config)) {
                    $temp_file = \Yii::getAlias('@temp_upload') . '/' . $file_name . ".{$ext}";
                    if( file_exists( $temp_file ) )
                        unlink($temp_file);

                    \Yii::info("Загрузка файла - $file_path успешно завершена", 'parser');
                } else {
                    \Yii::error("Загрузка файла - $file_path завершена с ошибкой", 'parser');
                }
                //при любом завершении скрипта файл с очереди автозагрузки нужно удалить
                $auto_file = $path_config['path'] . '/' . $file_name . ".{$ext}";
                if( file_exists( $auto_file ) )
                    unlink($auto_file);
            }
        }
    }


    protected function parseFile($file_path, $configuration)
    {
        // регистрация в лог
        $log_model = new Log();
        $log_model->importer_id = $configuration['importer_id'];
        $log_model->time_start = $this->start_time;
        $log_model->file_name = $file_path;
        $mode = $configuration['mode'];
        unset($configuration['mode']);
        $log_model->record_type = $mode;

        $log_msg = '';
        $has_error = true;

        if (!file_exists($file_path)){
            $log_msg = "$file_path - файл не найден!";
        } else {
            $parser_config = [];
            if (isset($configuration['parser_config'])) {
                $parser_config = $configuration['parser_config'];
            }
            $data = \Yii::$app->multiparser->parse($file_path, $parser_config);
            if (!$data) {
                $log_msg = "Ошибка обработки файла прайса. Парсер вернул пустой массив";
            } else {
                try {
                    $writer = new PriceWriter();
                    $writer->setMode($mode);
                    $writer->setConfiguration($configuration);
                    $writer->setData($data);

                    $writer->writePriceToDB();
                    $has_error = $writer->hasValidationError();
                    $log_msg = strip_tags( $writer->getValidatedMsg() );
                    if ( $has_error ) {
                        \Yii::error($log_msg, 'parser');
                    } else {
                        \Yii::info($log_msg, 'parser');
                    }
                } catch (\Exception $e) {
                    $log_msg = $e->getMessage();
                }
            }
        }
        $log_model->error = (int) $has_error;
        $log_model->log_msg = $log_msg;
        // запишем данные в лог
        $log_model->save();

        return true;
    }

    private function getMailAttachments($mail_reader, $importer_id_prefix = '')
    {
        // получим все внутренние ящики (по ярлыкам)
        $mailboxes = $mail_reader->getListMailboxes();
        // очистим массив в котором в итоге окажутся все файлы вложений, а также распакованные файлы из архивов
        $files = [];
        foreach ($mailboxes as $custom_label) {
            // получим поставщика исходя из маски ярлыка
            $importer_id = ImportersFiles::getIdFromMailBox($mail_reader->getHostname(), $custom_label);

            // читаем письма конкретного ярлыка
            $mail_reader->reOpen($custom_label);
            // создадим сейвер вложений для данного ярлыка (ящика)
            $saver = new MailAttachmentsSaver($mail_reader);
            // если данный ярлык содержит id поставщика, то все вложения нужно промаркировать (в начало файла добавить id поставщика + разделитель $importer_id_prefix)
            if ($importer_id) {
                $saver->setFileNamePrefix($importer_id . $importer_id_prefix);
                //    $importer_id = '';
            }
            // сохраняем вложения
            if ($saver->saveAttachmentsTo(\Yii::getAlias('@temp_upload'), 'UNSEEN')) {
                // закидываем вытащенные файлы в наш итоговый массив
                $files = array_merge($files, $saver->getSavedFilesArr());
            } else {
                // ящик не имеет писем с вложениями
                continue;
            }
        }

        return $files;
    }

    private function UnpackFiles(&$files, $importer_id_prefix = '')
    {
        // если в вложениях встречаются архивы - распакуем
        // иициируем фабрику архиваторов
        $arch_creator = new ArchiveCreator();
        // получим все расширения которые поддерживает фабрика
        $arch_extensions = $arch_creator->getHandleExtension();
        // выбираем только те файлы которые мы можем распаковать
        $arch_files = array_intersect($files, $arch_extensions);
        foreach ($arch_files as $arch_name => $arch_ext) {
            // создаем конкретный архиватор по расширению
            $arch_reader = $arch_creator->create($arch_name, $arch_ext);
            // определим ид поставщика по имени файла
            $importer_id = ImportersFiles::getIdFromFileName($arch_name);
            if ($importer_id) {
                // если файл архива содержит поставщика (на предыдущих этапах мы его туда записали)
                // то нужно все вложенные файлы также промаркировать
                $arch_reader->setFileNamePrefix($importer_id . $importer_id_prefix);
                //$importer_id = '';
            }

            // распаковываем файлы
            $arch_reader->extractTo(\Yii::getAlias('@temp_upload') . '/');
            // убираем файл архива из итогового массива
            unset($files[$arch_name]);
            // удаляем файл архива
            unlink($arch_name);
            // добавляем распакованные файлы к итоговому массиву
            $files = array_merge($files, $arch_reader->getExtractedFiles());
        }

    }

    private function registerAndReplaceFiles(&$files, $new_destination)
    {
        foreach ($files as $name => $ext) {
            // имена файлов для расширения csv нужно поменять,
            // для остальных оставляем оригинальные имена вложений (плюс ид поставщика если письмо от поставщика)
            $file_name = pathinfo($name, PATHINFO_BASENAME);
            if ($ext == 'csv') {
                // определим ид поставщика по имени файла
                $importer_id = ImportersFiles::getIdFromFileName(basename($name));;

                // зарегистрируем прайс
                if ($importer_id) {
                    $files_model = new ImportersFiles();
                    $files_model->importer_id = $importer_id;
                    if ($files_model->save()) {
                        // имя файла переименуем на id записи
                        $file_name = \Yii::$app->db->getLastInsertID() . '.csv';

                    } else {

                        $files_model->throwStringErrorException();
                    }
                }
            }
            if (rename($name, $new_destination . $file_name)) {
                \Yii::info("Вложение {$name} сохранено", 'mail');
            } else {
                new \ErrorException("Нет возможности переписать файл {$name}");
            }
        }
    }

    protected function getParsingConfiguration( $file_name, $mode, $ext ){

        if ($ext === 'xml') {
            $files_model = new ImportersFiles();
            // id поставщика всегда = 1 - Склад
            $files_model->importer_id = 1;
            try {
                $files_model->save();
            } catch (ErrorException  $e) {
                throw $e;
            }
            // получим id только что записанной записи
            $record_id = $files_model->find()
                ->where(['importer_id' => $files_model->importer_id])
                ->orderBy(['id' => SORT_DESC])
                ->one()
                ->id;

            $config = ['record_id' => $record_id,
                'importer_id' => 1,
                'mode' => $mode,
                'parser_config' => [
                    'mode' => 'console']
            ];
        } else {
            $importer_id = ImportersFiles::findOne(['id' => $file_name])->importer_id;
            $current_importer = Importers::findOne(['id' => $importer_id]);
            $keys = $current_importer->keys;
            // если меньше 5 ключей, то чего-то нехватает - обнуляем
            if ( count( $keys ) < 5 ) {
                $keys = NULL;
            }
            $mult_array = $current_importer->multiply;

            // получим настройки ценообразования и передадим их отдельно в конвертер
            $sign = '';
            $multiplier = '';
            extract($mult_array);

            $config = [
                'record_id' => $file_name,
                'importer_id' => $importer_id,
                'mode' => $mode,
                'parser_config' => ['keys' => $keys,
                    'converter_conf' =>
                        ['sign' => $sign,
                            'multiplier' => $multiplier],
                    'mode' => 'console']
            ];
        }
        return $config;
}
}