-
Notifications
You must be signed in to change notification settings - Fork 139
Expand file tree
/
Copy pathQueueHelper.php
More file actions
262 lines (219 loc) · 5.8 KB
/
Copy pathQueueHelper.php
File metadata and controls
262 lines (219 loc) · 5.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
<?php
declare(strict_types=1);
namespace Queue\View\Helper;
use Cake\View\Helper;
use DateInterval;
use Queue\Model\Entity\QueuedJob;
use Queue\Queue\Config;
use Queue\Queue\TaskFinder;
/**
* @property \Queue\Model\Table\QueuedJobsTable $QueuedJobs
*/
class QueueHelper extends Helper {
/**
* @var array<string, array<string, mixed>>
*/
protected array $taskConfig = [];
/**
* @param \Queue\Model\Entity\QueuedJob $queuedJob
*
* @return bool
*/
public function hasFailed(QueuedJob $queuedJob): bool {
if ($queuedJob->completed || !$queuedJob->fetched || !$queuedJob->attempts) {
return false;
}
// Restarted
if (!$queuedJob->failure_message) {
return false;
}
// Requeued
$taskConfig = $this->taskConfig($queuedJob->job_task);
return !($taskConfig && $queuedJob->attempts <= $taskConfig['retries']);
}
/**
* @param \Queue\Model\Entity\QueuedJob $queuedJob
*
* @return string|null
*/
public function attempts(QueuedJob $queuedJob): ?string {
if ($queuedJob->attempts < 1) {
return '0x';
}
$taskConfig = $this->taskConfig($queuedJob->job_task);
if ($taskConfig) {
$maxFails = $taskConfig['retries'] + 1;
return $queuedJob->attempts . '/' . $maxFails;
}
return $queuedJob->attempts . 'x';
}
/**
* Returns true if job has been requeued (has failure message but within retry limit).
*
* @param \Queue\Model\Entity\QueuedJob $queuedJob
*
* @return bool
*/
public function isRequeued(QueuedJob $queuedJob): bool {
if ($queuedJob->completed || !$queuedJob->fetched) {
return false;
}
// Must have a failure message to be considered "requeued"
if (!$queuedJob->failure_message) {
return false;
}
if ($queuedJob->attempts < 1) {
return false;
}
$taskConfig = $this->taskConfig($queuedJob->job_task);
return $taskConfig && $queuedJob->attempts <= $taskConfig['retries'];
}
/**
* Returns true if job has been manually restarted (reset).
*
* A restarted job has been fetched and has attempts, but no failure_message
* (the failure_message was cleared when the job was reset).
*
* @param \Queue\Model\Entity\QueuedJob $queuedJob
*
* @return bool
*/
public function isRestarted(QueuedJob $queuedJob): bool {
if ($queuedJob->completed || !$queuedJob->fetched) {
return false;
}
// Must have attempts (was previously run)
if ($queuedJob->attempts < 1) {
return false;
}
// Must NOT have a failure_message (it was cleared by reset)
return !$queuedJob->failure_message;
}
/**
* Returns failure status (message) if applicable.
*
* @param \Queue\Model\Entity\QueuedJob $queuedJob
*
* @return string|null
*/
public function failureStatus(QueuedJob $queuedJob): ?string {
if ($queuedJob->completed || !$queuedJob->fetched || !$queuedJob->attempts) {
return null;
}
if (!$queuedJob->failure_message) {
return __d('queue', 'Restarted');
}
$taskConfig = $this->taskConfig($queuedJob->job_task);
if ($taskConfig && $queuedJob->attempts <= $taskConfig['retries']) {
return __d('queue', 'Requeued');
}
return __d('queue', 'Aborted');
}
/**
* @param string $jobTask
*
* @return array<string, mixed>
*/
protected function taskConfig(string $jobTask): array {
if (!$this->taskConfig) {
$tasks = (new TaskFinder())->all();
$this->taskConfig = Config::taskConfig($tasks);
}
return $this->taskConfig[$jobTask] ?? [];
}
/**
* Formats seconds into a human-readable string for large values.
*
* Returns the seconds with human-readable addition in brackets for values >= 3600 (1 hour).
*
* @param int $seconds
*
* @return string
*/
public function secondsToHumanReadable(int $seconds): string {
if ($seconds < 3600) {
return (string)$seconds;
}
$parts = [];
$days = (int)floor($seconds / 86400);
$hours = (int)floor(($seconds % 86400) / 3600);
$minutes = (int)floor(($seconds % 3600) / 60);
if ($days > 0) {
$parts[] = $days . 'd';
}
if ($hours > 0) {
$parts[] = $hours . 'h';
}
if ($minutes > 0) {
$parts[] = $minutes . 'm';
}
return $seconds . ' (' . implode(' ', $parts) . ')';
}
/**
* Returns the duration of a completed job.
*
* @param \Queue\Model\Entity\QueuedJob $queuedJob
*
* @return string|null Duration string or null if not calculable
*/
public function duration(QueuedJob $queuedJob): ?string {
if (!$queuedJob->completed) {
return null;
}
if (!$queuedJob->fetched) {
return null;
}
$interval = $queuedJob->completed->diff($queuedJob->fetched);
return $this->formatInterval($interval);
}
/**
* Formats a DateInterval into a human-readable duration string.
*
* @param \DateInterval $interval
*
* @return string
*/
public function formatInterval(DateInterval $interval): string {
$parts = [];
if ($interval->d > 0) {
$parts[] = $interval->d . 'd';
}
if ($interval->h > 0) {
$parts[] = $interval->h . 'h';
}
if ($interval->i > 0) {
$parts[] = $interval->i . 'm';
}
if ($interval->s > 0 || empty($parts)) {
$parts[] = $interval->s . 's';
}
// Minimum display is "< 1s" when no time parts
if (implode('', $parts) === '0s') {
return '< 1s';
}
return implode(' ', $parts);
}
/**
* Returns a color for heatmap visualization based on intensity.
*
* Uses a green gradient from light (low activity) to dark (high activity).
*
* @param float $intensity Value between 0 and 1
*
* @return string CSS color value
*/
public function heatmapColor(float $intensity): string {
if ($intensity <= 0) {
return '#ebedf0'; // Empty/no activity
}
// Green gradient similar to GitHub contribution graph
$colors = [
'#9be9a8', // Light green (low)
'#40c463', // Medium green
'#30a14e', // Darker green
'#216e39', // Dark green (high)
];
$index = min((int)floor($intensity * count($colors)), count($colors) - 1);
return $colors[$index];
}
}