From 955549ac568db11344e9292882e5ba3a9ec6f438 Mon Sep 17 00:00:00 2001 From: Anton Sankov Date: Sat, 21 Aug 2021 00:29:15 +0300 Subject: [PATCH] wip: Add Performance Telemetry UI page still WIP, because the page breaks when given too much data. the algorithm for the data looks OK, because it works with the small data-set I left in the code. however, given dataset with more than 500 runs it fails to display the chart miserably. --- tcms/settings/common.py | 6 + .../telemetry/js/management/performance.js | 142 ++++++++++++++++++ .../static/telemetry/js/testing/breakdown.js | 3 + .../telemetry/management/performance.html | 28 ++++ tcms/telemetry/urls.py | 5 + tcms/telemetry/views.py | 7 + 6 files changed, 191 insertions(+) create mode 100644 tcms/telemetry/static/telemetry/js/management/performance.js create mode 100644 tcms/telemetry/templates/telemetry/management/performance.html diff --git a/tcms/settings/common.py b/tcms/settings/common.py index fe212b2ccb..fd1abe53f6 100644 --- a/tcms/settings/common.py +++ b/tcms/settings/common.py @@ -326,6 +326,12 @@ (_("TestCase health"), reverse_lazy("test-case-health")), ], ), + ( + _("Management"), + [ + (_("Performance"), reverse_lazy("management-performance")), + ], + ), ( "More coming soon", "http://kiwitcms.org/blog/kiwi-tcms-team/2019/03/03/legacy-reports-become-telemetry/", diff --git a/tcms/telemetry/static/telemetry/js/management/performance.js b/tcms/telemetry/static/telemetry/js/management/performance.js new file mode 100644 index 0000000000..9a815d6561 --- /dev/null +++ b/tcms/telemetry/static/telemetry/js/management/performance.js @@ -0,0 +1,142 @@ +// TODO: account for more users +const colors = [ + 'blue', + 'red', + 'gold', + 'orange', + 'green', + 'cyan', + 'purple', + 'black', + 'lightBlue', + 'lightGreen' +] + +$(document).ready(() => { + $('.selectpicker').selectpicker() + $('[data-toggle="tooltip"]').tooltip() + + loadInitialProduct(reloadCharts) + + $('#id_after').on('dp.change', reloadCharts) + $('#id_before').on('dp.change', reloadCharts) + document.getElementById('id_test_plan').onchange = reloadCharts + document.getElementById('id_product').onchange = () => { + updateTestPlanSelectFromProduct(reloadCharts) + } +}) + +function reloadCharts () { + const query = {} + + const testPlanIds = $('#id_test_plan').val() + const productIds = $('#id_product').val() + + if (testPlanIds.length) { + query.plan__in = testPlanIds + } else if (productIds.length) { + query.category__product_id__in = productIds + } + + const dateBefore = $('#id_before') + if (dateBefore.val()) { + query.create_date__lte = dateBefore.data('DateTimePicker').date().format('YYYY-MM-DD 23:59:59') + } + + const dateAfter = $('#id_after') + if (dateAfter.val()) { + query.create_date__gte = dateAfter.data('DateTimePicker').date().format('YYYY-MM-DD 00:00:00') + } + + jsonRPC('Management.performance', query, result => { + console.log(result) + + // the actual result is in the same format, only it can be much bigger + // and the chart may break + const r = { + 1: { + 1: 1, + 3: 2 + }, + 2: { + 1: 1, + 4: 2 + }, + 3: { + 1: 1, + 3: 1, + 5: 1 + }, + 4: { + 3: 1, + 2: 1 + }, + 5: { + 1: 5, + 3: 2, + 4: 1 + } + } + + drawChart(r) + }, true) +} + +function drawChart (data) { + // the X axis of the chart - run IDs + const groupedCategories = [] + // map of user ID -> table column. we use map here for faster lookup by user ID. + const groupedColumnsDataMap = {} + const userIds = new Set() + + // collect all the testers so that we know how much columns we will have + Object.entries(data).forEach(([_testRunId, asigneeCount]) => { + Object.entries(asigneeCount).forEach(([userId, _executionCount]) => userIds.add(userId)) + }) + + userIds.forEach(userId => (groupedColumnsDataMap[userId] = [`User ${userId}`])) + + Object.entries(data).forEach(([testRunId, _asigneeCount]) => { + groupedCategories.push(testRunId) + + const asigneesCount = data[testRunId] + + // for each user in the groupedColumnsDataMap check if that user + // is assigned any executions for this run. + Object.entries(groupedColumnsDataMap).forEach(([userId, data]) => { + const count = asigneesCount[userId] + if (count) { + data.push(count) + } else { + // TODO: find a way to hide the 0 valued-columns + data.push(0) + } + }) + }) + + // C3 does not need a map, but an array of values + // get rid of the keys, because we do not need them anymore + const groupedColumnsData = Object.values(groupedColumnsDataMap) + + const chartConfig = $().c3ChartDefaults().getDefaultGroupedBarConfig() + chartConfig.bindto = '#performance-chart' + chartConfig.axis = { + x: { + categories: groupedCategories, + type: 'category' + }, + y: { + tick: { + format: showOnlyRoundNumbers + } + } + } + chartConfig.data = { + type: 'bar', + columns: groupedColumnsData + } + chartConfig.color = { + pattern: colors + } + c3.generate(chartConfig) +} diff --git a/tcms/telemetry/static/telemetry/js/testing/breakdown.js b/tcms/telemetry/static/telemetry/js/testing/breakdown.js index 181cef71d6..9a357a4e65 100644 --- a/tcms/telemetry/static/telemetry/js/testing/breakdown.js +++ b/tcms/telemetry/static/telemetry/js/testing/breakdown.js @@ -114,6 +114,9 @@ function drawChart(data, type, selector) { chartData.push(dataEntry); }); + console.log('data', data) + console.log('groups', groups) + console.log('chartData', chartData) let chartConfig = $().c3ChartDefaults().getDefaultStackedBarConfig(); chartConfig.bindto = selector; chartConfig.axis = { diff --git a/tcms/telemetry/templates/telemetry/management/performance.html b/tcms/telemetry/templates/telemetry/management/performance.html new file mode 100644 index 0000000000..074b4cd47a --- /dev/null +++ b/tcms/telemetry/templates/telemetry/management/performance.html @@ -0,0 +1,28 @@ +{% extends "base.html" %} +{% load i18n %} +{% load static %} + +{% block title %}{% trans "Performance" %}{% endblock %} + +{% block contents %} + +
+ {% include "telemetry/include/filters.html" %} + +
+
{% trans "Performance" %}
+
+
+
+ + + + + + + + + + + +{% endblock %} diff --git a/tcms/telemetry/urls.py b/tcms/telemetry/urls.py index 650b0ce187..9061135fe5 100644 --- a/tcms/telemetry/urls.py +++ b/tcms/telemetry/urls.py @@ -23,4 +23,9 @@ views.TestingTestCaseHealth.as_view(), name="test-case-health", ), + re_path( + r"^management/performance/$", + views.ManagementPerformance.as_view(), + name="management-performance", + ), ] diff --git a/tcms/telemetry/views.py b/tcms/telemetry/views.py index cf2b1931be..6cb6145716 100644 --- a/tcms/telemetry/views.py +++ b/tcms/telemetry/views.py @@ -33,3 +33,10 @@ class TestingExecutionTrendsView(TemplateView): class TestingTestCaseHealth(TemplateView): template_name = "telemetry/testing/test-case-health.html" + +@method_decorator( + login_required, name="dispatch" +) # pylint: disable=missing-permission-required +class ManagementPerformance(TemplateView): + + template_name = "telemetry/management/performance.html"