mirror of
https://github.com/ArchiveBox/ArchiveBox.git
synced 2025-05-13 14:44:29 -04:00
better jobs dashboard with faster refresh
This commit is contained in:
parent
eb53145e4e
commit
c7bd9449d5
2 changed files with 77 additions and 16 deletions
|
@ -13,9 +13,18 @@
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
@keyframes pulse {
|
||||||
|
0% { opacity: 1; }
|
||||||
|
48% { opacity: 0.2; }
|
||||||
|
52% { opacity: 1; }
|
||||||
|
100% { opacity: 1; }
|
||||||
|
}
|
||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
h1 a {
|
||||||
|
animation: pulse 1s;
|
||||||
|
}
|
||||||
.dashboard {
|
.dashboard {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
@ -85,13 +94,21 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Job Dashboard <small><a href="?refresh=true">♻️ {{now}}</a></small></h1>
|
<h1>Job Dashboard <small><a href="?refresh=true" id="current-time">♻️ {{now}}}</a></small></h1>
|
||||||
<div id="dashboard" class="dashboard"></div>
|
<div id="dashboard" class="dashboard"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function formatDate(dateString) {
|
function formatDate(dateString) {
|
||||||
|
const now = Date.now()
|
||||||
|
const date = new Date(dateString)
|
||||||
// return new Date(dateString).toLocaleString();
|
// return new Date(dateString).toLocaleString();
|
||||||
return new Date(dateString).toISOString().split('T').at(-1).replace('Z', '');
|
// return date.toISOString().split('T').at(-1).replace('Z', '');
|
||||||
|
const seconds_diff = Math.round((date - now) / 1000, 0)
|
||||||
|
if (seconds_diff < 0) {
|
||||||
|
return `${seconds_diff}s ago`;
|
||||||
|
} else {
|
||||||
|
return `${seconds_diff}s in the future`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createJobElement(job) {
|
function createJobElement(job) {
|
||||||
|
@ -109,6 +126,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDashboard(data) {
|
function updateDashboard(data) {
|
||||||
|
const currentTime = document.getElementById('current-time');
|
||||||
|
window.now = new Date();
|
||||||
|
currentTime.innerHTML = `♻️ ${window.now.toISOString().split('T').at(-1).replace('Z', '')}`;
|
||||||
|
|
||||||
const dashboard = document.getElementById('dashboard');
|
const dashboard = document.getElementById('dashboard');
|
||||||
dashboard.innerHTML = '';
|
dashboard.innerHTML = '';
|
||||||
|
|
||||||
|
@ -117,16 +138,42 @@
|
||||||
card.className = 'card';
|
card.className = 'card';
|
||||||
card.innerHTML = `
|
card.innerHTML = `
|
||||||
<h2>${actor.model}</h2>
|
<h2>${actor.model}</h2>
|
||||||
<h3>Queue</h3>
|
<hr/>
|
||||||
<div class="scroll-area" id="queue-${actor.model}"></div>
|
Future
|
||||||
<h3>Past Tasks</h3>
|
<div class="scroll-area" style="background-color: white;" id="future-${actor.model}"></div>
|
||||||
<div class="scroll-area" id="past-${actor.model}"></div>
|
<hr/>
|
||||||
|
Pending
|
||||||
|
<div class="scroll-area" style="background-color: lightblue;" id="pending-${actor.model}"></div>
|
||||||
|
<hr/>
|
||||||
|
Stalled
|
||||||
|
<div class="scroll-area" style="background-color: lightcoral;" id="stalled-${actor.model}"></div>
|
||||||
|
<hr/>
|
||||||
|
Active
|
||||||
|
<div class="scroll-area" style="background-color: lightgreen;" id="active-${actor.model}"></div>
|
||||||
|
<hr/>
|
||||||
|
Past
|
||||||
|
<div class="scroll-area" style="background-color: lightgrey;" id="past-${actor.model}"></div>
|
||||||
`;
|
`;
|
||||||
dashboard.appendChild(card);
|
dashboard.appendChild(card);
|
||||||
|
|
||||||
const queueContainer = document.getElementById(`queue-${actor.model}`);
|
const futureContainer = document.getElementById(`future-${actor.model}`);
|
||||||
actor.queue.forEach(job => {
|
actor.future.forEach(job => {
|
||||||
queueContainer.appendChild(createJobElement(job));
|
futureContainer.appendChild(createJobElement(job));
|
||||||
|
});
|
||||||
|
|
||||||
|
const pendingContainer = document.getElementById(`pending-${actor.model}`);
|
||||||
|
actor.pending.forEach(job => {
|
||||||
|
pendingContainer.appendChild(createJobElement(job));
|
||||||
|
});
|
||||||
|
|
||||||
|
const stalledContainer = document.getElementById(`stalled-${actor.model}`);
|
||||||
|
actor.stalled.forEach(job => {
|
||||||
|
stalledContainer.appendChild(createJobElement(job));
|
||||||
|
});
|
||||||
|
|
||||||
|
const activeContainer = document.getElementById(`active-${actor.model}`);
|
||||||
|
actor.active.forEach(job => {
|
||||||
|
activeContainer.appendChild(createJobElement(job));
|
||||||
});
|
});
|
||||||
|
|
||||||
const pastContainer = document.getElementById(`past-${actor.model}`);
|
const pastContainer = document.getElementById(`past-${actor.model}`);
|
||||||
|
@ -149,7 +196,7 @@
|
||||||
|
|
||||||
fetchData();
|
fetchData();
|
||||||
|
|
||||||
setInterval(fetchData, 1000);
|
setInterval(fetchData, 750);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -7,9 +7,8 @@ from datetime import datetime
|
||||||
|
|
||||||
from ninja import Router, Schema
|
from ninja import Router, Schema
|
||||||
|
|
||||||
from .auth import API_AUTH_METHODS
|
|
||||||
|
|
||||||
router = Router(tags=['Workers and Tasks'], auth=API_AUTH_METHODS)
|
router = Router(tags=['Workers and Tasks'])
|
||||||
|
|
||||||
|
|
||||||
class TaskSchema(Schema):
|
class TaskSchema(Schema):
|
||||||
|
@ -50,7 +49,10 @@ class ActorSchema(Schema):
|
||||||
MAX_TICK_TIME: int
|
MAX_TICK_TIME: int
|
||||||
MAX_CONCURRENT_ACTORS: int
|
MAX_CONCURRENT_ACTORS: int
|
||||||
|
|
||||||
queue: list[TaskSchema]
|
future: list[TaskSchema]
|
||||||
|
pending: list[TaskSchema]
|
||||||
|
stalled: list[TaskSchema]
|
||||||
|
active: list[TaskSchema]
|
||||||
past: list[TaskSchema]
|
past: list[TaskSchema]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -72,10 +74,22 @@ class ActorSchema(Schema):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_FINAL_STATES(obj) -> list[str]:
|
def resolve_FINAL_STATES(obj) -> list[str]:
|
||||||
return [str(state) for state in obj.FINAL_STATES]
|
return [str(state) for state in obj.FINAL_STATES]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_queue(obj) -> list[TaskSchema]:
|
def resolve_future(obj) -> list[TaskSchema]:
|
||||||
return [obj for obj in obj.qs.filter(obj.pending_q | obj.future_q | obj.active_q | obj.stalled_q).order_by('-retry_at')]
|
return [obj for obj in obj.qs.filter(obj.future_q).order_by('-retry_at')]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_pending(obj) -> list[TaskSchema]:
|
||||||
|
return [obj for obj in obj.qs.filter(obj.pending_q).order_by('-retry_at')]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_stalled(obj) -> list[TaskSchema]:
|
||||||
|
return [obj for obj in obj.qs.filter(obj.stalled_q).order_by('-retry_at')]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_active(obj) -> list[TaskSchema]:
|
||||||
|
return [obj for obj in obj.qs.filter(obj.active_q).order_by('-retry_at')]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_past(obj) -> list[TaskSchema]:
|
def resolve_past(obj) -> list[TaskSchema]:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue