Merge ~sylvain-pineau/hwcert-jenkins-tools:advocacy_reports into hwcert-jenkins-tools:master

Proposed by Sylvain Pineau
Status: Merged
Approved by: Sylvain Pineau
Approved revision: bf76a5c539aca08ab7684766d462feeb1a305964
Merged at revision: aec276c1fb27877c94bece8eb41d8da21f032375
Proposed branch: ~sylvain-pineau/hwcert-jenkins-tools:advocacy_reports
Merge into: hwcert-jenkins-tools:master
Diff against target: 158 lines (+152/-0)
1 file modified
advocacy/plot.py (+152/-0)
Reviewer Review Type Date Requested Status
Sylvain Pineau (community) Approve
Review via email: mp+411764@code.launchpad.net

Description of the change

New advocacy script to plot graphs in static html reports

To post a comment you must log in.
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

self-approved

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/advocacy/plot.py b/advocacy/plot.py
2new file mode 100755
3index 0000000..7f6c72b
4--- /dev/null
5+++ b/advocacy/plot.py
6@@ -0,0 +1,152 @@
7+#!/usr/bin/env python3
8+# Copyright 2021 Canonical Ltd.
9+# All rights reserved.
10+#
11+# Written by:
12+# Sylvain Pineau <sylvain.pineau@canonical.com>
13+
14+import argparse
15+import io
16+import os
17+
18+import numpy as np
19+import plotly.graph_objects as go
20+import pandas as pd
21+import requests
22+
23+
24+def get_csv(filename=None):
25+ if filename:
26+ return filename
27+ url = "http://10.50.124.12:8086/query"
28+ h = {
29+ "Accept": "application/csv",
30+ }
31+ params = {
32+ "db": "desktopsnaps",
33+ "q": "SELECT * FROM startup_time"
34+ }
35+ a = requests.auth.HTTPBasicAuth('ce', os.getenv("INFLUX_PASS"))
36+ r = requests.post(url, headers=h, params=params, auth=a)
37+ return io.StringIO(r.text)
38+
39+
40+def plot(args):
41+ config = {'displaylogo': False}
42+ template = "%{y:.2f} s <b>%{customdata[0]} %{customdata[1]}"
43+ df = pd.read_csv(
44+ get_csv(args.csv),
45+ parse_dates={'date': ["time"]},
46+ date_parser=lambda time: pd.to_datetime(int(time)))
47+ snaps = sorted(set(df['snap']))
48+ prefix = "snap_baseline"
49+ triggers = ['linux-generic', 'snapd', 'core18', 'apparmor', 'libc6']
50+ if args.os_baseline:
51+ prefix = "os_baseline"
52+ triggers = snaps
53+ ymax = df[
54+ (df["hw_id"] == args.hw_id) & (df["cause"].isin(triggers))
55+ ]["cold"].max()
56+ include_js = True
57+
58+ with open(f'{args.folder}/{prefix}_{args.hw_id}.html', 'w') as f:
59+ for snap in snaps:
60+ data = df[
61+ (df["snap"] == snap) & (df["hw_id"] == args.hw_id) &
62+ (df["cause"].isin(triggers))]
63+ releases = sorted(set(data['release']))
64+ fig = go.Figure(layout_title_text=f"<b>{snap}")
65+ # Create traces
66+ for start in ['cold', 'hot']:
67+ for release in releases:
68+ release_data = data[(data["release"] == release)]
69+ fig.add_trace(
70+ go.Scatter(
71+ x=release_data['date'], y=release_data[start],
72+ mode='lines+markers',
73+ xhoverformat="%Y-%m-%d %H:%M:%S",
74+ customdata=np.stack((
75+ release_data['cause'],
76+ release_data['cause_version']), axis=-1),
77+ hovertemplate=template,
78+ name='{} start ({})'.format(start, release)))
79+ fig.update_yaxes(range=[0, ymax+1])
80+ fig.update_layout(
81+ margin=dict(l=80, r=100, t=80, b=20),
82+ height=500,
83+ hovermode="x unified",
84+ hoverlabel=dict(
85+ bgcolor='rgba(0,0,0,0.8)',
86+ font={'color': 'white'}
87+ ),
88+ yaxis={'tickformat': ".2f"},
89+ xaxis=dict(
90+ rangeselector=dict(
91+ buttons=list([
92+ dict(count=6,
93+ label="6m",
94+ step="month",
95+ stepmode="backward"),
96+ dict(count=1,
97+ label="YTD",
98+ step="year",
99+ stepmode="todate"),
100+ dict(count=1,
101+ label="1y",
102+ step="year",
103+ stepmode="backward"),
104+ dict(step="all")
105+ ])
106+ ),
107+ type="date"
108+ )
109+ )
110+ f.write(fig.to_html(
111+ full_html=False,
112+ config=config,
113+ include_plotlyjs=include_js))
114+ include_js = False
115+ headers = [f"<b>{snap.capitalize()}<br>startup time"]
116+ values = [["Mean", "Std Dev", "Last"]]
117+ for release in releases:
118+ release_data = data[(data["release"] == release)]
119+ headers.append(f"<b>{release.capitalize()}<br>Cold / Hot (s)")
120+ values.append([
121+ f"{release_data['cold'].mean():.2f}"
122+ f" / {release_data['hot'].mean():.2f}",
123+ f"{release_data['cold'].std():.2f}"
124+ f" / {release_data['hot'].std():.2f}",
125+ f"{release_data['cold'].iloc[-1]:.2f}"
126+ f" / {release_data['hot'].iloc[-1]:.2f}"])
127+ fig = go.Figure(
128+ data=[
129+ go.Table(
130+ header=dict(values=headers),
131+ cells=dict(values=values)
132+ )
133+ ])
134+ fig.update_layout(
135+ margin=dict(l=80, r=200, t=20, b=20),
136+ height=155)
137+ f.write(fig.to_html(
138+ full_html=False,
139+ config=config,
140+ include_plotlyjs=include_js))
141+
142+
143+def main():
144+ parser = argparse.ArgumentParser()
145+ parser.add_argument('hw_id', type=str, help='system tag')
146+ parser.add_argument('--folder', default='/tmp',
147+ help='Folder path to save reports')
148+ parser.add_argument(
149+ '--os-baseline', action='store_true', help='OS baseline')
150+ parser.add_argument(
151+ '--snap-baseline', action='store_true', help='Snap baseline')
152+ parser.add_argument('--csv', help='CSV influxdb export (optional)')
153+ args = parser.parse_args()
154+ plot(args)
155+
156+
157+if __name__ == "__main__":
158+ main()

Subscribers

People subscribed via source and target branches