. */ declare(strict_types=1); namespace FireflyIII\Console\Commands; use Artisan; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Import\Logging\CommandHandler; use FireflyIII\Import\Routine\ImportRoutine; use FireflyIII\Import\Routine\RoutineInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Console\Command; use Illuminate\Support\MessageBag; use Log; use Monolog\Formatter\LineFormatter; use Preferences; /** * Class CreateImport. */ class CreateImport extends Command { use VerifiesAccessToken; /** * The console command description. * * @var string */ protected $description = 'Use this command to create a new import. Your user ID can be found on the /profile page.'; /** * The name and signature of the console command. * * @var string */ protected $signature = 'firefly:create-import {file : The file to import.} {configuration : The configuration file to use for the import.} {--type=csv : The file type of the import.} {--user= : The user ID that the import should import for.} {--token= : The user\'s access token.} {--start : Starts the job immediately.}'; /** * Create a new command instance. */ public function __construct() { parent::__construct(); } /** * Run the command. * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot be helped * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly. * @throws FireflyException */ public function handle() { if (!$this->verifyAccessToken()) { $this->error('Invalid access token.'); return; } /** @var UserRepositoryInterface $userRepository */ $userRepository = app(UserRepositoryInterface::class); $file = $this->argument('file'); $configuration = $this->argument('configuration'); $user = $userRepository->find(intval($this->option('user'))); $cwd = getcwd(); $type = strtolower($this->option('type')); if (!$this->validArguments()) { return; } $configurationData = json_decode(file_get_contents($configuration), true); if (null === $configurationData) { $this->error(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd)); return; } $this->line(sprintf('Going to create a job to import file: %s', $file)); $this->line(sprintf('Using configuration file: %s', $configuration)); $this->line(sprintf('Import into user: #%d (%s)', $user->id, $user->email)); $this->line(sprintf('Type of import: %s', $type)); /** @var ImportJobRepositoryInterface $jobRepository */ $jobRepository = app(ImportJobRepositoryInterface::class); $jobRepository->setUser($user); $job = $jobRepository->create($type); $this->line(sprintf('Created job "%s"', $job->key)); Artisan::call('firefly:encrypt-file', ['file' => $file, 'key' => $job->key]); $this->line('Stored import data...'); $jobRepository->setConfiguration($job, $configurationData); $jobRepository->updateStatus($job, 'configured'); $this->line('Stored configuration...'); if (true === $this->option('start')) { $this->line('The import will start in a moment. This process is not visible...'); Log::debug('Go for import!'); // normally would refer to other firefly:start-import but that doesn't seem to work all to well... $monolog = Log::getMonolog(); $handler = new CommandHandler($this); $formatter = new LineFormatter(null, null, false, true); $handler->setFormatter($formatter); $monolog->pushHandler($handler); // start the actual routine: $type = $job->file_type === 'csv' ? 'file' : $job->file_type; $key = sprintf('import.routine.%s', $type); $className = config($key); if (null === $className || !class_exists($className)) { throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore } /** @var RoutineInterface $routine */ $routine = app($className); $routine->setJob($job); $routine->run(); // give feedback. /** @var MessageBag $error */ foreach ($routine->getErrors() as $index => $error) { $this->error(sprintf('Error importing line #%d: %s', $index, $error)); } $this->line( sprintf('The import has finished. %d transactions have been imported out of %d records.', $routine->getJournals()->count(), $routine->lines) ); } // clear cache for user: Preferences::setForUser($user, 'lastActivity', microtime()); return; } /** * Verify user inserts correct arguments. * * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly. */ private function validArguments(): bool { /** @var UserRepositoryInterface $userRepository */ $userRepository = app(UserRepositoryInterface::class); $file = $this->argument('file'); $configuration = $this->argument('configuration'); $user = $userRepository->find(intval($this->option('user'))); $cwd = getcwd(); $validTypes = config('import.options.file.import_formats'); $type = strtolower($this->option('type')); if (null === $user->id) { $this->error(sprintf('There is no user with ID %d.', $this->option('user'))); return false; } if (!in_array($type, $validTypes)) { $this->error(sprintf('Cannot import file of type "%s"', $type)); return false; } if (!file_exists($file)) { $this->error(sprintf('Firefly III cannot find file "%s" (working directory: "%s").', $file, $cwd)); return false; } if (!file_exists($configuration)) { $this->error(sprintf('Firefly III cannot find configuration file "%s" (working directory: "%s").', $configuration, $cwd)); return false; } return true; } }