Skip to content

Commit 1acdf53

Browse files
authored
Update MetricsDetails to add Save button to save both Test and Baseline Tables (#1178)
* Update MetricsDetails to add Save button to save both Test and Baseline -Related:https://github.ibm.com/runtimes/automation/issues/875 -Signed-off-by:Denny Chacko Jacob * Updated format * Added new endpoint updateStats to handle calculations to reflect excluded runs * Aded the new endpoint to index.js
1 parent f6e664e commit 1acdf53

File tree

3 files changed

+238
-2
lines changed

3 files changed

+238
-2
lines changed

TestResultSummaryService/routes/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,6 @@ app.post('/getSpecificData', require('./getSpecificData'));
6767
app.post('/upsertBuildList', require('./upsertBuildList'));
6868
app.post('/postTapFiles', require('./postTapFiles'));
6969
app.post('/postIssueFeedback', require('./postIssueFeedback'));
70+
app.put('/updateStats', require('./updateStats'));
7071

7172
module.exports = app;
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
const { TestResultsDB } = require('../Database');
2+
const ObjectID = require('mongodb').ObjectID;
3+
4+
module.exports = async (req, res) => {
5+
const db = new TestResultsDB();
6+
7+
const {
8+
testId,
9+
baselineId,
10+
benchmarkName,
11+
testStats,
12+
baselineStats,
13+
testDisabledIterations,
14+
baselineDisabledIterations
15+
} = req.body;
16+
17+
console.log('Received update request:', {
18+
testId,
19+
baselineId,
20+
benchmarkName,
21+
testDisabledIterations,
22+
baselineDisabledIterations
23+
});
24+
25+
try {
26+
// Helper function to update a document
27+
const updateDocument = async (docId, stats, disabledIterations, buildType) => {
28+
// Find the document
29+
const doc = await db.findOne({ _id: new ObjectID(docId) });
30+
31+
if (!doc) {
32+
console.error(`Document not found: ${docId}`);
33+
return { success: false, error: `Document ${docId} not found` };
34+
}
35+
36+
if (!doc.aggregateInfo) {
37+
console.error(`No aggregateInfo in document: ${docId}`);
38+
return { success: false, error: `No aggregateInfo in document` };
39+
}
40+
41+
// Find and update the matching aggregateInfo entry
42+
let updated = false;
43+
for (const key in doc.aggregateInfo) {
44+
const item = doc.aggregateInfo[key];
45+
if (item.benchmarkName === benchmarkName &&
46+
item.buildName &&
47+
item.buildName.includes(buildType)) {
48+
49+
if (item.metrics) {
50+
// Update each metric in this aggregateInfo
51+
item.metrics = item.metrics.map(metric => {
52+
return {
53+
...metric,
54+
55+
// original rawValues
56+
rawValues: metric.rawValues,
57+
58+
//original statValues
59+
statValues: metric.statValues,
60+
61+
// new field - filteredStatValues
62+
filteredStatValues: {
63+
mean: stats.mean,
64+
max: stats.max,
65+
min: stats.min,
66+
median: stats.median,
67+
std: stats.std,
68+
CI: stats.CI
69+
},
70+
71+
// disablediterations
72+
disabledIterations: disabledIterations || []
73+
};
74+
});
75+
76+
updated = true;
77+
}
78+
}
79+
}
80+
81+
if (!updated) {
82+
console.error(`Benchmark ${benchmarkName} with type ${buildType} not found`);
83+
return { success: false, error: `Benchmark not found` };
84+
}
85+
86+
// Update the document in database
87+
await db.update(
88+
{ _id: new ObjectID(docId) },
89+
{ $set: { aggregateInfo: doc.aggregateInfo } }
90+
);
91+
92+
console.log(`Successfully updated document: ${docId}`);
93+
return { success: true };
94+
};
95+
96+
// Update test document
97+
const testResult = await updateDocument(
98+
testId,
99+
testStats,
100+
testDisabledIterations,
101+
'test'
102+
);
103+
104+
if (!testResult.success) {
105+
return res.status(400).json(testResult);
106+
}
107+
108+
// Update baseline document
109+
const baselineResult = await updateDocument(
110+
baselineId,
111+
baselineStats,
112+
baselineDisabledIterations,
113+
'baseline'
114+
);
115+
116+
if (!baselineResult.success) {
117+
return res.status(400).json(baselineResult);
118+
}
119+
120+
res.json({
121+
success: true,
122+
message: 'Statistics updated successfully'
123+
});
124+
125+
} catch (error) {
126+
console.error('Error updating stats:', error);
127+
res.status(500).json({
128+
success: false,
129+
error: error.message
130+
});
131+
}
132+
};
Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,126 @@
1-
import { Divider } from 'antd';
1+
import React, { useState } from 'react';
2+
import { Divider, Button, message } from 'antd';
23
import MetricsTable from './MetricsTable';
34
import { getParams } from '../utils/query';
45

56
function MetricsDetails() {
67
const { testId, baselineId, benchmarkName } = getParams(location.search);
8+
9+
// State to track data from both tables
10+
const [testData, setTestData] = useState(null);
11+
const [baselineData, setBaselineData] = useState(null);
12+
const [testStats, setTestStats] = useState(null);
13+
const [baselineStats, setBaselineStats] = useState(null);
14+
const [saving, setSaving] = useState(false);
15+
16+
// Save handler for both tables
17+
const handleSave = async () => {
18+
if (!testData || !baselineData || !testStats || !baselineStats) {
19+
message.warning('Please wait for data to load');
20+
return;
21+
}
22+
23+
setSaving(true);
24+
25+
try {
26+
// Calculate which iterations are disabled for TEST
27+
const testDisabledIterations = testData
28+
.filter(item => !item.enabled)
29+
.map(item => item.iteration);
30+
31+
// Calculate which iterations are disabled for BASELINE
32+
const baselineDisabledIterations = baselineData
33+
.filter(item => !item.enabled)
34+
.map(item => item.iteration);
35+
36+
console.log('Saving BOTH test and baseline:', {
37+
testDisabledIterations,
38+
baselineDisabledIterations,
39+
testStats,
40+
baselineStats
41+
});
42+
43+
// One API call saves both TEST and BASELINE stats together
44+
const response = await fetch('/api/updateStats', {
45+
method: 'PUT',
46+
headers: {
47+
'Content-Type': 'application/json'
48+
},
49+
body: JSON.stringify({
50+
testId,
51+
baselineId,
52+
benchmarkName,
53+
testStats,
54+
baselineStats,
55+
testDisabledIterations,
56+
baselineDisabledIterations,
57+
})
58+
});
59+
60+
const result = await response.json();
61+
62+
if (result.success) {
63+
message.success('✓ Both TEST and BASELINE statistics saved successfully!');
64+
} else {
65+
message.error('Failed to save: ' + (result.error || 'Unknown error'));
66+
}
67+
} catch (error) {
68+
message.error('Error saving: ' + error.message);
69+
console.error('Save error:', error);
70+
} finally {
71+
setSaving(false);
72+
}
73+
};
74+
775
return (
876
<div>
77+
{/* One Save Button for bothH tables */}
78+
<div style={{
79+
marginBottom: 16,
80+
padding: 16,
81+
background: '#f5f5f5',
82+
borderRadius: 8,
83+
display: 'flex',
84+
justifyContent: 'space-between',
85+
alignItems: 'center'
86+
}}>
87+
<div>
88+
<div style={{ margin: 0, fontSize: 20, fontWeight: 'bold' }}>
89+
Metrics Details - {benchmarkName}
90+
</div>
91+
<div style={{ margin: 0, fontSize: 12, color: 'rgba(0, 0, 0, 1)' }}>
92+
Annotate bad runs for both test and baseline, then SAVE
93+
</div>
94+
</div>
95+
<Button
96+
type="primary"
97+
size="large"
98+
onClick={handleSave}
99+
loading={saving}
100+
disabled={!testStats || !baselineStats}
101+
>
102+
Save Both Statistics
103+
</Button>
104+
</div>
105+
106+
9107
<MetricsTable
10108
type="test"
11109
id={testId}
12110
benchmarkName={benchmarkName}
111+
onDataChange={setTestData}
112+
onStatsChange={setTestStats}
13113
/>
14114
<Divider />
15115
<MetricsTable
16116
type="baseline"
17117
id={baselineId}
18118
benchmarkName={benchmarkName}
119+
onDataChange={setBaselineData}
120+
onStatsChange={setBaselineStats}
19121
/>
20122
</div>
21123
);
22124
}
23-
export default MetricsDetails;
125+
126+
export default MetricsDetails;

0 commit comments

Comments
 (0)