diff --git a/modules/tracker/configs/module.ini b/modules/tracker/configs/module.ini index e47190b71..f07b8bff7 100644 --- a/modules/tracker/configs/module.ini +++ b/modules/tracker/configs/module.ini @@ -6,4 +6,4 @@ description = "Track scalar results over time" category = "Visualization" dependencies = api,scheduler uuid = "3048a9fa-89ab-4e61-a55e-a49379fa6dc" -version = "2.0.3" +version = "2.0.4" diff --git a/modules/tracker/controllers/components/ApiComponent.php b/modules/tracker/controllers/components/ApiComponent.php index 8d9defd40..158ff1562 100644 --- a/modules/tracker/controllers/components/ApiComponent.php +++ b/modules/tracker/controllers/components/ApiComponent.php @@ -336,11 +336,14 @@ public function submissionAdd($args) } /** - * Validate documents tied to a submission. + * Validate the producer configuration and submission documents that are tied + * to a submission, updating the properties of the producer based off of + * the producer configuration. * * @param uuid The uuid of the submission to validate documents for * @param producerConfig (Optional) JSON object describing the pipeline * @param submissionDocument (Optional) JSON object describing the submission + * @throws Exception */ public function submissionValidate($args) { @@ -357,9 +360,6 @@ public function submissionValidate($args) } if (isset($args['producerConfig'])) { - // At the current time, we are looking for the producerConfig, - // validating it, and logging a warning with any violations, we - // are not saving the producerConfig. $producerConfig = $args['producerConfig']; $refResolver = new JsonSchema\RefResolver(new JsonSchema\Uri\UriRetriever(), new JsonSchema\Uri\UriResolver()); $schemaPath = BASE_PATH.'/modules/tracker/schema/producer.json'; @@ -370,18 +370,176 @@ public function submissionValidate($args) if (!$validator->isValid()) { $this->getLogger()->warn('The supplied producerConfig JSON for uuid '.$uuid." does not validate. Violations:\n"); + /** @var array $error */ foreach ($validator->getErrors() as $error) { $this->getLogger()->warn(sprintf("[%s] %s\n", $error['property'], $error['message'])); } } else { $this->getLogger()->info('The supplied producerConfig JSON for uuid '.$uuid.' is valid.'); + + /** @var Tracker_ProducerModel $producerModel */ + $producerModel = MidasLoader::loadModel('Producer', 'tracker'); + /** @var Tracker_ProducerDao $producerDao */ + $producerDao = $submissionDao->getProducer(); + if (!$producerModel->policyCheck( + $producerDao, + $user, + MIDAS_POLICY_WRITE + )) { + throw new Exception('Write permission on the producer required', 403); + } + /** @var stdClass $producerDefinition */ + $producerDefinition = json_decode($producerConfig); + // Ensure that Producer and Community match. + if ($producerDefinition->producer !== $producerDao->getDisplayName()) { + throw new Exception('Producer schema name must match existing Producer display name', 404); + } + if ($producerDefinition->community !== $producerDao->getCommunity()->getName()) { + throw new Exception('Producer schema community name must match existing Producer Community name', 404); + } + + // Save the producer definition to the producer. + $producerDao->setProducerDefinition($producerConfig); + // Update top level fields on the producer based on the definition. + if (isset($producerDefinition->histogram_max_x)) { + $producerDao->setHistogramMaxX($producerDefinition->histogram_max_x); + } + if (isset($producerDefinition->grid_across_metric_groups)) { + $producerDao->setGridAcrossMetricGroups($producerDefinition->grid_across_metric_groups); + } + if (isset($producerDefinition->histogram_number_of_bins)) { + $producerDao->setHistogramNumberOfBins($producerDefinition->histogram_number_of_bins); + } + $producerModel->save($producerDao); + + $defaults = $producerDefinition->defaults; + if (!isset($defaults)) { + // Provide a default for $defaults so the below ?: logic works. + $defaults = new stdClass(); + } + + /** + * Helper function to populate a metric based on the overall + * metrics defaults, overriding any default values with any + * specified in the metric itself, populating all properties with + * some unassigned (false or null) value if no other value is found. + * @param stdClass $metric the metric with specific values + * @return array populated metric values + */ + $populateMetricValues = function ($metric) use ($defaults) { + $populatedMetricUnassigned = array( + 'abbreviation' => false, + 'min' => false, + 'max' => false, + 'warning' => false, + 'fail' => false, + // Special handling as false is meaningful in this case. + 'lower_is_better' => null, + ); + $populatedMetric = array(); + /** @var string $key */ + /** @var mixed $unassignedValue */ + foreach ($populatedMetricUnassigned as $key => $unassignedValue) { + if (isset($metric->$key)) { + $populatedMetric[$key] = $metric->$key; + } elseif (isset($defaults->$key)) { + $populatedMetric[$key] = $defaults->$key; + } else { + $populatedMetric[$key] = $unassignedValue; + } + } + if ($populatedMetric['lower_is_better'] === null && + $populatedMetric['warning'] !== false && + $populatedMetric['fail'] !== false) { + // We can infer in this case. + $populatedMetric['lower_is_better'] = + $populatedMetric['warning'] < $populatedMetric['fail']; + } + + return $populatedMetric; + }; + + // Add or update any key metrics and thresholds. + /** @var Tracker_TrendModel $trendModel */ + $trendModel = MidasLoader::loadModel('Trend', 'tracker'); + /** @var Tracker_TrendThresholdModel $trendThresholdModel */ + $trendThresholdModel = MidasLoader::loadModel('TrendThreshold', 'tracker'); + $keyMetrics = $producerDefinition->key_metrics; + /** @var stdClass $keyMetric */ + foreach ($keyMetrics as $keyMetric) { + // Set any needed trends to be key_metrics. + $trendModel->setAggregatableTrendAsKeyMetrics($producerDao, $keyMetric->name); + $metricValues = $populateMetricValues($keyMetric); + $trendThresholdModel->upsert( + $producerDao, + $keyMetric->name, + $metricValues['abbreviation'], + $metricValues['warning'], + $metricValues['fail'], + $metricValues['min'], + $metricValues['max'], + $metricValues['lower_is_better'] + ); + } + // Add or update any aggregate metrics and thresholds, based on matching + // the producer and spec. + $aggregateMetrics = $producerDefinition->aggregate_metrics; + /** @var Tracker_AggregateMetricSpecModel $aggregateMetricSpecModel */ + $aggregateMetricSpecModel = MidasLoader::loadModel('AggregateMetricSpec', 'tracker'); + /** @var Tracker_AggregateMetricNotificationModel $aggregateMetricNotificationModel */ + $aggregateMetricNotificationModel = MidasLoader::loadModel('AggregateMetricNotification', 'tracker'); + /** @var UserModel $userModel */ + $userModel = MidasLoader::loadModel('User'); + /** @var stdClass $aggregateMetric */ + foreach ($aggregateMetrics as $aggregateMetric) { + $metricValues = $populateMetricValues($aggregateMetric); + /** @var Tracker_AggregateMetricSpecDao $aggregateMetricSpecDao */ + $aggregateMetricSpecDao = $aggregateMetricSpecModel->upsert( + $producerDao, + $aggregateMetric->name, + $aggregateMetric->definition, + $metricValues['abbreviation'], + // Set empty string for description. + '', + $metricValues['warning'], + $metricValues['fail'], + $metricValues['min'], + $metricValues['max'], + $metricValues['lower_is_better'] + ); + // Delete any notifications tied to this Aggregate Metric, and create any + // as needed. + $staleNotifications = $aggregateMetricNotificationModel->findBy('aggregate_metric_spec_id', $aggregateMetricSpecDao->getAggregateMetricSpecId()); + /** @var Tracker_AggregateMetricNotificationDao $staleNotification */ + foreach ($staleNotifications as $staleNotification) { + $aggregateMetricNotificationModel->delete($staleNotification); + } + if (isset($aggregateMetric->notifications)) { + /** @var stdClass $notification */ + foreach ($aggregateMetric->notifications as $notification) { + /** @var Tracker_AggregateMetricNotificationDao $aggregateMetricNotificationDao */ + $aggregateMetricNotificationDao = MidasLoader::newDao('AggregateMetricNotificationDao', $this->moduleName); + $aggregateMetricNotificationDao->setAggregateMetricSpecId($aggregateMetricSpecDao->getAggregateMetricSpecId()); + $aggregateMetricNotificationDao->setBranch($notification->branch); + $aggregateMetricNotificationDao->setComparison($notification->comparison); + $aggregateMetricNotificationDao->setValue($notification->value); + $aggregateMetricNotificationModel->save($aggregateMetricNotificationDao); + if (isset($notification->emails)) { + foreach ($notification->emails as $email) { + // We can only add notifications for valid users. + $userDao = $userModel->getByEmail($email); + if ($userDao !== false) { + $aggregateMetricNotificationModel->createUserNotification($aggregateMetricNotificationDao, $userDao); + } + } + } + } + } + } } } if (isset($args['submissionDocument'])) { - // At the current time, we are looking for the submissionDocument, - // validating it, and logging a warning with any violations, we - // are not saving the producerConfig. $submissionDocument = $args['submissionDocument']; $refResolver = new JsonSchema\RefResolver(new JsonSchema\Uri\UriRetriever(), new JsonSchema\Uri\UriResolver()); $schemaPath = BASE_PATH.'/modules/tracker/schema/submission.json'; @@ -397,6 +555,20 @@ public function submissionValidate($args) } } else { $this->getLogger()->info('The supplied submissionDocument JSON for uuid '.$uuid.' is valid.'); + + /** @var Tracker_ProducerModel $producerModel */ + $producerModel = MidasLoader::loadModel('Producer', 'tracker'); + /** @var Tracker_ProducerDao $producerDao */ + $producerDao = $submissionDao->getProducer(); + if (!$producerModel->policyCheck( + $producerDao, + $user, + MIDAS_POLICY_WRITE + )) { + throw new Exception('Write permission on the producer required', 403); + } + $submissionDao->setDocument($submissionDocument); + $submissionModel->save($submissionDao); } } } diff --git a/modules/tracker/database/mysql/2.0.2.sql b/modules/tracker/database/mysql/2.0.2.sql index 9f41cb05d..c78792409 100644 --- a/modules/tracker/database/mysql/2.0.2.sql +++ b/modules/tracker/database/mysql/2.0.2.sql @@ -1,6 +1,6 @@ -- Midas Server. Copyright Kitware SAS. Licensed under the Apache License 2.0. --- MySQL database for the tracker module, version 2.0.1 +-- MySQL database for the tracker module, version 2.0.2 CREATE TABLE IF NOT EXISTS `tracker_producer` ( `producer_id` bigint(20) NOT NULL AUTO_INCREMENT, diff --git a/modules/tracker/database/mysql/2.0.3.sql b/modules/tracker/database/mysql/2.0.3.sql index 4b62b26c1..93538a026 100644 --- a/modules/tracker/database/mysql/2.0.3.sql +++ b/modules/tracker/database/mysql/2.0.3.sql @@ -1,6 +1,6 @@ -- Midas Server. Copyright Kitware SAS. Licensed under the Apache License 2.0. --- MySQL database for the tracker module, version 2.0.1 +-- MySQL database for the tracker module, version 2.0.3 CREATE TABLE IF NOT EXISTS `tracker_producer` ( `producer_id` bigint(20) NOT NULL AUTO_INCREMENT, diff --git a/modules/tracker/database/mysql/2.0.4.sql b/modules/tracker/database/mysql/2.0.4.sql new file mode 100644 index 000000000..5956be60f --- /dev/null +++ b/modules/tracker/database/mysql/2.0.4.sql @@ -0,0 +1,164 @@ +-- Midas Server. Copyright Kitware SAS. Licensed under the Apache License 2.0. + +-- MySQL database for the tracker module, version 2.0.4 + +CREATE TABLE IF NOT EXISTS `tracker_producer` ( + `producer_id` bigint(20) NOT NULL AUTO_INCREMENT, + `community_id` bigint(20) NOT NULL, + `repository` varchar(255) NOT NULL, + `executable_name` varchar(255) NOT NULL, + `display_name` varchar(255) NOT NULL, + `description` text NOT NULL, + `revision_url` text NOT NULL, + `histogram_max_x` double, + `grid_across_metric_groups` tinyint(4) NOT NULL DEFAULT '0', + `histogram_number_of_bins` int(11) NOT NULL DEFAULT '10', + `producer_definition` text, + PRIMARY KEY (`producer_id`), + KEY (`community_id`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_scalar` ( + `scalar_id` bigint(20) NOT NULL AUTO_INCREMENT, + `trend_id` bigint(20) NOT NULL, + `value` double, + `submission_id` bigint(20) NOT NULL, + PRIMARY KEY (`scalar_id`), + KEY (`trend_id`), + KEY (`submission_id`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_submission` ( + `submission_id` bigint(20) NOT NULL AUTO_INCREMENT, + `producer_id` bigint(20) NOT NULL, + `name` varchar(255) NOT NULL DEFAULT '', + `uuid` varchar(255) NOT NULL DEFAULT '', + `submit_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `producer_revision` varchar(255), + `user_id` bigint(20) NOT NULL DEFAULT '-1', + `official` tinyint(4) NOT NULL DEFAULT '1', + `build_results_url` text NOT NULL, + `branch` varchar(255) NOT NULL DEFAULT '', + `extra_urls` text, + `reproduction_command` text, + `document` text, + PRIMARY KEY (`submission_id`), + UNIQUE KEY (`uuid`), + KEY (`user_id`), + KEY (`submit_time`), + KEY (`branch`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_submission2item` ( + `submission_id` bigint(20) NOT NULL, + `item_id` bigint(20) NOT NULL, + `label` varchar(255) NOT NULL, + `trendgroup_id` bigint(20) NOT NULL, + KEY (`submission_id`), + KEY (`item_id`), + KEY (`trendgroup_id`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_threshold_notification` ( + `threshold_id` bigint(20) NOT NULL AUTO_INCREMENT, + `trend_id` bigint(20) NOT NULL, + `value` double, + `comparison` varchar(2), + `action` varchar(80) NOT NULL, + `recipient_id` bigint(20) NOT NULL, + PRIMARY KEY (`threshold_id`), + KEY (`trend_id`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_trend` ( + `trend_id` bigint(20) NOT NULL AUTO_INCREMENT, + `trendgroup_id` bigint(20), + `metric_name` varchar(255) NOT NULL, + `display_name` varchar(255) NOT NULL, + `unit` varchar(255) NOT NULL, + `key_metric` tinyint(4) NOT NULL DEFAULT '0', + PRIMARY KEY (`trend_id`), + KEY (`trendgroup_id`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_trend_threshold` ( + `trend_threshold_id` bigint(20) NOT NULL AUTO_INCREMENT, + `producer_id` bigint(20) NOT NULL, + `metric_name` varchar(255) NOT NULL, + `abbreviation` varchar(255) NOT NULL DEFAULT '', + `warning` double, + `fail` double, + `min` double, + `max` double, + `lower_is_better` tinyint(4) NOT NULL DEFAULT '0', + PRIMARY KEY (`trend_threshold_id`), + KEY (`producer_id`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_trendgroup` ( + `trendgroup_id` bigint(20) NOT NULL AUTO_INCREMENT, + `producer_id` bigint(20) NOT NULL, + `config_item_id` bigint(20), + `test_dataset_id` bigint(20), + `truth_dataset_id` bigint(20), + PRIMARY KEY (`trendgroup_id`), + KEY (`producer_id`), + KEY (`config_item_id`), + KEY (`test_dataset_id`), + KEY (`truth_dataset_id`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_param` ( + `param_id` bigint(20) NOT NULL AUTO_INCREMENT, + `submission_id` bigint(20) NOT NULL, + `param_name` varchar(255) NOT NULL, + `param_type` enum('text', 'numeric') NOT NULL, + `text_value` text, + `numeric_value` double, + PRIMARY KEY (`param_id`), + KEY (`submission_id`), + KEY (`param_name`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_aggregate_metric` ( + `aggregate_metric_id` bigint(20) NOT NULL AUTO_INCREMENT, + `aggregate_metric_spec_id` bigint(20) NOT NULL, + `submission_id` bigint(20) NOT NULL, + `value` double, + PRIMARY KEY (`aggregate_metric_id`), + KEY (`aggregate_metric_spec_id`), + KEY (`submission_id`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_aggregate_metric_spec` ( + `aggregate_metric_spec_id` bigint(20) NOT NULL AUTO_INCREMENT, + `producer_id` bigint(20) NOT NULL, + `name` varchar(255) NOT NULL DEFAULT '', + `description` varchar(255) NOT NULL DEFAULT '', + `spec` text NOT NULL DEFAULT '', + `abbreviation` varchar(255) NOT NULL DEFAULT '', + `warning` double, + `fail` double, + `min` double, + `max` double, + `lower_is_better` tinyint(4) NOT NULL DEFAULT '0', + PRIMARY KEY (`aggregate_metric_spec_id`), + KEY (`producer_id`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_aggregate_metric_notification` ( + `aggregate_metric_notification_id` bigint(20) NOT NULL AUTO_INCREMENT, + `aggregate_metric_spec_id` bigint(20) NOT NULL, + `branch` varchar(255) NOT NULL DEFAULT '', + `value` double, + `comparison` varchar(2) NOT NULL DEFAULT '', + PRIMARY KEY (`aggregate_metric_notification_id`), + KEY (`aggregate_metric_spec_id`), + KEY (`branch`) +) DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `tracker_user2aggregate_metric_notification` ( + `user_id` bigint(20) NOT NULL, + `aggregate_metric_notification_id` bigint(20) NOT NULL, + PRIMARY KEY (`user_id`, `aggregate_metric_notification_id`) +) DEFAULT CHARSET=utf8; diff --git a/modules/tracker/database/upgrade/2.0.4.php b/modules/tracker/database/upgrade/2.0.4.php new file mode 100644 index 000000000..650827c91 --- /dev/null +++ b/modules/tracker/database/upgrade/2.0.4.php @@ -0,0 +1,48 @@ +db->query( + 'ALTER TABLE `tracker_producer` '. + ' ADD COLUMN `grid_across_metric_groups` tinyint(4) NOT NULL DEFAULT 0,'. + ' ADD COLUMN `histogram_number_of_bins` int(11) NOT NULL DEFAULT 10,'. + ' ADD COLUMN `producer_definition` text;' + ); + $this->db->query( + 'ALTER TABLE `tracker_trend_threshold` '. + ' ADD COLUMN `min` double,'. + ' ADD COLUMN `lower_is_better` tinyint(4) NOT NULL DEFAULT 0;' + ); + $this->db->query( + 'ALTER TABLE `tracker_submission` '. + ' ADD COLUMN `document` text;' + ); + $this->db->query( + 'ALTER TABLE `tracker_aggregate_metric_spec` '. + ' ADD COLUMN `min` double,'. + ' ADD COLUMN `lower_is_better` tinyint(4) NOT NULL DEFAULT 0;' + ); + } +} diff --git a/modules/tracker/models/base/AggregateMetricSpecModelBase.php b/modules/tracker/models/base/AggregateMetricSpecModelBase.php index 9ef9a36b2..ba0d69261 100644 --- a/modules/tracker/models/base/AggregateMetricSpecModelBase.php +++ b/modules/tracker/models/base/AggregateMetricSpecModelBase.php @@ -38,7 +38,9 @@ public function __construct() 'abbreviation' => array('type' => MIDAS_DATA), 'warning' => array('type' => MIDAS_DATA), 'fail' => array('type' => MIDAS_DATA), + 'min' => array('type' => MIDAS_DATA), 'max' => array('type' => MIDAS_DATA), + 'lower_is_better' => array('type' => MIDAS_DATA), 'producer' => array( 'type' => MIDAS_MANY_TO_ONE, 'model' => 'Producer', @@ -81,6 +83,34 @@ public function createAggregateMetricSpec($producerDao, $name, $spec, $descripti return $aggregateMetricSpecDao; } + /** + * Create or update a Tracker AggregateMetricSpec, matching on the Producer + * and spec fields. + * + * @param Tracker_ProducerDao $producerDao + * @param string $name name of the aggregate metric + * @param string $spec the spec for the aggregate metric spec + * @param false|string $abbreviation name abbreviation for the threshold + * @param false|string $description the description for the aggregate metric spec + * @param false|float $warning warning value for this threshold + * @param false|float $fail fail value for this threshold + * @param false|float $min min value for display of this threshold + * @param false|float $max max value for display of this threshold + * @param false|bool $lowerIsBetter whether lower values are better for this threshold + * @return false|Tracker_AggregateMetricSpecDao created from inputs + */ + abstract public function upsert( + $producerDao, + $name, + $spec, + $abbreviation = false, + $description = false, + $warning = false, + $fail = false, + $min = false, + $max = false, + $lowerIsBetter = false); + /** * Return all AggregateMetricSpecDaos tied to the producer. * diff --git a/modules/tracker/models/base/ProducerModelBase.php b/modules/tracker/models/base/ProducerModelBase.php index e1345562c..56e6d6827 100644 --- a/modules/tracker/models/base/ProducerModelBase.php +++ b/modules/tracker/models/base/ProducerModelBase.php @@ -37,6 +37,9 @@ public function __construct() 'display_name' => array('type' => MIDAS_DATA), 'description' => array('type' => MIDAS_DATA), 'histogram_max_x' => array('type' => MIDAS_DATA), + 'grid_across_metric_groups' => array('type' => MIDAS_DATA), + 'histogram_number_of_bins' => array('type' => MIDAS_DATA), + 'producer_definition' => array('type' => MIDAS_DATA), 'community' => array( 'type' => MIDAS_MANY_TO_ONE, 'model' => 'Community', @@ -50,6 +53,13 @@ public function __construct() 'parent_column' => 'producer_id', 'child_column' => 'producer_id', ), + 'trendthresholds' => array( + 'type' => MIDAS_ONE_TO_MANY, + 'model' => 'TrendThreshold', + 'module' => $this->moduleName, + 'parent_column' => 'producer_id', + 'child_column' => 'producer_id', + ), ); $this->initialize(); diff --git a/modules/tracker/models/base/SubmissionModelBase.php b/modules/tracker/models/base/SubmissionModelBase.php index 755a1d2e8..f87217369 100644 --- a/modules/tracker/models/base/SubmissionModelBase.php +++ b/modules/tracker/models/base/SubmissionModelBase.php @@ -42,6 +42,7 @@ public function __construct() 'extra_urls' => array('type' => MIDAS_DATA), 'branch' => array('type' => MIDAS_DATA), 'producer_revision' => array('type' => MIDAS_DATA), + 'document' => array('type' => MIDAS_DATA), 'reproduction_command' => array('type' => MIDAS_DATA), 'producer' => array( 'type' => MIDAS_MANY_TO_ONE, diff --git a/modules/tracker/models/base/TrendModelBase.php b/modules/tracker/models/base/TrendModelBase.php index 0a25df14a..717280afa 100644 --- a/modules/tracker/models/base/TrendModelBase.php +++ b/modules/tracker/models/base/TrendModelBase.php @@ -96,6 +96,15 @@ abstract public function getScalars($trendDao, $startDate = null, $endDate = nul */ abstract public function getTrendsByGroup($producerDao, $onlyKey = false); + /** + * Set all Trends with the passed in metric_name to key_metrics, + * for the passed in Producer. + * + * @param Tracker_ProducerDao $producerDao producer DAO + * @param string $metricName The metric_name to match against trends + */ + abstract public function setAggregatableTrendAsKeyMetrics($producerDao, $metricName); + /** * Save the given trend. Ensure that null values are explicitly set in the database. * diff --git a/modules/tracker/models/base/TrendThresholdModelBase.php b/modules/tracker/models/base/TrendThresholdModelBase.php index 01e469b56..bc75738b3 100644 --- a/modules/tracker/models/base/TrendThresholdModelBase.php +++ b/modules/tracker/models/base/TrendThresholdModelBase.php @@ -36,7 +36,9 @@ public function __construct() 'abbreviation' => array('type' => MIDAS_DATA), 'warning' => array('type' => MIDAS_DATA), 'fail' => array('type' => MIDAS_DATA), + 'min' => array('type' => MIDAS_DATA), 'max' => array('type' => MIDAS_DATA), + 'lower_is_better' => array('type' => MIDAS_DATA), 'producer' => array( 'type' => MIDAS_MANY_TO_ONE, 'model' => 'Producer', @@ -48,4 +50,27 @@ public function __construct() $this->initialize(); } + + /** + * Create or update a Tracker TrendThreshold tied to the Producer and metric_name. + * + * @param Tracker_ProducerDao $producerDao + * @param string $metricName metric name of the trend threshold + * @param false|string $abbreviation name abbreviation for the threshold + * @param false|float $warning warning value for this threshold + * @param false|float $fail fail value for this threshold + * @param false|float $min min value for display of this threshold + * @param false|float $max max value for display of this threshold + * @param false|bool $lowerIsBetter whether lower values are better for this threshold + * @return Tracker_TrendThresholdDao updated or created DAO + */ + abstract public function upsert( + $producerDao, + $metricName, + $abbreviation = false, + $warning = false, + $fail = false, + $min = false, + $max = false, + $lowerIsBetter = false); } diff --git a/modules/tracker/models/dao/AggregateMetricSpecDao.php b/modules/tracker/models/dao/AggregateMetricSpecDao.php index 869f5ab1b..e4d0666df 100644 --- a/modules/tracker/models/dao/AggregateMetricSpecDao.php +++ b/modules/tracker/models/dao/AggregateMetricSpecDao.php @@ -37,8 +37,12 @@ * @method void setWarning(float $warning) * @method float getFail() * @method void setFail(float $fail) + * @method float getMin() + * @method void setMin(float $min) * @method float getMax() * @method void setMax(float $max) + * @method bool getLowerIsBetter() + * @method void setLowerIsBetter(boolean $lowerIsBetter) * @method Tracker_ProducerDao getProducer() * @method void setProducer(Tracker_ProducerDao $producerDao) */ diff --git a/modules/tracker/models/dao/ProducerDao.php b/modules/tracker/models/dao/ProducerDao.php index 547662dc1..24e2e075d 100644 --- a/modules/tracker/models/dao/ProducerDao.php +++ b/modules/tracker/models/dao/ProducerDao.php @@ -39,6 +39,16 @@ * @method void setCommunity(CommunityDao $communityDao) * @method array getTrendgroups() * @method void setTrendgroups(array $trendgroupDaos) + * @method array getTrendthresholds() + * @method void setTrendthresholds(array $trendthresholdDaos) + * @method void setHistogramMaxX(float $histogramMaxX) + * @method float getHistogramMaxX() + * @method bool getGridAcrossMetricGroups() + * @method void setGridAcrossMetricGroups(boolean $gridAcrossMetricGroups) + * @method int getHistogramNumberOfBins() + * @method void setHistogramNumberOfBins(int $histogramNumberOfBins) + * @method string getProducerDefinition() + * @method void setProducerDefinition(string $producerDefinition) */ class Tracker_ProducerDao extends Tracker_AppDao { diff --git a/modules/tracker/models/dao/SubmissionDao.php b/modules/tracker/models/dao/SubmissionDao.php index 9f0c61983..e99158e1a 100644 --- a/modules/tracker/models/dao/SubmissionDao.php +++ b/modules/tracker/models/dao/SubmissionDao.php @@ -49,6 +49,8 @@ * @method void setProducerRevision(string $producerRevision) * @method string getReproductionCommand() * @method void setReproductionCommand(string $reproductionCommand) + * @method string getDocument() + * @method void setDocument(string $document) */ class Tracker_SubmissionDao extends Tracker_AppDao { diff --git a/modules/tracker/models/dao/TrendThresholdDao.php b/modules/tracker/models/dao/TrendThresholdDao.php index ea3c1acd5..c7a3d42c9 100644 --- a/modules/tracker/models/dao/TrendThresholdDao.php +++ b/modules/tracker/models/dao/TrendThresholdDao.php @@ -33,8 +33,12 @@ * @method void setWarning(float $warning) * @method float getFail() * @method void setFail(float $fail) + * @method float getMin() + * @method void setMin(float $min) * @method float getMax() * @method void setMax(float $max) + * @method bool getLowerIsBetter() + * @method void setLowerIsBetter(boolean $lowerIsBetter) * @method Tracker_ProducerDao getProducer() * @method void setProducer(Tracker_ProducerDao $producerDao) */ diff --git a/modules/tracker/models/pdo/AggregateMetricSpecModel.php b/modules/tracker/models/pdo/AggregateMetricSpecModel.php index be0e34d4a..5b9cbb324 100644 --- a/modules/tracker/models/pdo/AggregateMetricSpecModel.php +++ b/modules/tracker/models/pdo/AggregateMetricSpecModel.php @@ -23,6 +23,74 @@ /** AggregateMetricSpec model for the tracker module. */ class Tracker_AggregateMetricSpecModel extends Tracker_AggregateMetricSpecModelBase { + /** + * Create or update a Tracker AggregateMetricSpec, matching on the Producer + * and spec fields. + * + * @param Tracker_ProducerDao $producerDao + * @param string $name name of the aggregate metric + * @param string $spec the spec for the aggregate metric spec + * @param false|string $abbreviation name abbreviation for the threshold + * @param false|string $description the description for the aggregate metric spec + * @param false|float $warning warning value for this threshold + * @param false|float $fail fail value for this threshold + * @param false|float $min min value for display of this threshold + * @param false|float $max max value for display of this threshold + * @param false|bool $lowerIsBetter whether lower values are better for this threshold + * @return false|Tracker_AggregateMetricSpecDao created from inputs + */ + public function upsert( + $producerDao, + $name, + $spec, + $abbreviation = false, + $description = false, + $warning = false, + $fail = false, + $min = false, + $max = false, + $lowerIsBetter = false) + { + if (is_null($producerDao) || $producerDao === false) { + return false; + } + $sql = $this->database->select()->setIntegrityCheck(false) + ->where('producer_id = ?', $producerDao->getProducerId()) + ->where('spec = ?', $spec); + /** @var Tracker_AggregateMetricSpecDao $aggregateMetricSpecDao */ + $aggregateMetricSpecDao = $this->initDao('AggregateMetricSpec', $this->database->fetchRow($sql), $this->moduleName); + if ($aggregateMetricSpecDao === false) { + $aggregateMetricSpecDao = MidasLoader::newDao('AggregateMetricSpecDao', $this->moduleName); + $aggregateMetricSpecDao->setProducerId($producerDao->getProducerId()); + $aggregateMetricSpecDao->setSpec($spec); + } + $aggregateMetricSpecDao->setName($name); + if ($abbreviation !== false) { + $aggregateMetricSpecDao->setAbbreviation($abbreviation); + } + if ($description !== false) { + $aggregateMetricSpecDao->setDescription($description); + } + if ($warning !== false) { + $aggregateMetricSpecDao->setWarning($warning); + } + if ($fail !== false) { + $aggregateMetricSpecDao->setFail($fail); + } + if ($min !== false) { + $aggregateMetricSpecDao->setMin($min); + } + if ($max !== false) { + $aggregateMetricSpecDao->setMax($max); + } + if ($lowerIsBetter !== false) { + $aggregateMetricSpecDao->setLowerIsBetter($lowerIsBetter); + } + $this->save($aggregateMetricSpecDao); + + return $aggregateMetricSpecDao; + } + /** * Delete the given aggregate metric spec, any metrics calculated based on that spec, * and any associated notifications. diff --git a/modules/tracker/models/pdo/TrendModel.php b/modules/tracker/models/pdo/TrendModel.php index 782dc874c..e7ce0d95f 100644 --- a/modules/tracker/models/pdo/TrendModel.php +++ b/modules/tracker/models/pdo/TrendModel.php @@ -151,6 +151,27 @@ public function getTrendsByGroup($producerDao, $onlyKey = false) return $results; } + /** + * Set all Trends with the passed in metric_name to key_metrics, + * for the passed in Producer. + * + * @param Tracker_ProducerDao $producerDao producer DAO + * @param string $metricName The metric_name to match against trends + */ + public function setAggregatableTrendAsKeyMetrics($producerDao, $metricName) + { + if (is_null($producerDao) || $producerDao === false) { + return false; + } + $producerIdQ = $this->database->getDB()->quote($producerDao->getProducerId()); + $metricNameQ = $this->database->getDB()->quote($metricName); + $update = 'UPDATE tracker_trend, tracker_trendgroup SET key_metric=1 WHERE'. + ' producer_id = '.$producerIdQ. + ' AND tracker_trend.trendgroup_id = tracker_trendgroup.trendgroup_id '. + ' AND tracker_trend.metric_name = '.$metricNameQ; + $this->database->getDB()->query($update); + } + /** * Return the trend DAOs that match the given associative array of database columns and values. * diff --git a/modules/tracker/models/pdo/TrendThresholdModel.php b/modules/tracker/models/pdo/TrendThresholdModel.php index fa73438d7..77c15515b 100644 --- a/modules/tracker/models/pdo/TrendThresholdModel.php +++ b/modules/tracker/models/pdo/TrendThresholdModel.php @@ -23,4 +23,62 @@ /** Trend Threshold model for the tracker module. */ class Tracker_TrendThresholdModel extends Tracker_TrendThresholdModelBase { + /** + * Create or update a Tracker TrendThreshold tied to the Producer and metric_name. + * + * @param Tracker_ProducerDao $producerDao + * @param string $metricName metric name of the trend threshold + * @param false|string $abbreviation name abbreviation for the threshold + * @param false|float $warning warning value for this threshold + * @param false|float $fail fail value for this threshold + * @param false|float $min min value for display of this threshold + * @param false|float $max max value for display of this threshold + * @param false|bool $lowerIsBetter whether lower values are better for this threshold + * @return Tracker_TrendThresholdDao updated or created DAO + */ + public function upsert( + $producerDao, + $metricName, + $abbreviation = false, + $warning = false, + $fail = false, + $min = false, + $max = false, + $lowerIsBetter = false) + { + if (is_null($producerDao) || $producerDao === false) { + return false; + } + $sql = $this->database->select()->setIntegrityCheck(false) + ->where('producer_id = ?', $producerDao->getProducerId()) + ->where('metric_name = ?', $metricName); + /** @var Tracker_TrendThresholdDao $trendThresholdDao */ + $trendThresholdDao = $this->initDao('TrendThreshold', $this->database->fetchRow($sql), $this->moduleName); + if ($trendThresholdDao === false) { + $trendThresholdDao = MidasLoader::newDao('TrendThresholdDao', $this->moduleName); + $trendThresholdDao->setProducerId($producerDao->getProducerId()); + $trendThresholdDao->setMetricName($metricName); + } + if ($abbreviation !== false) { + $trendThresholdDao->setAbbreviation($abbreviation); + } + if ($warning !== false) { + $trendThresholdDao->setWarning($warning); + } + if ($fail !== false) { + $trendThresholdDao->setFail($fail); + } + if ($min !== false) { + $trendThresholdDao->setMin($min); + } + if ($max !== false) { + $trendThresholdDao->setMax($max); + } + if ($lowerIsBetter !== false) { + $trendThresholdDao->setLowerIsBetter($lowerIsBetter); + } + $this->save($trendThresholdDao); + + return $trendThresholdDao; + } }