Commit 85551001 authored by kainuk's avatar kainuk
Browse files

Add a Compare Filter

parent 860d3098
# Version 1.37 (not yet released)
* Added filter to compare two fields.
* Fixed #94. Renamed the calculation Percentage to Percentage change (to avoid confusion).
* Make the totals (calculations that add up) and percentages sortable.
* Fixed #91. Duplicate column name 'participant_payment_participant_id' when a Data Processor is configured to use two or more Pariticipant Sources by !85
......
......@@ -145,6 +145,7 @@ class Factory {
$this->addFilter('date_filter', new Definition('Civi\DataProcessor\FilterHandler\DateFilter'), E::ts('Date filter'));
$this->addFilter('date_month_filter', new Definition('Civi\DataProcessor\FilterHandler\DateMonthFilter'), E::ts('Month filter'));
$this->addFilter('multiple_field_filter', new Definition('Civi\DataProcessor\FilterHandler\MultipleFieldFilter'), E::ts('Text in multiple fields Filter'));
$this->addFilter('compare_filter', new Definition('Civi\DataProcessor\FilterHandler\CompareFieldFilter'), E::ts('Compare two fields'));
$this->addFilter('activity_filter', new Definition('Civi\DataProcessor\FilterHandler\ActivityFilter'), E::ts('Activity filter'));
$this->addFilter('event_filter', new Definition('Civi\DataProcessor\FilterHandler\EventFilter'), E::ts('Event filter'));
$this->addFilter('checksum_filter', new Definition('Civi\DataProcessor\FilterHandler\ChecksumFilter'), E::ts('Checksum filter'));
......
......@@ -119,4 +119,10 @@ abstract class AbstractFieldFilterHandler extends AbstractFilterHandler {
}
}
protected function getOperatorOptions(\Civi\DataProcessor\DataSpecification\FieldSpecification $fieldSpec) {
return array(
'=' => E::ts('Is equal to'),
);
}
}
<?php
/**
* @author Klaas Eikelboom <klaas.eikelboom@civicoop.org>
* @license AGPL-3.0
*/
namespace Civi\DataProcessor\FilterHandler;
use Civi\DataProcessor\DataFlow\SqlDataFlow;
use Civi\DataProcessor\DataSpecification\CustomFieldSpecification;
use Civi\DataProcessor\DataSpecification\FieldSpecification;
use Civi\DataProcessor\Exception\DataSourceNotFoundException;
use Civi\DataProcessor\Exception\FieldNotFoundException;
use Civi\DataProcessor\Exception\InvalidConfigurationException;
use CRM_Dataprocessor_ExtensionUtil as E;
class CompareFieldFilter extends AbstractFieldFilterHandler {
/**
* @var \Civi\DataProcessor\DataSpecification\FieldSpecification
*/
protected $fieldSpecification;
/**
* @var \Civi\DataProcessor\Source\SourceInterface
*/
protected $dataSource;
/**
* @var \Civi\DataProcessor\DataSpecification\FieldSpecification
*/
protected $fieldSpecificationRight;
/**
* @var \Civi\DataProcessor\Source\SourceInterface
*/
protected $dataSourceRight;
/**
* @var
*/
protected $op;
/**
* @var \Civi\DataProcessor\DataFlow\SqlDataFlow\WhereClauseInterface
*/
protected $whereClause;
/**
* Initialize the filter
*
* @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException
* @throws \Civi\DataProcessor\Exception\InvalidConfigurationException
* @throws \Civi\DataProcessor\Exception\FieldNotFoundException
*/
protected function doInitialization() {
[$dataSourceLeft, $fieldLeft] = explode("::", $this->configuration['field_left'] );
$this->initializeFieldLeft($dataSourceLeft, $fieldLeft);
[$dataSourceRight, $fieldRight] = explode("::", $this->configuration['field_right'] );
$this->initializeFieldRight($dataSourceRight, $fieldRight);
$this->fieldSpecification->type = 'String';
$this->op = $this->configuration['op'];
}
/**
* @param $datasource_name
* @param $field_name
*
* @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException
* @throws \Civi\DataProcessor\Exception\FieldNotFoundException
*/
protected function initializeFieldLeft($datasource_name, $field_name) {
$this->dataSource = $this->data_processor->getDataSourceByName($datasource_name);
if (!$this->dataSource ) {
throw new DataSourceNotFoundException(E::ts("Filter %1 requires data source '%2' which could not be found. Did you rename or delete the data source?", array(1=>$this->title, 2=>$datasource_name)));
}
$this->fieldSpecification = $this->dataSource ->getAvailableFilterFields()->getFieldSpecificationByAlias($field_name);
if (!$this->fieldSpecification) {
!$this->fieldSpecification = $this->dataSource ->getAvailableFilterFields()
->getFieldSpecificationByName($field_name);
}
if (! $this->fieldSpecification) {
throw new FieldNotFoundException(E::ts("Filter %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", [
1 => $this->title,
2 => $field_name,
3 => $datasource_name
]));
}
}
/**
* @param $datasource_name
* @param $field_name
*
* @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException
* @throws \Civi\DataProcessor\Exception\FieldNotFoundException
*/
protected function initializeFieldRight($datasource_name, $field_name) {
$this->dataSourceRight = $this->data_processor->getDataSourceByName($datasource_name);
if (!$this->dataSourceRight ) {
throw new DataSourceNotFoundException(E::ts("Filter %1 requires data source '%2' which could not be found. Did you rename or delete the data source?", array(1=>$this->title, 2=>$datasource_name)));
}
$this->fieldSpecificationRight = $this->dataSourceRight ->getAvailableFilterFields()->getFieldSpecificationByAlias($field_name);
if (!$this->fieldSpecificationRight) {
!$this->fieldSpecificationRight = $this->dataSourceRight ->getAvailableFilterFields()
->getFieldSpecificationByName($field_name);
}
if (! $this->fieldSpecificationRight) {
throw new FieldNotFoundException(E::ts("Filter %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", [
1 => $this->title,
2 => $field_name,
3 => $datasource_name
]));
}
}
/**
* Returns true when this filter has additional configuration
*
* @return bool
*/
public function hasConfiguration() {
return true;
}
/**
* When this filter type has additional configuration you can add
* the fields on the form with this function.
*
* @param \CRM_Core_Form $form
* @param array $filter
*/
public function buildConfigurationForm(\CRM_Core_Form $form, $filter=array()) {
$fieldSelect = \CRM_Dataprocessor_Utils_DataSourceFields::getAvailableFilterFieldsInDataSources($filter['data_processor_id']);
$form->add('select', 'field_left', E::ts('Left Hand Field'), $fieldSelect, true, array(
'style' => 'min-width:250px',
'class' => 'crm-select2 huge data-processor-field-for-name',
'placeholder' => E::ts('- select -'),
'multiple' => false,
));
$form->add('select', 'field_right', E::ts('Right Hand Field'), $fieldSelect, true, array(
'style' => 'min-width:250px',
'class' => 'crm-select2 huge data-processor-field-for-name',
'placeholder' => E::ts('- select -'),
'multiple' => false,
));
$form->add('select', 'op', E::ts('Operator'), $this->getCompareOperatorOptions(), true, array(
'style' => 'min-width:250px',
'class' => 'crm-select2 huge data-processor-field-for-name',
'placeholder' => E::ts('- select -'),
'multiple' => false,
));
if (isset($filter['configuration'])) {
$configuration = $filter['configuration'];
if (isset($configuration['field_left'])) {
$defaults['field_left'] = $configuration['field_left'];
}
if (isset($configuration['field_right'])) {
$defaults['field_right'] = $configuration['field_right'];
}
$defaults['op'] = $configuration['op'] ?? '=';
$form->setDefaults($defaults);
}
}
/**
* When this filter type has configuration specify the template file name
* for the configuration form.
*
* @return false|string
*/
public function getConfigurationTemplateFileName() {
return "CRM/Dataprocessor/Form/Filter/Configuration/CompareFieldFilter.tpl";
}
/**
* Process the submitted values and create a configuration array
*
* @param $submittedValues
* @return array
*/
public function processConfiguration($submittedValues) {
$configuration['field_left'] = $submittedValues['field_left'];
$configuration['field_right'] = $submittedValues['field_right'];
$configuration['op'] = $submittedValues['op'];
return $configuration;
}
/**
* File name of the template to add this filter to the criteria form.
*
* @return string
*/
public function getTemplateFileName() {
return "CRM/Dataprocessor/Form/Filter/CompareFieldFilter.tpl";
}
/**
* @return \Civi\DataProcessor\DataSpecification\FieldSpecification
*/
public function getFieldSpecification() {
return $this->fieldSpecification;
}
/**
* @param \CRM_Core_Form $form
* @param array $defaultFilterValue
* @param string $size
*
* @return array|void
*/
public function addToFilterForm(\CRM_Core_Form $form, $defaultFilterValue, $size='full'){
$expression = "{$this->dataSource->getSourceTitle()} :: {$this->fieldSpecification->title}";
$expression .= " {$this->op} ";
$expression .= "{$this->dataSourceRight->getSourceTitle()} :: {$this->fieldSpecificationRight->title}";
$form->assign('expression',$expression);
}
/**
* @param $submittedValues
*
* @throws \Exception
*/
public function applyFilterFromSubmittedFilterParams($submittedValues) {
$this->setFilter([]);
}
/**
* @param array $filter
* The filter settings
* @return mixed
* @throws \Exception
*/
public function setFilter($filter) {
$this->resetFilter();
$dataFlowLeft = $this->dataSource->ensureField($this->fieldSpecification);
$dataFlowRight = $this->dataSourceRight->ensureField($this->fieldSpecificationRight);
if ($dataFlowLeft && $dataFlowRight && $dataFlowLeft instanceof SqlDataFlow && $dataFlowRight instanceof SqlDataFlow) {
$tableAliasLeft = $this->getTableAlias($dataFlowLeft);
$tableAliasRight = $this->getTableAlias($dataFlowRight);
$clause = "`{$tableAliasLeft}`.`{$this->fieldSpecification->getName()}`";
$clause .= " {$this->op} ";
$clause .= "`{$tableAliasRight}`.`{$this->fieldSpecificationRight->getName()}`";
$this->whereClause = new SqlDataFlow\PureSqlStatementClause($clause);
$dataFlowLeft->addWhereClause($this->whereClause);
}
}
/**
* Returns all the operators that can be used in sql experssions
* @return array
*/
private function getCompareOperatorOptions() {
return array(
'=' => E::ts('Is equal'),
'<>' => E::ts('Is not equal'),
'>' => E::ts('Is greater than'),
'>=' => E::ts('Is greater than or equal to'),
'<' => E::ts('Is les than'),
'<=' => E::ts('Is les than or equal to'),
);
}
}
{crmScope extensionKey='dataprocessor'}
<tr>
<td class="label">{ts}Compare Expression{/ts}</td>
<td colspan='2'>{$expression}</td>
</tr>
{/crmScope}
{crmScope extensionKey='dataprocessor'}
<div class="crm-section">
<div class="label">{$form.field_left.label}</div>
<div class="content">{$form.field_left.html}</div>
<div class="clear"></div>
</div>
<div class="crm-section">
<div class="label">{$form.op.label}</div>
<div class="content">{$form.op.html}</div>
<div class="clear"></div>
</div>
<div class="crm-section">
<div class="label">{$form.field_right.label}</div>
<div class="content">{$form.field_right.html}</div>
<div class="clear"></div>
</div>
{/crmScope}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment