diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index eacd89d09b..c1e2b718a9 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -15,30 +15,26 @@ namespace FireflyIII\Http\Controllers; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Requests\ImportUploadRequest; use FireflyIII\Import\Configurator\ConfiguratorInterface; -use FireflyIII\Import\FileProcessor\FileProcessorInterface; -use FireflyIII\Import\ImportProcedureInterface; use FireflyIII\Import\Routine\ImportRoutine; -use FireflyIII\Import\Storage\ImportStorage; use FireflyIII\Models\ImportJob; use FireflyIII\Models\Tag; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use FireflyIII\Repositories\Tag\TagRepositoryInterface; -use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Http\Request; use Illuminate\Http\Response as LaravelResponse; -use Illuminate\Support\Collection; use Log; use Response; -use Validator; use View; /** - * Class ImportController + * Class ImportController. * * @package FireflyIII\Http\Controllers */ class ImportController extends Controller { + /** @var ImportJobRepositoryInterface */ + public $repository; + /** * */ @@ -50,6 +46,7 @@ class ImportController extends Controller function ($request, $next) { View::share('mainTitleIcon', 'fa-archive'); View::share('title', trans('firefly.import_data_full')); + $this->repository = app(ImportJobRepositoryInterface::class); return $next($request); } @@ -71,6 +68,8 @@ class ImportController extends Controller // is the job already configured? if ($configurator->isJobConfigured()) { + $this->repository->updateStatus($job, 'configured'); + return redirect(route('import.status', [$job->key])); } $view = $configurator->getNextView(); @@ -82,11 +81,11 @@ class ImportController extends Controller } /** - * Generate a JSON file of the job's config and send it to the user. + * Generate a JSON file of the job's configuration and send it to the user. * * @param ImportJob $job * - * @return mixed + * @return string */ public function download(ImportJob $job) { @@ -140,70 +139,72 @@ class ImportController extends Controller /** * This is step 2. It creates an Import Job. Stores the import. * - * @param ImportUploadRequest $request - * @param ImportJobRepositoryInterface $repository - * @param UserRepositoryInterface $userRepository + * @param ImportUploadRequest $request * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function initialize(ImportUploadRequest $request, ImportJobRepositoryInterface $repository, UserRepositoryInterface $userRepository) + public function initialize(ImportUploadRequest $request) { Log::debug('Now in initialize()'); // create import job: $type = $request->get('import_file_type'); - $job = $repository->create($type); + $job = $this->repository->create($type); Log::debug('Created new job', ['key' => $job->key, 'id' => $job->id]); // process file: - $repository->processFile($job, $request->files->get('import_file')); + $this->repository->processFile($job, $request->files->get('import_file')); // process config, if present: if ($request->files->has('configuration_file')) { - $repository->processConfiguration($job, $request->files->get('configuration_file')); + $this->repository->processConfiguration($job, $request->files->get('configuration_file')); } + $this->repository->updateStatus($job, 'initialized'); + return redirect(route('import.configure', [$job->key])); } /** + * + * Show status of import job in JSON. + * * @param ImportJob $job * * @return \Illuminate\Http\JsonResponse */ public function json(ImportJob $job) { - $result = [ + $result = [ 'started' => false, 'finished' => false, 'running' => false, - 'errors' => $job->extended_status['errors'], + 'errors' => array_values($job->extended_status['errors']), 'percentage' => 0, - 'steps' => $job->extended_status['total_steps'], - 'stepsDone' => $job->extended_status['steps_done'], + 'steps' => $job->extended_status['steps'], + 'done' => $job->extended_status['done'], 'statusText' => trans('firefly.import_status_' . $job->status), 'status' => $job->status, 'finishedText' => '', ]; - $percentage = 0; - if ($job->extended_status['total_steps'] !== 0) { - $percentage = round(($job->extended_status['steps_done'] / $job->extended_status['total_steps']) * 100, 0); + + if ($job->extended_status['steps'] !== 0) { + $result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0); } + if ($job->status === 'finished') { -// $tagId = $job->extended_status['importTag']; -// /** @var TagRepositoryInterface $repository */ -// $repository = app(TagRepositoryInterface::class); -// $tag = $repository->find($tagId); - $tag = new Tag; + // $tagId = $job->extended_status['importTag']; + // /** @var TagRepositoryInterface $repository */ + // $repository = app(TagRepositoryInterface::class); + // $tag = $repository->find($tagId); + $tag = new Tag; $result['finished'] = true; $result['finishedText'] = trans('firefly.import_finished_link', ['link' => route('tags.show', [$tag->id]), 'tag' => $tag->tag]); } if ($job->status === 'running') { - $result['started'] = true; - $result['running'] = true; - $result['percentage'] = $percentage; - $result['showPercentage'] = true; + $result['started'] = true; + $result['running'] = true; } return Response::json($result); @@ -212,13 +213,12 @@ class ImportController extends Controller /** * Step 4. Save the configuration. * - * @param Request $request - * @param ImportJobRepositoryInterface $repository - * @param ImportJob $job + * @param Request $request + * @param ImportJob $job * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function postConfigure(Request $request, ImportJobRepositoryInterface $repository, ImportJob $job) + public function postConfigure(Request $request, ImportJob $job) { Log::debug('Now in postConfigure()', ['job' => $job->key]); $configurator = $this->makeConfigurator($job); @@ -237,25 +237,31 @@ class ImportController extends Controller /** * @param ImportJob $job * - * @return string + * @return \Illuminate\Http\JsonResponse + * @throws FireflyException */ public function start(ImportJob $job) { $routine = new ImportRoutine($job); - $routine->run(); + $result = $routine->run(); + if ($result) { + return Response::json(['run' => 'ok']); + } - return 'done!'; + throw new FireflyException('Job did not complete succesfully.'); } /** - * This is the last step before the import starts. - * * @param ImportJob $job * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View */ public function status(ImportJob $job) { + $statuses = ['configured', 'running', 'finished']; + if (!in_array($job->status, $statuses)) { + return redirect(route('import.configure', [$job->key])); + } $subTitle = trans('firefly.import_status'); $subTitleIcon = 'fa-star'; diff --git a/app/Import/Configurator/CsvConfigurator.php b/app/Import/Configurator/CsvConfigurator.php index 4933a8afca..a550b8a6cd 100644 --- a/app/Import/Configurator/CsvConfigurator.php +++ b/app/Import/Configurator/CsvConfigurator.php @@ -103,9 +103,6 @@ class CsvConfigurator implements ConfiguratorInterface && $this->job->configuration['column-roles-complete'] && $this->job->configuration['column-mapping-complete'] ) { - $this->job->status = 'configured'; - $this->job->save(); - return true; } diff --git a/app/Import/Converter/AccountId.php b/app/Import/Converter/AccountId.php deleted file mode 100644 index 88161515ab..0000000000 --- a/app/Import/Converter/AccountId.php +++ /dev/null @@ -1,69 +0,0 @@ - $value]); - if ($value === 0) { - $this->setCertainty(0); - - return new Account; - } - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $repository->setUser($this->user); - - if (isset($this->mapping[$value])) { - Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $account = $repository->find(intval($this->mapping[$value])); - if (!is_null($account->id)) { - Log::debug('Found account by ID', ['id' => $account->id]); - - $this->setCertainty(100); - - return $account; - } - } - $account = $repository->find($value);// not mapped? Still try to find it first: - if (!is_null($account->id)) { - $this->setCertainty(90); - Log::debug('Found account by ID ', ['id' => $account->id]); - - return $account; - } - $this->setCertainty(0); // should not really happen. If the ID does not match FF, what is FF supposed to do? - - return new Account; - - } -} diff --git a/app/Import/Converter/AssetAccountIban.php b/app/Import/Converter/AssetAccountIban.php deleted file mode 100644 index f98bef6110..0000000000 --- a/app/Import/Converter/AssetAccountIban.php +++ /dev/null @@ -1,87 +0,0 @@ - $value]); - - if (strlen($value) === 0) { - $this->setCertainty(0); - - return new Account; - } - - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $repository->setUser($this->user); - - - if (isset($this->mapping[$value])) { - Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $account = $repository->find(intval($this->mapping[$value])); - if (!is_null($account->id)) { - $this->setCertainty(100); - Log::debug('Found account by ID', ['id' => $account->id]); - - return $account; - } - } - - // not mapped? Still try to find it first: - $account = $repository->findByIban($value, [AccountType::ASSET]); - if (!is_null($account->id)) { - Log::debug('Found account by IBAN', ['id' => $account->id]); - $this->setCertainty(50); - - return $account; - } - - - $account = $repository->store( - ['name' => 'Asset account with IBAN ' . $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, - 'active' => true, 'openingBalance' => 0] - ); - - if (is_null($account->id)) { - $this->setCertainty(0); - Log::info('Could not store new asset account by IBAN', $account->getErrors()->toArray()); - - return new Account; - } - - $this->setCertainty(100); - - return $account; - } -} diff --git a/app/Import/Converter/AssetAccountName.php b/app/Import/Converter/AssetAccountName.php deleted file mode 100644 index 8355c3b056..0000000000 --- a/app/Import/Converter/AssetAccountName.php +++ /dev/null @@ -1,90 +0,0 @@ - $value]); - - if (strlen($value) === 0) { - $this->setCertainty(0); - - return new Account; - } - - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $repository->setUser($this->user); - - - if (isset($this->mapping[$value])) { - Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $account = $repository->find(intval($this->mapping[$value])); - if (!is_null($account->id)) { - Log::debug('Found account by ID', ['id' => $account->id]); - $this->setCertainty(100); - - return $account; - } - } - - // not mapped? Still try to find it first: - $account = $repository->findByName($value, [AccountType::ASSET]); - if (!is_null($account->id)) { - Log::debug('Found asset account by name', ['value' => $value, 'id' => $account->id]); - - return $account; - } - - - $account = $repository->store( - ['name' => $value, 'iban' => null, 'openingBalance' => 0, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0, - 'active' => true] - ); - - if (is_null($account->id)) { - $this->setCertainty(0); - Log::info('Could not store new asset account by name', $account->getErrors()->toArray()); - - return new Account; - } - - $this->setCertainty(100); - - Log::debug('Created new asset account ', ['name' => $account->name, 'id' => $account->id]); - - return $account; - - - } -} diff --git a/app/Import/Converter/AssetAccountNumber.php b/app/Import/Converter/AssetAccountNumber.php deleted file mode 100644 index 880b4d3061..0000000000 --- a/app/Import/Converter/AssetAccountNumber.php +++ /dev/null @@ -1,96 +0,0 @@ - $value]); - - if (strlen($value) === 0) { - return new Account; - } - - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $repository->setUser($this->user); - - - if (isset($this->mapping[$value])) { - Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $account = $repository->find(intval($this->mapping[$value])); - if (!is_null($account->id)) { - Log::debug('Found account by ID', ['id' => $account->id]); - - return $account; - } - } - - // not mapped? Still try to find it first: - $account = $repository->findByAccountNumber($value, [AccountType::ASSET]); - if (!is_null($account->id)) { - Log::debug('Found account by name', ['id' => $account->id]); - $this->setCertainty(50); - - return $account; - } - - // try to find by the name we would give it: - $accountName = 'Asset account with number ' . e($value); - $account = $repository->findByName($accountName, [AccountType::ASSET]); - if (!is_null($account->id)) { - Log::debug('Found account by name', ['id' => $account->id]); - $this->setCertainty(50); - - return $account; - } - - - $account = $repository->store( - ['name' => $accountName, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id, - 'accountType' => 'asset', - 'virtualBalance' => 0, 'accountNumber' => $value, 'active' => true] - ); - - if (is_null($account->id)) { - $this->setCertainty(0); - Log::info('Could not store new asset account by account number', $account->getErrors()->toArray()); - - return new Account; - } - - $this->setCertainty(100); - - return $account; - - } -} diff --git a/app/Import/Converter/BillId.php b/app/Import/Converter/BillId.php deleted file mode 100644 index 11c91d6536..0000000000 --- a/app/Import/Converter/BillId.php +++ /dev/null @@ -1,76 +0,0 @@ - $value]); - - if ($value === 0) { - $this->setCertainty(0); - - return new Bill; - } - - /** @var BillRepositoryInterface $repository */ - $repository = app(BillRepositoryInterface::class); - $repository->setUser($this->user); - - if (isset($this->mapping[$value])) { - Log::debug('Found bill in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $bill = $repository->find(intval($this->mapping[$value])); - if (!is_null($bill->id)) { - Log::debug('Found bill by ID', ['id' => $bill->id]); - $this->setCertainty(100); - - return $bill; - } - } - - // not mapped? Still try to find it first: - $bill = $repository->find($value); - if (!is_null($bill->id)) { - Log::debug('Found bill by ID ', ['id' => $bill->id]); - $this->setCertainty(100); - - return $bill; - } - - // should not really happen. If the ID does not match FF, what is FF supposed to do? - Log::info(sprintf('Could not find bill with ID %d. Will return NULL', $value)); - - $this->setCertainty(0); - - return new Bill; - - } -} diff --git a/app/Import/Converter/BillName.php b/app/Import/Converter/BillName.php deleted file mode 100644 index 2ba4ad2761..0000000000 --- a/app/Import/Converter/BillName.php +++ /dev/null @@ -1,99 +0,0 @@ - $value]); - - if (strlen($value) === 0) { - $this->setCertainty(0); - - return new Bill; - } - - /** @var BillRepositoryInterface $repository */ - $repository = app(BillRepositoryInterface::class); - $repository->setUser($this->user); - - if (isset($this->mapping[$value])) { - Log::debug('Found bill in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $bill = $repository->find(intval($this->mapping[$value])); - if (!is_null($bill->id)) { - Log::debug('Found bill by ID', ['id' => $bill->id]); - $this->setCertainty(100); - - return $bill; - } - } - - // not mapped? Still try to find it first: - $bill = $repository->findByName($value); - if (!is_null($bill->id)) { - Log::debug('Found bill by name ', ['id' => $bill->id]); - $this->setCertainty(100); - - return $bill; - } - - // create new bill. Use a lot of made up values. - $bill = $repository->store( - [ - 'name' => $value, - 'match' => $value, - 'amount_min' => 1, - 'user' => $this->user->id, - 'amount_max' => 10, - 'date' => date('Ymd'), - 'repeat_freq' => 'monthly', - 'skip' => 0, - 'automatch' => 0, - 'active' => 1, - - ] - ); - if (is_null($bill->id)) { - $this->setCertainty(0); - Log::info('Could not store new bill by name', $bill->getErrors()->toArray()); - - return new Bill; - } - - $this->setCertainty(100); - - return $bill; - - - } -} diff --git a/app/Import/Converter/BudgetId.php b/app/Import/Converter/BudgetId.php deleted file mode 100644 index ea74b8302f..0000000000 --- a/app/Import/Converter/BudgetId.php +++ /dev/null @@ -1,76 +0,0 @@ - $value]); - - if ($value === 0) { - $this->setCertainty(0); - - return new Budget; - } - - /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class); - $repository->setUser($this->user); - - if (isset($this->mapping[$value])) { - Log::debug('Found budget in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $budget = $repository->find(intval($this->mapping[$value])); - if (!is_null($budget->id)) { - Log::debug('Found budget by ID', ['id' => $budget->id]); - $this->setCertainty(100); - - return $budget; - } - } - - // not mapped? Still try to find it first: - $budget = $repository->find($value); - if (!is_null($budget->id)) { - Log::debug('Found budget by ID ', ['id' => $budget->id]); - $this->setCertainty(100); - - return $budget; - } - - // should not really happen. If the ID does not match FF, what is FF supposed to do? - $this->setCertainty(0); - - Log::info(sprintf('Could not find budget with ID %d. Will return NULL', $value)); - - return new Budget; - - } -} diff --git a/app/Import/Converter/BudgetName.php b/app/Import/Converter/BudgetName.php deleted file mode 100644 index 5d36a109ac..0000000000 --- a/app/Import/Converter/BudgetName.php +++ /dev/null @@ -1,80 +0,0 @@ - $value]); - - if (strlen($value) === 0) { - $this->setCertainty(0); - - return new Budget; - } - - /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class); - $repository->setUser($this->user); - - if (isset($this->mapping[$value])) { - Log::debug('Found budget in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $budget = $repository->find(intval($this->mapping[$value])); - if (!is_null($budget->id)) { - Log::debug('Found budget by ID', ['id' => $budget->id]); - $this->setCertainty(100); - - return $budget; - } - } - - // not mapped? Still try to find it first: - $budget = $repository->findByName($value); - if (!is_null($budget->id)) { - Log::debug('Found budget by name ', ['id' => $budget->id]); - $this->setCertainty(100); - - return $budget; - } - - // create new budget. Use a lot of made up values. - $budget = $repository->store( - [ - 'name' => $value, - 'user' => $this->user->id, - ] - ); - $this->setCertainty(100); - - return $budget; - - } -} diff --git a/app/Import/Converter/CategoryId.php b/app/Import/Converter/CategoryId.php deleted file mode 100644 index 4b5cd4e6af..0000000000 --- a/app/Import/Converter/CategoryId.php +++ /dev/null @@ -1,76 +0,0 @@ - $value]); - - if ($value === 0) { - $this->setCertainty(0); - - return new Category; - } - - /** @var CategoryRepositoryInterface $repository */ - $repository = app(CategoryRepositoryInterface::class); - $repository->setUser($this->user); - - if (isset($this->mapping[$value])) { - Log::debug('Found category in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $category = $repository->find(intval($this->mapping[$value])); - if (!is_null($category->id)) { - Log::debug('Found category by ID', ['id' => $category->id]); - $this->setCertainty(100); - - return $category; - } - } - - // not mapped? Still try to find it first: - $category = $repository->find($value); - if (!is_null($category->id)) { - Log::debug('Found category by ID ', ['id' => $category->id]); - $this->setCertainty(100); - - return $category; - } - - // should not really happen. If the ID does not match FF, what is FF supposed to do? - $this->setCertainty(0); - - Log::info(sprintf('Could not find category with ID %d. Will return NULL', $value)); - - return new Category; - - } -} diff --git a/app/Import/Converter/CategoryName.php b/app/Import/Converter/CategoryName.php deleted file mode 100644 index fcd52413cc..0000000000 --- a/app/Import/Converter/CategoryName.php +++ /dev/null @@ -1,80 +0,0 @@ - $value]); - - if (strlen($value) === 0) { - $this->setCertainty(0); - - return new Category; - } - - /** @var CategoryRepositoryInterface $repository */ - $repository = app(CategoryRepositoryInterface::class); - $repository->setUser($this->user); - - if (isset($this->mapping[$value])) { - Log::debug('Found category in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $category = $repository->find(intval($this->mapping[$value])); - if (!is_null($category->id)) { - Log::debug('Found category by ID', ['id' => $category->id]); - $this->setCertainty(100); - - return $category; - } - } - - // not mapped? Still try to find it first: - $category = $repository->findByName($value); - if (!is_null($category->id)) { - Log::debug('Found category by name ', ['id' => $category->id]); - $this->setCertainty(100); - - return $category; - } - - // create new category. Use a lot of made up values. - $category = $repository->store( - [ - 'name' => $value, - 'user' => $this->user->id, - ] - ); - $this->setCertainty(100); - - return $category; - - } -} diff --git a/app/Import/Converter/CurrencyCode.php b/app/Import/Converter/CurrencyCode.php deleted file mode 100644 index 1afa778292..0000000000 --- a/app/Import/Converter/CurrencyCode.php +++ /dev/null @@ -1,71 +0,0 @@ - $value]); - - /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class); - $repository->setUser($this->user); - - if (isset($this->mapping[$value])) { - Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $currency = $repository->find(intval($this->mapping[$value])); - if (!is_null($currency->id)) { - Log::debug('Found currency by ID', ['id' => $currency->id]); - $this->setCertainty(100); - - return $currency; - } - } - - // not mapped? Still try to find it first: - $currency = $repository->findByCode($value); - if (!is_null($currency->id)) { - Log::debug('Found currency by code', ['id' => $currency->id]); - $this->setCertainty(100); - - return $currency; - } - $currency = $repository->store( - [ - 'name' => $value, - 'code' => $value, - 'symbol' => $value, - ] - ); - $this->setCertainty(100); - - return $currency; - } -} diff --git a/app/Import/Converter/CurrencyId.php b/app/Import/Converter/CurrencyId.php deleted file mode 100644 index d3b74da000..0000000000 --- a/app/Import/Converter/CurrencyId.php +++ /dev/null @@ -1,75 +0,0 @@ - $value]); - - if ($value === 0) { - $this->setCertainty(0); - - return new TransactionCurrency; - } - - /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class); - $repository->setUser($this->user); - - if (isset($this->mapping[$value])) { - Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $currency = $repository->find(intval($this->mapping[$value])); - if (!is_null($currency->id)) { - Log::debug('Found currency by ID', ['id' => $currency->id]); - $this->setCertainty(100); - - return $currency; - } - } - - // not mapped? Still try to find it first: - $currency = $repository->find($value); - if (!is_null($currency->id)) { - Log::debug('Found currency by ID ', ['id' => $currency->id]); - $this->setCertainty(100); - - return $currency; - } - $this->setCertainty(0); - // should not really happen. If the ID does not match FF, what is FF supposed to do? - - Log::info(sprintf('Could not find category with ID %d. Will return NULL', $value)); - - return new TransactionCurrency; - - } -} diff --git a/app/Import/Converter/CurrencyName.php b/app/Import/Converter/CurrencyName.php deleted file mode 100644 index f68ec043a1..0000000000 --- a/app/Import/Converter/CurrencyName.php +++ /dev/null @@ -1,81 +0,0 @@ - $value]); - - if (strlen($value) === 0) { - $this->setCertainty(0); - - return new TransactionCurrency; - } - - /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class); - $repository->setUser($this->user); - - if (isset($this->mapping[$value])) { - Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $currency = $repository->find(intval($this->mapping[$value])); - if (!is_null($currency->id)) { - Log::debug('Found currency by ID', ['id' => $currency->id]); - $this->setCertainty(100); - - return $currency; - } - } - - // not mapped? Still try to find it first: - $currency = $repository->findByName($value); - if (!is_null($currency->id)) { - Log::debug('Found currency by name ', ['id' => $currency->id]); - $this->setCertainty(100); - - return $currency; - } - - // create new currency - $currency = $repository->store( - [ - 'name' => $value, - 'code' => strtoupper(substr($value, 0, 3)), - 'symbol' => strtoupper(substr($value, 0, 1)), - ] - ); - $this->setCertainty(100); - - return $currency; - - } -} diff --git a/app/Import/Converter/CurrencySymbol.php b/app/Import/Converter/CurrencySymbol.php deleted file mode 100644 index a40b06af40..0000000000 --- a/app/Import/Converter/CurrencySymbol.php +++ /dev/null @@ -1,81 +0,0 @@ - $value]); - - if (strlen($value) === 0) { - $this->setCertainty(0); - - return new TransactionCurrency; - } - - /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class); - $repository->setUser($this->user); - - if (isset($this->mapping[$value])) { - Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $currency = $repository->find(intval($this->mapping[$value])); - if (!is_null($currency->id)) { - Log::debug('Found currency by ID', ['id' => $currency->id]); - $this->setCertainty(100); - - return $currency; - } - } - - // not mapped? Still try to find it first: - $currency = $repository->findBySymbol($value); - if (!is_null($currency->id)) { - Log::debug('Found currency by symbol ', ['id' => $currency->id]); - $this->setCertainty(100); - - return $currency; - } - - // create new currency - $currency = $repository->store( - [ - 'name' => 'Currency ' . $value, - 'code' => $value, - 'symbol' => $value, - ] - ); - $this->setCertainty(100); - - return $currency; - - } -} diff --git a/app/Import/Converter/Date.php b/app/Import/Converter/Date.php deleted file mode 100644 index b799aed9e7..0000000000 --- a/app/Import/Converter/Date.php +++ /dev/null @@ -1,53 +0,0 @@ - $value]); - Log::debug('Format: ', ['format' => $this->config['date-format']]); - try { - $date = Carbon::createFromFormat($this->config['date-format'], $value); - } catch (InvalidArgumentException $e) { - Log::info($e->getMessage()); - Log::info('Cannot convert this string using the given format.', ['value' => $value, 'format' => $this->config['date-format']]); - $this->setCertainty(0); - - return new Carbon; - } - Log::debug('Converted date', ['converted' => $date->toAtomString()]); - $this->setCertainty(100); - - return $date; - } -} diff --git a/app/Import/Converter/Description.php b/app/Import/Converter/Description.php deleted file mode 100644 index 9eb507acb8..0000000000 --- a/app/Import/Converter/Description.php +++ /dev/null @@ -1,39 +0,0 @@ -setCertainty(100); - - return strval($value); - - } -} diff --git a/app/Import/Converter/ExternalId.php b/app/Import/Converter/ExternalId.php deleted file mode 100644 index feb2e8c3d1..0000000000 --- a/app/Import/Converter/ExternalId.php +++ /dev/null @@ -1,39 +0,0 @@ -setCertainty(100); - - return strval(trim($value)); - - } -} diff --git a/app/Import/Converter/Ignore.php b/app/Import/Converter/Ignore.php deleted file mode 100644 index ac619bac3a..0000000000 --- a/app/Import/Converter/Ignore.php +++ /dev/null @@ -1,34 +0,0 @@ - $value]); - - if (strlen($value) === 0) { - $this->setCertainty(0); - - return new Account; - } - - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $repository->setUser($this->user); - - - if (isset($this->mapping[$value])) { - Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $account = $repository->find(intval($this->mapping[$value])); - if (!is_null($account->id)) { - Log::debug('Found account by ID', ['id' => $account->id]); - $this->setCertainty(100); - - return $account; - } - } - - // not mapped? Still try to find it first: - $account = $repository->findByIban($value, []); - if (!is_null($account->id)) { - Log::debug('Found account by IBAN', ['id' => $account->id]); - Log::info( - 'The match between IBAN and account is uncertain because the type of transactions may not have been determined.', - ['id' => $account->id, 'iban' => $value] - ); - $this->setCertainty(50); - - return $account; - } - - // the IBAN given may not be a valid IBAN. If not, we cannot store by - // iban and we have no opposing account. There should be some kind of fall back - // routine. - try { - $account = $repository->store( - ['name' => $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'import', 'virtualBalance' => 0, 'active' => true, - 'openingBalance' => 0] - ); - $this->setCertainty(100); - } catch (FireflyException $e) { - Log::error($e); - - $account = new Account; - } - - return $account; - } -} diff --git a/app/Import/Converter/OpposingAccountName.php b/app/Import/Converter/OpposingAccountName.php deleted file mode 100644 index 90a959408c..0000000000 --- a/app/Import/Converter/OpposingAccountName.php +++ /dev/null @@ -1,89 +0,0 @@ - $value]); - - if (strlen($value) === 0) { - $this->setCertainty(0); - - return new Account; - } - - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $repository->setUser($this->user); - - - if (isset($this->mapping[$value])) { - Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $account = $repository->find(intval($this->mapping[$value])); - if (!is_null($account->id)) { - Log::debug('Found account by ID', ['id' => $account->id]); - $this->setCertainty(100); - - return $account; - } - } - - // not mapped? Still try to find it first: - $account = $repository->findByName($value, []); - if (!is_null($account->id)) { - Log::debug('Found opposing account by name', ['id' => $account->id]); - Log::info( - 'The match between name and account is uncertain because the type of transactions may not have been determined.', - ['id' => $account->id, 'name' => $value] - ); - $this->setCertainty(50); - - return $account; - } - - $account = $repository->store( - ['name' => $value, 'iban' => null, 'user' => $this->user->id, 'accountType' => 'import', 'virtualBalance' => 0, 'active' => true, - 'openingBalance' => 0, - ] - ); - if (is_null($account->id)) { - $this->setCertainty(0); - - return new Account; - } - $this->setCertainty(100); - - Log::debug('Created new opposing account ', ['name' => $account->name, 'id' => $account->id]); - - return $account; - } -} diff --git a/app/Import/Converter/OpposingAccountNumber.php b/app/Import/Converter/OpposingAccountNumber.php deleted file mode 100644 index 8ede15ed85..0000000000 --- a/app/Import/Converter/OpposingAccountNumber.php +++ /dev/null @@ -1,91 +0,0 @@ - $value]); - - if (strlen($value) === 0) { - $this->setCertainty(0); - - return new Account; - } - - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $repository->setUser($this->user); - - - if (isset($this->mapping[$value])) { - Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); - $account = $repository->find(intval($this->mapping[$value])); - if (!is_null($account->id)) { - Log::debug('Found account by ID', ['id' => $account->id]); - $this->setCertainty(100); - - return $account; - } - } - - // not mapped? Still try to find it first: - $account = $repository->findByAccountNumber($value, []); - if (!is_null($account->id)) { - Log::debug('Found account by number', ['id' => $account->id]); - $this->setCertainty(50); - - return $account; - } - - // try to find by the name we would give it: - $accountName = 'Import account with number ' . e($value); - $account = $repository->findByName($accountName, [AccountType::IMPORT]); - if (!is_null($account->id)) { - Log::debug('Found account by name', ['id' => $account->id]); - $this->setCertainty(50); - - return $account; - } - - - $account = $repository->store( - ['name' => $accountName, 'openingBalance' => 0, 'iban' => null, 'user' => $this->user->id, - 'accountType' => 'import', - 'virtualBalance' => 0, 'accountNumber' => $value, 'active' => true] - ); - $this->setCertainty(100); - - return $account; - - } -} diff --git a/app/Import/FileProcessor/CsvProcessor.php b/app/Import/FileProcessor/CsvProcessor.php index 56b196ee8a..010a0580d9 100644 --- a/app/Import/FileProcessor/CsvProcessor.php +++ b/app/Import/FileProcessor/CsvProcessor.php @@ -67,25 +67,28 @@ class CsvProcessor implements FileProcessorInterface Log::debug('Now in CsvProcessor run(). Job is now running...'); $entries = $this->getImportArray(); - $count = 0; + $index = 0; Log::notice('Building importable objects from CSV file.'); foreach ($entries as $index => $row) { // verify if not exists already: - if ($this->hashPresent($row)) { - Log::info(sprintf('Row #%d has already been imported.', $index)); + if ($this->rowAlreadyImported($row)) { + $message = sprintf('Row #%d has already been imported.', $index); + $this->job->addError($index, $message); + $this->job->addStepsDone(5); // all steps. + Log::info($message); continue; } $this->objects->push($this->importRow($index, $row)); - /** - * 1. Build import entry. - * 2. Validate import entry. - * 3. Store journal. - * 4. Run rules. - */ $this->job->addStepsDone(1); - $count++; - sleep(1); } + // if job has no step count, set it now: + $extended = $this->job->extended_status; + if ($extended['steps'] === 0) { + $extended['steps'] = $index * 5; + $this->job->extended_status = $extended; + $this->job->save(); + } + return true; } @@ -148,36 +151,6 @@ class CsvProcessor implements FileProcessorInterface return $results; } - /** - * Checks if the row has not been imported before. - * - * TODO for debugging, will always return false. - * - * @param array $array - * - * @noinspection PhpUnreachableStatementInspection - * @return bool - */ - private function hashPresent(array $array): bool - { - $string = json_encode($array); - $hash = hash('sha256', json_encode($string)); - $json = json_encode($hash); - $entry = TransactionJournalMeta:: - leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') - ->where('data', $json) - ->where('name', 'importHash') - ->first(); - - return false; - if (!is_null($entry)) { - return true; - } - - return false; - - } - /** * Take a row, build import journal by annotating each value and storing it in the import journal. * @@ -207,6 +180,33 @@ class CsvProcessor implements FileProcessorInterface return $journal; } + /** + * Checks if the row has not been imported before. + * + * @param array $array + * + * @return bool + */ + private function rowAlreadyImported(array $array): bool + { + $string = json_encode($array); + $hash = hash('sha256', json_encode($string)); + $json = json_encode($hash); + $entry = TransactionJournalMeta:: + leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') + ->where('data', $json) + ->where('name', 'importHash') + ->first(); + + return rand(1, 10) === 3; + if (!is_null($entry)) { + return true; + } + + return false; + + } + /** * And this is the point where the specifix go to work. * diff --git a/app/Import/Routine/ImportRoutine.php b/app/Import/Routine/ImportRoutine.php index 78e167d028..9a71e79348 100644 --- a/app/Import/Routine/ImportRoutine.php +++ b/app/Import/Routine/ImportRoutine.php @@ -40,6 +40,7 @@ class ImportRoutine $this->job = $job; $this->journals = new Collection; $this->errors = new Collection; + Log::debug(sprintf('Job ID is #%d', $job->id)); } /** @@ -48,7 +49,7 @@ class ImportRoutine public function run(): bool { if ($this->job->status !== 'configured') { - Log::error(sprintf('Job %s is in state %s so it cannot be started.', $this->job->key, $this->job->status)); + Log::error(sprintf('Job %s is in state "%s" so it cannot be started.', $this->job->key, $this->job->status)); return false; } diff --git a/app/Import/Storage/ImportStorage.php b/app/Import/Storage/ImportStorage.php index 68b9cb09fa..bb20d2ad89 100644 --- a/app/Import/Storage/ImportStorage.php +++ b/app/Import/Storage/ImportStorage.php @@ -58,8 +58,6 @@ class ImportStorage $this->objects = new Collection; $this->journals = new Collection; $this->errors = new Collection; - $this->rules = $this->getUserRules(); - } /** @@ -75,7 +73,8 @@ class ImportStorage */ public function setJob(ImportJob $job) { - $this->job = $job; + $this->job = $job; + $this->rules = $this->getUserRules(); } /** @@ -100,6 +99,7 @@ class ImportStorage * @var ImportJournal $object */ foreach ($this->objects as $index => $object) { + sleep(4); Log::debug(sprintf('Going to store object #%d with description "%s"', $index, $object->description)); $errors = new MessageBag; @@ -148,11 +148,20 @@ class ImportStorage $journal->order = 0; $journal->tag_count = 0; $journal->encrypted = 0; - $journal->completed = 0; + $journal->completed = false; + + if (rand(1, 10) === 3) { + $journal->date = null; + $journal->description = null; + } + if (!$journal->save()) { $errorText = join(', ', $journal->getErrors()->all()); - $errors->add('no-key', sprintf('Error storing journal: %s', $errorText)); + $this->addErrorToJob($index, sprintf('Error storing line #%d: %s', $index, $errorText)); Log::error(sprintf('Could not store line #%d: %s', $index, $errorText)); + // add the rest of the steps: + $this->job->addStepsDone(3); + continue; } $journal->setMeta('importHash', $object->hash); @@ -184,7 +193,6 @@ class ImportStorage Log::debug(sprintf('Created transaction with ID #%d and account #%d', $two->id, $opposing->id)); $this->job->addStepsDone(1); - sleep(1); // category $category = $object->category->getCategory(); @@ -222,6 +230,11 @@ class ImportStorage if (strlen($object->notes) > 0) { $journal->setMeta('notes', $object->notes); } + + // set journal completed: + $journal->completed = true; + $journal->save(); + $this->job->addStepsDone(1); // run rules: @@ -230,9 +243,6 @@ class ImportStorage $this->journals->push($journal); $this->errors->push($errors); - - - sleep(1); } @@ -262,6 +272,18 @@ class ImportStorage return true; } + /** + * @param int $index + * @param string $error + */ + private function addErrorToJob(int $index, string $error) + { + $extended = $this->job->extended_status; + $extended['errors'][$index][] = $error; + $this->job->extended_status = $extended; + $this->job->save(); + } + /** * @return Collection */ diff --git a/app/Models/ImportJob.php b/app/Models/ImportJob.php index 61338a2fa0..5b3febd6c5 100644 --- a/app/Models/ImportJob.php +++ b/app/Models/ImportJob.php @@ -66,13 +66,28 @@ class ImportJob extends Model throw new NotFoundHttpException; } + /** + * @param int $index + * @param string $message + * + * @return bool + */ + public function addError(int $index, string $message): bool + { + $extended = $this->extended_status; + $extended['errors'][$index][] = $message; + $this->extended_status = $extended; + + return true; + } + /** * @param int $count */ public function addStepsDone(int $count) { - $status = $this->extended_status; - $status['steps_done'] += $count; + $status = $this->extended_status; + $status['done'] += $count; $this->extended_status = $status; $this->save(); @@ -84,7 +99,7 @@ class ImportJob extends Model public function addTotalSteps(int $count) { $status = $this->extended_status; - $status['total_steps'] += $count; + $status['steps'] += $count; $this->extended_status = $status; $this->save(); @@ -165,7 +180,7 @@ class ImportJob extends Model $disk = Storage::disk('upload'); $encryptedContent = $disk->get($fileName); $content = Crypt::decrypt($encryptedContent); - Log::debug(sprintf('Content size is %d bytes.', $content)); + Log::debug(sprintf('Content size is %d bytes.', strlen($content))); return $content; } diff --git a/app/Repositories/ImportJob/ImportJobRepository.php b/app/Repositories/ImportJob/ImportJobRepository.php index 74eeb508da..bc54f07503 100644 --- a/app/Repositories/ImportJob/ImportJobRepository.php +++ b/app/Repositories/ImportJob/ImportJobRepository.php @@ -58,12 +58,12 @@ class ImportJobRepository implements ImportJobRepositoryInterface $importJob->file_type = $fileType; $importJob->key = Str::random(12); $importJob->status = 'new'; + $importJob->configuration = []; $importJob->extended_status = [ - 'total_steps' => 0, - 'steps_done' => 0, - 'import_count' => 0, - 'importTag' => 0, - 'errors' => [], + 'steps' => 0, + 'done' => 0, + 'importTag' => 0, + 'errors' => [], ]; $importJob->save(); @@ -157,8 +157,6 @@ class ImportJobRepository implements ImportJobRepositoryInterface $disk->put($newName, $contentEncrypted); Log::debug('Uploaded file', ['name' => $file->getClientOriginalName(), 'size' => $file->getSize(), 'mime' => $file->getClientMimeType()]); } - $job->status = 'initialized'; - $job->save(); return true; } diff --git a/app/Support/Import/Configuration/Csv/Map.php b/app/Support/Import/Configuration/Csv/Map.php index 3286acd753..1f8df8449b 100644 --- a/app/Support/Import/Configuration/Csv/Map.php +++ b/app/Support/Import/Configuration/Csv/Map.php @@ -95,7 +95,7 @@ class Map implements ConfigurationInterface // save number of rows, thus number of steps, in job: $steps = $rowIndex * 5; $extended = $this->job->extended_status; - $extended['total_steps'] = $steps; + $extended['steps'] = $steps; $this->job->extended_status = $extended; $this->job->save(); diff --git a/public/js/ff/import/status.js b/public/js/ff/import/status.js index a2fc1e123f..68150eb85d 100644 --- a/public/js/ff/import/status.js +++ b/public/js/ff/import/status.js @@ -10,153 +10,175 @@ /** global: jobImportUrl, langImportSingleError, langImportMultiError, jobStartUrl, langImportTimeOutError, langImportFinished, langImportFatalError */ -var displayStatus = 'initial'; var timeOutId; - - -// var startedImport = false; -var startInterval = 2000; +var startInterval = 1000; var interval = 500; -// var timeoutLimit = 5000; -// var currentLimit = 0; -// var stepCount = 0; -// after this many tries, stop checking when the job is not running anyway. -var maxNotRunningCount = 20; -var notRunningCount = 0; +// these vars are used to detect a stalled job: +var numberOfSteps = 0; +var numberOfReports = 0; +var jobFailed = false; + +// counts how many errors have been detected +var knownErrors = 0; $(function () { "use strict"; - - //$('#import-status-intro').hide(); - //$('#import-status-more-info').hide(); - - // check status, every 500 ms. timeOutId = setTimeout(checkImportStatus, startInterval); - - // button to start import routine: $('.start-job').click(startJob); - }); -function startJob() { - console.log('Job started.'); - $.post(jobStartUrl); +/** + * Downloads some JSON and responds to its content to see what the status is of the current import. + */ +function checkImportStatus() { + console.log('checkImportStatus()'); + $.getJSON(jobImportUrl).done(reportOnJobImport).fail(failedJobImport); +} - // reset not running thing - notRunningCount = 0; - // check status, every 500 ms. - timeOutId = setTimeout(checkImportStatus, startInterval); +/** + * This method is called when the JSON query returns an error. If possible, this error is relayed to the user. + */ +function failedJobImport(jqxhr, textStatus, error) { + console.log('failedJobImport()'); + console.log(textStatus); + console.log(error); + + // hide all possible boxes: + $('.statusbox').hide(); + + // fill in some details: + var errorMessage = textStatus + " " + error; + + $('.import_error_txt').text(errorMessage); + + // show the error box: + $('.errorbox').show(); +} + +/** + * This method is called when the job enquiry (JSON) returns some info. + * It also decides whether or not to check again. + * + * @param data + */ +function reportOnJobImport(data) { + console.log('reportOnJobImport()'); + console.log('Job status is: "' + data.status + '".'); + + switch (data.status) { + case "configured": + // job is ready. Do not check again, just show the start-box. Hide the rest. + $('.statusbox').hide(); + $('.status_configured').show(); + break; + case "running": + // job is running! Show the running box: + $('.statusbox').hide(); + $('.status_running').show(); + + // update the bar + updateBar(data); + + // update the status text: + updateStatusText(data); + + // report on detected errors: + reportOnErrors(data); + + if (jobIsStalled(data)) { + // do something + showStalledBox(); + } else { + // check again in 500ms + timeOutId = setTimeout(checkImportStatus, interval); + } + break; + case "finished": + $('.statusbox').hide(); + $('.status_finished').show(); + // show text: + + + break; + } +} + +/** + * Shows an error when the job seems to be stalled. + */ +function showStalledBox() { + $('.statusbox').hide(); + $('.errorbox').show(); + $('.import_error_txt').text(langImportTimeOutError); +} + +/** + * Detects if a job is frozen. + * + * @param data + */ +function jobIsStalled(data) { + console.log('jobIsStalled(' + numberOfSteps + ', ' + numberOfReports + ')'); + if (data.steps === numberOfSteps) { + numberOfReports++; + } + if (data.done !== numberOfSteps) { + numberOfReports = 0; + } + if (numberOfReports > 20) { + return true; + } + numberOfSteps = data.done; return false; } -function checkImportStatus() { - "use strict"; - $.getJSON(jobImportUrl).done(reportOnJobImport).fail(failedJobImport); +/** + * This function tells Firefly start the job. It will also initialize a re-check in 500ms time. + */ +function startJob() { + // disable the button, add loading thing. + $('.start-job').prop('disabled', true).text('...'); + console.log('startJob()'); + $.post(jobStartUrl).fail(reportOnSubmitError); + + // check status, every 500 ms. + timeOutId = setTimeout(checkImportStatus, startInterval); } -function reportToConsole(data) { - console.log('status: ' + data.status + ', steps: ' + data.steps + ', stepsDone: ' + data.stepsDone); +function reportOnSubmitError() { + // stop the refresh thing + console.error('Clear time out'); + clearTimeout(timeOutId); - //console.log('more status: ' + data); -} + // hide all possible boxes: + $('.statusbox').hide(); -function reportOnJobImport(data) { - "use strict"; - if (data.running == false) { - notRunningCount++; - } + // fill in some details: + var errorMessage = "Time out."; - displayCorrectBox(data.status); - reportToConsole(data); - updateBar(data); - //reportErrors(data); - reportStatus(data); - //updateTimeout(data); + $('.import_error_txt').text(errorMessage); - //if (importJobFinished(data)) { - // finishedJob(data); - // return; - //} - - - // same number of steps as last time? - //if (currentLimit > timeoutLimit) { - // timeoutError(); - // return; - //} - - // if the job has not actually started, do so now: - //if (!data.started && !startedImport) { - // kickStartJob(); - // return; - //} - - // trigger another check. - if (notRunningCount < maxNotRunningCount && data.finished === false) { - timeOutId = setTimeout(checkImportStatus, interval); - } - if (notRunningCount >= maxNotRunningCount && data.finished === false) { - console.error('Job still not running, stop checking for it.'); - } - if(data.finished === true) { - console.log('Job is done'); - } + // show the error box: + $('.errorbox').show(); + jobFailed = true; } -function displayCorrectBox(status) { - console.log('Current job state is ' + status); - if (status === 'configured' && displayStatus === 'initial') { - // hide some boxes: - $('.status_initial').hide(); - return; - } - if (status === 'running') { - // hide some boxes: - $('.status_initial').hide(); - $('.status_running').show(); - $('.status_configured').hide(); - - - return; - } - - if(status === 'finished') { - $('.status_initial').hide(); - $('.status_running').hide(); - $('.status_configured').hide(); - $('.status_finished').show(); - } - - - console.error('CANNOT HANDLE CURRENT STATE'); -} - - -function importComplete() { - "use strict"; - var bar = $('#import-status-bar'); - bar.removeClass('active'); -} - +/** + * This method updates the percentage bar thing if the job is running! + */ function updateBar(data) { - "use strict"; - + console.log('updateBar()'); var bar = $('#import-status-bar'); - if (data.showPercentage) { + if (data.percentage > 0) { console.log('Going to update bar with percentage.'); bar.addClass('progress-bar-success').removeClass('progress-bar-info'); bar.attr('aria-valuenow', data.percentage); bar.css('width', data.percentage + '%'); - $('#import-status-bar').text(data.stepsDone + '/' + data.steps); - - if (data.percentage >= 100) { - importComplete(); - return; - } - return; + $('#import-status-bar').text(data.done + '/' + data.steps); + return true; } console.log('Going to update bar without percentage.'); // dont show percentage: @@ -164,9 +186,223 @@ function updateBar(data) { bar.attr('aria-valuenow', 100); bar.css('width', '100%'); } + +/** + * Add text with current import status. + * @param data + */ +function updateStatusText(data) { + "use strict"; + console.log('Going to report status: ' + data.statusText); + $('#import-status-txt').removeClass('text-danger').text(data.statusText); +} + +/** + * Report on errors found in import: + * @param data + */ +function reportOnErrors(data) { + console.log('reportOnErrors()') + if (knownErrors === data.errors.length) { + console.log(knownErrors + ' = ' + data.errors.length); + return; + } + if (data.errors.length === 0) { + return; + } + + if (data.errors.length === 1) { + $('#import-status-error-intro').text(langImportSingleError); + //'An error has occured during the import. The import can continue, however.' + } + if (data.errors.length > 1) { + // 'Errors have occured during the import. The import can continue, however.' + $('#import-status-error-intro').text(langImportMultiError); + } + $('.info_errors').show(); + // fill the list with error texts + $('#import-status-error-list').empty(); + for (var i = 0; i < data.errors.length; i++) { + var errorSet = data.errors[i]; + for (var j = 0; j < errorSet.length; j++) { + var item = $('
  • ').html(errorSet[j]); + $('#import-status-error-list').append(item); + } + } + return; + +} + // +// var displayStatus = 'initial'; +// +// +// +// // var startedImport = false; +// + +// // var timeoutLimit = 5000; +// // var currentLimit = 0; +// // var stepCount = 0; +// +// // count the number of errors so we have an idea if the list must be recreated. +// var errorCount = 0; +// +// // after this many tries, stop checking when the job is not running anyway. +// var maxNotRunningCount = 20; +// var notRunningCount = 0; +// +// $(function () { +// "use strict"; +// +// //$('#import-status-intro').hide(); +// //$('#import-status-more-info').hide(); +// +// // check status, every 500 ms. +// //timeOutId = setTimeout(checkImportStatus, startInterval); +// +// // button to start import routine: +// $('.start-job').click(startJob); +// +// }); +// +// function startJob() { +// console.log('Job started.'); +// $.post(jobStartUrl); +// +// // reset not running thing +// notRunningCount = 0; +// // check status, every 500 ms. +// timeOutId = setTimeout(checkImportStatus, startInterval); +// +// return false; +// } +// +// function checkImportStatus() { +// "use strict"; +// $.getJSON(jobImportUrl).done(reportOnJobImport).fail(failedJobImport); +// } +// +// function reportToConsole(data) { +// console.log('status: ' + data.status + ', steps: ' + data.steps + ', done: ' + data.done); +// +// //console.log('more status: ' + data); +// } +// +// function reportOnJobImport(data) { +// "use strict"; +// if (data.running == false) { +// notRunningCount++; +// } +// +// displayCorrectBox(data.status); +// reportToConsole(data); +// updateBar(data); +// reportErrors(data); +// reportStatus(data); +// //updateTimeout(data); +// +// //if (importJobFinished(data)) { +// // finishedJob(data); +// // return; +// //} +// +// +// // same number of steps as last time? +// //if (currentLimit > timeoutLimit) { +// // timeoutError(); +// // return; +// //} +// +// // if the job has not actually started, do so now: +// //if (!data.started && !startedImport) { +// // kickStartJob(); +// // return; +// //} +// +// // trigger another check. +// if (notRunningCount < maxNotRunningCount && data.finished === false) { +// timeOutId = setTimeout(checkImportStatus, interval); +// } +// if (notRunningCount >= maxNotRunningCount && data.finished === false) { +// console.error('Job still not running, stop checking for it.'); +// } +// if (data.finished === true) { +// console.log('Job is done'); +// } +// +// } +// +// function displayCorrectBox(status) { +// console.log('Current job state is ' + status); +// if (status === 'configured' && displayStatus === 'initial') { +// // hide some boxes: +// $('.status_initial').hide(); +// return; +// } +// if (status === 'running') { +// // hide some boxes: +// $('.status_initial').hide(); +// $('.status_running').show(); +// $('.status_configured').hide(); +// +// +// return; +// } +// +// if (status === 'finished') { +// $('.status_initial').hide(); +// $('.status_running').hide(); +// $('.status_configured').hide(); +// $('.status_finished').show(); +// } +// +// +// console.error('CANNOT HANDLE CURRENT STATE'); +// } +// +// +// function importComplete() { +// "use strict"; +// var bar = $('#import-status-bar'); +// bar.removeClass('active'); +// } +// +// function updateBar(data) { +// "use strict"; +// +// var bar = $('#import-status-bar'); +// if (data.showPercentage) { +// console.log('Going to update bar with percentage.'); +// bar.addClass('progress-bar-success').removeClass('progress-bar-info'); +// bar.attr('aria-valuenow', data.percentage); +// bar.css('width', data.percentage + '%'); +// $('#import-status-bar').text(data.done + '/' + data.steps); +// +// if (data.percentage >= 100) { +// importComplete(); +// return; +// } +// return; +// } +// console.log('Going to update bar without percentage.'); +// // dont show percentage: +// bar.removeClass('progress-bar-success').addClass('progress-bar-info'); +// bar.attr('aria-valuenow', 100); +// bar.css('width', '100%'); +// } +// // // function reportErrors(data) { // "use strict"; +// console.log('Will now reportErrors() with ' + data.errors.length + ' errors.'); +// if (data.errors.length < 1) { +// return; +// } +// $('.info_errors').show(); +// if (data.errors.length === errorCount) { +// console.log('Error count is the same as before, do not response.'); +// } +// errorCount = data.errors.length; // if (data.errors.length === 1) { // $('#import-status-error-intro').text(langImportSingleError); // //'An error has occured during the import. The import can continue, however.' @@ -179,78 +415,81 @@ function updateBar(data) { // // fill the list with error texts // $('#import-status-error-list').empty(); // for (var i = 0; i < data.errors.length; i++) { -// var item = $('
  • ').html(data.errors[i]); -// $('#import-status-error-list').append(item); +// var errorSet = data.errors[i]; +// for (var j = 0; j < errorSet.length; j++) { +// var item = $('
  • ').html(errorSet[j]); +// $('#import-status-error-list').append(item); +// } // } // } -// -function reportStatus(data) { - "use strict"; - console.log('Going to report status: ' + data.statusText); - $('#import-status-txt').removeClass('text-danger').text(data.statusText); -} -// -// function kickStartJob() { +// // +// function reportStatus(data) { // "use strict"; -// $.post(jobStartUrl); -// startedTheImport(); -// startedImport = true; +// console.log('Going to report status: ' + data.statusText); +// $('#import-status-txt').removeClass('text-danger').text(data.statusText); // } +// // +// // function kickStartJob() { +// // "use strict"; +// // $.post(jobStartUrl); +// // startedTheImport(); +// // startedImport = true; +// // } +// // +// // function updateTimeout(data) { +// // "use strict"; +// // if (data.done !== stepCount) { +// // stepCount = data.done; +// // currentLimit = 0; +// // return; +// // } +// // +// // currentLimit = currentLimit + interval; +// // } +// // +// // function timeoutError() { +// // "use strict"; +// // // set status +// // $('#import-status-txt').addClass('text-danger').text(langImportTimeOutError); +// // +// // // remove progress bar. +// // $('#import-status-holder').hide(); +// // +// // } +// // +// // function importJobFinished(data) { +// // "use strict"; +// // return data.finished; +// // } +// // +// // function finishedJob(data) { +// // "use strict"; +// // // "There was an error during the import routine. Please check the log files. The error seems to be: '" +// // $('#import-status-txt').removeClass('text-danger').addClass('text-success').text(langImportFinished); +// // +// // // remove progress bar. +// // $('#import-status-holder').hide(); +// // +// // // show info: +// // $('#import-status-intro').show(); +// // $('#import-status-more-info').html(data.finishedText).show(); +// // +// // } +// // +// // +// // +// // function startedTheImport() { +// // "use strict"; +// // setTimeout(checkImportStatus, interval); +// // } // -// function updateTimeout(data) { -// "use strict"; -// if (data.stepsDone !== stepCount) { -// stepCount = data.stepsDone; -// currentLimit = 0; -// return; -// } -// -// currentLimit = currentLimit + interval; -// } -// -// function timeoutError() { +// function failedJobImport(jqxhr, textStatus, error) { // "use strict"; +// console.error('Job status failed!'); // // set status -// $('#import-status-txt').addClass('text-danger').text(langImportTimeOutError); -// -// // remove progress bar. -// $('#import-status-holder').hide(); -// -// } -// -// function importJobFinished(data) { -// "use strict"; -// return data.finished; -// } -// -// function finishedJob(data) { -// "use strict"; // // "There was an error during the import routine. Please check the log files. The error seems to be: '" -// $('#import-status-txt').removeClass('text-danger').addClass('text-success').text(langImportFinished); +// $('#import-status-txt').addClass('text-danger').text(langImportFatalError + ' ' + textStatus + ' ' + error); // // // remove progress bar. // $('#import-status-holder').hide(); -// -// // show info: -// $('#import-status-intro').show(); -// $('#import-status-more-info').html(data.finishedText).show(); -// -// } -// -// -// -// function startedTheImport() { -// "use strict"; -// setTimeout(checkImportStatus, interval); -// } - -function failedJobImport(jqxhr, textStatus, error) { - "use strict"; - console.error('Job status failed!'); - // set status - // "There was an error during the import routine. Please check the log files. The error seems to be: '" - $('#import-status-txt').addClass('text-danger').text(langImportFatalError + ' ' + textStatus + ' ' + error); - - // remove progress bar. - $('#import-status-holder').hide(); -} \ No newline at end of file +// } \ No newline at end of file diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index cd1b366763..8e13a610b1 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -987,7 +987,7 @@ return [ 'import_file_help' => 'Select your file', 'import_status_settings_complete' => 'The import is ready to start.', 'import_status_import_complete' => 'The import has completed.', - 'import_status_running' => 'The import is currently running. Please be patient.', + 'import_status_running' => 'The import is currently running. Please be patient.', 'import_status_header' => 'Import status and progress', 'import_status_errors' => 'Import errors', 'import_status_report' => 'Import report', @@ -1009,6 +1009,10 @@ return [ 'import_finished_intro' => 'The import has finished! You can now see the new transactions in Firefly.', 'import_finished_text_without_link' => 'It seems there is no tag that points to all your imported transactions. Please look for your imported data in the menu on the left, under "Transactions".', 'import_finished_text_with_link' => 'You can find a list of your imported transactions on the page of the tag that was created for this import.', + 'please_wait_for_status' => 'Please wait for Firefly III to check on your import', + 'box_will_refresh' => 'This box will auto-refresh...', + 'import_job_status' => 'Import routine status..', + 'see_help_top_right' => 'Welcome to Firefly\'s import routine. Please check out the help pages in the top right corner.', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'This function is not available when you are using Firefly III within a Sandstorm.io environment.', diff --git a/resources/views/import/status.twig b/resources/views/import/status.twig index 575c3fd744..1f1412a818 100644 --- a/resources/views/import/status.twig +++ b/resources/views/import/status.twig @@ -7,20 +7,45 @@ {# Initial display. Will refresh (and disappear almost immediately. #} -