Skip to content

Commit c472214

Browse files
authored
Merge pull request #459 from bncer/build-time-queue
Add elapsed time in pending status
2 parents 4cb7f2d + e6a3a01 commit c472214

File tree

2 files changed

+107
-4
lines changed

2 files changed

+107
-4
lines changed

src/templates.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
use crate::database::{MergeableState::*, PullRequestModel, QueueStatus, TreeState};
1+
use crate::database::{
2+
BuildModel, BuildStatus, MergeableState::*, PullRequestModel, QueueStatus, TreeState,
3+
};
24
use askama::Template;
35
use axum::response::{Html, IntoResponse, Response};
6+
use chrono::{DateTime, Local, Utc};
47
use http::StatusCode;
58

69
/// Build status to display on the queue page.
@@ -67,6 +70,21 @@ pub struct QueueTemplate {
6770
pub oauth_client_id: Option<String>,
6871
}
6972

73+
impl QueueTemplate {
74+
fn to_local_time(&self, time: DateTime<Utc>) -> DateTime<Local> {
75+
time.into()
76+
}
77+
}
78+
7079
#[derive(Template)]
7180
#[template(path = "not_found.html")]
7281
pub struct NotFoundTemplate {}
82+
83+
pub fn get_pending_build(pr: &PullRequestModel) -> Option<&BuildModel> {
84+
if let Some(auto_build) = &pr.auto_build
85+
&& auto_build.status == BuildStatus::Pending
86+
{
87+
return Some(auto_build);
88+
}
89+
None
90+
}

templates/queue.html

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@
66
<link rel="stylesheet" href="https://cdn.datatables.net/2.3.4/css/dataTables.dataTables.min.css" />
77
<link rel="stylesheet" href="https://cdn.datatables.net/rowgroup/1.5.1/css/rowGroup.dataTables.min.css" />
88
<style>
9+
@keyframes barber-pole {
10+
/* This animation is used to make the status indicator next to
11+
pending pulls look like a barber poll. We do that with a
12+
diagonal linear gradient. CSS does not allow us to animate a
13+
gradient, so instead we make the indicator a little taller
14+
than shown, and then animate the whole thing upward. */
15+
from{
16+
transform: translate(0, 0);
17+
}
18+
to {
19+
/* The magic number 11.314 is sqrt(8^2 + 8^2), based on how
20+
far vertically it takes to repeat a 45 degree gradient
21+
that is 8 pixels long before it repeats. */
22+
transform: translate(0, -11.314px);
23+
}
24+
}
925
main {
1026
max-width: 100rem;
1127
width: 100%;
@@ -34,6 +50,30 @@
3450
td.select-checkbox {
3551
width: 2.5rem;
3652
}
53+
table.dataTable > tbody > tr > td.status {
54+
position: relative;
55+
overflow: hidden;
56+
padding-left: 16px;
57+
white-space: nowrap;
58+
}
59+
td.status:before {
60+
content: " ";
61+
position: absolute;
62+
display: block;
63+
left: 6px;
64+
width: 6px;
65+
top: 0;
66+
bottom: 0;
67+
}
68+
td.status[data-status="pending"]:before {
69+
/* Give the pending state a little bit of animation to make it
70+
clear that these items are the ones that are being tested
71+
right now. */
72+
bottom: -20px;
73+
background-color: #F0DE57;
74+
background-image: repeating-linear-gradient(135deg, #F0DE57 0, #F0DE57 4px, #FBEE97 4px, #FBEE97 8px, #F0DE57 0);
75+
animation: barber-pole 1s linear infinite;
76+
}
3777

3878
#rollupModal {
3979
display: none;
@@ -90,7 +130,7 @@ <h1>
90130
</div>
91131

92132
<div class="table-wrapper">
93-
<table id="table">
133+
<table id="table" class="stripe">
94134
<thead>
95135
<tr>
96136
<th class="select-checkbox"></th>
@@ -105,7 +145,6 @@ <h1>
105145
<th>Rollup</th>
106146
</tr>
107147
</thead>
108-
<table id="table" class="stripe">
109148

110149
<tbody>
111150
{% for pr in prs %}
@@ -114,12 +153,19 @@ <h1>
114153
<td data-pr-number="{{ pr.number.0 }}">
115154
<a href="{{ repo_url }}/pull/{{ pr.number }}">{{ pr.number.0 }}</a>
116155
</td>
117-
<td data-status="{{ crate::templates::status_text(pr) }}">
156+
{% let pending_build = crate::templates::get_pending_build(pr) %}
157+
<td class="status" data-status="{{ crate::templates::status_text(pr) }}"
158+
{% if let Some(build) = pending_build %}
159+
title="Started at {{ to_local_time(*build.created_at).format("%d.%m.%Y %H:%M:%S") }}"
160+
data-created-at="{{ build.created_at.timestamp_millis() }}"
161+
{% endif %}>
162+
118163
{% if let Some(try_build) = pr.try_build %}
119164
<a href="../results/{{ repo_name }}/{{ pr.number }}">{{ crate::templates::status_text(pr) }}</a> (try)
120165
{% else %}
121166
{{ crate::templates::status_text(pr) }}
122167
{% endif %}
168+
<span class="elapsed-time-display"></span>
123169
</td>
124170
<td>
125171
{% match pr.mergeable_state %}
@@ -364,5 +410,44 @@ <h1>
364410

365411
window.location.href = oauthUrl.toString();
366412
});
413+
414+
function formatElapsedTime(ms) {
415+
const totalSeconds = Math.floor(ms / 1000);
416+
const hours = Math.floor(totalSeconds / 3600);
417+
const remainingSeconds = totalSeconds % 3600;
418+
const minutes = Math.floor(remainingSeconds / 60);
419+
420+
let output = '';
421+
if (hours > 0) {
422+
output += `${hours}h`;
423+
}
424+
425+
if (minutes > 0) {
426+
if (output.length > 0) {
427+
output += ' ';
428+
}
429+
output += `${minutes}m`;
430+
}
431+
if (output.length > 0) {
432+
return ` (${output})`;
433+
}
434+
return '';
435+
}
436+
437+
function updateElapsedTimes() {
438+
const now = Date.now();
439+
document.querySelectorAll('[data-created-at]').forEach(td => {
440+
const createdAtMs = parseInt(td.dataset.createdAt, 10);
441+
if (createdAtMs > 0) {
442+
const elapsedMs = now - createdAtMs;
443+
const formattedTime = formatElapsedTime(elapsedMs);
444+
let display = td.querySelector('.elapsed-time-display');
445+
display.textContent = formattedTime;
446+
}
447+
});
448+
}
449+
450+
updateElapsedTimes();
451+
setInterval(updateElapsedTimes, 1000);
367452
</script>
368453
{% endblock %}

0 commit comments

Comments
 (0)