Update rule handling and views.

This commit is contained in:
James Cole 2019-07-27 15:01:13 +02:00
parent 67c0ef6ec6
commit 5524941c90
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
14 changed files with 112 additions and 478 deletions

View File

@ -27,10 +27,11 @@ use FireflyIII\Api\V1\Requests\RuleRequest;
use FireflyIII\Api\V1\Requests\RuleTestRequest;
use FireflyIII\Api\V1\Requests\RuleTriggerRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Jobs\ExecuteRuleOnExistingTransactions;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Rule;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\TransactionRules\Engine\RuleEngine;
use FireflyIII\TransactionRules\TransactionMatcher;
use FireflyIII\Transformers\RuleTransformer;
use FireflyIII\Transformers\TransactionGroupTransformer;
@ -239,19 +240,27 @@ class RuleController extends Controller
// Get parameters specified by the user
$parameters = $request->getTriggerParameters();
// Create a job to do the work asynchronously
$job = new ExecuteRuleOnExistingTransactions($rule);
/** @var RuleEngine $ruleEngine */
$ruleEngine = app(RuleEngine::class);
$ruleEngine->setUser(auth()->user());
// Apply parameters to the job
/** @var User $user */
$user = auth()->user();
$job->setUser($user);
$job->setAccounts($parameters['accounts']);
$job->setStartDate($parameters['start_date']);
$job->setEndDate($parameters['end_date']);
$rules = [$rule->id];
// Dispatch a new job to execute it in a queue
$this->dispatch($job);
$ruleEngine->setRulesToApply($rules);
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts($parameters['accounts']);
$collector->setRange($parameters['start_date'], $parameters['end_date']);
$journals = $collector->getExtractedJournals();
/** @var array $journal */
foreach ($journals as $journal) {
Log::debug('Start of new journal.');
$ruleEngine->processJournalArray($journal);
Log::debug('Done with all rules for this group + done with journal.');
}
return response()->json([], 204);
}

View File

@ -28,11 +28,12 @@ use FireflyIII\Api\V1\Requests\RuleGroupRequest;
use FireflyIII\Api\V1\Requests\RuleGroupTestRequest;
use FireflyIII\Api\V1\Requests\RuleGroupTriggerRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Jobs\ExecuteRuleOnExistingTransactions;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\TransactionRules\Engine\RuleEngine;
use FireflyIII\TransactionRules\TransactionMatcher;
use FireflyIII\Transformers\RuleGroupTransformer;
use FireflyIII\Transformers\RuleTransformer;
@ -296,25 +297,34 @@ class RuleGroupController extends Controller
*/
public function triggerGroup(RuleGroupTriggerRequest $request, RuleGroup $group): JsonResponse
{
// Get parameters specified by the user
/** @var User $user */
$user = auth()->user();
$parameters = $request->getTriggerParameters();
/** @var Collection $rules */
$rules = $this->ruleGroupRepository->getActiveRules($group);
foreach ($rules as $rule) {
// Create a job to do the work asynchronously
$job = new ExecuteRuleOnExistingTransactions($rule);
/** @var Collection $collection */
$collection = $this->ruleGroupRepository->getActiveRules($group);
$rules = [];
/** @var Rule $item */
foreach ($collection as $item) {
$rules[] = $item->id;
}
// Apply parameters to the job
$job->setUser($user);
$job->setAccounts($parameters['accounts']);
$job->setStartDate($parameters['start_date']);
$job->setEndDate($parameters['end_date']);
// start looping.
/** @var RuleEngine $ruleEngine */
$ruleEngine = app(RuleEngine::class);
$ruleEngine->setUser(auth()->user());
$ruleEngine->setRulesToApply($rules);
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
// Dispatch a new job to execute it in a queue
$this->dispatch($job);
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts($parameters['accounts']);
$collector->setRange($parameters['start_date'], $parameters['end_date']);
$journals = $collector->getExtractedJournals();
/** @var array $journal */
foreach ($journals as $journal) {
Log::debug('Start of new journal.');
$ruleEngine->processJournalArray($journal);
Log::debug('Done with all rules for this group + done with journal.');
}
return response()->json([], 204);

View File

@ -26,6 +26,7 @@ namespace FireflyIII\Http\Controllers\Rule;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\SelectTransactionsRequest;
use FireflyIII\Http\Requests\TestRuleFormRequest;
@ -34,6 +35,7 @@ use FireflyIII\Models\Rule;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Http\Controllers\RequestInformation;
use FireflyIII\Support\Http\Controllers\RuleManagement;
use FireflyIII\TransactionRules\Engine\RuleEngine;
use FireflyIII\TransactionRules\TransactionMatcher;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
@ -88,18 +90,26 @@ class SelectController extends Controller
$accounts = $this->accountRepos->getAccountsById($request->get('accounts'));
$startDate = new Carbon($request->get('start_date'));
$endDate = new Carbon($request->get('end_date'));
$rules = [$rule->id];
// Create a job to do the work asynchronously
$job = new ExecuteRuleOnExistingTransactions($rule);
/** @var RuleEngine $ruleEngine */
$ruleEngine = app(RuleEngine::class);
$ruleEngine->setUser(auth()->user());
$ruleEngine->setRulesToApply($rules);
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
// Apply parameters to the job
$job->setUser($user);
$job->setAccounts($accounts);
$job->setStartDate($startDate);
$job->setEndDate($endDate);
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts);
$collector->setRange($startDate, $endDate);
$journals = $collector->getExtractedJournals();
// Dispatch a new job to execute it in a queue
$this->dispatch($job);
/** @var array $journal */
foreach ($journals as $journal) {
Log::debug('Start of new journal.');
$ruleEngine->processJournalArray($journal);
Log::debug('Done with all rules for this group + done with journal.');
}
// Tell the user that the job is queued
session()->flash('success', (string)trans('firefly.applied_rule_selection', ['title' => $rule->title]));

View File

@ -23,13 +23,16 @@ namespace FireflyIII\Http\Controllers\RuleGroup;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\SelectTransactionsRequest;
use FireflyIII\Jobs\ExecuteRuleGroupOnExistingTransactions;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\User;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\TransactionRules\Engine\RuleEngine;
use Illuminate\Http\RedirectResponse;
use Log;
/**
* Class ExecutionController
@ -39,6 +42,9 @@ class ExecutionController extends Controller
/** @var AccountRepositoryInterface */
private $repository;
/** @var RuleGroupRepositoryInterface */
private $ruleGroupRepository;
/**
* ExecutionController constructor.
* @codeCoverageIgnore
@ -52,7 +58,8 @@ class ExecutionController extends Controller
app('view')->share('title', (string)trans('firefly.rules'));
app('view')->share('mainTitleIcon', 'fa-random');
$this->repository = app(AccountRepositoryInterface::class);
$this->repository = app(AccountRepositoryInterface::class);
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
return $next($request);
}
@ -72,23 +79,36 @@ class ExecutionController extends Controller
public function execute(SelectTransactionsRequest $request, RuleGroup $ruleGroup): RedirectResponse
{
// Get parameters specified by the user
/** @var User $user */
$user = auth()->user();
$accounts = $this->repository->getAccountsById($request->get('accounts'));
$startDate = new Carbon($request->get('start_date'));
$endDate = new Carbon($request->get('end_date'));
// Create a job to do the work asynchronously
$job = new ExecuteRuleGroupOnExistingTransactions($ruleGroup);
// start looping.
/** @var RuleEngine $ruleEngine */
$ruleEngine = app(RuleEngine::class);
$ruleEngine->setUser(auth()->user());
// Apply parameters to the job
$job->setUser($user);
$job->setAccounts($accounts);
$job->setStartDate($startDate);
$job->setEndDate($endDate);
$rules = [];
/** @var Rule $rule */
foreach ($this->ruleGroupRepository->getActiveRules($ruleGroup) as $rule) {
$rules[] = $rule->id;
}
// Dispatch a new job to execute it in a queue
$this->dispatch($job);
$ruleEngine->setRulesToApply($rules);
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts);
$collector->setRange($startDate, $endDate);
$journals = $collector->getExtractedJournals();
/** @var array $journal */
foreach ($journals as $journal) {
Log::debug('Start of new journal.');
$ruleEngine->processJournalArray($journal);
Log::debug('Done with all rules for this group + done with journal.');
}
// Tell the user that the job is queued
session()->flash('success', (string)trans('firefly.applied_rule_group_selection', ['title' => $ruleGroup->title]));

View File

@ -1,214 +0,0 @@
<?php
/**
* ExecuteRuleGroupOnExistingTransactions.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Jobs;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\RuleGroup;
use FireflyIII\TransactionRules\Processor;
use FireflyIII\User;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
/**
* Class ExecuteRuleGroupOnExistingTransactions.
* TODO make sure this job honors the "stop_processing" rules.
*/
class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
/** @var Collection Set of accounts */
private $accounts;
/** @var Carbon The end date */
private $endDate;
/** @var RuleGroup The rule group */
private $ruleGroup;
/** @var Carbon The start date */
private $startDate;
/** @var User The user */
private $user;
/**
* Create a new job instance.
*
* @param RuleGroup $ruleGroup
*/
public function __construct(RuleGroup $ruleGroup)
{
$this->ruleGroup = $ruleGroup;
}
/**
* Get accounts.
*
* @return Collection
*/
public function getAccounts(): Collection
{
return $this->accounts;
}
/**
* Set accounts.
*
* @param Collection $accounts
*/
public function setAccounts(Collection $accounts)
{
$this->accounts = $accounts;
}
/**
* Get end date.
*
* @return \Carbon\Carbon
*/
public function getEndDate(): Carbon
{
return $this->endDate;
}
/**
* Set end date.
*
* @param Carbon $date
*/
public function setEndDate(Carbon $date)
{
$this->endDate = $date;
}
/**
* Get start date.
*
* @return \Carbon\Carbon
*/
public function getStartDate(): Carbon
{
return $this->startDate;
}
/**
* Set start date.
*
* @param Carbon $date
*/
public function setStartDate(Carbon $date)
{
$this->startDate = $date;
}
/**
* Get user.
*
* @return User
*/
public function getUser(): User
{
return $this->user;
}
/**
* Set user.
*
* @param User $user
*/
public function setUser(User $user)
{
$this->user = $user;
}
/**
* Execute the job.
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function handle()
{
// Lookup all journals that match the parameters specified
$journals = $this->collectJournals();
// Find processors for each rule within the current rule group
$processors = $this->collectProcessors();
// Execute the rules for each transaction
foreach ($processors as $processor) {
/** @var array $journal */
foreach ($journals as $journal) {
/** @var Processor $processor */
$processor->handleJournalArray($journal);
}
// Stop processing this group if the rule specifies 'stop_processing'
// TODO Fix this.
if ($processor->getRule()->stop_processing) {
break;
}
}
}
/**
* Collect all journals that should be processed.
*
* @return array
*/
protected function collectJournals(): array
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user);
$collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate);
return $collector->getExtractedJournals();
}
/**
* Collects a list of rule processors, one for each rule within the rule group.
*
* @return array
*/
protected function collectProcessors(): array
{
// Find all rules belonging to this rulegroup
$rules = $this->ruleGroup->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'store-journal')
->where('rules.active', 1)
->get(['rules.*']);
// Create a list of processors for these rules
return array_map(
function ($rule) {
/** @var Processor $processor */
$processor = app(Processor::class);
$processor->make($rule);
return $processor;
},
$rules->all()
);
}
}

View File

@ -1,202 +0,0 @@
<?php
/**
* ExecuteRuleOnExistingTransactions.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Jobs;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Rule;
use FireflyIII\TransactionRules\Processor;
use FireflyIII\User;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Log;
/**
* Class ExecuteRuleOnExistingTransactions.
*/
class ExecuteRuleOnExistingTransactions extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
/** @var Collection The accounts */
private $accounts;
/** @var Carbon The end date */
private $endDate;
/** @var Rule The current rule */
private $rule;
/** @var Carbon The start date */
private $startDate;
/** @var User The user */
private $user;
/**
* Create a new job instance.
*
* @param Rule $rule
*/
public function __construct(Rule $rule)
{
$this->rule = $rule;
}
/**
* Get accounts.
*
* @return Collection
*/
public function getAccounts(): Collection
{
return $this->accounts;
}
/**
* Set accounts.
*
* @param Collection $accounts
*/
public function setAccounts(Collection $accounts)
{
$this->accounts = $accounts;
}
/**
* Get end date.
*
* @return \Carbon\Carbon
*/
public function getEndDate(): Carbon
{
return $this->endDate;
}
/**
* Set end date.
*
* @param Carbon $date
*/
public function setEndDate(Carbon $date)
{
$this->endDate = $date;
}
/**
* Get rule.
*
* @return Rule
*/
public function getRule(): Rule
{
return $this->rule;
}
/**
* Get start date.
*
* @return \Carbon\Carbon
*/
public function getStartDate(): Carbon
{
return $this->startDate;
}
/**
* Set start date.
*
* @param Carbon $date
*/
public function setStartDate(Carbon $date)
{
$this->startDate = $date;
}
/**
* Get user.
*
* @return User
*/
public function getUser(): User
{
return $this->user;
}
/**
* Set user.
*
* @param User $user
*/
public function setUser(User $user)
{
$this->user = $user;
}
/**
* Execute the job.
*
* @throws \FireflyIII\Exceptions\FireflyException
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function handle()
{
// Lookup all journals that match the parameters specified
$journals = $this->collectJournals();
/** @var Processor $processor */
$processor = app(Processor::class);
$processor->make($this->rule, true);
$hits = 0;
$misses = 0;
$total = 0;
// Execute the rules for each transaction
/** @var array $journal */
foreach ($journals as $journal) {
++$total;
$result = $processor->handleJournalArray($journal);
if ($result) {
++$hits;
}
if (!$result) {
++$misses;
}
Log::info(sprintf('Current progress: %d Transactions. Hits: %d, misses: %d', $total, $hits, $misses));
}
Log::info(sprintf('Total transactions: %d. Hits: %d, misses: %d', $total, $hits, $misses));
}
/**
* Collect all journals that should be processed.
*
* @return array
*/
protected function collectJournals(): array
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user);
$collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate);
return $collector->getExtractedJournals();
}
}

View File

@ -165,7 +165,8 @@ class RuleEngine
*/
public function processJournalArray(array $journal): void
{
Log::debug(sprintf('Will process transaction journal #%d ("%s")', $journal['id'], $journal['description']));
$journalId = $journal['id'] ?? $journal['transaction_journal_id'];
Log::debug(sprintf('Will process transaction journal #%d ("%s")', $journalId, $journal['description']));
/** @var RuleGroup $group */
foreach ($this->ruleGroups as $group) {
Log::debug(sprintf('Now at rule group #%d', $group->id));

View File

@ -6,7 +6,7 @@
{% block content %}
{% if bills.count == 0 %}
{% include 'partials.empty' with {what: 'default', type: 'bills',route: route('bills.create')} %}
{% include 'partials.empty' with {objectType: 'default', type: 'bills',route: route('bills.create')} %}
{% else %}
<div class="row">
<div class="col-lg-12 col-sm-12 col-md-12">

View File

@ -85,7 +85,7 @@
</div>
</div>
{% if paginator.count == 0 and inactive.count == 0 and page == 1 %}
{% include 'partials.empty' with {what: 'default', type: 'budgets',route: route('budgets.create')} %}
{% include 'partials.empty' with {objectType: 'default', type: 'budgets',route: route('budgets.create')} %}
{# make FF ignore demo for now. #}
{% set shownDemo = true %}
{% endif %}

View File

@ -35,7 +35,7 @@
</div>
</div>
{% else %}
{% include 'partials.empty' with {what: 'default', type: 'categories',route: route('categories.create')} %}
{% include 'partials.empty' with {objectType: 'default', type: 'categories',route: route('categories.create')} %}
{% endif %}
{% endblock %}

View File

@ -2,19 +2,19 @@
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12 col-lg-offset-3 col-md-offset-3">
<div class="box box-success">
<div class="box-header with-border">
<h3 class="box-title">{{ ('no_'~type~'_title_'~what)|_ }}</h3>
<h3 class="box-title">{{ ('no_'~type~'_title_'~objectType)|_ }}</h3>
</div>
<div class="box-body table-responsive">
<p>
{{ ('no_'~type~'_intro_'~what)|_ }}
{{ ('no_'~type~'_intro_'~objectType)|_ }}
</p>
<p>
{{ ('no_'~type~'_imperative_'~what)|_ }}
{{ ('no_'~type~'_imperative_'~objectType)|_ }}
</p>
<p style="text-align: center;">
<a class="btn btn-lg btn-success" href="{{ route }}">{{ ('no_'~type~'_create_'~what)|_ }}</a>
<a class="btn btn-lg btn-success" href="{{ route }}">{{ ('no_'~type~'_create_'~objectType)|_ }}</a>
</p>
</div>

View File

@ -6,7 +6,7 @@
{% block content %}
{% if piggyBanks|length == 0 %}
{% include 'partials.empty' with {what: 'default', type: 'piggies',route: route('piggy-banks.create')} %}
{% include 'partials.empty' with {objectType: 'default', type: 'piggies',route: route('piggy-banks.create')} %}
{% else %}
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">

View File

@ -137,7 +137,7 @@
{% if total == 0 and page == 1 %}
{% include 'partials.empty' with {what: 'default', type: 'recurring',route: route('recurring.create')} %}
{% include 'partials.empty' with {objectType: 'default', type: 'recurring',route: route('recurring.create')} %}
{% endif %}
{% endblock %}

View File

@ -6,7 +6,7 @@
{% block content %}
{% if count == 0 %}
{% include 'partials.empty' with {what: 'default', type: 'tags',route: route('tags.create')} %}
{% include 'partials.empty' with {objectType: 'default', type: 'tags',route: route('tags.create')} %}
{% else %}
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">