Skip to content

Commit 88ea4e8

Browse files
committed
Merge branch 'main' into stderr-utf8
* main: Command fix. Must be list append Refactorings Moved tinyproxy out of if clause Refactoring for error messages and security fix for path echoing (#636) GMT color via own commit hash (#634) Hotfix for branch not main Non-Blocking starlette body read (#633) Bump fastapi from 0.105.0 to 0.108.0 (#632) Updated XGBoost submodule Bump pydantic from 2.5.2 to 2.5.3 (#628) Added stddev to timeline (#627)
2 parents bad1c6c + 1016234 commit 88ea4e8

File tree

12 files changed

+139
-66
lines changed

12 files changed

+139
-66
lines changed

api/api_helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def get_timeline_query(uri, filename, machine_id, branch, metrics, phase, start_
125125
if filename is None or filename.strip() == '':
126126
filename = 'usage_scenario.yml'
127127

128-
if branch is None or branch.strip() != '':
128+
if branch is None or branch.strip() == '':
129129
branch = 'main'
130130

131131
params = [uri, filename, branch, machine_id, f"%{phase}"]
@@ -163,7 +163,7 @@ def get_timeline_query(uri, filename, machine_id, branch, metrics, phase, start_
163163
query = f"""
164164
SELECT
165165
r.id, r.name, r.created_at, p.metric, p.detail_name, p.phase,
166-
p.value, p.unit, r.commit_hash, r.commit_timestamp,
166+
p.value, p.unit, r.commit_hash, r.commit_timestamp, r.gmt_hash,
167167
row_number() OVER () AS row_num
168168
FROM runs as r
169169
LEFT JOIN phase_stats as p ON

api/main.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,25 +84,21 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE
8484

8585
@app.exception_handler(StarletteHTTPException)
8686
async def http_exception_handler(request, exc):
87-
await log_exception(request, exc, body='StarletteHTTPException handler cannot read body atm. Waiting for FastAPI upgrade.', details=exc.detail)
87+
body = await request.body()
88+
await log_exception(request, exc, body=body, details=exc.detail)
8889
return ORJSONResponse(
8990
status_code=exc.status_code,
9091
content=jsonable_encoder({'success': False, 'err': exc.detail}),
9192
)
9293

9394
async def catch_exceptions_middleware(request: Request, call_next):
9495
#pylint: disable=broad-except
96+
body = None
9597
try:
98+
body = await request.body()
9699
return await call_next(request)
97100
except Exception as exc:
98-
# body = await request.body() # This blocks the application. Unclear atm how to handle it properly
99-
# seems like a bug: https://github.com/tiangolo/fastapi/issues/394
100-
# Although the issue is closed the "solution" still behaves with same failure
101-
# Actually Starlette, the underlying library to FastAPI has already introduced this functionality:
102-
# https://github.com/encode/starlette/pull/1692
103-
# However FastAPI does not support the new Starlette 0.31.1
104-
# The PR relevant here is: https://github.com/tiangolo/fastapi/pull/9939
105-
await log_exception(request, exc, body='Middleware cannot read body atm. Waiting for FastAPI upgrade')
101+
await log_exception(request, exc, body=body)
106102
return ORJSONResponse(
107103
content={
108104
'success': False,

docker/requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
gunicorn==21.2.0
22
psycopg[binary]==3.1.16
3-
fastapi==0.105.0
3+
fastapi==0.108.0
4+
starlette>=0.32
45
uvicorn[standard]==0.25.0
56
pandas==2.1.4
67
PyYAML==6.0.1

frontend/js/helpers/charts.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
const calculateStatistics = (data) => {
2+
const mean = data.reduce((sum, value) => sum + value.value, 0) / data.length;
3+
const stddev = Math.sqrt(data.reduce((sum, value) => sum + Math.pow(value.value - mean, 2), 0) / data.length);
4+
return { mean, stddev };
5+
}
6+
17
const getCompareChartOptions = (legend, series, chart_type='line', x_axis='time', y_axis_name, mark_area=null, graphic=null) => {
28
let tooltip_trigger = (chart_type=='line') ? 'axis' : 'item';
39

@@ -106,7 +112,7 @@ const calculateMA = (series, factor) => {
106112
return result;
107113
}
108114

109-
const getLineBarChartOptions = (legend, labels, series, x_axis_name=null, y_axis_name='', x_axis='time', mark_area=null, no_toolbox=false, graphic=null, moving_average=false, show_x_axis_label=true) => {
115+
const getLineBarChartOptions = (legend, labels, series, x_axis_name=null, y_axis_name='', x_axis='time', mark_area=null, no_toolbox=false, graphic=null, moving_average=false, show_x_axis_label=true, stddev=false) => {
110116

111117
if(Object.keys(series).length == 0) {
112118
return {graphic: getChartGraphic("No energy reporter active")};
@@ -128,6 +134,31 @@ const getLineBarChartOptions = (legend, labels, series, x_axis_name=null, y_axis
128134
})
129135
}
130136

137+
if(stddev) {
138+
const { mean, stddev } = calculateStatistics(series[0].data);
139+
140+
legend.push('Stddev')
141+
series.push({
142+
name: 'Stddev',
143+
type: 'line',
144+
markArea: {
145+
label: {
146+
show: true,
147+
name: "MarkArea",
148+
position: 'top'
149+
},
150+
data: [
151+
[
152+
{ yAxis: mean + stddev, name: `StdDev: ${(stddev/mean * 100).toFixed(2)} %`},
153+
{ yAxis: mean - stddev},
154+
]
155+
156+
],
157+
}
158+
});
159+
}
160+
161+
131162
let options = {
132163
tooltip: { trigger: tooltip_trigger },
133164
grid: {

frontend/js/timeline.js

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ function* colorIterator() {
3939
}
4040
}
4141

42-
const generateColoredValues = (values) => {
42+
const generateColoredValues = (values, key) => {
4343
const color_iterator = colorIterator()
44-
let last_commit_hash = null
44+
let last_hash = null
4545
let color = null;
4646
return values.map((value) => {
47-
if(last_commit_hash != value.commit_hash) {
48-
last_commit_hash = value.commit_hash
47+
if(last_hash != value[key]) {
48+
last_hash = value[key]
4949
color = color_iterator.next().value
5050
}
5151
return {value: value.value, itemStyle: {color: color}}
@@ -159,15 +159,15 @@ const loadCharts = async () => {
159159
let prun_id = null
160160

161161
phase_stats_data.forEach( (data) => {
162-
let [run_id, run_name, created_at, metric_name, detail_name, phase, value, unit, commit_hash, commit_timestamp] = data
162+
let [run_id, run_name, created_at, metric_name, detail_name, phase, value, unit, commit_hash, commit_timestamp, gmt_hash] = data
163163

164164

165165
if (series[`${metric_name} - ${detail_name}`] == undefined) {
166166
series[`${metric_name} - ${detail_name}`] = {labels: [], values: [], notes: [], unit: unit, metric_name: metric_name, detail_name: detail_name}
167167
}
168168

169169
series[`${metric_name} - ${detail_name}`].labels.push(commit_timestamp)
170-
series[`${metric_name} - ${detail_name}`].values.push({value: value, commit_hash: commit_hash})
170+
series[`${metric_name} - ${detail_name}`].values.push({value: value, commit_hash: commit_hash, gmt_hash: gmt_hash})
171171
series[`${metric_name} - ${detail_name}`].notes.push({
172172
run_name: run_name,
173173
created_at: created_at,
@@ -176,6 +176,7 @@ const loadCharts = async () => {
176176
phase: phase,
177177
run_id: run_id,
178178
prun_id: prun_id,
179+
gmt_hash: gmt_hash,
179180
})
180181

181182
prun_id = run_id
@@ -203,7 +204,7 @@ const loadCharts = async () => {
203204

204205
const chart_instance = echarts.init(element);
205206

206-
const my_values = generateColoredValues(series[my_series].values);
207+
const my_values = generateColoredValues(series[my_series].values, $('.radio-coloring:checked').val());
207208

208209
let data_series = [{
209210
name: my_series,
@@ -218,34 +219,53 @@ const loadCharts = async () => {
218219
}
219220
}]
220221

221-
let options = getLineBarChartOptions([], series[my_series].labels, data_series, 'Time', series[my_series].unit, 'category', null, false, null, true, false);
222+
let options = getLineBarChartOptions([], series[my_series].labels, data_series, 'Time', series[my_series].unit, 'category', null, false, null, true, false, true);
222223

223224
options.tooltip = {
224-
trigger: 'item',
225+
triggerOn: 'click',
225226
formatter: function (params, ticket, callback) {
226-
if(params.componentType != 'series') return; // no notes for the MovingAverage
227+
if(series[params.seriesName]?.notes == null) return; // no notes for the MovingAverage
227228
return `<strong>${series[params.seriesName].notes[params.dataIndex].run_name}</strong><br>
229+
run_id: <a href="/stats.html?id=${series[params.seriesName].notes[params.dataIndex].run_id}" target="_blank">${series[params.seriesName].notes[params.dataIndex].run_id}</a><br>
228230
date: ${series[params.seriesName].notes[params.dataIndex].created_at}<br>
229231
metric_name: ${params.seriesName}<br>
230232
phase: ${series[params.seriesName].notes[params.dataIndex].phase}<br>
231233
value: ${numberFormatter.format(series[params.seriesName].values[params.dataIndex].value)}<br>
232234
commit_timestamp: ${series[params.seriesName].notes[params.dataIndex].commit_timestamp}<br>
233-
commit_hash: ${series[params.seriesName].notes[params.dataIndex].commit_hash}<br>
235+
commit_hash: <a href="${$("#uri").text()}/commit/${series[params.seriesName].notes[params.dataIndex].commit_hash}" target="_blank">${series[params.seriesName].notes[params.dataIndex].commit_hash}</a><br>
236+
gmt_hash: <a href="https://github.com/green-coding-berlin/green-metrics-tool/commit/${series[params.seriesName].notes[params.dataIndex].gmt_hash}" target="_blank">${series[params.seriesName].notes[params.dataIndex].gmt_hash}</a><br>
237+
234238
<br>
235-
<i>Click to diff measurement with previous</i>
239+
👉 <a href="/compare.html?ids=${series[params.seriesName].notes[params.dataIndex].run_id},${series[params.seriesName].notes[params.dataIndex].prun_id}" target="_blank">Diff with previous run</a>
236240
`;
237241
}
238242
};
239243

240-
chart_instance.on('click', function (params) {
241-
if(params.componentType != 'series') return; // no notes for the MovingAverage
242-
window.open(`/compare.html?ids=${series[params.seriesName].notes[params.dataIndex].run_id},${series[params.seriesName].notes[params.dataIndex].prun_id}`, '_blank');
243-
244-
});
244+
options.dataZoom = {
245+
show: false,
246+
start: 0,
247+
end: 100,
248+
};
245249

246250

247251
chart_instance.setOption(options);
248252
chart_instances.push(chart_instance);
253+
chart_instance.on('datazoom', function(e, f) {
254+
const data = chart_instance.getOption().series[0].data
255+
const dataZoomOption = chart_instance.getOption().dataZoom[0];
256+
const startPercent = dataZoomOption.start;
257+
const endPercent = dataZoomOption.end;
258+
const totalDataPoints = data.length;
259+
const startIndex = Math.floor(startPercent / 100 * totalDataPoints);
260+
const endIndex = Math.ceil(endPercent / 100 * totalDataPoints) - 1;
261+
const { mean, stddev } = calculateStatistics(data.slice(startIndex, endIndex+1));
262+
263+
let options = chart_instance.getOption()
264+
options.series[2].markArea.data[0][0].name = `StdDev: ${(stddev/mean * 100).toFixed(2)} %`
265+
options.series[2].markArea.data[0][0].yAxis = mean + stddev
266+
options.series[2].markArea.data[0][1].yAxis = mean - stddev;
267+
chart_instance.setOption(options)
268+
});
249269

250270
}
251271

frontend/timeline.html

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<link rel="stylesheet" type="text/css" href="/css/green-coding.css">
3838
<style type="text/css">
3939
.hide-for-single-stats { display: none !important; }
40+
.statistics-chart div { pointer-events: all !important }
4041
</style>
4142
</head>
4243
<body class="preload">
@@ -195,6 +196,21 @@ <h4>What is Timeline View?</h4>
195196
<i class="info circle icon"></i> Will not affect badge
196197
</div>
197198
</div>
199+
<div class="inline fields">
200+
<label>Coloring:</label>
201+
<div class="field">
202+
<div class="ui radio checkbox">
203+
<input class="radio-coloring" id="coloring-commit_hash" type="radio" name="coloring" value="commit_hash" checked>
204+
<label for="coloring-commit_hash">Software commit hash</label>
205+
</div>
206+
</div>
207+
<div class="field">
208+
<div class="ui radio checkbox">
209+
<input class="radio-coloring" id="coloring-gmt_hash" type="radio" name="coloring" value="gmt_hash">
210+
<label for="coloring-gmt_hash">GMT commit hash</label>
211+
</div>
212+
</div>
213+
</div>
198214
<div class="three fields">
199215
<div class="field">
200216
<label>Start date</label>
@@ -233,7 +249,8 @@ <h4>What is Timeline View?</h4>
233249
<div class="header">
234250
Graph Info
235251
</div>
236-
<p>Graphs show every measurement as a bar. We color the bars according to the commit timestamp and change the color for every new commit. After some time the color repeats ...</p>
252+
<p>Graphs show every measurement as a bar. We color the bars according to the commit timestamp and change the color for every new commit (You can also change to color by the GMT tool hash). After some time the color repeats ...</p>
253+
<p>To show details click on the bars! A new menu will appear.</p>
237254
</div>
238255
</div>
239256
<div class="ui two cards" id="api-loader">

install_linux.sh

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,12 @@ git submodule update --init
117117
print_message "Installing needed binaries for building ..."
118118
if lsb_release -is | grep -q "Fedora"; then
119119
sudo dnf -y install lm_sensors lm_sensors-devel glib2 glib2-devel tinyproxy lshw
120-
sudo systemctl stop tinyproxy
121-
sudo systemctl disable tinyproxy
122120
else
123121
sudo apt-get update
124122
sudo apt-get install -y lm-sensors libsensors-dev libglib2.0-0 libglib2.0-dev tinyproxy lshw
125-
sudo systemctl stop tinyproxy
126-
sudo systemctl disable tinyproxy
127123
fi
124+
sudo systemctl stop tinyproxy
125+
sudo systemctl disable tinyproxy
128126

129127
print_message "Building binaries ..."
130128
metrics_subdir="metric_providers"

lib/error_helpers.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ def end_error(*errors):
1111

1212

1313
def format_error(*errors):
14-
err = 'Error: '
14+
err = ''
1515

1616
for error in errors:
17-
err += str(error)
17+
err += str(error) + "\n"
1818

1919
error_string = f"""
2020
\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 0_o >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n
21-
{err}
21+
Error: {err}
2222
\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 0_o >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n
2323
{traceback.format_exc()}
2424
\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 0_o >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n
@@ -30,11 +30,15 @@ def format_error(*errors):
3030
def log_error(*errors):
3131
error_log_file = GlobalConfig().config['machine']['error_log_file']
3232

33+
err = ''
34+
for error in errors:
35+
err += str(error) + "\n"
36+
3337
if error_log_file:
3438
try:
3539
with open(error_log_file, 'a', encoding='utf-8') as file:
3640
print('\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 0_o >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n', file=file)
37-
print('Error: ', *errors, file=file)
41+
print('Error: ', err, file=file)
3842
print('\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 0_o >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n', file=file)
3943
print(traceback.format_exc(), file=file)
4044
print('\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 0_o >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n', file=file)
@@ -46,6 +50,6 @@ def log_error(*errors):
4650
'\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 0_o >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n', file=sys.stderr)
4751
print(traceback.format_exc(), file=sys.stderr)
4852
print('\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 0_o >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n', file=sys.stderr)
49-
print('Error: ', *errors, file=sys.stderr)
53+
print('Error: ', err, file=sys.stderr)
5054
print('\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 0_o >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n',
5155
TerminalColors.ENDC, file=sys.stderr)

requirements-dev.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
-r requirements.txt
2-
pydantic==2.5.2
2+
pydantic==2.5.3
33
pytest==7.4.3
44
requests==2.31.0
55
pylint==3.0.3
6-
fastapi==0.105.0
6+
fastapi==0.108.0
7+
starlette>=0.32
78
anybadge==1.14.0
89

910
# just to clear the pylint errors for the files in /api

0 commit comments

Comments
 (0)