Skip to content

Commit 955549a

Browse files
committed
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.
1 parent 62a61f6 commit 955549a

File tree

6 files changed

+191
-0
lines changed

6 files changed

+191
-0
lines changed

tcms/settings/common.py

+6
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,12 @@
326326
(_("TestCase health"), reverse_lazy("test-case-health")),
327327
],
328328
),
329+
(
330+
_("Management"),
331+
[
332+
(_("Performance"), reverse_lazy("management-performance")),
333+
],
334+
),
329335
(
330336
"More coming soon",
331337
"http://kiwitcms.org/blog/kiwi-tcms-team/2019/03/03/legacy-reports-become-telemetry/",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// TODO: account for more users
2+
const colors = [
3+
'blue',
4+
'red',
5+
'gold',
6+
'orange',
7+
'green',
8+
'cyan',
9+
'purple',
10+
'black',
11+
'lightBlue',
12+
'lightGreen'
13+
]
14+
15+
$(document).ready(() => {
16+
$('.selectpicker').selectpicker()
17+
$('[data-toggle="tooltip"]').tooltip()
18+
19+
loadInitialProduct(reloadCharts)
20+
21+
$('#id_after').on('dp.change', reloadCharts)
22+
$('#id_before').on('dp.change', reloadCharts)
23+
document.getElementById('id_test_plan').onchange = reloadCharts
24+
document.getElementById('id_product').onchange = () => {
25+
updateTestPlanSelectFromProduct(reloadCharts)
26+
}
27+
})
28+
29+
function reloadCharts () {
30+
const query = {}
31+
32+
const testPlanIds = $('#id_test_plan').val()
33+
const productIds = $('#id_product').val()
34+
35+
if (testPlanIds.length) {
36+
query.plan__in = testPlanIds
37+
} else if (productIds.length) {
38+
query.category__product_id__in = productIds
39+
}
40+
41+
const dateBefore = $('#id_before')
42+
if (dateBefore.val()) {
43+
query.create_date__lte = dateBefore.data('DateTimePicker').date().format('YYYY-MM-DD 23:59:59')
44+
}
45+
46+
const dateAfter = $('#id_after')
47+
if (dateAfter.val()) {
48+
query.create_date__gte = dateAfter.data('DateTimePicker').date().format('YYYY-MM-DD 00:00:00')
49+
}
50+
51+
jsonRPC('Management.performance', query, result => {
52+
console.log(result)
53+
54+
// the actual result is in the same format, only it can be much bigger
55+
// and the chart may break
56+
const r = {
57+
1: {
58+
1: 1,
59+
3: 2
60+
},
61+
2: {
62+
1: 1,
63+
4: 2
64+
},
65+
3: {
66+
1: 1,
67+
3: 1,
68+
5: 1
69+
},
70+
4: {
71+
3: 1,
72+
2: 1
73+
},
74+
5: {
75+
1: 5,
76+
3: 2,
77+
4: 1
78+
}
79+
}
80+
81+
drawChart(r)
82+
}, true)
83+
}
84+
85+
function drawChart (data) {
86+
// the X axis of the chart - run IDs
87+
const groupedCategories = []
88+
// map of user ID -> table column. we use map here for faster lookup by user ID.
89+
const groupedColumnsDataMap = {}
90+
const userIds = new Set()
91+
92+
// collect all the testers so that we know how much columns we will have
93+
Object.entries(data).forEach(([_testRunId, asigneeCount]) => {
94+
Object.entries(asigneeCount).forEach(([userId, _executionCount]) => userIds.add(userId))
95+
})
96+
97+
userIds.forEach(userId => (groupedColumnsDataMap[userId] = [`User ${userId}`]))
98+
99+
Object.entries(data).forEach(([testRunId, _asigneeCount]) => {
100+
groupedCategories.push(testRunId)
101+
102+
const asigneesCount = data[testRunId]
103+
104+
// for each user in the groupedColumnsDataMap check if that user
105+
// is assigned any executions for this run.
106+
Object.entries(groupedColumnsDataMap).forEach(([userId, data]) => {
107+
const count = asigneesCount[userId]
108+
if (count) {
109+
data.push(count)
110+
} else {
111+
// TODO: find a way to hide the 0 valued-columns
112+
data.push(0)
113+
}
114+
})
115+
})
116+
117+
// C3 does not need a map, but an array of values
118+
// get rid of the keys, because we do not need them anymore
119+
const groupedColumnsData = Object.values(groupedColumnsDataMap)
120+
121+
const chartConfig = $().c3ChartDefaults().getDefaultGroupedBarConfig()
122+
chartConfig.bindto = '#performance-chart'
123+
chartConfig.axis = {
124+
x: {
125+
categories: groupedCategories,
126+
type: 'category'
127+
},
128+
y: {
129+
tick: {
130+
format: showOnlyRoundNumbers
131+
}
132+
}
133+
}
134+
chartConfig.data = {
135+
type: 'bar',
136+
columns: groupedColumnsData
137+
}
138+
chartConfig.color = {
139+
pattern: colors
140+
}
141+
c3.generate(chartConfig)
142+
}

tcms/telemetry/static/telemetry/js/testing/breakdown.js

+3
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ function drawChart(data, type, selector) {
114114
chartData.push(dataEntry);
115115
});
116116

117+
console.log('data', data)
118+
console.log('groups', groups)
119+
console.log('chartData', chartData)
117120
let chartConfig = $().c3ChartDefaults().getDefaultStackedBarConfig();
118121
chartConfig.bindto = selector;
119122
chartConfig.axis = {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{% extends "base.html" %}
2+
{% load i18n %}
3+
{% load static %}
4+
5+
{% block title %}{% trans "Performance" %}{% endblock %}
6+
7+
{% block contents %}
8+
9+
<div class="container-fluid container-cards-pf">
10+
{% include "telemetry/include/filters.html" %}
11+
12+
<div class="col-md-12" style="text-align: center">
13+
<div style="text-align: center; font-weight: bold">{% trans "Performance" %}</div>
14+
<div id="performance-chart"></div>
15+
</div>
16+
</div>
17+
18+
<script src="{% static 'c3/c3.min.js' %}"></script>
19+
<script src="{% static 'd3/d3.min.js' %}"></script>
20+
21+
<script src="{% static 'js/utils.js' %}"></script>
22+
<script src="{% static 'js/jsonrpc.js' %}"></script>
23+
24+
<script src="{% static 'telemetry/js/testing/utils.js' %}"></script>
25+
<script src="{% static 'telemetry/js/management/performance.js' %}"></script>
26+
27+
<!-- <link rel="stylesheet" type="text/css" href="{% static 'telemetry/css/testing/test-case-health.css' %}"/> -->
28+
{% endblock %}

tcms/telemetry/urls.py

+5
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,9 @@
2323
views.TestingTestCaseHealth.as_view(),
2424
name="test-case-health",
2525
),
26+
re_path(
27+
r"^management/performance/$",
28+
views.ManagementPerformance.as_view(),
29+
name="management-performance",
30+
),
2631
]

tcms/telemetry/views.py

+7
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,10 @@ class TestingExecutionTrendsView(TemplateView):
3333
class TestingTestCaseHealth(TemplateView):
3434

3535
template_name = "telemetry/testing/test-case-health.html"
36+
37+
@method_decorator(
38+
login_required, name="dispatch"
39+
) # pylint: disable=missing-permission-required
40+
class ManagementPerformance(TemplateView):
41+
42+
template_name = "telemetry/management/performance.html"

0 commit comments

Comments
 (0)