From 0ab8ce90cb1dd9d7b1c9633994845ec00a19fc5f Mon Sep 17 00:00:00 2001 From: Jaap Jansma <jaap.jansma@civicoop.org> Date: Wed, 24 Apr 2024 13:49:09 +0200 Subject: [PATCH] added bounded calculation --- Civi/DataProcessor/Factory.php | 1 + ...dedAggregateFunctionFieldOutputHandler.php | 141 ++++++++++++++++++ ...dedAggregateFunctionFieldOutputHandler.tpl | 61 ++++++++ 3 files changed, 203 insertions(+) create mode 100644 Civi/DataProcessor/FieldOutputHandler/BoundedAggregateFunctionFieldOutputHandler.php create mode 100644 templates/CRM/Dataprocessor/Form/Field/Configuration/BoundedAggregateFunctionFieldOutputHandler.tpl diff --git a/Civi/DataProcessor/Factory.php b/Civi/DataProcessor/Factory.php index 8a6aa276..9b9f650c 100644 --- a/Civi/DataProcessor/Factory.php +++ b/Civi/DataProcessor/Factory.php @@ -227,6 +227,7 @@ class Factory { $this->addOutputHandler('calculations_total', new Definition('Civi\DataProcessor\FieldOutputHandler\Calculations\TotalFieldOutputHandler'), E::ts('Calculations (on multiple fields): Adding up')); $this->addOutputHandler('calculations_percentage_change', new Definition('Civi\DataProcessor\FieldOutputHandler\Calculations\PercentageChangeFieldOutputHandler'), E::ts('Calculations (on multiple fields): Percentage Change')); $this->addOutputHandler('aggregation_function', new Definition('Civi\DataProcessor\FieldOutputHandler\AggregateFunctionFieldOutputHandler'), E::ts('Aggregation function')); + $this->addOutputHandler('bounded_aggregation_function', new Definition('Civi\DataProcessor\FieldOutputHandler\BoundedAggregateFunctionFieldOutputHandler'), E::ts('Bounded Aggregation function')); $this->addOutputHandler('image', new Definition('Civi\DataProcessor\FieldOutputHandler\ImageFieldOutputHandler'), E::ts('Image')); $this->addOutputHandler('array', new Definition('Civi\DataProcessor\FieldOutputHandler\ArrayFieldOutputHandler'), E::ts('Multivalue Field as Array')); $this->addOutputHandler('sanitize_string', new Definition('Civi\DataProcessor\FieldOutputHandler\SanitizeString'), E::ts('Replaces special characters by their HTML code and newlines by \\n.')); diff --git a/Civi/DataProcessor/FieldOutputHandler/BoundedAggregateFunctionFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/BoundedAggregateFunctionFieldOutputHandler.php new file mode 100644 index 00000000..93a44610 --- /dev/null +++ b/Civi/DataProcessor/FieldOutputHandler/BoundedAggregateFunctionFieldOutputHandler.php @@ -0,0 +1,141 @@ +<?php +/** + * Copyright (C) 2024 Jaap Jansma (jaap.jansma@civicoop.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +namespace Civi\DataProcessor\FieldOutputHandler; + +use CRM_Dataprocessor_ExtensionUtil as E; + +class BoundedAggregateFunctionFieldOutputHandler extends AggregateFunctionFieldOutputHandler { + + /** + * @var float + */ + protected $minBound = null; + + /** + * @var float + */ + protected $minValue = 0.00; + + /** + * @var float + */ + protected $maxBound = null; + + /** + * @var float + */ + protected $maxValue = 0.00; + + /** + * Initialize the processor + * + * @param String $alias + * @param String $title + * @param array $configuration + * @param \Civi\DataProcessor\ProcessorType\AbstractProcessorType $processorType + */ + public function initialize($alias, $title, $configuration) { + parent::initialize($alias, $title, $configuration); + if (isset($configuration['min_bound']) && strlen($configuration['min_bound'])) { + $this->minBound = (float) $configuration['min_bound']; + $this->minValue = (float) $configuration['min_value']; + } + if (isset($configuration['max_bound']) && strlen($configuration['max_bound'])) { + $this->maxBound = (float) $configuration['max_bound']; + $this->maxValue = (float) $configuration['max_value']; + } + } + + /** + * Returns the formatted value + * + * @param $rawRecord + * @param $formattedRecord + * + * @return \Civi\DataProcessor\FieldOutputHandler\FieldOutput + */ + public function formatField($rawRecord, $formattedRecord) { + $value = (float) $rawRecord[$this->aggregateField->alias]; + if ($this->minBound !== null && $value < $this->minBound) { + $value = $this->minValue; + } elseif ($this->maxBound !== null && $value > $this->maxBound) { + $value = $this->maxValue; + } elseif ($this->roundToNearestWholeNumber) { + $value = $this->roundUpToNearest($value, $this->roundToNearestWholeNumber); + } + return $this->formatOutput($value); + } + + /** + * When this handler has additional configuration you can add + * the fields on the form with this function. + * + * @param \CRM_Core_Form $form + * @param array $field + */ + public function buildConfigurationForm(\CRM_Core_Form $form, $field=array()) { + parent::buildConfigurationForm($form, $field); + $form->add('number', 'min_bound', E::ts('Bound Value (if value is less then)'), ['class' => 'six', 'min' => 1], true); + $form->add('number', 'min_value', E::ts('Min Value'), ['class' => 'six', 'min' => 1], true); + $form->add('number', 'max_bound', E::ts('Bound Value (if value is more than)'), ['class' => 'six', 'min' => 1], true); + $form->add('number', 'max_value', E::ts('Max Value'), ['class' => 'six', 'min' => 1], true); + if (isset($field['configuration'])) { + $configuration = $field['configuration']; + if (isset($configuration['min_bound'])) { + $this->defaults['min_bound'] = $configuration['min_bound']; + } + if (isset($configuration['min_value'])) { + $this->defaults['min_value'] = $configuration['min_value']; + } + if (isset($configuration['max_bound'])) { + $this->defaults['max_bound'] = $configuration['max_bound']; + } + if (isset($configuration['max_value'])) { + $this->defaults['max_value'] = $configuration['max_value']; + } + $form->setDefaults($this->defaults); + } + } + + /** + * When this handler has configuration specify the template file name + * for the configuration form. + * + * @return false|string + */ + public function getConfigurationTemplateFileName() { + return "CRM/Dataprocessor/Form/Field/Configuration/BoundedAggregateFunctionFieldOutputHandler.tpl"; + } + + /** + * Process the submitted values and create a configuration array + * + * @param $submittedValues + * @return array + */ + public function processConfiguration($submittedValues) { + $configuration = parent::processConfiguration($submittedValues); + $configuration['min_bound'] = $submittedValues['min_bound']; + $configuration['min_value'] = $submittedValues['min_value']; + $configuration['max_bound'] = $submittedValues['max_bound']; + $configuration['max_value'] = $submittedValues['max_value']; + return $configuration; + } + +} diff --git a/templates/CRM/Dataprocessor/Form/Field/Configuration/BoundedAggregateFunctionFieldOutputHandler.tpl b/templates/CRM/Dataprocessor/Form/Field/Configuration/BoundedAggregateFunctionFieldOutputHandler.tpl new file mode 100644 index 00000000..8d32df16 --- /dev/null +++ b/templates/CRM/Dataprocessor/Form/Field/Configuration/BoundedAggregateFunctionFieldOutputHandler.tpl @@ -0,0 +1,61 @@ +{crmScope extensionKey='dataprocessor'} + <div class="crm-section"> + <div class="label">{$form.function.label}</div> + <div class="content">{$form.function.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.field.label}</div> + <div class="content">{$form.field.html}</div> + <div class="clear"></div> + </div> + + <div class="crm-section"> + <div class="label">{$form.number_of_decimals.label}</div> + <div class="content">{$form.number_of_decimals.html} + <p class="description">{ts}Leave empty for no formatting{/ts}</p> + </div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.decimal_separator.label}</div> + <div class="content">{$form.decimal_separator.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.thousand_separator.label}</div> + <div class="content">{$form.thousand_separator.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.prefix.label}</div> + <div class="content">{$form.prefix.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.suffix.label}</div> + <div class="content">{$form.suffix.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.min_bound.label}</div> + <div class="content">{$form.min_bound.html} {$form.min_value.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.max_bound.label}</div> + <div class="content">{$form.max_bound.html} {$form.max_value.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.roundToNearestWholeNumber.label}</div> + <div class="content">{$form.roundToNearestWholeNumber.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.multiplier.label}</div> + <div class="content">{$form.multiplier.html}</div> + <div class="clear"></div> + </div> + +{/crmScope} -- GitLab