Skip to content

Commit 5fed2fd

Browse files
authored
Merge branch 'master' into gsoc-final-contribution
2 parents 4ac5040 + 1acdf53 commit 5fed2fd

File tree

3 files changed

+218
-96
lines changed

3 files changed

+218
-96
lines changed

TestResultSummaryService/routes/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,6 @@ app.post('/getSpecificData', require('./getSpecificData'));
6969
app.post('/upsertBuildList', require('./upsertBuildList'));
7070
app.post('/postTapFiles', require('./postTapFiles'));
7171
app.post('/postIssueFeedback', require('./postIssueFeedback'));
72+
app.put('/updateStats', require('./updateStats'));
7273

7374
module.exports = app;
Lines changed: 123 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,132 @@
1-
const { TestResultsDB, ObjectID } = require('../Database');
2-
3-
/**
4-
* updateStats updates the cached statistics for a build based on excluded runs.
5-
*
6-
* @route POST /api/updateStats
7-
* @group TrafficLight - Operations related to traffic light stats
8-
* @param {string} id.body.required - build ID
9-
* @param {string} benchmarkName.body.required - name of the benchmark
10-
* @param {object} excludedRuns.body.required - map of build names to arrays of excluded iteration indices
11-
* @return {object} 200 - success message
12-
*/
1+
const { TestResultsDB } = require('../Database');
2+
const ObjectID = require('mongodb').ObjectID;
133

144
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+
1525
try {
16-
const { id, benchmarkName, excludedRuns } = req.body;
17-
if (!id || !benchmarkName) {
18-
return res
19-
.status(400)
20-
.send({
21-
error: 'Missing required parameters: id, benchmarkName',
22-
});
23-
}
24-
25-
const testResultsDB = new TestResultsDB();
26-
const build = await testResultsDB.findOne({ _id: new ObjectID(id) });
27-
28-
if (!build || !build.aggregateInfo) {
29-
return res
30-
.status(404)
31-
.send({ error: 'Build or aggregated info not found' });
32-
}
33-
34-
const aggregateInfo = build.aggregateInfo;
35-
const keys = Object.keys(aggregateInfo);
36-
37-
let updated = false;
38-
for (const key of keys) {
39-
const item = aggregateInfo[key];
40-
if (
41-
item.benchmarkName === benchmarkName &&
42-
excludedRuns[item.buildName]
43-
) {
44-
// Update the item in aggregateInfo
45-
// We'll store the excluded runs indices so the frontend can retrieve them later
46-
item.excludedRuns = excludedRuns[item.buildName];
47-
updated = true;
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` };
4834
}
49-
}
50-
51-
if (updated) {
52-
await testResultsDB.update(
53-
{ _id: new ObjectID(id) },
54-
{ $set: { aggregateInfo } }
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 } }
5590
);
56-
res.send({ status: 'success', message: 'Statistics updated' });
57-
} else {
58-
res.status(404).send({
59-
error: 'Matching benchmark/build not found for update',
60-
});
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);
61118
}
119+
120+
res.json({
121+
success: true,
122+
message: 'Statistics updated successfully'
123+
});
124+
62125
} catch (error) {
63-
res.status(500).send({ error: error.message });
126+
console.error('Error updating stats:', error);
127+
res.status(500).json({
128+
success: false,
129+
error: error.message
130+
});
64131
}
65132
};

test-result-summary-client/src/TrafficLight/MetricsDetails.jsx

Lines changed: 94 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,68 +6,122 @@ import { postData } from '../utils/Utils';
66

77
function MetricsDetails() {
88
const { testId, baselineId, benchmarkName } = getParams(location.search);
9-
const [testExcluded, setTestExcluded] = useState({});
10-
const [baselineExcluded, setBaselineExcluded] = useState({});
9+
10+
// State to track data from both tables
11+
const [testData, setTestData] = useState(null);
12+
const [baselineData, setBaselineData] = useState(null);
13+
const [testStats, setTestStats] = useState(null);
14+
const [baselineStats, setBaselineStats] = useState(null);
15+
const [saving, setSaving] = useState(false);
1116

17+
// Save handler for both tables
1218
const handleSave = async () => {
19+
if (!testData || !baselineData || !testStats || !baselineStats) {
20+
message.warning('Please wait for data to load');
21+
return;
22+
}
23+
24+
setSaving(true);
25+
1326
try {
14-
if (testId) {
15-
await postData('/api/updateStats', {
16-
id: testId,
17-
benchmarkName,
18-
excludedRuns: testExcluded,
19-
});
20-
}
21-
if (baselineId) {
22-
await postData('/api/updateStats', {
23-
id: baselineId,
27+
// Calculate which iterations are disabled for TEST
28+
const testDisabledIterations = testData
29+
.filter(item => !item.enabled)
30+
.map(item => item.iteration);
31+
32+
// Calculate which iterations are disabled for BASELINE
33+
const baselineDisabledIterations = baselineData
34+
.filter(item => !item.enabled)
35+
.map(item => item.iteration);
36+
37+
console.log('Saving BOTH test and baseline:', {
38+
testDisabledIterations,
39+
baselineDisabledIterations,
40+
testStats,
41+
baselineStats
42+
});
43+
44+
// One API call saves both TEST and BASELINE stats together
45+
const response = await fetch('/api/updateStats', {
46+
method: 'PUT',
47+
headers: {
48+
'Content-Type': 'application/json'
49+
},
50+
body: JSON.stringify({
51+
testId,
52+
baselineId,
2453
benchmarkName,
25-
excludedRuns: baselineExcluded,
26-
});
54+
testStats,
55+
baselineStats,
56+
testDisabledIterations,
57+
baselineDisabledIterations,
58+
})
59+
});
60+
61+
const result = await response.json();
62+
63+
if (result.success) {
64+
message.success('✓ Both TEST and BASELINE statistics saved successfully!');
65+
} else {
66+
message.error('Failed to save: ' + (result.error || 'Unknown error'));
2767
}
28-
message.success('Statistics saved successfully');
29-
} catch (e) {
30-
message.error('Failed to save statistics');
68+
} catch (error) {
69+
message.error('Error saving: ' + error.message);
70+
console.error('Save error:', error);
71+
} finally {
72+
setSaving(false);
3173
}
3274
};
3375

3476
return (
35-
<div style={{ padding: 20 }}>
36-
<div
37-
style={{
38-
display: 'flex',
39-
justifyContent: 'flex-end',
40-
marginBottom: 20,
41-
}}
42-
>
43-
<Button type="primary" size="large" onClick={handleSave}>
44-
Save both Test and Baseline Tables
77+
<div>
78+
{/* One Save Button for bothH tables */}
79+
<div style={{
80+
marginBottom: 16,
81+
padding: 16,
82+
background: '#f5f5f5',
83+
borderRadius: 8,
84+
display: 'flex',
85+
justifyContent: 'space-between',
86+
alignItems: 'center'
87+
}}>
88+
<div>
89+
<div style={{ margin: 0, fontSize: 20, fontWeight: 'bold' }}>
90+
Metrics Details - {benchmarkName}
91+
</div>
92+
<div style={{ margin: 0, fontSize: 12, color: 'rgba(0, 0, 0, 1)' }}>
93+
Annotate bad runs for both test and baseline, then SAVE
94+
</div>
95+
</div>
96+
<Button
97+
type="primary"
98+
size="large"
99+
onClick={handleSave}
100+
loading={saving}
101+
disabled={!testStats || !baselineStats}
102+
>
103+
Save Both Statistics
45104
</Button>
46105
</div>
106+
107+
47108
<MetricsTable
48109
type="test"
49110
id={testId}
50111
benchmarkName={benchmarkName}
51-
onExcludedRunsChange={(buildName, excluded) => {
52-
setTestExcluded((prev) => ({
53-
...prev,
54-
[buildName]: excluded,
55-
}));
56-
}}
112+
onDataChange={setTestData}
113+
onStatsChange={setTestStats}
57114
/>
58115
<Divider />
59116
<MetricsTable
60117
type="baseline"
61118
id={baselineId}
62119
benchmarkName={benchmarkName}
63-
onExcludedRunsChange={(buildName, excluded) => {
64-
setBaselineExcluded((prev) => ({
65-
...prev,
66-
[buildName]: excluded,
67-
}));
68-
}}
120+
onDataChange={setBaselineData}
121+
onStatsChange={setBaselineStats}
69122
/>
70123
</div>
71124
);
72125
}
73-
export default MetricsDetails;
126+
127+
export default MetricsDetails;

0 commit comments

Comments
 (0)