Правка багов рассчета процента закрытия спринта
parent
4b7828a47d
commit
1ded77c5dc
|
|
@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS teams (
|
|||
team_board_id INT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TYPE role AS ENUM ('analytic', 'backend', 'designer', 'frontend', 'qa');
|
||||
CREATE TYPE role AS ENUM ('analytic', 'backend', 'designer', 'frontend', 'qa', 'project');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS employees (
|
||||
id SERIAL PRIMARY KEY,
|
||||
|
|
|
|||
|
|
@ -82,7 +82,9 @@ VALUES
|
|||
(61, 'Иван Монастырский', 'i.monastyrskiy', 'frontend'),
|
||||
(62, 'Дамир Насыров', 'd.nasyrov', 'frontend'),
|
||||
(63, 'Максим Миклушов', 'm.miklushov', 'qa'),
|
||||
(64, 'Дмитрий Михайлов', 'd.mikhaylov', 'qa');
|
||||
(64, 'Дмитрий Михайлов', 'd.mikhaylov', 'qa'),
|
||||
(67, 'Борис Печорин', 'b.pechorin', 'backend'),
|
||||
(68, 'Наталья Чеботарева', 'n.telentinova', 'project');
|
||||
|
||||
INSERT INTO team_members (team_id, employee_id, start_datetime)
|
||||
VALUES
|
||||
|
|
@ -159,4 +161,7 @@ VALUES
|
|||
(4, 61, '2026-01-01 00:00:00Z'),
|
||||
(4, 62, '2026-01-01 00:00:00Z'),
|
||||
(4, 63, '2026-01-01 00:00:00Z'),
|
||||
(4, 64, '2026-01-01 00:00:00Z');
|
||||
(4, 64, '2026-01-01 00:00:00Z'),
|
||||
(4, 67, '2026-01-01 00:00:00Z'),
|
||||
(4, 4, '2026-01-01 00:00:00Z'),
|
||||
(4, 68, '2026-01-01 00:00:00Z');
|
||||
|
|
|
|||
|
|
@ -168,6 +168,12 @@ function parseSprintIds(?string $raw): array {
|
|||
*/
|
||||
function wasIssueInSprintAtStart(array $issue, int $jiraSprintId, int $startTs): bool {
|
||||
$sprintIdStr = (string)$jiraSprintId;
|
||||
$issueCreatedAt = !empty($issue['fields']['created']) ? strtotime($issue['fields']['created']) : false;
|
||||
|
||||
// Задача создалась после начала спринта - она не спринтовая
|
||||
if ($issueCreatedAt > $startTs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Собираем изменения поля "Sprint" с метками времени.
|
||||
$changes = [];
|
||||
|
|
@ -180,39 +186,48 @@ function wasIssueInSprintAtStart(array $issue, int $jiraSprintId, int $startTs):
|
|||
if (($item['field'] ?? null) !== 'Sprint') {
|
||||
continue;
|
||||
}
|
||||
$changes[] = [
|
||||
'at' => $createdAt,
|
||||
'from' => parseSprintIds($item['from'] ?? ''),
|
||||
'to' => parseSprintIds($item['to'] ?? ''),
|
||||
$fromIds = parseSprintIds($item['from'] ?? '');
|
||||
$toIds = parseSprintIds($item['to'] ?? '');
|
||||
|
||||
// Если id спринта и там и там - спринт не менялся нужный
|
||||
// Если нет id спринта в обоих местах - тоже не интересует
|
||||
if (in_array($sprintIdStr, $fromIds, true) == in_array($sprintIdStr, $toIds, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$changes[$createdAt] = [
|
||||
'from' => $fromIds,
|
||||
'to' => $toIds,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($changes)) {
|
||||
// Поле "Sprint" не менялось: задача в этом спринте с момента создания.
|
||||
$issueCreatedAt = !empty($issue['fields']['created']) ? strtotime($issue['fields']['created']) : false;
|
||||
return $issueCreatedAt !== false && $issueCreatedAt <= $startTs;
|
||||
return true;
|
||||
}
|
||||
|
||||
usort($changes, static fn ($a, $b) => $a['at'] <=> $b['at']);
|
||||
$timeList = array_keys($changes);
|
||||
$changesBeforeStart = array_filter($timeList, fn ($change) => $change < $startTs);
|
||||
$changeBeforeStart = empty($changesBeforeStart) ? 0 : max($changesBeforeStart);
|
||||
|
||||
// Восстанавливаем значение поля "Sprint", действовавшее на момент старта спринта.
|
||||
$valueAtStart = null;
|
||||
$hasPriorChange = false;
|
||||
foreach ($changes as $change) {
|
||||
if ($change['at'] <= $startTs) {
|
||||
$valueAtStart = $change['to'];
|
||||
$hasPriorChange = true;
|
||||
} else {
|
||||
// Первое изменение уже после старта: значение до него — это его "from".
|
||||
if (!$hasPriorChange) {
|
||||
$valueAtStart = $change['from'];
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ($changeBeforeStart) {
|
||||
// Если добавили в спринт до начала спринта - то значит спринтовая
|
||||
// Если нет, то значит удалили из спринта до старта
|
||||
return in_array($jiraSprintId, $changes[$changeBeforeStart]['to']);
|
||||
}
|
||||
|
||||
return $valueAtStart !== null && in_array($sprintIdStr, $valueAtStart, true);
|
||||
$changesAfterStart = array_filter($timeList, fn ($change) => $change > $startTs);
|
||||
$changeAfterStart = empty($changesAfterStart) ? 0 : min($changesAfterStart);
|
||||
|
||||
if ($changeAfterStart) {
|
||||
// Если первое событие - добавление в спринт - не спринтовая
|
||||
// Если удаление из спринта, значит она там была
|
||||
return in_array($jiraSprintId, $changes[$changeAfterStart]['from']);
|
||||
}
|
||||
|
||||
// Если до сюда дошли, значит случилась какая-то хуйня и я это не учел и пора падать
|
||||
throw new Exception('Беда с задачей '.$issue['key']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue