File::triggerCompleteRow()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 0
1
<?php
2
3
namespace execut\import\models;
4
5
use execut\actions\action\adapter\viewRenderer\DynaGridRow;
6
use execut\crudFields\Behavior;
7
use execut\crudFields\BehaviorStub;
8
use execut\crudFields\fields\Date;
9
use execut\crudFields\fields\Field;
10
use execut\crudFields\fields\HasManyMultipleInput;
11
use execut\crudFields\fields\HasOneSelect2;
12
use execut\crudFields\fields\RadiobuttonGroup;
13
use execut\crudFields\ModelsHelperTrait;
14
use execut\import\components\ToArrayConverter;
15
use execut\crudFields\fields\File as FileField;
16
17
use yii\behaviors\TimestampBehavior;
18
use yii\db\Exception;
19
use yii\db\Expression;
20
use yii\db\mysql\Schema;
21
use yii\helpers\ArrayHelper;
22
use yii\helpers\FileHelper;
23
use yii\web\UploadedFile;
24
25
/**
26
 * This is the model class for table "import_files".
27
 *
28
 * @property integer $id
29
 * @property string $created
30
 * @property string $updated
31
 * @property string $name
32
 * @property resource $content
33
 * @property string $md5
34
 * @property string $import_files_source_id
35
 * @property string $use_id
36
 * @property UploadedFile $contentFile
37
 *
38
 * @property \execut\import\models\FilesSource $importFilesSource
39
 * @property \execut\import\models\User $use
40
 */
41
class File extends base\File implements DynaGridRow
42
{
43
    use ModelsHelperTrait, BehaviorStub;
44
45
    const MODEL_NAME = '{n,plural,=0{Files} =1{File} other{Files}}';
46
    public $eventsCount = null;
47
    protected $rows = null;
48
    public $contentFile = null;
49
    public $preview = null;
50
    public $progress = null;
51
52
    public function setTime_complete_calculated() {
53
    }
54
55
56
    /**
57
     * @inheritdoc
58
     */
59
    public function rules()
60
    {
61
        return $this->getBehavior('fields')->rules();
62
    }
63
64
    public function behaviors()
65
    {
66
        return [
67
            'fields' => [
68
                'class' => Behavior::class,
69
                'module' => 'import',
70
                'fields' => $this->getStandardFields(['visible', 'name'], [
71
                    'contentFile' => [
72
                        'class' => FileField::class,
73
                        'attribute' => 'contentFile',
74
                        'md5Attribute' => 'md5',
75
                        'dataAttribute' => 'content',
76
                        'downloadUrl' => [
77
                            '/import/files/download'
78
                        ],
79
                        'allowedExtensions' => $this->getAllowedExtensions(),
80
                    ],
81
                    'import_setting_id' => [
82
                        'class' => HasOneSelect2::class,
83
                        'required' => true,
84
                        'attribute' => 'import_setting_id',
85
                        'relation' => 'setting',
86
                        'url' => [
87
                            '/import/settings'
88
                        ],
89
                    ],
90
                    'import_files_statuse_id' => [
91
                        'class' => RadiobuttonGroup::class,
92
                        'attribute' => 'import_files_statuse_id',
93
                        'relation' => 'statuse',
94
                        'required' => true,
95
                        'defaultValue' => FilesStatuse::getIdByKey(FilesStatuse::NEW),
96
                        'data' => function () {
97
                            return $this->getAllowedStatusesList();
98
                        },
99
                        'rules' => [
100
                            'validateStatus' => ['import_files_statuse_id', 'in', 'range' => function () {
101
                                return array_keys($this->getAllowedStatusesList());
102
                            }],
103
                        ],
104
//                        'rules' => [
105
//                            'defaultValueOnForm' => [
106
//                                'import_files_statuse_id',
107
//                                'default',
108
//                                'value' => FilesStatuse::getIdByKey(FilesStatuse::NEW),
109
//                                'on' => Field::SCENARIO_FORM,
110
//                            ],
111
//                        ],
112
                    ],
113
                    'import_files_source_id' => [
114
                        'class' => HasOneSelect2::class,
115
                        'attribute' => 'import_files_source_id',
116
                        'required' => true,
117
                        'relation' => 'source',
118
//                        'defaultValue' => FilesStatuse::getIdByKey('new'),
119
                    ],
120
                    'rows_count' => [
121
                        'attribute' => 'rows_count',
122
                        'displayOnly' => true,
123
                    ],
124
                    'rows_errors' => [
125
                        'attribute' => 'rows_errors',
126
                        'displayOnly' => true,
127
                    ],
128
                    'rows_success' => [
129
                        'attribute' => 'rows_success',
130
                        'displayOnly' => true,
131
                    ],
132
                    'time_complete_calculated' => [
133
                        'attribute' => 'time_complete_calculated',
134
                        'displayOnly' => true,
135
                        'scope' => false,
136
                        'column' => [
137
                            'filter' => false,
138
//                            'value' => function () {
139
//                                return $this->getTime_complete_calculated();
140
//                            }
141
                        ],
142
                    ],
143
//                    'eventsCount' => [
144
//                        'attribute' => 'eventsCount',
145
//                        'column' => [
146
//                            'filter' => [
147
//                                '0' => '0',
148
//                                '1' => '>0',
149
//                            ],
150
//                        ],
151
//                        'scope' => function ($q, $model) {
152
//                            $q->byEventsCount($model->eventsCount)
153
//                                ->withEventsCount();
154
//                        },
155
//                        'displayOnly' => true,
156
//                    ],
157
                    'progress' => [
158
                        'attribute' => 'progress',
159
                        'field' => [
160
                            'format' => ['percent', 2],
161
                        ],
162
                        'column' => [
163
                            'format' => ['percent', 2],
164
                        ],
165
                        'displayOnly' => true,
166
                        'rules' => false,
167
                    ],
168
                    'errorsPercent' => [
169
                        'attribute' => 'errorsPercent',
170
                        'field' => [
171
                            'format' => ['percent', 2],
172
                        ],
173
                        'column' => [
174
                            'format' => ['percent', 2],
175
                        ],
176
                        'displayOnly' => true,
177
                        'rules' => false,
178
                    ],
179
                    'start_date' => [
180
                        'class' => Date::class,
181
                        'attribute' => 'start_date',
182
                        'isTime' => true,
183
                        'displayOnly' => true,
184
                    ],
185
                    'end_date' => [
186
                        'class' => Date::class,
187
                        'attribute' => 'end_date',
188
                        'isTime' => true,
189
                        'displayOnly' => true,
190
                    ],
191
                    'logsGrouped' => [
192
                        'class' => HasManyMultipleInput::class,
193
                        'order' => 115,
194
                        'columnRecordsLimit' => 10,
195
                        'attribute' => 'logsGrouped',
196
                        'relation' => 'logsGrouped',
197
                        'isGridForOldRecords' => true,
198
                        'scope' => false,
199
                        'column' => false,
200
                        'with' => false,
201
                        'field' => false,
202
                        'fieldGrid' => new HasManyMultipleInput\Grid\Field([
0 ignored issues
show
Bug introduced by
The type execut\crudFields\fields...ultipleInput\Grid\Field was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
203
                            'responsiveWrap' => false,
204
                            'showPageSummary' => true,
205
                        ]),
206
                    ],
207
                    'logs' => [
208
                        'class' => HasManyMultipleInput::class,
209
                        'order' => 115,
210
                        'columnRecordsLimit' => 10,
211
                        'attribute' => 'logs',
212
                        'relation' => 'logs',
213
                        'with' => false,
214
                        'isGridForOldRecords' => true,
215
                        'scope' => false,
216
                        'column' => false,
217
                        'field' => false,
218
                        'fieldGrid' => new HasManyMultipleInput\Grid\Field([
219
                            'responsiveWrap' => false,
220
                            'showPageSummary' => true,
221
                        ]),
222
                    ],
223
                    'process_id' => [
224
                        'attribute' => 'process_id',
225
                        'displayOnly' => true
226
                    ],
227
                ]),
228
                'plugins' => \yii::$app->getModule('import')->getFilesCrudFieldsPlugins(),
229
            ],
230
            'date' => [
231
                'class' => TimestampBehavior::class,
232
                'createdAtAttribute' => 'created',
233
                'updatedAtAttribute' => 'updated',
234
                'value' => new Expression('NOW()'),
235
            ],
236
        ];
237
    }
238
239
    public function getOldStatuse() {
240
        if ($attributes = $this->getDirtyAttributes(['import_files_statuse_id'])) {
0 ignored issues
show
Unused Code introduced by
The assignment to $attributes is dead and can be removed.
Loading history...
241
            if (empty($this->oldAttributes['import_files_statuse_id'])) {
242
                return;
243
            }
244
245
            $id = $this->oldAttributes['import_files_statuse_id'];
246
        } else {
247
            $id = $this->import_files_statuse_id;
248
        }
249
250
        if ($id) {
251
            return FilesStatuse::findOne($id);
252
        }
253
    }
254
255
    public function getAllowedStatusesList() {
256
        $q = FilesStatuse::find();
257
        if ($this->scenario === 'form') {
258
            if ($statuse = $this->getOldStatuse()) {
259
                $q->isAllowedForKey($statuse->key);
260
            } else {
261
                $q->byKey(FilesStatuse::NEW);
262
            }
263
        }
264
265
        return $q->forSelect();
266
    }
267
268
    public function getTime_complete_calculated() {
269
        $secondsElapse = false;
270
        if (!$this->isLoading()) {
271
            $startTime = strtotime($this->start_date);
272
            $endTime = strtotime($this->end_date);
273
274
            $secondsElapse = $endTime - $startTime;
275
        } else {
276
277
            $startTime = strtotime($this->start_date);
278
            $currentTime = time();
279
280
            $totalRows = ($this->rows_success + $this->rows_errors);
281
            if ($totalRows > 0) {
282
                $secondsElapse = ($currentTime - $startTime) / $totalRows * $this->rows_count;
283
            }
284
285
        }
286
287
        if ($secondsElapse) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $secondsElapse of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
288
            return date('H:i:s', $startTime + $secondsElapse) . ' (' . gmdate("H:i:s", $secondsElapse) . ')';
289
        }
290
    }
291
292
    public function search() {
293
        $dp = $this->getBehavior('fields')->search();
294
        $q = $dp->query;
295
296
        $select = $this->attributes();
297
        unset($select[array_search('content', $select)]);
298
299
        $sort = $dp->sort;
300
//        $sort->attributes['eventsCount'] = [
301
//            'asc' => ['eventsCount' => SORT_ASC],
302
//            'desc' => ['eventsCount' => SORT_DESC],
303
//        ];
304
305
        $sort->attributes['progress'] = [
306
            'asc' => ['progress' => SORT_ASC],
307
            'desc' => ['progress' => SORT_DESC],
308
        ];
309
310
        $sort->attributes['errorsPercent'] = [
311
            'asc' => ['errorsPercent' => SORT_ASC],
312
            'desc' => ['errorsPercent' => SORT_DESC],
313
        ];
314
315
        unset($q->select[array_search('*', $q->select)]);
316
        $q->select = array_merge($q->select, $select);
317
318
        return $dp;
319
    }
320
321
322
    public function beforeValidate()
323
    {
324
        if (!empty($this->contentFile)) {
325
            $this->name = $this->md5 = $this->extension = $this->mime_type = null;
326
        }
327
328
        return parent::beforeValidate(); // TODO: Change the autogenerated stub
329
    }
330
331
    public function isLoading() {
332
        return $this->import_files_statuse_id === FilesStatuse::getIdByKey(FilesStatuse::LOADING);
333
    }
334
335
    public function isError() {
336
        return $this->import_files_statuse_id === FilesStatuse::getIdByKey(FilesStatuse::ERROR);
337
    }
338
339
    public function isComplete() {
340
        return $this->import_files_statuse_id === FilesStatuse::getIdByKey(FilesStatuse::LOADED);
341
    }
342
343
344
    public function getAllowedExtensions() {
345
        return [
346
            'rar',
347
            'zip',
348
            'xls',
349
            'xlt',
350
            'xlsx',
351
            'xlsm',
352
            'xltx',
353
            'xltm',
354
            'ods',
355
            'ots',
356
            'slk',
357
            'xml',
358
            'csv',
359
            'txt',
360
            'gnumeric',
361
        ];
362
    }
363
364
    /**
365
     * @return \execut\import\models\queries\File
366
     */
367
    public static function find()
368
    {
369
        $q = new \execut\import\models\queries\File(__CLASS__);
370
        return $q->withErrorsPercent()->withProgress();
371
    }
372
373
    public function isDeleting() {
374
        return File::find()->isDeleting()->byId($this->id)->count() > 0;
375
    }
376
377
    public function isDelete() {
378
        return File::find()->isDelete()->byId($this->id)->count() > 0;
379
    }
380
381
    public function isStop() {
382
        return File::find()->isStop()->byId($this->id)->count() > 0;
383
    }
384
385
    public function isCancelLoading() {
386
        return File::find()->isCancelLoading()->byId($this->id)->count() > 0;
387
    }
388
389
    public function triggerStop() {
390
        $this->setStatus(FilesStatuse::STOPED);
391
        $this->save(false);
392
    }
393
394
    public function triggerDeleting() {
395
        $this->setStatus(FilesStatuse::DELETING);
396
        $this->save();
397
    }
398
399
    public function triggerLoading() {
400
        $this->deleteLogs();
401
        $this->process_id = getmypid();
0 ignored issues
show
Bug Best Practice introduced by
The property process_id does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
402
        $this->start_date = date('Y-m-d H:i:s');
403
        $this->rows_errors = 0;
404
        $this->rows_success = 0;
405
        $this->setStatus(FilesStatuse::LOADING);
406
        $this->save();
407
    }
408
409
    public function getCalculatedSetsCount() {
410
        return count($this->getSettings()) * $this->getCalculatedRowsCount();
411
    }
412
413
    public function getErrorsPercent() {
414
        $percent = 0;
415
        if (($this->rows_success || $this->rows_errors) && ($this->rows_success + $this->rows_errors) > 0) {
416
            $percent = $this->rows_errors / ($this->rows_success + $this->rows_errors);
417
        }
418
419
        return $percent;
420
    }
421
422
    public function triggerLoaded() {
423
        $this->end_date = date('Y-m-d H:i:s');
424
        $this->setStatus(FilesStatuse::LOADED);
425
        $this->save(false, ['end_date', 'import_files_statuse_id']);
426
    }
427
428
    public function triggerErrorRow() {
429
        $this->rows_errors += count($this->getSettings());
430
        $this->triggerCompleteRow();
431
    }
432
433
    public function triggerException() {
434
        $this->end_date = date('Y-m-d H:i:s');
435
        $this->setStatus(FilesStatuse::ERROR);
436
        $this->save();
437
    }
438
439
    public function triggerSuccessRow() {
440
        $this->rows_success++;
441
        $this->triggerCompleteRow();
442
    }
443
444
    public function scenarios()
445
    {
446
        return array_merge(parent::scenarios(), [
447
            'import' => [
448
                'import_files_statuse_id',
449
                'start_date',
450
                'rows_count',
451
                'rows_errors',
452
                'rows_success',
453
            ],
454
        ]);
455
    }
456
457
    protected $currentStep = 0;
458
    protected $saveStep = 10;
459
    public function triggerCompleteRow() {
460
        if ($this->currentStep === $this->saveStep || $this->completeRows == $this->calculatedRowsCount) {
0 ignored issues
show
Bug Best Practice introduced by
The property completeRows does not exist on execut\import\models\File. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property calculatedRowsCount does not exist on execut\import\models\File. Since you implemented __get, consider adding a @property annotation.
Loading history...
461
            $this->currentStep = 0;
462
            $this->save(false, ['rows_errors', 'rows_success']);
463
        } else {
464
            $this->currentStep++;
465
        }
466
    }
467
468
    public function getCompleteRows() {
469
        return $this->rows_errors + $this->rows_success;
470
    }
471
472
    public function getCalculatedRowsCount() {
473
        return count($this->getRows());
474
    }
475
476
    public function getRows() {
477
        if ($this->rows !== null) {
478
            return $this->rows;
479
        }
480
481
        $fileHandler = $this->content;
482
        $isUnlink = false;
483
        if (self::getDb()->schema instanceof Schema) {
484
            $file = tempnam(sys_get_temp_dir(), 'import_');
485
            file_put_contents($file, $fileHandler);
486
            $fileHandler = $file;
487
            $isUnlink = true;
488
        }
489
490
        $setting = $this->setting;
0 ignored issues
show
Bug Best Practice introduced by
The property setting does not exist on execut\import\models\File. Since you implemented __get, consider adding a @property annotation.
Loading history...
491
        $mimeType = $this->detectMimeType();
492
        $converter = new ToArrayConverter([
493
            'file' => $fileHandler,
494
            'trim' => '\'',
495
            'encoding' => $setting->filesEncoding->key,
496
            'mimeType' => $mimeType,
497
        ]);
498
        if (!empty($setting->csv_delimiter)) {
499
            $converter->delimiter = $setting->csv_delimiter;
500
        }
501
502
        if (!empty($setting->csv_enclosure)) {
503
            $converter->enclosure = $setting->csv_enclosure;
504
        }
505
506
        $data = $converter->convert();
507
        $startFrom = $setting->ignored_lines;
508
509
        $data = array_splice($data, $startFrom);
510
        if ($isUnlink) {
511
            unlink($file);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $file does not seem to be defined for all execution paths leading up to this point.
Loading history...
512
        }
513
514
        return $this->rows = $data;
515
    }
516
517
    protected function detectMimeType() {
518
        if ($this->setting->is_check_mime_type) {
0 ignored issues
show
Bug Best Practice introduced by
The property setting does not exist on execut\import\models\File. Since you implemented __get, consider adding a @property annotation.
Loading history...
519
            return;
520
        }
521
522
        $mimeType = FileHelper::getMimeTypeByExtension($this->name);
523
524
        return $mimeType;
525
    }
526
527
    public function isCheckExtension() {
528
        if (!$this->setting->is_check_mime_type) {
0 ignored issues
show
Bug Best Practice introduced by
The property setting does not exist on execut\import\models\File. Since you implemented __get, consider adding a @property annotation.
Loading history...
529
            return true;
530
        }
531
532
        if (empty($this->mime_type)) {
533
            return false;
534
        }
535
536
        $extensionsByMimeType = FileHelper::getExtensionsByMimeType($this->mime_type);
537
538
        if (!in_array($this->extension, $extensionsByMimeType, true)) {
539
            return false;
540
        }
541
542
        return true;
543
    }
544
545
    /**
546
     * @param $attributes
547
     */
548
    public function logError($attributes)
549
    {
550
        $attributes['import_file_id'] = $this->id;
551
        $log = new Log($attributes);
552
        if (!$log->save()) {
553
//            var_dump($attributes);
554
//            exit;
555
        }
556
    }
557
558
    public function deleteLogs() {
559
        for ($tryCount = 0; $tryCount < 4; $tryCount++) {
560
            try {
561
                Log::deleteAll(['import_file_id' => $this->id]);
562
563
                return true;
564
            } catch (Exception $e) {
565
                if (strpos($e->getMessage(), 'Deadlock detected') !== false && $tryCount == 3) {
566
                    throw $e;
567
                }
568
569
                sleep(10);
570
            }
571
        }
572
    }
573
574
    public function checkWhatSheetsIsNotEmpty() {
575
        if ($this->setting) {
0 ignored issues
show
Bug Best Practice introduced by
The property setting does not exist on execut\import\models\File. Since you implemented __get, consider adding a @property annotation.
Loading history...
576
            if (empty($this->setting->settingsSheets)) {
577
                $this->addError('import_setting_id', 'You must add at least one sheet to selected setting');
578
            }
579
        }
580
    }
581
582
    public function beforeDelete()
583
    {
584
        $this->deleteLogs();
585
        return parent::beforeDelete(); // TODO: Change the autogenerated stub
586
    }
587
588
    /**
589
     * @param $key
590
     */
591
    protected function setStatus($key)
592
    {
593
        $this->import_files_statuse_id = FilesStatuse::getIdByKey($key);
594
    }
595
596
    public function getSettings() {
597
        return $this->setting->settingsSheets[0]->getSettings();
0 ignored issues
show
Bug Best Practice introduced by
The property setting does not exist on execut\import\models\File. Since you implemented __get, consider adding a @property annotation.
Loading history...
598
    }
599
600
    public function getLogsGrouped()
601
    {
602
        $result = parent::getLogs(); // TODO: Change the autogenerated stub
603
        $result->modelClass = LogGrouped::class;
604
605
        return $result->select([
606
            'message',
607
            'category',
608
            'logsCount' => new Expression('count(category)'),
609
            ])->groupBy([
610
                'message',
611
                'category',
612
            ])->orderBy('logsCount DESC');
613
    }
614
615
    public function getLogs()
616
    {
617
        $result = parent::getLogs(); // TODO: Change the autogenerated stub
618
        $result->modelClass = Log::class;
619
620
        return $result;
621
    }
622
623
    public function __toString()
624
    {
625
        return '#' . $this->id . ' ' . $this->name;
626
    }
627
}
628