diff --git a/backend/.gitignore b/backend/.gitignore index abe251a..da2fd6f 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1 +1,2 @@ -/temp \ No newline at end of file +/temp +/uploads \ No newline at end of file diff --git a/backend/components/parsers/CustomCsvParser.php b/backend/components/parsers/CustomCsvParser.php index 407b20a..dd28725 100644 --- a/backend/components/parsers/CustomCsvParser.php +++ b/backend/components/parsers/CustomCsvParser.php @@ -11,7 +11,7 @@ namespace backend\components\parsers; class CustomCsvParser extends \yii\multiparser\CsvParser { - //public $last_line = 10; + public $last_line = 10; //public $hasHeaderRow = true; // public $keys = ['first','second', 'third', 'forth', 'fifth']; public function setupConverter() diff --git a/backend/controllers/CheckPriceController.php b/backend/controllers/CheckPriceController.php new file mode 100644 index 0000000..4931632 --- /dev/null +++ b/backend/controllers/CheckPriceController.php @@ -0,0 +1,98 @@ + [ + 'class' => AccessControl::className(), + 'rules' => [ + [ + 'actions' => ['index', 'view'], + 'allow' => true, + 'roles' => ['@'], + ], + ], + ], +// 'verbs' => [ +// 'class' => VerbFilter::className(), +// 'actions' => [ +// 'logout' => ['post'], +// ], +// ], + ]; + } + + /** + * @inheritdoc + */ + public function actions() + { + return [ + 'error' => [ + 'class' => 'yii\web\ErrorAction', + ], + ]; + } + + + public function actionIndex() + { + + if(Yii::$app->request->isAjax){ + CustomVarDamp::dumpAndDie(1); + } + + //$query = (new Query())->select('*')->from('{{%importer_files}}')->where(['not', ['time_end' => null]])->orderBy(['upload_time' => SORT_DESC]); + $query = Importer::find()->where(['active' => true])->orderBy(['price_date_update' => SORT_DESC]); + + $provider = new ActiveDataProvider([ + 'query' => $query, + 'pagination' => [ + 'pageSize' => 10, + ], + ]); + return $this->render('index', + [ + 'dataProvider' => $provider, + ]); + } + + + public function actionView ($id) + { + $query = Details::find()->where(['IMPORT_ID' => $id])->orderBy(['timestamp' => SORT_DESC]); + + $provider = new ActiveDataProvider([ + 'query' => $query, + 'pagination' => [ + 'pageSize' => 16, + ], + ]); + return $this->render('view', + ['dataProvider' => $provider]); + } +} diff --git a/backend/controllers/ParserController.php b/backend/controllers/ParserController.php index 9d0ddf8..9797d53 100644 --- a/backend/controllers/ParserController.php +++ b/backend/controllers/ParserController.php @@ -36,7 +36,7 @@ class ParserController extends BaseController 'class' => AccessControl::className(), 'rules' => [ [ - 'actions' => ['index', 'results', 'write','check_price'], + 'actions' => ['index', 'results', 'write', 'check_price'], 'allow' => true, 'roles' => ['@'], ], @@ -64,24 +64,57 @@ class ParserController extends BaseController } - public function actionIndex() + public function actionIndex($mode = 0) { $model = new UploadFileParsingForm(); + // установим режим, 0 - ручная загрузка, 1 - автозагрузка + $model->mode = $mode; return $this->render('index', ['model' => $model]); } - public function actionResults() + public function actionResults($mode = 0) { - $model = new UploadFileParsingForm(); + + $model = new UploadFileParsingForm(['mode' => $mode]); $data = []; if ($model->load(Yii::$app->request->post())) { $model->file = UploadedFile::getInstance($model, 'file'); - // первый проход - валидируем, сохраняем файл, ложим в кеш отпарсенные данные и параметры модели (потом при записи в базу данных они пригодятся) + // первый проход - валидируем, сохраняем файл, ложим в кеш (для ручной загрузки) отпарсенные данные и параметры модели (потом при записи в базу данных они пригодятся) if ($model->validate()) { - $model->file_path = Yii::getAlias('@webroot') . '/uploads/' . $model->file->baseName . '.' . $model->file->extension; + // запишем дату загрузки файла в таблицу файлов поставщика (ImportersFiles) + $files_model = new ImporterFiles(); + // id поставщика получим из конфигурации + $files_model->load(['ImporterFiles' => $model->toArray()]); + try { + $files_model->save(); + } catch (ErrorException $e) { + CustomVarDamp::dump($e->getMessage()); + } + // получим id только что записанной записи - его запишем в название файла + $id = $files_model->find() + ->where(['importer_id' => $files_model->importer_id]) + ->orderBy(['id' => SORT_DESC]) + ->one() + ->id; + + $file_name = $id . '.' . $model->file->extension; + + if ($model->mode) { + $model->file_path = Yii::getAlias('@auto_upload') . '/' . $file_name; + } else { + $model->file_path = Yii::getAlias('@manual_upload') . '/' . $file_name; + } $model->file->saveAs($model->file_path); + + // для авто загрузки, обработка завершена + if ($model->mode) { + $model->success = true; + return $this->render('index', ['model' => $model]); + } + + // === ручная загрузка =========== //запускаем парсинг $data = $model->readFile(); // сохраняем в кеш отпарсенные даные @@ -90,6 +123,14 @@ class ParserController extends BaseController Yii::$app->getCache()->set('parser_configuration', serialize($model)); + } else { + // не прошла валидация форма загрузки файлов + //@todo - отправка на страницу ошибок + $errors_arr = $model->getErrors(); + foreach ($errors_arr as $error) { + CustomVarDamp::dump(array_values($error)); + } + die; } // листаем пагинатором, или повторно вызываем - считываем из кеша отпрасенные данные } else if (Yii::$app->getCache()->get('parser_data')) { @@ -148,69 +189,63 @@ class ParserController extends BaseController // 1. запишем дату старта в таблицу файлов поставщика (ImportersFiles) $files_model = new ImporterFiles(); - // id поставщика получим из конфигурации + // id поставщика и id загруженного файла получим из конфигурации $files_model->load(['ImporterFiles' => $configuration->toArray()]); - if ($files_model->validate()) { + + $update_date = date('Y-m-d H:i:s'); + $files_model->time_start = $update_date; + // запишем дату начала загрузки + if (!$files_model->save()) { + CustomVarDamp::dumpAndDie($files_model->getErrors()); + } + + + // 2. запишем полученные данные в таблицу товаров (Details) + $details_model = new Details(); + // проверим все ли обязательные колонки были указаны пользователем + $details_model->load(['Details' => $data[0]]); + if ($details_model->validate()) { + // дополним данные значением импортера и даты обновления цены + $data = \Yii::$app->multiparser->addColumns($data, ['IMPORT_ID' => $configuration['importer_id'], 'timestamp' => $update_date]); + try { - $files_model->save(); - $update_date = $files_model->find() - ->where(['importer_id' => $files_model->importer_id]) - ->orderBy(['id' => SORT_DESC]) - ->one() - ->upload_time; - $update_date = date('Y-m-d H:i:s',strtotime($update_date)); - // CustomVarDamp::dumpAndDie(strtotime($update_date)); - } catch (ErrorException $e) { - CustomVarDamp::dump($e->getMessage()); - } + //@todo add transaction + // попытаемся вставить данные в БД с апдейтом по ключам + $details_model->ManualInsert($data); + // 3. зафиксируем дату конца загрузки в файлах поставщика - // 2. запишем полученные данные в таблицу товаров (Details) - $details_model = new Details(); - // проверим все ли обязательные колонки были указаны пользователем - $details_model->load(['Details' => $data[0]]); - if ($details_model->validate()) { - // дополним данные значением импортера и даты обновления цены - $data = \Yii::$app->multiparser->addColumns( $data, ['IMPORT_ID' => $configuration['importer_id'],'timestamp' => $update_date] ); - - try { - //@todo add transaction - // попытаемся вставить данные в БД с апдейтом по ключам - $details_model->ManualInsert($data); - - // 3. зафиксируем дату начала и конца загрузки в файлах поставщика (для ручной загрузки начало приравниваем time_start и update_date) - $files_model->time_start = $update_date; - $files_model->time_end = date('Y-m-d H:i:s'); - // CustomVarDamp::dumpAndDie($files_model); - $files_model->save(); - - // 4. зафиксируем дату загрузки в таблице поставщиков - $imp_model = Importer::findOne( $configuration['importer_id'] ); - $imp_model->price_date_update = $update_date; - - if (!$imp_model->save()) { - CustomVarDamp::dumpAndDie( $imp_model->getErrors() ); - } - $configuration['success'] = true; - // все прошло успешно - очищаем кеш - Yii::$app->getCache()->delete('parser_data'); - Yii::$app->getCache()->delete('parser_configuration'); - // @todo - Delete the file - - // все успешно - возвращаемся в начало - return $this->render('index', ['model' => $configuration]); - } catch (ErrorException $e) { - CustomVarDamp::dump($e->getMessage()); + $files_model->time_end = date('Y-m-d H:i:s'); + // CustomVarDamp::dumpAndDie($files_model); + if (!$files_model->save()) { + CustomVarDamp::dumpAndDie($files_model->getErrors()); } - } - if ($details_model->hasErrors()) { - $errors_arr = $details_model->getErrors(); - foreach ($errors_arr as $error) { - CustomVarDamp::dump(array_values($error)); + + // 4. зафиксируем дату загрузки в таблице поставщиков + $imp_model = Importer::findOne($configuration['importer_id']); + $imp_model->price_date_update = $update_date; + + if (!$imp_model->save()) { + CustomVarDamp::dumpAndDie($imp_model->getErrors()); } + $configuration['success'] = true; + // все прошло успешно - очищаем кеш + Yii::$app->getCache()->delete('parser_data'); + Yii::$app->getCache()->delete('parser_configuration'); - } + unlink($configuration['file_path']); + return $this->render('index', ['model' => $configuration]); + + } catch (ErrorException $e) { + CustomVarDamp::dump($e->getMessage()); + } + } + if ($details_model->hasErrors()) { + $errors_arr = $details_model->getErrors(); + foreach ($errors_arr as $error) { + CustomVarDamp::dump(array_values($error)); + } } @@ -219,19 +254,19 @@ class ParserController extends BaseController } -// public function actionCheck_price () -// { -// //$query = (new Query())->select('*')->from('{{%importer_files}}')->where(['not', ['time_end' => null]])->orderBy(['upload_time' => SORT_DESC]); -// $query = Importer::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 actionAutoUpload() + { + //$query = (new Query())->select('*')->from('{{%importer_files}}')->where(['not', ['time_end' => null]])->orderBy(['upload_time' => SORT_DESC]); + $query = Importer::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]); + } } diff --git a/backend/models/Details.php b/backend/models/Details.php index e172845..38c3e07 100644 --- a/backend/models/Details.php +++ b/backend/models/Details.php @@ -70,6 +70,7 @@ class Details extends BaseActiveRecord public function ManualInsert ($data) { + // \common\components\CustomVarDamp::dumpAndDie($data); $table_name = self::tableName(); $keys_arr = array_keys( $data[0] ); // найдем те поля которые не являются ключами. Их нужно будет при дубляже апдейтить diff --git a/backend/models/UploadFileParsingForm.php b/backend/models/UploadFileParsingForm.php index 346a180..150d96b 100644 --- a/backend/models/UploadFileParsingForm.php +++ b/backend/models/UploadFileParsingForm.php @@ -25,10 +25,22 @@ class UploadFileParsingForm extends Model // служебные атрибуты public $file_path; public $success; + public $mode; //0 - режим ручной загрузки, 1 - режим автозагрузки /** * @return array the validation rules. */ + public function __construct($config = []) + { + parent::__construct($config); + if ( $this->mode ) { + // автозагрузка, проставим сценарий + $this->scenario = 'auto'; + } + + } + + public function rules() { return [ @@ -38,8 +50,9 @@ class UploadFileParsingForm extends Model [['file'], 'file'],// 'extensions' => ['csv', 'xml'] ], // 'wrongMimeType' => 'Указан неподдерживаемый тип файла. Можно выбирать csv, xml файлы.' ], ['importer_id', 'integer','max' => 999999, 'min' => 0 ], - [['action','delete_prefix', 'delete_price', 'success'], 'boolean'], + [['action','delete_prefix', 'delete_price', 'success'], 'boolean', 'except' => 'auto' ], // только для ручной загрузки ['delimiter', 'string', 'max' => 1], + ['mode', 'safe'], ['delimiter', 'default', 'value' => ';'], [ 'success', 'default', 'value' => false] @@ -65,4 +78,21 @@ class UploadFileParsingForm extends Model return $data; } + public function fields() + { + return [ + + 'importer_id', + 'delimiter', + 'delete_price', + 'delete_prefix', + 'file_path', + // id записи таблицы ImportersFiles, получаем из имени загруженного файла + 'id' => function () { + return $this->file->getBaseName(); + }, + ]; + } + + } \ No newline at end of file diff --git a/backend/views/check-price/index.php b/backend/views/check-price/index.php new file mode 100644 index 0000000..e34f765 --- /dev/null +++ b/backend/views/check-price/index.php @@ -0,0 +1,58 @@ +title = 'Проверка прайсов'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+ +

title) ?>

+ + + $dataProvider, + 'columns' => [['class' => SerialColumn::className()], + [ + 'class' => ActionColumn::className(), + 'template'=>'{view}', + 'contentOptions' => function ($model, $key, $index, $column){ + return ['data' => ['id' => $model->id, 'date' => $model->price_date_update]]; + } + ], + [ + 'label' =>'Поставщик', + 'value' => function ($data) { + return '№ ' .$data->id . ' ' . $data->name; + }, + ], + ['label' =>'Дата обновления', + 'attribute' => 'price_date_update' ], + ['label' => 'Кол-во дней', + 'value' => function ($data) { + $date1 = new DateTime("now"); + $date2 = new DateTime( $data->price_date_update ); + $quo_days = $date2->diff($date1)->format('%R%a'); + // уберем первый символ - там знак "+" + $quo_days = substr( $quo_days, 1, strlen($quo_days) ); + $quo_days = (int) $quo_days; + + if($quo_days > 15) + $quo_days = '>15'; + + return $quo_days; + } + ], + ]] );?> + + + + +
\ No newline at end of file diff --git a/backend/views/check-price/view.php b/backend/views/check-price/view.php new file mode 100644 index 0000000..f6d9ff0 --- /dev/null +++ b/backend/views/check-price/view.php @@ -0,0 +1,32 @@ +title = 'Проверка прайсов'; +$this->params['breadcrumbs'][] = $this->title; + +?> +
+ +

title) ?>

+ + $dataProvider, + + ] ); + + + ?> + + + +
+ \ No newline at end of file diff --git a/backend/views/layouts/column.php b/backend/views/layouts/column.php index eed7fc0..930e164 100644 --- a/backend/views/layouts/column.php +++ b/backend/views/layouts/column.php @@ -283,8 +283,9 @@ $this->beginContent('@app/views/layouts/main.php'); 'options' => ['class' => 'sidebar-menu'], 'items' => [ ['label' => "Загрузка файлов", 'url' => ['#'], 'items' => [ + ['label' => 'Загрузить файл на сервер', 'url' => ['parser/index', 'mode' => 1]], ['label' => 'Ручная загрузка', 'url' => ['parser/index']], - ['label' => 'Проверка прайс файлов', 'url' => ['check_price/index']], + ['label' => 'Проверка прайс файлов', 'url' => ['check-price/index']], ], ], ['label' => 'Управление ролями', 'url' => ['#'], 'items' => [ diff --git a/backend/views/parser/index.php b/backend/views/parser/index.php index 6eb914b..a7c2646 100644 --- a/backend/views/parser/index.php +++ b/backend/views/parser/index.php @@ -3,16 +3,24 @@ use yii\widgets\ActiveForm; use yii\helpers\Html; use backend\models\Importer; use yii\helpers\ArrayHelper; +if ( $model->mode ) { + // авто загрузка + $mode = 1; + $button_label = 'Загрузить'; +} else { + // ручная загрузка + $mode = 0; + $button_label = 'Прочитать'; +} ?>
- ['enctype' => 'multipart/form-data',],'action'=>['parser/results']]); + ['enctype' => 'multipart/form-data',],'action'=>['parser/results', 'mode' => $mode]]); if (!$model->action) { $model->action = 1; } if ($model->success) { // вернулись после успешной загрузки данного файла - //echo "

Файл успешно загружен

"; echo Html::tag('h3', 'Файл успешно загружен',['class'=>'bg-success']); } ?> @@ -20,16 +28,25 @@ use yii\helpers\ArrayHelper; field($model, 'importer_id')->dropDownList(ArrayHelper::map( Importer::find()->all(), 'id','name' )); ?> - field($model, 'delete_price')->checkbox(['label' => 'Загрузить с удалением старого прайса']) ?> + + field($model, 'delete_price')->checkbox(['label' => 'Загрузить с удалением старого прайса']); + } + ?> + field($model, 'file')->fileInput()->label(false) ?> - field($model, 'action')->radioList([1 => 'Стандартная обработка', 0 => 'С разделителем'])->label(false) ?> - field($model, 'delimiter', ['inputOptions' => ['value' => ';']]) ?> - field($model, 'delete_prefix')->checkbox(['label' => 'Удалять префикс']) ?> + field($model, 'action')->radioList([1 => 'Стандартная обработка', 0 => 'С разделителем'])->label(false); + echo $form->field($model, 'delimiter', ['inputOptions' => ['value' => ';']]); + + echo $form->field($model, 'delete_prefix')->checkbox(['label' => 'Удалять префикс']); + } + ?>
- 'btn btn-primary']) ?> + 'btn btn-primary']) ?>
diff --git a/common/config/bootstrap.php b/common/config/bootstrap.php index ecc13e5..df33c86 100644 --- a/common/config/bootstrap.php +++ b/common/config/bootstrap.php @@ -3,3 +3,5 @@ Yii::setAlias('common', dirname(__DIR__)); Yii::setAlias('frontend', dirname(dirname(__DIR__)) . '/frontend'); Yii::setAlias('backend', dirname(dirname(__DIR__)) . '/backend'); Yii::setAlias('console', dirname(dirname(__DIR__)) . '/console'); +Yii::setAlias('auto_upload', dirname(dirname(__DIR__)) . '/backend/uploads/auto'); +Yii::setAlias('manual_upload', dirname(dirname(__DIR__)) . '/backend/uploads/manual'); -- libgit2 0.21.4