AutoImport::importFile()   A
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 31
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 20
c 2
b 0
f 0
dl 0
loc 31
rs 9.2888
cc 5
nc 6
nop 1
1
<?php
2
declare(strict_types=1);
3
/**
4
 * AutoImport.php
5
 * Copyright (c) 2020 [email protected]
6
 *
7
 * This file is part of the Firefly III CSV importer
8
 * (https://github.com/firefly-iii/csv-importer).
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License as
12
 * published by the Free Software Foundation, either version 3 of the
13
 * License, or (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License
21
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22
 */
23
24
namespace App\Console\Commands;
25
26
use App\Console\HaveAccess;
27
use App\Console\StartImport;
28
use App\Console\VerifyJSON;
29
use App\Exceptions\ImportException;
30
use Illuminate\Console\Command;
31
use JsonException;
32
use Log;
33
34
/**
35
 * Class AutoImport
36
 */
37
class AutoImport extends Command
38
{
39
    use HaveAccess, VerifyJSON, StartImport;
40
41
    /** @var array */
42
    private const IGNORE = ['.', '..'];
43
    /**
44
     * The console command description.
45
     *
46
     * @var string
47
     */
48
    protected $description = 'Will automatically import from the given directory and use the JSON and CSV files found.';
49
    /**
50
     * The name and signature of the console command.
51
     *
52
     * @var string
53
     */
54
    protected $signature = 'csv:auto-import {directory : The directory from which to import automatically.}';
55
    /** @var string */
56
    private $directory = './';
57
58
    /**
59
     * Execute the console command.
60
     *
61
     * @return int
62
     */
63
    public function handle(): int
64
    {
65
        $access = $this->haveAccess();
66
        if (false === $access) {
67
            $this->error('Could not connect to your local Firefly III instance.');
68
69
            return 1;
70
        }
71
72
        $this->directory = (string) ($this->argument('directory') ?? './');
73
        $this->line(sprintf('Going to automatically import everything found in %s', $this->directory));
74
75
        $files = $this->getFiles();
76
        if (0 === count($files)) {
77
            $this->info(sprintf('There are no files in directory %s', $this->directory));
78
            $this->info('To learn more about this process, read the docs:');
79
            $this->info('https://firefly-iii.gitbook.io/firefly-iii-csv-importer/installing-and-running/docker');
80
81
            return 1;
82
        }
83
        $this->line(sprintf('Found %d CSV + JSON file sets in %s', count($files), $this->directory));
84
        try {
85
            $this->importFiles($files);
86
        } catch (ImportException $e) {
87
            Log::error($e->getMessage());
88
            $this->error(sprintf('Import exception (see the logs): %s', $e->getMessage()));
89
        }
90
91
        return 0;
92
    }
93
94
    /**
95
     * @param string $file
96
     *
97
     * @return string
98
     */
99
    private function getExtension(string $file): string
100
    {
101
        $parts = explode('.', $file);
102
        if (1 === count($parts)) {
103
            return '';
104
        }
105
106
        return $parts[count($parts) - 1];
107
    }
108
109
    /**
110
     * @return array
111
     */
112
    private function getFiles(): array
113
    {
114
        if (null === $this->directory || '' === $this->directory) {
115
            $this->error(sprintf('Directory "%s" is empty or invalid.', $this->directory));
116
117
            return [];
118
        }
119
        $array = scandir($this->directory);
120
        if (!is_array($array)) {
121
            $this->error(sprintf('Directory "%s" is empty or invalid.', $this->directory));
122
123
            return [];
124
        }
125
        $files  = array_diff($array, self::IGNORE);
126
        $return = [];
127
        foreach ($files as $file) {
128
            if ('csv' === $this->getExtension($file) && $this->hasJsonConfiguration($file)) {
129
                $return[] = $file;
130
            }
131
        }
132
133
        return $return;
134
    }
135
136
    /**
137
     * @param string $file
138
     *
139
     * @return bool
140
     */
141
    private function hasJsonConfiguration(string $file): bool
142
    {
143
        $short    = substr($file, 0, -4);
144
        $jsonFile = sprintf('%s.json', $short);
145
        $fullJson = sprintf('%s/%s', $this->directory, $jsonFile);
146
        if (!file_exists($fullJson)) {
147
            $this->warn(sprintf('Can\'t find JSON file "%s" expected to go with CSV file "%s". CSV file will be ignored.', $fullJson, $file));
148
149
            return false;
150
        }
151
152
        return true;
153
    }
154
155
    /**
156
     * @param string $file
157
     *
158
     * @throws ImportException
159
     */
160
    private function importFile(string $file): void
161
    {
162
        $csvFile  = sprintf('%s/%s', $this->directory, $file);
163
        $jsonFile = sprintf('%s/%s.json', $this->directory, substr($file, 0, -4));
164
165
        // do JSON check
166
        $jsonResult = $this->verifyJSON($jsonFile);
167
        if (false === $jsonResult) {
168
            $message = sprintf('The importer can\'t import %s: could not decode the JSON in config file %s.', $csvFile, $jsonFile);
169
            $this->error($message);
170
171
            return;
172
        }
173
        try {
174
            $configuration = json_decode(file_get_contents($jsonFile), true, 512, JSON_THROW_ON_ERROR);
175
        } catch (JsonException $e) {
176
            Log::error($e->getMessage());
177
            throw new ImportException(sprintf('Bad JSON in configuration file: %s', $e->getMessage()));
178
        }
179
        $this->line(sprintf('Going to import from file %s using configuration %s.', $csvFile, $jsonFile));
180
        // create importer
181
        $csv    = file_get_contents($csvFile);
182
        $result = $this->startImport($csv, $configuration);
183
        if (0 === $result) {
184
            $this->line('Import complete.');
185
        }
186
        if (0 !== $result) {
187
            $this->warn('The import finished with errors.');
188
        }
189
190
        $this->line(sprintf('Done importing from file %s using configuration %s.', $csvFile, $jsonFile));
191
    }
192
193
    /**
194
     * @param array $files
195
     *
196
     * @throws ImportException
197
     */
198
    private function importFiles(array $files): void
199
    {
200
        /** @var string $file */
201
        foreach ($files as $file) {
202
            $this->importFile($file);
203
        }
204
    }
205
}
206