Skip to content

Commit

Permalink
Schedule Criteria: Custom conditional (#2866)
Browse files Browse the repository at this point in the history
  • Loading branch information
nadzpogi authored Jan 24, 2025
1 parent 7d31a92 commit c7a3eaf
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 13 deletions.
6 changes: 6 additions & 0 deletions lib/Connector/CapConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -532,12 +532,18 @@ public function onScheduleCriteriaRequest(ScheduleCriteriaRequestInterface $even
// Initialize Emergency Alerts schedule criteria parameters
$event->addType('emergency_alerts', __('Emergency Alerts'))
->addMetric('emergency_alert_status', __('Status'))
->addCondition([
'eq' => __('Equal to')
])
->addValues('dropdown', [
self::ACTUAL_ALERT => __('Actual Alerts'),
self::TEST_ALERT => __('Test Alerts'),
self::NO_ALERT => __('No Alerts')
])
->addMetric('emergency_alert_category', __('Category'))
->addCondition([
'eq' => __('Equal to')
])
->addValues('dropdown', [
'Geo' => __('Geo'),
'Met' => __('Met'),
Expand Down
8 changes: 6 additions & 2 deletions lib/Controller/Schedule.php
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,7 @@ public function addForm(Request $request, Response $response, ?string $from, ?in

// Retrieve the criteria data from the event
$criteria = $event->getCriteria();
$criteriaDefaultCondition = $event->getCriteriaDefaultCondition();

$addFormData = [
'dayParts' => $this->dayPartFactory->allWithSystem(),
Expand All @@ -791,7 +792,8 @@ public function addForm(Request $request, Response $response, ?string $from, ?in
'isScheduleNow' => false,
'relativeTime' => 0,
'setDisplaysFromFilter' => true,
'scheduleCriteria' => $criteria
'scheduleCriteria' => $criteria,
'criteriaDefaultCondition' => $criteriaDefaultCondition
];
$formNowData = [];

Expand Down Expand Up @@ -1339,6 +1341,7 @@ function editForm(Request $request, Response $response, $id)

// Retrieve the data from the event
$criteria = $event->getCriteria();
$criteriaDefaultCondition = $event->getCriteriaDefaultCondition();

if (!$this->isEventEditable($schedule)) {
throw new AccessDeniedException();
Expand Down Expand Up @@ -1403,7 +1406,8 @@ function editForm(Request $request, Response $response, $id)
'eventStart' => $eventStart,
'eventEnd' => $eventEnd,
'eventTypes' => \Xibo\Entity\Schedule::getEventTypesForm(),
'scheduleCriteria' => $criteria
'scheduleCriteria' => $criteria,
'criteriaDefaultCondition' => $criteriaDefaultCondition
]);

return $this->render($request, $response);
Expand Down
78 changes: 77 additions & 1 deletion lib/Event/ScheduleCriteriaRequestEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,31 @@
/**
* This class represents a schedule criteria request event. It is responsible for initializing,
* managing, and retrieving schedule criteria. The class provides methods for adding types,
* metrics, and their associated values.
* metrics, and their associated conditions and values.
*/
class ScheduleCriteriaRequestEvent extends Event implements ScheduleCriteriaRequestInterface
{
public static $NAME = 'schedule.criteria.request';
private $criteria = [];
private $currentTypeIndex = null;
private $currentMetric = null;
private array $defaultConditions = [];

public function __construct()
{
// Initialize default conditions in key-value format
$this->defaultConditions = [
'set' => __('Is set'),
'lt' => __('Less than'),
'lte' => __('Less than or equal to'),
'eq' => __('Equal to'),
'neq' => __('Not equal to'),
'gte' => __('Greater than or equal to'),
'gt' => __('Greater than'),
'contains' => __('Contains'),
'ncontains' => __('Not contains'),
];
}

/**
* @inheritDoc
Expand All @@ -61,6 +78,7 @@ public function addMetric(string $id, string $name): self
$metric = [
'id' => $id,
'name' => $name,
'conditions' => $this->formatConditions($this->defaultConditions),
'values' => null
];

Expand All @@ -75,6 +93,54 @@ public function addMetric(string $id, string $name): self
return $this;
}


/**
* @inheritDoc
*/
public function addCondition(array $conditions): self
{
// Ensure current type is set
if (!isset($this->criteria['types'][$this->currentTypeIndex])) {
throw new ConfigurationException(__('Current type is not set.'));
}

// Validate conditions
foreach ($conditions as $id => $name) {
if (!array_key_exists($id, $this->defaultConditions)) {
throw new ConfigurationException(__('Invalid condition ID: %s', $id));
}
}

// Assign conditions to the current metric
foreach ($this->criteria['types'][$this->currentTypeIndex]['metrics'] as &$metric) {
if ($metric['name'] === $this->currentMetric['name']) {
$metric['conditions'] = $this->formatConditions($conditions);
break;
}
}

return $this;
}

/**
* Format conditions from key-value to the required array structure.
*
* @param array $conditions
* @return array
*/
private function formatConditions(array $conditions): array
{
$formattedConditions = [];
foreach ($conditions as $id => $name) {
$formattedConditions[] = [
'id' => $id,
'name' => $name,
];
}

return $formattedConditions;
}

/**
* @inheritDoc
*/
Expand Down Expand Up @@ -122,4 +188,14 @@ public function getCriteria(): array
{
return $this->criteria;
}

/**
* Get the default conditions array.
*
* @return array
*/
public function getCriteriaDefaultCondition(): array
{
return $this->formatConditions($this->defaultConditions);
}
}
50 changes: 44 additions & 6 deletions lib/Event/ScheduleCriteriaRequestInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,19 @@
use Xibo\Support\Exception\ConfigurationException;

/**
* Interface for managing schedule criteria types, metrics, and values.
* Interface for managing schedule criteria types, metrics, conditions, and values.
*
* Allows the addition of types, metrics, and values in a chained manner:
* - Start with addType() to add a new type. Call addType() multiple times to add multiple types.
* - Follow with addMetric() to add metrics under the specified type. Call addMetric() multiple times to add multiple
* Allows the addition of types, metrics, conditions, and values in a chained manner:
* - Start with `addType()` to add a new type. Call `addType()` multiple times to add multiple types.
* - Follow with `addMetric()` to add metrics under the specified type. Call `addMetric()` multiple times to add multiple
* metrics to the current type.
* - Conclude with addValues() immediately after addMetric() to specify a set of values for the metric. Each metric can
* - Optionally, call `addCondition()` after `addMetric()` to specify a set of conditions for the metric. If not called,
* the system will automatically apply default conditions, which include all supported conditions.
* - Conclude with `addValues()` immediately after `addMetric()` or `addCondition()` to specify a set of values for the metric. Each metric can
* have one set of values.
*
* The added criteria are then parsed and displayed in the Schedule Criteria Form, enabling users to configure
* scheduling conditions based on the specified types, metrics, and values.
* scheduling conditions based on the specified parameters.
*/
interface ScheduleCriteriaRequestInterface
{
Expand All @@ -58,6 +60,42 @@ public function addType(string $id, string $type): self;
*/
public function addMetric(string $id, string $name): self;

/**
* Add conditions to the current metric.
*
* This method allows you to specify conditions for the currently added metric.
* The list of accepted conditions includes:
* - 'set' => 'Is set'
* - 'lt' => 'Less than'
* - 'lte' => 'Less than or equal to'
* - 'eq' => 'Equal to'
* - 'neq' => 'Not equal to'
* - 'gte' => 'Greater than or equal to'
* - 'gt' => 'Greater than'
* - 'contains' => 'Contains'
* - 'ncontains' => 'Not contains'
*
* **Important Notes:**
* - The `addMetric` method **must** be called before using `addCondition`.
* If no metric is currently set, this method will throw a `ConfigurationException`.
* - If this method is **not called** for a metric, the system will automatically
* provide the default conditions, which include **all the accepted conditions** listed above.
*
* Example usage:
* ```
* $event->addMetric('temp', 'Temperature')
* ->addCondition([
* 'eq' => 'Equal to',
* 'gt' => 'Greater than',
* ]);
* ```
*
* @param array $conditions An associative array of conditions, where the key is the condition ID and the value is its name.
* @return $this
* @throws ConfigurationException If the current metric is not set.
*/
public function addCondition(array $conditions): self;

/**
* Add values to the current metric. The input type must be either "dropdown", "string", "date", or "number".
*
Expand Down
76 changes: 74 additions & 2 deletions ui/src/core/xibo-calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,16 @@ $(function() {
const selectedType = $target.val();
const $fields = $('#scheduleCriteriaFields');
const scheduleCriteria = $fields.data('scheduleCriteria');
const criteriaDefaultCondition = $fields.data('criteriaDefaultCondition');

if (scheduleCriteria) {
if (selectedType === 'custom') {
// Use a text input for metrics
updateMetricsFieldAsText($row);
// Use a text input for values
updateValueFieldAsText($row);
// Revert condition field to default
updateConditionFieldToDefault($row, criteriaDefaultCondition);
} else if (scheduleCriteria) {
// Update metrics based on the selected type
// and change text field to dropdown
Expand All @@ -76,6 +79,8 @@ $(function() {
const $metricLabel = $row.find('label[for="criteria_metric[]"]');
const $metricSelect =
$('<select class="form-control" name="criteria_metric[]"></select>');
const $fields = $('#scheduleCriteriaFields');
const criteriaDefaultCondition = $fields.data('criteriaDefaultCondition');

// Check if scheduleCriteria has types
if (scheduleCriteria.types) {
Expand All @@ -97,6 +102,14 @@ $(function() {
} else {
updateValueFieldAsText($row);
}

// Update the condition field based on the selected metric
if (metricData && metricData.conditions) {
updateConditionField($row, metricData.conditions);
} else {
// If no conditions are defined, use the default conditions
updateConditionFieldToDefault($row, criteriaDefaultCondition);
}
}
}

Expand Down Expand Up @@ -128,6 +141,7 @@ $(function() {
const selectedMetric = $target.val();
const $fields = $('#scheduleCriteriaFields');
const scheduleCriteria = $fields.data('scheduleCriteria');
const criteriaDefaultCondition = $fields.data('criteriaDefaultCondition');
const selectedType = $row.find('select[name="criteria_type[]"]').val();

if (scheduleCriteria && selectedType) {
Expand All @@ -143,6 +157,14 @@ $(function() {
} else {
updateValueFieldAsText($row);
}

// Update the condition field based on the selected metric
if (metricData && metricData.conditions) {
updateConditionField($row, metricData.conditions);
} else {
// If no conditions are defined, use the default conditions
updateConditionFieldToDefault($row, criteriaDefaultCondition);
}
}
}
});
Expand Down Expand Up @@ -909,6 +931,46 @@ $(function() {
}
});

// Function to update the Condition dropdown according to the selected metric's available condition
function updateConditionField($row, conditions, selectedCondition) {
const $conditionField = $row.find('select[name="criteria_condition[]"]');

if ($conditionField.length > 0) {
// Clear existing options
$conditionField.empty();

// Populate with provided conditions
conditions.forEach((condition) => {
$conditionField.append(
$('<option>', { value: condition.id }).text(condition.name)
);
});

// Pre-select the condition if provided, otherwise select the first condition
$conditionField.val(selectedCondition || conditions[0]?.id || '');
}
}

// Function to revert the Condition dropdown to its default selection
function updateConditionFieldToDefault($row, defaultConditions, selectedCondition) {
const $conditionField = $row.find('select[name="criteria_condition[]"]');

if ($conditionField.length > 0 && defaultConditions) {
// Clear existing options
$conditionField.empty();

// Populate with default conditions
defaultConditions.forEach((condition) => {
$conditionField.append(
$('<option>', { value: condition.id }).text(condition.name)
);
});

// Pre-select the condition if provided, otherwise select the first condition
$conditionField.val(selectedCondition || defaultConditions[0]?.id || '');
}
}

/**
* Callback for the schedule form
*/
Expand Down Expand Up @@ -2205,7 +2267,6 @@ const setupSelectForSchedule = function(dialog) {
* Configure criteria fields on the schedule add/edit forms.
* @param {object} dialog - Dialog object
*/

const configureCriteriaFields = function(dialog) {
const $fields = dialog.find('#scheduleCriteriaFields');
if ($fields.length <= 0) {
Expand Down Expand Up @@ -2233,6 +2294,7 @@ const configureCriteriaFields = function(dialog) {
typeId,
selectedMetric,
elementValue,
selectedCondition
) {
const $metricLabel = $row.find('label[for="criteria_metric[]"]');
let $metricSelect;
Expand Down Expand Up @@ -2272,9 +2334,18 @@ const configureCriteriaFields = function(dialog) {
const type = types ? types.find((t) => t.id === typeId) : null;
if (type) {
const metric = type.metrics.find((m) => m.id === selectedMetric);
// update value field if metric is present
// update value and condition fields if metric is present
if (metric) {
updateValueField($row, metric, elementValue);

// Update the condition field based on the selected metric
if (metric.conditions) {
updateConditionField($row, metric.conditions, selectedCondition);
} else {
// Use default conditions if none are defined
const criteriaDefaultCondition = $('#scheduleCriteriaFields').data('criteriaDefaultCondition');
updateConditionFieldToDefault($row, criteriaDefaultCondition, selectedCondition);
}
}
}
}
Expand Down Expand Up @@ -2342,6 +2413,7 @@ const configureCriteriaFields = function(dialog) {
element.type,
element.metric,
element.value,
element.condition
);
});
} else {
Expand Down
2 changes: 1 addition & 1 deletion views/schedule-form-add.twig
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@
</div>
</div>

<div id="scheduleCriteriaFields" data-criteria="" data-schedule-criteria="{{ scheduleCriteria|json_encode|e('html_attr') }}"></div>
<div id="scheduleCriteriaFields" data-criteria="" data-schedule-criteria="{{ scheduleCriteria|json_encode|e('html_attr') }}" data-criteria-default-condition="{{ criteriaDefaultCondition|json_encode|e('html_attr') }}"></div>
</div>
</div>
</form>
Expand Down
Loading

0 comments on commit c7a3eaf

Please sign in to comment.