diff --git a/classes/DataWarehouse/Access/Usage.php b/classes/DataWarehouse/Access/Usage.php index 586533dde9..f42adbcab9 100644 --- a/classes/DataWarehouse/Access/Usage.php +++ b/classes/DataWarehouse/Access/Usage.php @@ -38,6 +38,99 @@ class Usage extends Common */ const DEFAULT_SCALE = 1.0; + /** + * return the metadata about the summary charts for a given realm & group_by. The + * chart data itself is not queried by this call. + */ + private function getSummaryCharts(XDUser $user) { + + $usageCharts = array(); + + $requestedRealms = array_map('trim', explode(',', $this->request['realm'])); + foreach ($requestedRealms as $usageRealm) { + + $usageGroupBy = \xd_utilities\array_get($this->request, 'group_by', 'none'); + + $usageRealmAggregateClass = "\\DataWarehouse\\Query\\$usageRealm\\Aggregate"; + $usageGroupByObject = $usageRealmAggregateClass::getGroupBy($usageGroupBy); + + $usageSubnotes = array(); + if ($usageGroupBy === 'resource' || array_key_exists('resource', $this->request)) { + $usageSubnotes[] = '* Resources marked with asterisk do not provide processor' + . ' counts per job when submitting to the ' + . ORGANIZATION_NAME . ' Central Database. This affects the' + . ' accuracy of the following (and related) statistics: Job' + . ' Size and CPU Consumption'; + } + + $usageChartSettings = array( + 'dataset_type' => \xd_utilities\array_get($this->request, 'dataset_type', $usageGroupByObject->getDefaultDatasetType()), + 'display_type' => \xd_utilities\array_get($this->request, 'display_type', $usageGroupByObject->getDefaultDisplayType($usageGroupByObject->getDefaultDatasetType())), + 'combine_type' => \xd_utilities\array_get($this->request, 'combine_type', $usageGroupByObject->getDefaultCombineMethod()), + 'show_legend' => \xd_utilities\array_get($this->request, 'show_legend', $usageGroupByObject->getDefaultShowLegend()), + 'show_guide_lines' => \xd_utilities\array_get($this->request, 'show_guide_lines', $usageGroupByObject->getDefaultShowGuideLines()), + 'log_scale' => \xd_utilities\array_get($this->request, 'log_scale', $usageGroupByObject->getDefaultLogScale()), + 'limit' => \xd_utilities\array_get($this->request, 'limit', $usageGroupByObject->getDefaultLimit()), + 'offset' => \xd_utilities\array_get($this->request, 'offset', $usageGroupByObject->getDefaultOffset()), + 'show_trend_line' => \xd_utilities\array_get($this->request, 'show_trend_line', $usageGroupByObject->getDefaultShowTrendLine()), + 'show_error_bars' => \xd_utilities\array_get($this->request, 'show_error_bars', $usageGroupByObject->getDefaultShowErrorBars()), + 'show_aggregate_labels' => \xd_utilities\array_get($this->request, 'show_aggregate_labels', $usageGroupByObject->getDefaultShowAggregateLabels()), + 'show_error_labels' => \xd_utilities\array_get($this->request, 'show_error_labels', $usageGroupByObject->getDefaultShowErrorLabels()), + 'hide_tooltip' => \xd_utilities\array_get($this->request, 'hide_tooltip', false), + 'enable_errors' => 'n', + 'thumbnail' => 'y', + 'enable_trend_line' => \xd_utilities\array_get($this->request, 'enable_trend_line', $usageGroupByObject->getDefaultEnableTrendLine()), + 'realm' => $usageRealm, + 'group_by' => $usageGroupBy + ); + + $userStatistics = Acls::getPermittedStatistics($user, $usageRealm, $usageGroupBy); + + foreach ($userStatistics as $userStatistic) { + + $statsClass = $usageRealmAggregateClass::getStatistic($userStatistic); + + if (!$statsClass->isVisible()) { + continue; + } + + $errorstat = 'sem_' . $userStatistic; + if (in_array($errorstat, array_keys($usageRealmAggregateClass::getRegisteredStatistics())) ) { + $usageChartSettings['enable_errors'] = 'y'; + } else { + $usageChartSettings['enable_errors'] = 'n'; + } + $usageChartSettings['statistic'] = $userStatistic; + + $usageChart = array( + 'hc_jsonstore' => array('title' => array('text' => '')), + 'id' => "statistic_${usageRealm}_${usageGroupBy}_${userStatistic}", + 'short_title' => $statsClass->getLabel(), + 'random_id' => 'chart_' . mt_rand(), + 'subnotes' => $usageSubnotes, + 'group_description' => $usageGroupByObject->getDescription(), + 'description' => $statsClass->getDescription($usageGroupByObject), + 'chart_settings' => json_encode($usageChartSettings), + ); + + $usageCharts[] = $usageChart; + } + } + + usort($usageCharts, function ($a, $b) { + return strcmp($a['short_title'], $b['short_title']); + }); + + $data = array( + 'success' => true, + 'message' => 'success', + 'totalCount' => count($usageCharts), + 'data' => $usageCharts + ); + + return $this->exportImage($data, null, null, null, 'hc_jsonstore', null); + } + /** * Get charts by converting a Usage tab-style request to Metric Explorer * requests. @@ -50,6 +143,11 @@ class Usage extends Common * headers: Headers to set on the response. */ public function getCharts(XDUser $user, $chartsKey = 'data') { + + if (isset($this->request['summary'])) { + return $this->getSummaryCharts($user); + } + // Determine which realms are being requested. if (empty($this->request['realm'])) { throw new Exception('One or more realms must be specified.'); diff --git a/html/gui/js/modules/Usage.js b/html/gui/js/modules/Usage.js index 09279aeed0..60533211b9 100644 --- a/html/gui/js/modules/Usage.js +++ b/html/gui/js/modules/Usage.js @@ -431,6 +431,8 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { var self = this; + var handleDataException; + var public_user = this.public_user || CCR.xdmod.publicUser; /* @@ -1185,6 +1187,7 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { // --------------------------------------------------------- function onSelectNode(model, n) { + var parameters; if (!n || !n.text) return; if (!self.getDurationSelector().validate()) return; @@ -1246,7 +1249,7 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { this.legendTypeComboBox.disable(); - var parameters = []; + parameters = []; if (n.attributes.node_type == 'statistic') { parameters = getChartParameters(n); @@ -1321,7 +1324,7 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { } else if (n.attributes.node_type == 'statistic') { view.tpl = largeChartTemplate; - var parameters = getChartParameters(n); + parameters = getChartParameters(n); this.legendTypeComboBox.enable(); this.chartTitleField.enable(); @@ -1344,12 +1347,13 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { } else if (n.attributes.node_type == 'group_by') { view.tpl = thumbnailChartTemplate; - var parameters = getMenuParameters(n); + parameters = getMenuParameters(n); chartStore.removeAll(true); maximizeScale.call(this); this.legendTypeComboBox.disable(); this.chartTitleField.disable(); + parameters.summary = true; parameters.operation = 'get_charts'; chartStore.load({ params: parameters @@ -1405,20 +1409,58 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { this.charts = []; - var ind = 0; + var menuParams = getMenuParameters(n); chartStore.each(function (r) { var id = r.get('random_id'); - var task = new Ext.util.DelayedTask(function () { + var chartContainer = Ext.get(id); + + if (!chartContainer) { + return; + } + + chartContainer.setWidth(CCR.xdmod.ui.thumbWidth * chartThumbScale); + chartContainer.setHeight(CCR.xdmod.ui.thumbHeight * chartThumbScale); + chartContainer.mask('Loading...'); + + var chart_params = Ext.apply({}, Ext.util.JSON.decode(r.get('chart_settings')), menuParams); + + var deferStore = new Ext.data.JsonStore({ + autoDestroy: true, + root: 'data', + totalProperty: 'totalCount', + successProperty: 'success', + messageProperty: 'message', + fields: self.chartDataFields, + baseParams: { + operation: 'get_charts', + public_user: public_user, + controller_module: self.module_id + }, + proxy: new Ext.data.HttpProxy({ + method: 'POST', + url: this.chartDataURL + }), + listeners: { + exception: function (reader, type, action, opt, response) { + handleDataException(response, type); + } + } + }); + var chartBuilder = function (chartRecords, options, success) { // If the rendering target no longer // exists, don't create the chart. if (!Ext.get(id)) { return; } + if (!success) { + return; + } + var baseChartOptions = { chart: { @@ -1463,20 +1505,23 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { }; //baseChartOptions - var chartOptions = r.get('hc_jsonstore'); + var chartOptions = {}; + if (chartRecords.length > 0) { + chartOptions = chartRecords[0].get('hc_jsonstore'); + } jQuery.extend(true, chartOptions, baseChartOptions); chartOptions.exporting.enabled = false; chartOptions.credits.enabled = false; this.charts.push(new Highcharts.Chart(chartOptions)); + }; - }, this); //task - - task.delay(0); - - return true; - + deferStore.load({ + params: chart_params, + callback: chartBuilder, + scope: self + }); }, this); //chartStore.each(function(r) self.getDurationSelector().enable(); @@ -1538,7 +1583,7 @@ Ext.extend(XDMoD.Module.Usage, XDMoD.PortalModule, { // --------------------------------------------------------- - var handleDataException = function (response, exceptionType) { + handleDataException = function (response, exceptionType) { var viewer = CCR.xdmod.ui.Viewer.getViewer(); if (exceptionType === 'response') {