First set of data mappers.

This commit is contained in:
James Cole 2016-07-02 20:40:23 +02:00
parent 57b5981904
commit 162c762973
9 changed files with 404 additions and 67 deletions

View File

@ -14,6 +14,7 @@ namespace FireflyIII\Import\Importer;
use ExpandedForm;
use FireflyIII\Crud\Account\AccountCrud;
use FireflyIII\Import\Mapper\MapperInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\ImportJob;
use Illuminate\Http\Request;
@ -111,54 +112,18 @@ class CsvImporter implements ImporterInterface
*/
public function getDataForSettings(): array
{
$config = $this->job->configuration;
$data = [
'columns' => [],
'columnCount' => 0,
];
if ($this->doColumnRoles()) {
// show user column role configuration.
$content = $this->job->uploadFileContents();
// create CSV reader.
$reader = Reader::createFromString($content);
$start = $config['has-headers'] ? 1 : 0;
$end = $start + self::EXAMPLE_ROWS; // first X rows
// collect example data in $data['columns']
while ($start < $end) {
$row = $reader->fetchOne($start);
foreach ($row as $index => $value) {
$value = trim($value);
if (strlen($value) > 0) {
$data['columns'][$index][] = $value;
}
}
$start++;
$data['columnCount'] = count($row);
}
// make unique example data
foreach ($data['columns'] as $index => $values) {
$data['columns'][$index] = array_unique($values);
}
$data['set_roles'] = [];
// collect possible column roles:
$data['available_roles'] = [];
foreach (array_keys(config('csv.import_roles')) as $role) {
$data['available_roles'][$role] = trans('csv.column_' . $role);
}
$config['column-count'] = $data['columnCount'];
$this->job->configuration = $config;
$this->job->save();
$data = $this->getDataForColumnRoles();
return $data;
}
if ($this->doColumnMapping()) {
$data = $this->getDataForColumnMapping();
return $data;
}
echo 'no settings to do.';
exit;
@ -176,6 +141,11 @@ class CsvImporter implements ImporterInterface
if ($this->doColumnRoles()) {
return 'import.csv.roles';
}
if ($this->doColumnMapping()) {
return 'import.csv.map';
}
echo 'no view for settings';
exit;
}
@ -269,6 +239,14 @@ class CsvImporter implements ImporterInterface
}
}
/**
* @return bool
*/
private function doColumnMapping(): bool
{
return $this->job->configuration['column-mapping-complete'] === false;
}
/**
* @return bool
*/
@ -276,4 +254,94 @@ class CsvImporter implements ImporterInterface
{
return $this->job->configuration['column-roles-complete'] === false;
}
/**
* @return array
*/
private function getDataForColumnMapping(): array
{
$config = $this->job->configuration;
$data = [];
foreach ($config['column-do-mapping'] as $index => $mustBeMapped) {
if ($mustBeMapped) {
$column = $config['column-roles'][$index] ?? '_ignore';
$canBeMapped = config('csv.import_roles.' . $column . '.mappable');
if ($canBeMapped) {
$mapperName = '\FireflyIII\Import\Mapper\\' . config('csv.import_roles.' . $column . '.mapper');
/** @var MapperInterface $mapper */
$mapper = new $mapperName;
$data[$index] = [
'name' => $column,
'mapper' => $mapperName,
'options' => $mapper->getMap(),
'values' => [],
];
}
}
}
echo '<pre>';
var_dump($data);
var_dump($config);
exit;
}
/**
* @return array
*/
private function getDataForColumnRoles():array
{
$config = $this->job->configuration;
$data = [
'columns' => [],
'columnCount' => 0,
];
// show user column role configuration.
$content = $this->job->uploadFileContents();
// create CSV reader.
$reader = Reader::createFromString($content);
$start = $config['has-headers'] ? 1 : 0;
$end = $start + self::EXAMPLE_ROWS; // first X rows
// collect example data in $data['columns']
while ($start < $end) {
$row = $reader->fetchOne($start);
foreach ($row as $index => $value) {
$value = trim($value);
if (strlen($value) > 0) {
$data['columns'][$index][] = $value;
}
}
$start++;
$data['columnCount'] = count($row);
}
// make unique example data
foreach ($data['columns'] as $index => $values) {
$data['columns'][$index] = array_unique($values);
}
$data['set_roles'] = [];
// collect possible column roles:
$data['available_roles'] = [];
foreach (array_keys(config('csv.import_roles')) as $role) {
$data['available_roles'][$role] = trans('csv.column_' . $role);
}
$config['column-count'] = $data['columnCount'];
$this->job->configuration = $config;
$this->job->save();
return $data;
}
}

View File

@ -0,0 +1,53 @@
<?php
/**
* AssetAccounts.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Import\Mapper;
use FireflyIII\Crud\Account\AccountCrudInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
/**
* Class AssetAccounts
*
* @package FireflyIII\Import\Mapper
*/
class AssetAccounts implements MapperInterface
{
/**
* @return array
*/
public function getMap(): array
{
/** @var AccountCrudInterface $crud */
$crud = app(AccountCrudInterface::class);
$set = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$list = [];
/** @var Account $account */
foreach ($set as $account) {
$name = $account->name;
$iban = $account->iban ?? '';
if (strlen($iban) > 0) {
$name .= ' (' . $account->iban . ')';
}
$list[$account->id] = $name;
}
asort($list);
$list = [0 => trans('csv.do_not_map')] + $list;
return $list;
}
}

View File

@ -0,0 +1,26 @@
<?php
/**
* MapperInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Import\Mapper;
/**
* Interface MapperInterface
*
* @package FireflyIII\Import\Mapper
*/
interface MapperInterface
{
/**
* @return array
*/
public function getMap(): array;
}

View File

@ -0,0 +1,57 @@
<?php
/**
* OpposingAccounts.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Import\Mapper;
use FireflyIII\Crud\Account\AccountCrudInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
/**
* Class OpposingAccounts
*
* @package FireflyIII\Import\Mapper
*/
class OpposingAccounts implements MapperInterface
{
/**
* @return array
*/
public function getMap(): array
{
/** @var AccountCrudInterface $crud */
$crud = app(AccountCrudInterface::class);
$set = $crud->getAccountsByType(
[
AccountType::DEFAULT, AccountType::ASSET,
AccountType::EXPENSE, AccountType::BENEFICIARY,
AccountType::REVENUE
]);
$list = [];
/** @var Account $account */
foreach ($set as $account) {
$name = $account->name;
$iban = $account->iban ?? '';
if (strlen($iban) > 0) {
$name .= ' (' . $account->iban . ')';
}
$list[$account->id] = $name;
}
asort($list);
$list = [0 => trans('csv.do_not_map')] + $list;
return $list;
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* TransactionCurrencies.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Import\Mapper;
use FireflyIII\Models\TransactionCurrency as TC;
/**
* Class TransactionCurrencies
*
* @package FireflyIII\Import\Mapper
*/
class TransactionCurrencies implements MapperInterface
{
/**
* @return array
*/
public function getMap(): array
{
$currencies = TC::get();
$list = [];
foreach ($currencies as $currency) {
$list[$currency->id] = $currency->name . ' (' . $currency->code . ')';
}
asort($list);
$list = [0 => trans('csv.do_not_map')] + $list;
return $list;
}
}

View File

@ -13,6 +13,8 @@ namespace FireflyIII\Models;
use Auth;
use Crypt;
use FireflyIII\Exceptions\FireflyException;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -183,10 +185,14 @@ class Account extends Model
*/
public function getIbanAttribute($value): string
{
if (is_null($value)) {
if (is_null($value) || strlen(strval($value)) === 0) {
return '';
}
$result = Crypt::decrypt($value);
try {
$result = Crypt::decrypt($value);
} catch (DecryptException $e) {
throw new FireflyException('Cannot decrypt value "' . $value . '" for account #' . $this->id);
}
if (is_null($result)) {
return '';
}

View File

@ -25,37 +25,37 @@ return [
'mappable' => false,
'field' => 'bill',
'converter' => 'BillId',
'mapper' => 'Bill',
'mapper' => 'Bills',
],
'bill-name' => [
'mappable' => true,
'converter' => 'BillName',
'field' => 'bill',
'mapper' => 'Bill',
'mapper' => 'Bills',
],
'currency-id' => [
'mappable' => true,
'converter' => 'CurrencyId',
'field' => 'currency',
'mapper' => 'TransactionCurrency'
'mapper' => 'TransactionCurrencies'
],
'currency-name' => [
'mappable' => true,
'converter' => 'CurrencyName',
'field' => 'currency',
'mapper' => 'TransactionCurrency'
'mapper' => 'TransactionCurrencies'
],
'currency-code' => [
'mappable' => true,
'converter' => 'CurrencyCode',
'field' => 'currency',
'mapper' => 'TransactionCurrency'
'mapper' => 'TransactionCurrencies'
],
'currency-symbol' => [
'mappable' => true,
'converter' => 'CurrencySymbol',
'field' => 'currency',
'mapper' => 'TransactionCurrency'
'mapper' => 'TransactionCurrencies'
],
'description' => [
'mappable' => false,
@ -76,13 +76,13 @@ return [
'mappable' => true,
'converter' => 'BudgetId',
'field' => 'budget',
'mapper' => 'Budget',
'mapper' => 'Budgets',
],
'budget-name' => [
'mappable' => true,
'converter' => 'BudgetName',
'field' => 'budget',
'mapper' => 'Budget',
'mapper' => 'Budgets',
],
'rabo-debet-credit' => [
'mappable' => false,
@ -98,73 +98,73 @@ return [
'mappable' => true,
'converter' => 'CategoryId',
'field' => 'category',
'mapper' => 'Category',
'mapper' => 'Categories',
],
'category-name' => [
'mappable' => true,
'converter' => 'CategoryName',
'field' => 'category',
'mapper' => 'Category',
'mapper' => 'Categories',
],
'tags-comma' => [
'mappable' => true,
'field' => 'tags',
'converter' => 'TagsComma',
'mapper' => 'Tag',
'mapper' => 'Tags',
],
'tags-space' => [
'mappable' => true,
'field' => 'tags',
'converter' => 'TagsSpace',
'mapper' => 'Tag',
'mapper' => 'Tags',
],
'account-id' => [
'mappable' => true,
'mapper' => 'AssetAccount',
'mapper' => 'AssetAccountId',
'field' => 'asset-account-id',
'converter' => 'AccountId'
'converter' => 'AssetAccounts'
],
'account-name' => [
'mappable' => true,
'mapper' => 'AssetAccount',
'mapper' => 'AssetAccountName',
'field' => 'asset-account-name',
'converter' => 'AssetAccountName'
'converter' => 'AssetAccounts'
],
'account-iban' => [
'mappable' => true,
'converter' => 'AssetAccountIban',
'field' => 'asset-account-iban',
'mapper' => 'AssetAccount'
'mapper' => 'AssetAccounts'
],
'account-number' => [
'mappable' => true,
'converter' => 'AssetAccountNumber',
'field' => 'asset-account-number',
'mapper' => 'AssetAccount'
'mapper' => 'AssetAccounts'
],
'opposing-id' => [
'mappable' => true,
'field' => 'opposing-account-id',
'converter' => 'OpposingAccountId',
'mapper' => 'AnyAccount',
'mapper' => 'OpposingAccounts',
],
'opposing-name' => [
'mappable' => true,
'field' => 'opposing-account-name',
'converter' => 'OpposingAccountName',
'mapper' => 'AnyAccount',
'mapper' => 'OpposingAccounts',
],
'opposing-iban' => [
'mappable' => true,
'field' => 'opposing-account-iban',
'converter' => 'OpposingAccountIban',
'mapper' => 'AnyAccount',
'mapper' => 'OpposingAccounts',
],
'opposing-number' => [
'mappable' => true,
'field' => 'opposing-account-number',
'converter' => 'OpposingAccountNumber',
'mapper' => 'AnyAccount',
'mapper' => 'OpposingAccounts',
],
'amount' => [
'mappable' => false,

View File

@ -32,6 +32,7 @@ return [
'column' => 'Column',
'no_example_data' => 'No example data available',
'store_column_roles' => 'Continue import',
'do_not_map' => '(do not map)',
'column__ignore' => '(ignore this column)',
'column_account-iban' => 'Asset account (IBAN)',

View File

@ -0,0 +1,84 @@
{% extends "./layout/default.twig" %}
{% block breadcrumbs %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('csv.map_title') }}</h3>
</div>
<div class="box-body">
<p>
{{ trans('csv.map_text') }}
</p>
</div>
</div>
</div>
</div>
<form action="{{ route('import.postSettings', job.key) }}" method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
{#
{% for index,columnName in map %}
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ Config.get('csv.roles.'~columnName~'.name') }}</h3>
</div>
<div class="box-body no-padding">
<table class="table table-hover">
<thead>
<tr>
<th style="width:50%;">{{ 'csv_field_value'|_ }}</th>
<th>{{ 'csv_field_mapped_to'|_ }}</th>
</tr>
</thead>
<tbody>
{% for value in values[index] %}
<tr>
<td><code>{{ value }}</code></td>
<td>
{{ Form.select('mapping['~index~']['~value~']',options[index], mapped[index][value], {class: 'form-control'}) }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endfor %}
#}
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-body">
<button type="submit" class="btn btn-success pull-right">
{{ trans('csv.store_column_mapping') }} <i class="fa fa-arrow-right"></i>
</button>
</div>
</div>
</div>
</div>
</form>
{% endblock %}