Правка багов рассчета процента закрытия спринта
parent
4b7828a47d
commit
1ded77c5dc
|
|
@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS teams (
|
||||||
team_board_id INT NOT NULL
|
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 (
|
CREATE TABLE IF NOT EXISTS employees (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,9 @@ VALUES
|
||||||
(61, 'Иван Монастырский', 'i.monastyrskiy', 'frontend'),
|
(61, 'Иван Монастырский', 'i.monastyrskiy', 'frontend'),
|
||||||
(62, 'Дамир Насыров', 'd.nasyrov', 'frontend'),
|
(62, 'Дамир Насыров', 'd.nasyrov', 'frontend'),
|
||||||
(63, 'Максим Миклушов', 'm.miklushov', 'qa'),
|
(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)
|
INSERT INTO team_members (team_id, employee_id, start_datetime)
|
||||||
VALUES
|
VALUES
|
||||||
|
|
@ -159,4 +161,7 @@ VALUES
|
||||||
(4, 61, '2026-01-01 00:00:00Z'),
|
(4, 61, '2026-01-01 00:00:00Z'),
|
||||||
(4, 62, '2026-01-01 00:00:00Z'),
|
(4, 62, '2026-01-01 00:00:00Z'),
|
||||||
(4, 63, '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 {
|
function wasIssueInSprintAtStart(array $issue, int $jiraSprintId, int $startTs): bool {
|
||||||
$sprintIdStr = (string)$jiraSprintId;
|
$sprintIdStr = (string)$jiraSprintId;
|
||||||
|
$issueCreatedAt = !empty($issue['fields']['created']) ? strtotime($issue['fields']['created']) : false;
|
||||||
|
|
||||||
|
// Задача создалась после начала спринта - она не спринтовая
|
||||||
|
if ($issueCreatedAt > $startTs) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Собираем изменения поля "Sprint" с метками времени.
|
// Собираем изменения поля "Sprint" с метками времени.
|
||||||
$changes = [];
|
$changes = [];
|
||||||
|
|
@ -180,39 +186,48 @@ function wasIssueInSprintAtStart(array $issue, int $jiraSprintId, int $startTs):
|
||||||
if (($item['field'] ?? null) !== 'Sprint') {
|
if (($item['field'] ?? null) !== 'Sprint') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$changes[] = [
|
$fromIds = parseSprintIds($item['from'] ?? '');
|
||||||
'at' => $createdAt,
|
$toIds = parseSprintIds($item['to'] ?? '');
|
||||||
'from' => parseSprintIds($item['from'] ?? ''),
|
|
||||||
'to' => 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)) {
|
if (empty($changes)) {
|
||||||
// Поле "Sprint" не менялось: задача в этом спринте с момента создания.
|
// Поле "Sprint" не менялось: задача в этом спринте с момента создания.
|
||||||
$issueCreatedAt = !empty($issue['fields']['created']) ? strtotime($issue['fields']['created']) : false;
|
return true;
|
||||||
return $issueCreatedAt !== false && $issueCreatedAt <= $startTs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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", действовавшее на момент старта спринта.
|
if ($changeBeforeStart) {
|
||||||
$valueAtStart = null;
|
// Если добавили в спринт до начала спринта - то значит спринтовая
|
||||||
$hasPriorChange = false;
|
// Если нет, то значит удалили из спринта до старта
|
||||||
foreach ($changes as $change) {
|
return in_array($jiraSprintId, $changes[$changeBeforeStart]['to']);
|
||||||
if ($change['at'] <= $startTs) {
|
|
||||||
$valueAtStart = $change['to'];
|
|
||||||
$hasPriorChange = true;
|
|
||||||
} else {
|
|
||||||
// Первое изменение уже после старта: значение до него — это его "from".
|
|
||||||
if (!$hasPriorChange) {
|
|
||||||
$valueAtStart = $change['from'];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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