diff --git a/html/gui/js/modules/metric_explorer/MetricExplorer.js b/html/gui/js/modules/metric_explorer/MetricExplorer.js index 4985c9e1c9..5d21967526 100644 --- a/html/gui/js/modules/metric_explorer/MetricExplorer.js +++ b/html/gui/js/modules/metric_explorer/MetricExplorer.js @@ -412,6 +412,127 @@ Ext.apply(XDMoD.Module.MetricExplorer, { return result; }; + const generatePythonCode = function (config) { + let duration; + if (config.timeframe_label === 'User Defined' && config.start_date && config.end_date) { + duration = `${config.start_date} , ${config.end_date}`; + } else if (config.timeframe_label) { + duration = config.timeframe_label; + } else { + duration = 'Previous Month'; + } + const dataType = config.timeseries ? 'timeseries' : 'aggregate'; + const aggregationUnit = config.aggregation_unit || 'Auto'; + const swapXY = config.swap_xy; + let filters = ''; + const filterDict = {}; + let subTitle = ''; + for (let i = 0; i < config.global_filters.total; i += 1) { + const { dimension_id: id, value_name: value } = config.global_filters.data[i]; + if (filterDict[id]) { + filterDict[id].push(value); + } else { + filterDict[id] = [value]; + } + } + for (const id in filterDict) { + if (Object.prototype.hasOwnProperty.call(filterDict, id)) { + const values = filterDict[id].join("', '"); + filters += `\n\t\t'${id}': ('${values}'),`; + subTitle += `${id}: ${values.replace(/'/g, '')}`; + } + } + const multiChart = []; + for (let i = 0; i < config.data_series.total; i += 1) { + const { + realm = 'Jobs', + metric = 'CPU Hours: Total', + group_by: dimension = 'none', + log_scale: logScale, + display_type: displayType + } = config.data_series.data[i]; + let graphType = displayType || 'line'; + let lineShape = ''; + if (graphType === 'column') { + graphType = 'bar'; + lineShape = "barmode='group',"; + } else if (graphType === 'spline') { + graphType = 'line'; + lineShape = "\nline_shape='spline',"; + } else if (graphType === 'line' && dataType === 'aggregate' && dimension === 'none') { + graphType = 'scatter'; + } else if (graphType === 'areaspline') { + graphType = 'area'; + lineShape = "\nline_shape='spline',"; + } + let axis = ''; + if (swapXY && graphType !== 'pie') { + axis = `\ty= data_${i}.columns[0],\n\tx= data_${i}.columns[1:],`; + } else { + axis = `labels={"value": dw.describe_metrics('${realm}').loc['${metric}', 'label']},`; + } + let dataView; + + if (dataType === 'aggregate') { + let graph; + if (graphType === 'pie') { + graph = ` +if(data_${i}.size > 10): + others_sum=data_${i}[~data_${i}.isin(top_ten)].sum() + data_${i} = top_ten.combine_first(pd.Series({'Other ' + String(data_${i}.size - 10): others_sum}))\n`; + } else { + graph = `\n\tdata_${i} = top_ten`; + } + dataView = ` +\n# Process the data series, combine the lower values into a single Other category, and change to series to a dataframe + top_ten=data_${i}.nlargest(10) + ${graph} + data_${i} = data_${i}.to_frame() + columns_list = data_${i}.columns.tolist()`; + } else { + dataView = ` +\n# Limit the number of data items/source to at most 10 and sort by descending'; + columns_list = data_${i}.columns.tolist() + if (len(columns_list) > 10): + column_sums = data_${i}.sum() + top_ten_columns = column_sums.nlargest(10).index.tolist() + data_${i} = data_${i}[top_ten_columns]\n`; + } + + const chart = ` + data_${i} = dw.get_data( + duration=('${duration}'), + realm='${realm}', + metric='${metric}', + dimension='${dimension}', + filters={${filters}}, + dataset_type='${dataType}', + aggregation_unit='${aggregationUnit}', + )\n + ${dataView} + ${(swapXY && graphType !== 'pie') ? `\tdata_${i} = data_${i}.reset_index()` : ''} +# Format and draw the graph to the screen\n + plot = px.${graphType}( + data_${i}, ${(graphType === 'pie') ? `\nvalues= columns_list[0],\n names= data_${i}.index,` : ''} + ${axis} + title='${config.title || 'Untitled Query'}',${subTitle ? `\n<br><sup>${subTitle}</sup>,` : ''}${logScale ? `log_${swapXY ? 'x' : 'y'}=True,` : ''}${lineShape} + ) + plot.update_layout( + xaxis_automargin=True, + ) + plot.show()\n`; + multiChart[i] = chart; + } + let dataCalls = ` +import pandas as pd +# Call to Data Analytics Framework requesting data +with dw:`; + multiChart.forEach((chart) => { + dataCalls += chart; + }); + return dataCalls; + }; + const chartJSON = JSON.stringify(filterConfigForExport(instance.getConfig()), null, 4); menu.add({ text: 'View chart json', @@ -439,6 +560,25 @@ Ext.apply(XDMoD.Module.MetricExplorer, { win.show(); } }); + menu.add({ + text: 'View python code', + iconCls: 'custom_chart', + handler: () => { + const win = new Ext.Window({ + title: 'API Code', + width: 800, + height: 600, + layout: 'fit', + autoScroll: true, + closeAction: 'destroy', + items: [{ + autoScroll: true, + html: `
Python API code \n************************************************\n` + }] + }); + win.show(); + } + }); const chartLayoutJSON = JSON.stringify(instance.plotlyPanel.chartOptions.layout, null, 4); menu.add({ text: 'View Plotly JS chart layout', @@ -2089,7 +2229,6 @@ Ext.extend(XDMoD.Module.MetricExplorer, XDMoD.PortalModule, { chartLinkButton: true }, - show_filters: true, show_warnings: true, font_size: 3, diff --git a/tests/ui/test/specs/xdmod/metricExplorer.js b/tests/ui/test/specs/xdmod/metricExplorer.js index 5221f8a9da..ae231bd1f9 100644 --- a/tests/ui/test/specs/xdmod/metricExplorer.js +++ b/tests/ui/test/specs/xdmod/metricExplorer.js @@ -194,6 +194,18 @@ describe('Metric Explorer', function metricExplorer() { it('Undo Trend Line looks the same as previous run', function () { me.checkChart(chartName, 'Node Hours: Total', expected.legend); }); + it('Open Context Menu', () => { + const elems = browser.elements('//div[@id="metric_explorer"]//div[contains(@class, "plot-container")]//*[local-name() = "svg"]/*[name()="g" and contains(@class, "draglayer")]//*[contains(@class, "xy")]//*[contains(@class, "nsewdrag drag")]'); + elems.value[0].doubleClick(); + browser.waitForVisible(me.selectors.chart.contextMenu.menuByTitle('Chart Options:')); + }); + it('Press Genererate code', () => { + browser.click(me.selectors.chart.contextMenu.menuItemByText('Chart Options:', 'View python code')); + }); + it('Check for code pop up', () => { + browser.waitUntilNotExist(me.selectors.chart.contextMenu.menuItemByText('Chart Options:', 'View python code')); + browser.waitForVisible('//div[contains(@class, "x-window x-resizable-pinned")]//div[contains(@class, "x-window-tl")]//span[contains(text(),"API Code")]', 10000); + }); }); /* The following tests are disabled until such a time as they can be changed to work * reliably without browser.pause()${generatePythonCode(instance.getConfig())}
\n************************************************
The link to the data analytisc API can be found here
Infomation about the Plotly Express Libary can be found here
Example XDmod API Notebooks can be found here