Greatly expanded the CSV routine.

This commit is contained in:
James Cole
2015-07-05 06:18:02 +02:00
parent d2c018f7da
commit 65122f0144
22 changed files with 1282 additions and 210 deletions

View File

@@ -0,0 +1,46 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 05/07/15
* Time: 05:49
*/
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
/**
* Class AccountIban
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class AccountIban extends BasicConverter implements ConverterInterface
{
/**
* @return Account|null
*/
public function convert()
{
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$account = Auth::user()->accounts()->find($this->mapped[$this->index][$this->value]);
} else {
// find or create new account:
$accountType = AccountType::where('type', 'Asset account')->first();
$account = Account::firstOrCreateEncrypted(
[
'name' => $this->value,
//'iban' => $this->value,
'user_id' => Auth::user()->id,
'account_type_id' => $accountType->id
]
);
}
return $account;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 05/07/15
* Time: 05:49
*/
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Account;
/**
* Class Amount
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class Amount extends BasicConverter implements ConverterInterface
{
/**
* @return Account|null
*/
public function convert()
{
if (is_numeric($this->value)) {
return $this->value;
}
return 0;
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class BasicConverter
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class BasicConverter
{
/** @var array */
protected $data;
protected $index;
/** @var array */
protected $mapped;
protected $role;
protected $value;
/**
* @return array
*/
public function getData()
{
return $this->data;
}
/**
* @param array $data
*/
public function setData($data)
{
$this->data = $data;
}
/**
* @return mixed
*/
public function getIndex()
{
return $this->index;
}
/**
* @param mixed $index
*/
public function setIndex($index)
{
$this->index = $index;
}
/**
* @return array
*/
public function getMapped()
{
return $this->mapped;
}
/**
* @param array $mapped
*/
public function setMapped($mapped)
{
$this->mapped = $mapped;
}
/**
* @return mixed
*/
public function getRole()
{
return $this->role;
}
/**
* @param mixed $role
*/
public function setRole($role)
{
$this->role = $role;
}
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* @param mixed $value
*/
public function setValue($value)
{
$this->value = $value;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 05/07/15
* Time: 05:42
*/
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Interface ConverterInterface
*
* @package FireflyIII\Helpers\Csv\Converter
*/
interface ConverterInterface
{
/**
* @return mixed
*/
public function convert();
/**
* @param $index
*/
public function setIndex($index);
/**
* @param $mapped
*/
public function setMapped($mapped);
/**
* @param $role
*/
public function setRole($role);
/**
* @param $value
*/
public function setValue($value);
/**
* @param array $data
*
* @return mixed
*/
public function setData(array $data);
}

View File

@@ -0,0 +1,27 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\TransactionCurrency;
/**
* Class CurrencyCode
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CurrencyCode extends BasicConverter implements ConverterInterface
{
/**
* @return mixed|static
*/
public function convert()
{
if (isset($this->mapped[$this->index][$this->value])) {
$currency = TransactionCurrency::find($this->mapped[$this->index][$this->value]);
} else {
$currency = TransactionCurrency::whereCode($this->value)->first();
}
return $currency;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 05/07/15
* Time: 05:49
*/
namespace FireflyIII\Helpers\Csv\Converter;
use Carbon\Carbon;
use Session;
/**
* Class Date
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class Date extends BasicConverter implements ConverterInterface
{
/**
* @return Carbon
*/
public function convert()
{
$format = Session::get('csv-date-format');
$date = Carbon::createFromFormat($format, $this->value);
return $date;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Account;
/**
* Class Amount
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class Ignore extends BasicConverter implements ConverterInterface
{
/**
* @return Account|null
*/
public function convert()
{
return null;
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 05/07/15
* Time: 06:12
*/
namespace FireflyIII\Helpers\Csv\Converter;
class RabobankDebetCredit extends BasicConverter implements ConverterInterface
{
/**
* @return mixed
*/
public function convert()
{
if ($this->value == 'D') {
return -1;
}
return 1;
}
}

235
app/Helpers/Csv/Data.php Normal file
View File

@@ -0,0 +1,235 @@
<?php
namespace FireflyIII\Helpers\Csv;
use Crypt;
use League\Csv\Reader;
use Session;
/**
* Class Data
*
* @package FireflyIII\Helpers\Csv
*/
class Data
{
/** @var string */
protected $csvFileContent;
/** @var string */
protected $csvFileLocation;
/** @var string */
protected $dateFormat;
/** @var bool */
protected $hasHeaders;
/** @var array */
protected $map;
/** @var array */
protected $mapped;
/** @var Reader */
protected $reader;
/** @var array */
protected $roles;
/**
*
*/
public function __construct()
{
$this->sessionHasHeaders();
$this->sessionDateFormat();
$this->sessionCsvFileLocation();
$this->sessionMap();
$this->sessionRoles();
$this->sessionMapped();
}
protected function sessionHasHeaders()
{
if (Session::has('csv-has-headers')) {
$this->hasHeaders = (bool)Session::get('csv-has-headers');
}
}
protected function sessionDateFormat()
{
if (Session::has('csv-date-format')) {
$this->dateFormat = (string)Session::get('csv-date-format');
}
}
protected function sessionCsvFileLocation()
{
if (Session::has('csv-file')) {
$this->csvFileLocation = (string)Session::get('csv-file');
}
}
protected function sessionMap()
{
if (Session::has('csv-map')) {
$this->map = (array)Session::get('csv-map');
}
}
protected function sessionRoles()
{
if (Session::has('csv-roles')) {
$this->roles = (array)Session::get('csv-roles');
}
}
protected function sessionMapped()
{
if (Session::has('csv-mapped')) {
$this->mapped = (array)Session::get('csv-mapped');
}
}
/**
* @return string
*/
public function getDateFormat()
{
return $this->dateFormat;
}
/**
* @param mixed $dateFormat
*/
public function setDateFormat($dateFormat)
{
Session::put('csv-date-format', $dateFormat);
$this->dateFormat = $dateFormat;
}
/**
* @return bool
*/
public function getHasHeaders()
{
return $this->hasHeaders;
}
/**
* @param bool $hasHeaders
*/
public function setHasHeaders($hasHeaders)
{
Session::put('csv-has-headers', $hasHeaders);
$this->hasHeaders = $hasHeaders;
}
/**
* @return array
*/
public function getMap()
{
return $this->map;
}
/**
* @param array $map
*/
public function setMap(array $map)
{
Session::put('csv-map', $map);
$this->map = $map;
}
/**
* @return array
*/
public function getMapped()
{
return $this->mapped;
}
/**
* @param array $mapped
*/
public function setMapped(array $mapped)
{
Session::put('csv-mapped', $mapped);
$this->mapped = $mapped;
}
/**
* @return Reader
*/
public function getReader()
{
if (strlen($this->csvFileContent) === 0) {
$this->loadCsvFile();
}
if (is_null($this->reader)) {
$this->reader = Reader::createFromString($this->getCsvFileContent());
}
return $this->reader;
}
protected function loadCsvFile()
{
$file = $this->getCsvFileLocation();
$content = file_get_contents($file);
$contentDecrypted = Crypt::decrypt($content);
$this->setCsvFileContent($contentDecrypted);
}
/**
* @return string
*/
public function getCsvFileLocation()
{
return $this->csvFileLocation;
}
/**
* @param string $csvFileLocation
*/
public function setCsvFileLocation($csvFileLocation)
{
Session::put('csv-file', $csvFileLocation);
$this->csvFileLocation = $csvFileLocation;
}
/**
* @return string
*/
public function getCsvFileContent()
{
return $this->csvFileContent;
}
/**
* @param string $csvFileContent
*/
public function setCsvFileContent($csvFileContent)
{
$this->csvFileContent = $csvFileContent;
}
/**
* @return array
*/
public function getRoles()
{
return $this->roles;
}
/**
* @param array $roles
*/
public function setRoles(array $roles)
{
Session::put('csv-roles', $roles);
$this->roles = $roles;
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace FireflyIII\Helpers\Csv;
use Auth;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionCurrency;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* Class DataGrabber
*
* Class dedicated to retreiving all sorts of data related to the CSV import.
*
* @package FireflyIII\Helpers\Csv
*/
class DataGrabber
{
/**
* @return array
*/
public function getAssetAccounts()
{
$result = Auth::user()->accounts()->with(
['accountmeta' => function (HasMany $query) {
$query->where('name', 'accountRole');
}]
)->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
$list = [];
/** @var Account $account */
foreach ($result as $account) {
$list[$account->id] = $account->name;
}
return $list;
}
/**
* @return array
*/
public function getCurrencies()
{
$currencies = TransactionCurrency::get();
$list = [];
foreach ($currencies as $currency) {
$list[$currency->id] = $currency->name . ' (' . $currency->code . ')';
}
return $list;
}
}

View File

@@ -0,0 +1,175 @@
<?php
namespace FireflyIII\Helpers\Csv;
use App;
use Carbon\Carbon;
use Config;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Csv\Converter\ConverterInterface;
/**
* Class Importer
*
* @package FireflyIII\Helpers\Csv
*/
class Importer
{
/** @var Data */
protected $data;
/** @var array */
protected $map;
/** @var array */
protected $mapped;
/** @var array */
protected $roles;
/**
* @param $value
*/
public function parseRaboDebetCredit($value)
{
if ($value == 'D') {
return -1;
}
return 1;
}
/**
*
*/
public function run()
{
$this->map = $this->data->getMap();
$this->roles = $this->data->getRoles();
$this->mapped = $this->data->getMapped();
foreach ($this->data->getReader() as $row) {
$this->importRow($row);
}
}
/**
* @param $row
*
* @throws FireflyException
*/
protected function importRow($row)
{
/*
* These fields are necessary to create a new transaction journal. Some are optional:
*/
$data = $this->getFiller();
foreach ($row as $index => $value) {
$role = isset($this->roles[$index]) ? $this->roles[$index] : '_ignore';
$class = Config::get('csv.roles.' . $role . '.converter');
$field = Config::get('csv.roles.' . $role . '.field');
if (is_null($class)) {
throw new FireflyException('No converter for field of type "' . $role . '".');
}
if (is_null($field)) {
throw new FireflyException('No place to store value of type "' . $role . '".');
}
/** @var ConverterInterface $converter */
$converter = App::make('FireflyIII\Helpers\Csv\Converter\\' . $class);
$converter->setData($data); // the complete array so far.
$converter->setIndex($index);
$converter->setValue($value);
$converter->setRole($role);
// if (is_array($field)) {
// $convertResult = $converter->convert();
// foreach ($field as $fieldName) {
// $data[$fieldName] = $convertResult[$fieldName];
// }
// } else {
$data[$field] = $converter->convert();
// }
// case 'description':
// $data['description'] .= ' ' . $value;
// break;
// case '_ignore':
// ignore! (duh)
// break;
// case 'account-iban':
// $data['asset-account'] = $this->findAssetAccount($index, $value);
// break;
// case 'currency-code':
// $data['currency'] = $this->findCurrency($index, $value, $role);
// break;
// case 'date-transaction':
// $data['date'] = $this->parseDate($value);
// break;
// case 'rabo-debet-credit':
// $data['amount-modifier'] = $this->parseRaboDebetCredit($value);
// break;
// default:
// throw new FireflyException('Cannot process row of type "' . $role . '".');
// break;
}
$data = $this->postProcess($data);
var_dump($data);
exit;
}
/**
* @return array
*/
protected function getFiller()
{
return [
'description' => '',
'asset-account' => null,
'date' => null,
'currency' => null,
'amount' => null,
'amount-modifier' => 1,
'ignored' => null,
];
}
/**
* @param array $data
*
* @return array
*/
protected function postProcess(array $data)
{
$data['description'] = trim($data['description']);
return $data;
}
/**
* @param Data $data
*/
public function setData($data)
{
$this->data = $data;
}
/**
* @param $value
*
* @return Carbon
*/
protected function parseDate($value)
{
return Carbon::createFromFormat($this->data->getDateFormat(), $value);
}
}

178
app/Helpers/Csv/Wizard.php Normal file
View File

@@ -0,0 +1,178 @@
<?php
namespace FireflyIII\Helpers\Csv;
use Auth;
use Config;
use Crypt;
use FireflyIII\Exceptions\FireflyException;
use League\Csv\Reader;
use Session;
/**
* Class Wizard
*
* @package FireflyIII\Helpers\Csv
*/
class Wizard implements WizardInterface
{
/**
* @param Reader $reader
* @param array $map
* @param bool $hasHeaders
*
* @return array
*/
public function getMappableValues($reader, array $map, $hasHeaders)
{
$values = [];
/*
* Loop over the CSV and collect mappable data:
*/
foreach ($reader as $index => $row) {
if (($hasHeaders && $index > 1) || !$hasHeaders) {
// collect all map values
foreach ($map as $column => $irrelevant) {
// check if $irrelevant is mappable!
$values[$column][] = $row[$column];
}
}
}
/*
* Make each one unique.
*/
foreach ($values as $column => $found) {
$values[$column] = array_unique($found);
}
return $values;
}
/**
* @param array $roles
* @param mixed $map
*
* @return array
*/
public function processSelectedMapping(array $roles, $map)
{
$configRoles = Config::get('csv.roles');
$maps = [];
if (is_array($map)) {
foreach ($map as $index => $field) {
if (isset($roles[$index])) {
$name = $roles[$index];
if ($configRoles[$name]['mappable']) {
$maps[$index] = $name;
}
}
}
}
return $maps;
}
/**
* @param mixed $input
*
* @return array
*/
public function processSelectedRoles($input)
{
$roles = [];
/*
* Store all rows for each column:
*/
if (is_array($input)) {
foreach ($input as $index => $role) {
if ($role != '_ignore') {
$roles[$index] = $role;
}
}
}
return $roles;
}
/**
* @param array $fields
*
* @return bool
*/
public function sessionHasValues(array $fields)
{
foreach ($fields as $field) {
if (!Session::has($field)) {
return false;
}
}
return true;
}
/**
* @param array $map
*
* @return array
* @throws FireflyException
*/
public function showOptions(array $map)
{
$dataGrabber = new DataGrabber;
$options = [];
foreach ($map as $index => $columnRole) {
/*
* Depending on the column role, get the relevant data from the database.
* This needs some work to be optimal.
*/
switch ($columnRole) {
default:
throw new FireflyException('Cannot map field of type "' . $columnRole . '".');
break;
case 'account-iban':
$set = $dataGrabber->getAssetAccounts();
break;
case 'currency-code':
$set = $dataGrabber->getCurrencies();
break;
}
/*
* Make select list kind of thing:
*/
$options[$index] = $set;
}
return $options;
}
/**
* @param $path
*
* @return string
*/
public function storeCsvFile($path)
{
$time = str_replace(' ', '-', microtime());
$fileName = 'csv-upload-' . Auth::user()->id . '-' . $time . '.csv.encrypted';
$fullPath = storage_path('upload') . DIRECTORY_SEPARATOR . $fileName;
$content = file_get_contents($path);
$contentEncrypted = Crypt::encrypt($content);
file_put_contents($fullPath, $contentEncrypted);
return $fullPath;
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace FireflyIII\Helpers\Csv;
use League\Csv\Reader;
/**
* Interface WizardInterface
*
* @package FireflyIII\Helpers\Csv
*/
interface WizardInterface
{
/**
* @param Reader $reader
* @param array $map
* @param bool $hasHeaders
*
* @return array
*/
public function getMappableValues($reader, array $map, $hasHeaders);
/**
* @param array $roles
* @param mixed $map
*
* @return array
*/
public function processSelectedMapping(array $roles, $map);
/**
* @param mixed $input
*
* @return array
*/
public function processSelectedRoles($input);
/**
* @param array $fields
*
* @return bool
*/
public function sessionHasValues(array $fields);
/**
* @param array $map
*
* @return array
*/
public function showOptions(array $map);
/**
* @param $path
*
* @return string
*/
public function storeCsvFile($path);
}