Skip to content

Commit d8cd94d

Browse files
authored
Merge pull request #22 from mircealungu/graphs
Implementing more visualizations
2 parents 567f77d + 7badd3a commit d8cd94d

File tree

5 files changed

+308
-44
lines changed

5 files changed

+308
-44
lines changed

dashboard/database/endpoint.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
Contains all functions that access a single endpoint
33
"""
4-
from sqlalchemy import func, text
4+
from sqlalchemy import func, text, asc
55
from sqlalchemy.orm.exc import NoResultFound
66
import datetime
77

@@ -24,12 +24,38 @@ def get_line_results(endpoint):
2424
return data
2525

2626

27+
def get_num_requests(endpoint):
28+
""" Returns a list with all dates on which an endpoint is accessed.
29+
:param endpoint: if None, the result is the sum of all endpoints
30+
"""
31+
with session_scope() as db_session:
32+
query = text("""select
33+
datetime(CAST(strftime('%s', time)/3600 AS INT)*3600, 'unixepoch') AS newTime,
34+
count(execution_time) as count
35+
from functioncalls
36+
where (endpoint=:val OR :val='None') group by newTime""")
37+
result = db_session.execute(query, {'val': str(endpoint)})
38+
data = result.fetchall()
39+
return data
40+
41+
2742
def get_endpoint_column(endpoint, column):
43+
""" Returns a list of entries from column in which the endpoint is involved. """
44+
with session_scope() as db_session:
45+
result = db_session.query(column,
46+
func.min(FunctionCall.time).label('startedUsingOn')). \
47+
filter(FunctionCall.endpoint == endpoint). \
48+
group_by(column).order_by(asc('startedUsingOn')).all()
49+
db_session.expunge_all()
50+
return result
51+
52+
53+
def get_endpoint_column_user_sorted(endpoint, column):
2854
""" Returns a list of entries from column in which the endpoint is involved. """
2955
with session_scope() as db_session:
3056
result = db_session.query(column). \
3157
filter(FunctionCall.endpoint == endpoint). \
32-
group_by(column).all()
58+
group_by(column).order_by(asc(column)).all()
3359
db_session.expunge_all()
3460
return result
3561

dashboard/database/function_calls.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,25 @@
33
"""
44

55
from flask import request
6-
from sqlalchemy import func, desc
6+
from sqlalchemy import func, desc, text, asc
77
from dashboard import config
88
import datetime
99
from dashboard.database import session_scope, FunctionCall
1010

1111

12+
def get_reqs_endpoint_day():
13+
""" Retrieves the number of requests per endpoint per day. """
14+
with session_scope() as db_session:
15+
query = text("""select strftime('%Y-%m-%d', time) AS newTime,
16+
count(endpoint) AS cnt,
17+
endpoint
18+
from functioncalls
19+
group by newTime, endpoint""")
20+
result = db_session.execute(query)
21+
data = result.fetchall()
22+
return data
23+
24+
1225
def add_function_call(time, endpoint):
1326
""" Add a measurement to the database. """
1427
with session_scope() as db_session:
@@ -45,3 +58,38 @@ def get_data():
4558
FunctionCall.ip).all()
4659
db_session.expunge_all()
4760
return result
61+
62+
63+
def get_data_per_version(version):
64+
""" Returns all data in the FuctionCall table, grouped by their version. """
65+
with session_scope() as db_session:
66+
result = db_session.query(FunctionCall.execution_time, FunctionCall.version). \
67+
filter(FunctionCall.version == version).all()
68+
db_session.expunge_all()
69+
return result
70+
71+
72+
def get_versions():
73+
with session_scope() as db_session:
74+
result = db_session.query(FunctionCall.version,
75+
func.min(FunctionCall.time).label('startedUsingOn')). \
76+
group_by(FunctionCall.version).order_by(asc('startedUsingOn')).all()
77+
db_session.expunge_all()
78+
return result
79+
80+
81+
def get_data_per_endpoint(end):
82+
with session_scope() as db_session:
83+
result = db_session.query(FunctionCall.execution_time, FunctionCall.endpoint). \
84+
filter(FunctionCall.endpoint == end).all()
85+
db_session.expunge_all()
86+
return result
87+
88+
89+
def get_endpoints():
90+
with session_scope() as db_session:
91+
result = db_session.query(FunctionCall.endpoint,
92+
func.count(FunctionCall.endpoint).label('cnt')). \
93+
group_by(FunctionCall.endpoint).order_by(asc('cnt')).all()
94+
db_session.expunge_all()
95+
return result

dashboard/routings/result.py

Lines changed: 153 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
from dashboard import blueprint, config
77
from dashboard.database import FunctionCall
88
from dashboard.database.endpoint import get_endpoint_column, get_endpoint_results, get_monitor_rule, \
9-
get_last_accessed_times, get_line_results, get_all_measurement_per_column
10-
from dashboard.database.function_calls import get_times
9+
get_last_accessed_times, get_line_results, get_all_measurement_per_column, get_num_requests, \
10+
get_endpoint_column_user_sorted
11+
from dashboard.database.function_calls import get_times, get_data_per_version, get_versions, get_reqs_endpoint_day, \
12+
get_endpoints, get_data_per_endpoint
1113
from dashboard.security import secure
1214

1315
import plotly
@@ -17,9 +19,62 @@
1719
@blueprint.route('/measurements')
1820
@secure
1921
def measurements():
20-
t = get_times()
21-
la = get_last_accessed_times()
22-
return render_template('measurements.html', link=config.link, curr=2, times=t, access=la, session=session)
22+
return render_template('measurements.html', link=config.link, curr=2, times=get_times(),
23+
access=get_last_accessed_times(), session=session,
24+
heatmap=get_heatmap(end=None), etpv=get_boxplot_per_version(), rpepd=get_stacked_bar(),
25+
etpe=get_boxplot_per_endpoint())
26+
27+
28+
def get_boxplot_per_version():
29+
"""
30+
Creates a graph with the execution times per version
31+
:return:
32+
"""
33+
versions = [str(v.version) for v in get_versions()]
34+
35+
data = []
36+
for v in versions:
37+
values = [c.execution_time for c in get_data_per_version(v)]
38+
data.append(go.Box(x=values, name=v))
39+
40+
layout = go.Layout(
41+
autosize=False,
42+
width=900,
43+
height=350 + 40 * len(versions),
44+
plot_bgcolor='rgba(249,249,249,1)',
45+
showlegend=False,
46+
title='Execution time for every version',
47+
xaxis=dict(title='Execution time (ms)'),
48+
yaxis=dict(title='Version')
49+
)
50+
return plotly.offline.plot(go.Figure(data=data, layout=layout), output_type='div', show_link=False)
51+
52+
53+
def get_boxplot_per_endpoint():
54+
"""
55+
Creates a graph with the execution times per endpoint
56+
:return:
57+
"""
58+
endpoints = [str(e.endpoint) for e in get_endpoints()]
59+
60+
data = []
61+
for e in endpoints:
62+
values = [c.execution_time for c in get_data_per_endpoint(e)]
63+
if len(e) > 16:
64+
e = '...' + e[-14:]
65+
data.append(go.Box(x=values, name=e))
66+
67+
layout = go.Layout(
68+
autosize=False,
69+
width=900,
70+
height=350 + 40 * len(endpoints),
71+
plot_bgcolor='rgba(249,249,249,1)',
72+
showlegend=False,
73+
title='Execution time for every endpoint',
74+
xaxis=dict(title='Execution time (ms)'),
75+
yaxis=dict(tickangle=-45)
76+
)
77+
return plotly.offline.plot(go.Figure(data=data, layout=layout), output_type='div', show_link=False)
2378

2479

2580
def formatter(ms):
@@ -28,7 +83,7 @@ def formatter(ms):
2883
:param ms: the number of ms
2984
:return: a string representing the same amount, but now represented in seconds and ms.
3085
"""
31-
sec = math.floor(ms/1000)
86+
sec = math.floor(ms / 1000)
3287
ms = round(ms % 1000, 2)
3388
if sec == 0:
3489
return '{0}ms'.format(ms)
@@ -81,6 +136,33 @@ def get_graphs_per_hour(end):
81136
return graph1.render_data_uri(), graph2.render_data_uri()
82137

83138

139+
def get_stacked_bar():
140+
data = get_reqs_endpoint_day()
141+
graph = pygal.graph.horizontalstackedbar.HorizontalStackedBar(legend_at_bottom=True, height=100 + len(data) * 7)
142+
graph.title = 'Number of requests per endpoint per day'
143+
graph.x_labels = []
144+
endpoints = []
145+
for d in data:
146+
if d.newTime not in graph.x_labels:
147+
graph.x_labels.append(d.newTime)
148+
if d.endpoint not in endpoints:
149+
endpoints.append(d.endpoint)
150+
151+
for e in endpoints:
152+
lst = []
153+
for t in graph.x_labels:
154+
found = False
155+
for d in data:
156+
if e == d.endpoint and t == d.newTime:
157+
found = True
158+
lst.append(d.cnt)
159+
if not found:
160+
lst.append(0)
161+
graph.add(e, lst)
162+
163+
return graph.render_data_uri()
164+
165+
84166
def get_dot_charts(end, versions):
85167
"""
86168
Function that builds two dot charts:
@@ -148,33 +230,85 @@ def get_boxplots(end, versions):
148230
width=900,
149231
height=350 + 40 * len(versions),
150232
plot_bgcolor='rgba(249,249,249,1)',
151-
showlegend=False
233+
showlegend=False,
234+
title='Execution time for every version',
235+
xaxis=dict(title='Execution time (ms)'),
236+
yaxis=dict(title='Version')
152237
)
153238
graph1 = plotly.offline.plot(go.Figure(data=data, layout=layout), output_type='div', show_link=False)
154239

155-
# boxplot: execution time per versions
156-
users = [str(c.group_by) for c in get_endpoint_column(endpoint=end, column=FunctionCall.group_by)]
157-
240+
users = [str(c.group_by) for c in get_endpoint_column_user_sorted(endpoint=end, column=FunctionCall.group_by)]
158241
data = []
159242
for u in users:
160243
values = [str(c.execution_time) for c in
161244
get_all_measurement_per_column(endpoint=end, column=FunctionCall.group_by, value=u)]
162-
data.append(go.Box(x=values, name=u))
245+
data.append(go.Box(x=values, name='{0} -'.format(u)))
163246

164247
layout = go.Layout(
165248
autosize=False,
166249
width=900,
167250
height=350 + 40 * len(users),
168251
plot_bgcolor='rgba(249,249,249,1)',
169-
showlegend=False
252+
showlegend=False,
253+
title='Execution time for every user',
254+
xaxis=dict(title='Execution time (ms)'),
255+
yaxis=dict(title='User')
170256
)
171257
graph2 = plotly.offline.plot(go.Figure(data=data, layout=layout), output_type='div', show_link=False)
172258
return graph1, graph2
173259

174260

175-
@blueprint.route('/show-graph/<end>')
261+
def get_heatmap(end):
262+
# list of hours: 1:00 - 23:00
263+
hours = ['0' + str(hour) + ':00' for hour in range(0, 10)] + \
264+
[str(hour) + ':00' for hour in range(10, 24)]
265+
266+
data = get_num_requests(end)
267+
# list of days (format: year-month-day)
268+
days = [str(d.newTime[:10]) for d in data]
269+
# remove duplicates and sort the result
270+
days = sorted(list(set(days)))
271+
272+
# create empty 2D-dictionary with the keys: [hour][day]
273+
requests = {}
274+
for hour in hours:
275+
requests_day = {}
276+
for day in days:
277+
requests_day[day] = 0
278+
requests[hour] = requests_day
279+
280+
# add data to the dictionary
281+
for d in data:
282+
day = str(d.newTime[:10])
283+
hour = str(d.newTime[11:16])
284+
requests[hour][day] = d.count
285+
286+
# create a 2D-list out of the dictionary
287+
requests_list = []
288+
for hour in hours:
289+
day_list = []
290+
for day in days:
291+
day_list.append(requests[hour][day])
292+
requests_list.append(day_list)
293+
294+
layout = go.Layout(
295+
autosize=False,
296+
width=900,
297+
height=800,
298+
plot_bgcolor='rgba(249,249,249,1)',
299+
showlegend=False,
300+
title='Heatmap of number of requests',
301+
xaxis=dict(title='Date'),
302+
yaxis=dict(title='Time')
303+
)
304+
305+
trace = go.Heatmap(z=requests_list, x=days, y=hours)
306+
return plotly.offline.plot(go.Figure(data=[trace], layout=layout), output_type='div', show_link=False)
307+
308+
309+
@blueprint.route('/result/<end>')
176310
@secure
177-
def show_graph(end):
311+
def result(end):
178312
rule = get_monitor_rule(end)
179313
url = get_url(end)
180314
versions = [str(c.version) for c in get_endpoint_column(end, FunctionCall.version)]
@@ -188,6 +322,9 @@ def show_graph(end):
188322
# (5) Execution time per version and (6) Execution time per user
189323
graph5, graph6 = get_boxplots(end, versions)
190324

191-
return render_template('show-graph.html', link=config.link, session=session, rule=rule, url=url,
325+
# (7) Number of requests per hour
326+
graph7 = get_heatmap(end)
327+
328+
return render_template('endpoint.html', link=config.link, session=session, rule=rule, url=url,
192329
times_data=graph1, hits_data=graph2, dot_chart_user=graph3,
193-
dot_chart_ip=graph4, div_versions=graph5, div_users=graph6)
330+
dot_chart_ip=graph4, div_versions=graph5, div_users=graph6, div_heatmap=graph7)

0 commit comments

Comments
 (0)