Правка багов рассчета процента закрытия спринта

master
an.nechaev 2026-06-18 11:51:34 +04:00
parent 4b7828a47d
commit 1ded77c5dc
3 changed files with 45 additions and 25 deletions

View File

@ -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,

View File

@ -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');

View File

@ -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']);
}
/**