mirror of
https://github.com/ohwgiles/laminar.git
synced 2024-10-27 20:34:20 +00:00
frontend: better dynamic update of graphs
react to jobCompletion messages by updating graphs in more cases so manual refresh is not necessary to see most up to date representation.
This commit is contained in:
parent
60f7ee5402
commit
a50514a135
@ -470,6 +470,12 @@ std::string Laminar::getStatus(MonitorScope scope) {
|
|||||||
j.EndObject();
|
j.EndObject();
|
||||||
});
|
});
|
||||||
j.EndArray();
|
j.EndArray();
|
||||||
|
j.startObject("completedCounts");
|
||||||
|
db->stmt("SELECT name, COUNT(*) FROM builds WHERE result IS NOT NULL GROUP BY name")
|
||||||
|
.fetch<str, uint>([&](str job, uint count){
|
||||||
|
j.set(job.c_str(), count);
|
||||||
|
});
|
||||||
|
j.EndObject();
|
||||||
}
|
}
|
||||||
j.EndObject();
|
j.EndObject();
|
||||||
return j.str();
|
return j.str();
|
||||||
|
@ -203,15 +203,19 @@ const Charts = (() => {
|
|||||||
if (c.data.labels[j] == name) {
|
if (c.data.labels[j] == name) {
|
||||||
c.data.datasets[0].data[j]++;
|
c.data.datasets[0].data[j]++;
|
||||||
c.update();
|
c.update();
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// if we get here, it's a new/unknown job
|
||||||
|
c.data.labels.push(name);
|
||||||
|
c.data.datasets[0].data.push(1);
|
||||||
|
c.update();
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
},
|
},
|
||||||
createTimePerJobChart: (id, data) => {
|
createTimePerJobChart: (id, data, completedCounts) => {
|
||||||
const scale = timeScale(Math.max(...Object.values(data)));
|
const scale = timeScale(Math.max(...Object.values(data)));
|
||||||
return new Chart(document.getElementById(id), {
|
const c = new Chart(document.getElementById(id), {
|
||||||
type: 'horizontalBar',
|
type: 'horizontalBar',
|
||||||
data: {
|
data: {
|
||||||
labels: Object.keys(data),
|
labels: Object.keys(data),
|
||||||
@ -232,23 +236,38 @@ const Charts = (() => {
|
|||||||
}
|
}
|
||||||
}]},
|
}]},
|
||||||
tooltips:{callbacks:{
|
tooltips:{callbacks:{
|
||||||
label: (tip, data) => data.datasets[tip.datasetIndex].label + ': ' + tip.xLabel + ' ' + scale.label.toLowerCase()
|
label: (tip, data) => data.datasets[tip.datasetIndex].label + ': ' + tip.xLabel.toFixed(2) + ' ' + scale.label.toLowerCase()
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
c.jobCompleted = (name, time) => {
|
||||||
|
for (var j = 0; j < c.data.datasets[0].data.length; ++j) {
|
||||||
|
if (c.data.labels[j] == name) {
|
||||||
|
c.data.datasets[0].data[j] = ((completedCounts[name]-1) * c.data.datasets[0].data[j] + time * scale.factor) / completedCounts[name];
|
||||||
|
c.update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we get here, it's a new/unknown job
|
||||||
|
c.data.labels.push(name);
|
||||||
|
c.data.datasets[0].data.push(time * scale.factor);
|
||||||
|
c.update();
|
||||||
|
};
|
||||||
|
return c;
|
||||||
},
|
},
|
||||||
createRunTimeChangesChart: (id, data) => {
|
createRunTimeChangesChart: (id, data) => {
|
||||||
const scale = timeScale(Math.max(...data.map(e => Math.max(...e.durations))));
|
const scale = timeScale(Math.max(...data.map(e => Math.max(...e.durations))));
|
||||||
return new Chart(document.getElementById(id), {
|
const dataValue = (name, durations) => ({
|
||||||
|
label: name,
|
||||||
|
data: durations.map(x => x * scale.factor),
|
||||||
|
borderColor: 'hsl('+(name.hashCode() % 360)+', 27%, 57%)',
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
});
|
||||||
|
const c = new Chart(document.getElementById(id), {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: [...Array(10).keys()],
|
labels: [...Array(10).keys()],
|
||||||
datasets: data.map(e => ({
|
datasets: data.map(e => dataValue(e.name, e.durations))
|
||||||
label: e.name,
|
|
||||||
data: e.durations.map(x => x * scale.factor),
|
|
||||||
borderColor: 'hsl('+(e.name.hashCode() % 360)+', 27%, 57%)',
|
|
||||||
backgroundColor: 'transparent'
|
|
||||||
}))
|
|
||||||
},
|
},
|
||||||
options:{
|
options:{
|
||||||
title: { display: true, text: 'Run time changes' },
|
title: { display: true, text: 'Run time changes' },
|
||||||
@ -268,10 +287,25 @@ const Charts = (() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
c.jobCompleted = (name, time) => {
|
||||||
|
for (var j = 0; j < c.data.datasets.length; ++j) {
|
||||||
|
if (c.data.datasets[j].label == name) {
|
||||||
|
if(c.data.datasets[j].data.length == 10)
|
||||||
|
c.data.datasets[j].data.shift();
|
||||||
|
c.data.datasets[j].data.push(time * scale.factor);
|
||||||
|
c.update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we get here, it's a new/unknown job
|
||||||
|
c.data.datasets.push(dataValue(name, [time]));
|
||||||
|
c.update();
|
||||||
|
};
|
||||||
|
return c;
|
||||||
},
|
},
|
||||||
createRunTimeChart: (id, jobs, avg) => {
|
createRunTimeChart: (id, jobs, avg) => {
|
||||||
const scale = timeScale(Math.max(...jobs.map(v=>v.completed-v.started)));
|
const scale = timeScale(Math.max(...jobs.map(v=>v.completed-v.started)));
|
||||||
return new Chart(document.getElementById(id), {
|
const c = new Chart(document.getElementById(id), {
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: {
|
data: {
|
||||||
labels: jobs.map(e => '#' + e.number).reverse(),
|
labels: jobs.map(e => '#' + e.number).reverse(),
|
||||||
@ -319,6 +353,22 @@ const Charts = (() => {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
c.jobCompleted = (num, result, time) => {
|
||||||
|
let avg = c.data.datasets[0].data[0].y / scale.factor;
|
||||||
|
avg = ((avg * (num - 1)) + time) / num;
|
||||||
|
c.data.datasets[0].data[0].y = avg * scale.factor;
|
||||||
|
c.data.datasets[0].data[1].y = avg * scale.factor;
|
||||||
|
if(c.data.datasets[1].data.length == 20) {
|
||||||
|
c.data.labels.shift();
|
||||||
|
c.data.datasets[1].data.shift();
|
||||||
|
c.data.datasets[1].backgroundColor.shift();
|
||||||
|
}
|
||||||
|
c.data.labels.push('#' + num);
|
||||||
|
c.data.datasets[1].data.push(time * scale.factor);
|
||||||
|
c.data.datasets[1].backgroundColor.push(result == 'success' ? '#74af77': '#883d3d');
|
||||||
|
c.update();
|
||||||
|
};
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
@ -341,6 +391,7 @@ const Home = templateId => {
|
|||||||
lowPassRates: [],
|
lowPassRates: [],
|
||||||
};
|
};
|
||||||
let chtUtilization, chtBuildsPerDay, chtBuildsPerJob, chtTimePerJob;
|
let chtUtilization, chtBuildsPerDay, chtBuildsPerJob, chtTimePerJob;
|
||||||
|
let completedCounts;
|
||||||
return {
|
return {
|
||||||
template: templateId,
|
template: templateId,
|
||||||
data: () => state,
|
data: () => state,
|
||||||
@ -351,6 +402,7 @@ const Home = templateId => {
|
|||||||
state.jobsRecent = msg.recent;
|
state.jobsRecent = msg.recent;
|
||||||
state.resultChanged = msg.resultChanged;
|
state.resultChanged = msg.resultChanged;
|
||||||
state.lowPassRates = msg.lowPassRates;
|
state.lowPassRates = msg.lowPassRates;
|
||||||
|
completedCounts = msg.completedCounts;
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
|
|
||||||
// defer charts to nextTick because they get DOM elements which aren't rendered yet
|
// defer charts to nextTick because they get DOM elements which aren't rendered yet
|
||||||
@ -358,7 +410,7 @@ const Home = templateId => {
|
|||||||
chtUtilization = Charts.createExecutorUtilizationChart("chartUtil", msg.executorsBusy, msg.executorsTotal);
|
chtUtilization = Charts.createExecutorUtilizationChart("chartUtil", msg.executorsBusy, msg.executorsTotal);
|
||||||
chtBuildsPerDay = Charts.createRunsPerDayChart("chartBpd", msg.buildsPerDay);
|
chtBuildsPerDay = Charts.createRunsPerDayChart("chartBpd", msg.buildsPerDay);
|
||||||
chtBuildsPerJob = Charts.createRunsPerJobChart("chartBpj", msg.buildsPerJob);
|
chtBuildsPerJob = Charts.createRunsPerJobChart("chartBpj", msg.buildsPerJob);
|
||||||
chtTimePerJob = Charts.createTimePerJobChart("chartTpj", msg.timePerJob);
|
chtTimePerJob = Charts.createTimePerJobChart("chartTpj", msg.timePerJob, completedCounts);
|
||||||
chtBuildTimeChanges = Charts.createRunTimeChangesChart("chartBuildTimeChanges", msg.buildTimeChanges);
|
chtBuildTimeChanges = Charts.createRunTimeChangesChart("chartBuildTimeChanges", msg.buildTimeChanges);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -373,8 +425,10 @@ const Home = templateId => {
|
|||||||
chtUtilization.executorBusyChanged(true);
|
chtUtilization.executorBusyChanged(true);
|
||||||
},
|
},
|
||||||
job_completed: function(data) {
|
job_completed: function(data) {
|
||||||
for (var i = 0; i < state.jobsRunning.length; ++i) {
|
if(!(job.name in completedCounts))
|
||||||
var job = state.jobsRunning[i];
|
completedCounts[job.name] = 0;
|
||||||
|
for(let i = 0; i < state.jobsRunning.length; ++i) {
|
||||||
|
const job = state.jobsRunning[i];
|
||||||
if (job.name == data.name && job.number == data.number) {
|
if (job.name == data.name && job.number == data.number) {
|
||||||
state.jobsRunning.splice(i, 1);
|
state.jobsRunning.splice(i, 1);
|
||||||
state.jobsRecent.splice(0, 0, data);
|
state.jobsRecent.splice(0, 0, data);
|
||||||
@ -382,9 +436,28 @@ const Home = templateId => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for(let i = 0; i < state.resultChanged.length; ++i) {
|
||||||
|
const job = state.resultChanged[i];
|
||||||
|
if(job.name == data.name) {
|
||||||
|
job[data.result === 'success' ? 'lastSuccess' : 'lastFailure'] = data.number;
|
||||||
|
this.$forceUpdate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(let i = 0; i < state.lowPassRates.length; ++i) {
|
||||||
|
const job = state.lowPassRates[i];
|
||||||
|
if(job.name == data.name) {
|
||||||
|
job.passRate = ((completedCounts[job.name] - 1) * job.passRate + (data.result === 'success' ? 1 : 0)) / completedCounts[job.name];
|
||||||
|
this.$forceUpdate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
completedCounts[job.name]++;
|
||||||
chtBuildsPerDay.jobCompleted(data.result === 'success')
|
chtBuildsPerDay.jobCompleted(data.result === 'success')
|
||||||
chtUtilization.executorBusyChanged(false);
|
chtUtilization.executorBusyChanged(false);
|
||||||
chtBuildsPerJob.jobCompleted(data.name)
|
chtBuildsPerJob.jobCompleted(data.name);
|
||||||
|
chtTimePerJob.jobCompleted(data.name, data.completed - data.started);
|
||||||
|
chtBuildTimeChanges.jobCompleted(data.name, data.completed - data.started);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -494,7 +567,7 @@ const Job = templateId => {
|
|||||||
pages: 0,
|
pages: 0,
|
||||||
sort: {}
|
sort: {}
|
||||||
};
|
};
|
||||||
let chtBt = null;
|
let chtBuildTime = null;
|
||||||
return {
|
return {
|
||||||
template: templateId,
|
template: templateId,
|
||||||
props: ['route'],
|
props: ['route'],
|
||||||
@ -512,12 +585,12 @@ const Job = templateId => {
|
|||||||
|
|
||||||
// "status" comes again if we change page/sorting. Delete the
|
// "status" comes again if we change page/sorting. Delete the
|
||||||
// old chart and recreate it to prevent flickering of old data
|
// old chart and recreate it to prevent flickering of old data
|
||||||
if(chtBt)
|
if(chtBuildTime)
|
||||||
chtBt.destroy();
|
chtBuildTime.destroy();
|
||||||
|
|
||||||
// defer chart to nextTick because they get DOM elements which aren't rendered yet
|
// defer chart to nextTick because they get DOM elements which aren't rendered yet
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
chtBt = Charts.createRunTimeChart("chartBt", msg.recent, msg.averageRuntime);
|
chtBuildTime = Charts.createRunTimeChart("chartBt", msg.recent, msg.averageRuntime);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
job_queued: function() {
|
job_queued: function() {
|
||||||
@ -534,8 +607,8 @@ const Job = templateId => {
|
|||||||
state.jobsRunning.splice(i, 1);
|
state.jobsRunning.splice(i, 1);
|
||||||
state.jobsRecent.splice(0, 0, data);
|
state.jobsRecent.splice(0, 0, data);
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
// TODO: update the chart
|
|
||||||
}
|
}
|
||||||
|
chtBuildTime.jobCompleted(data.number, data.result, data.completed - data.started);
|
||||||
},
|
},
|
||||||
page_next: function() {
|
page_next: function() {
|
||||||
state.sort.page++;
|
state.sort.page++;
|
||||||
|
Loading…
Reference in New Issue
Block a user