143 lines
5.7 KiB
PHP
143 lines
5.7 KiB
PHP
|
|
global$config; #!/usr/bin/env php
|
|||
|
|
<?php
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @var array $config
|
|||
|
|
* @var array $teams
|
|||
|
|
* @var string[] $holidays
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
use GuzzleHttp\Client;
|
|||
|
|
use GuzzleHttp\Exception\GuzzleException;
|
|||
|
|
use Nechaev\Reports\StatusHelper;
|
|||
|
|
|
|||
|
|
require __DIR__ . '/../init.php';
|
|||
|
|
require __DIR__ . '/functions.php';
|
|||
|
|
|
|||
|
|
// --- 1. Обработка параметров командной строки ---
|
|||
|
|
$options = getopt('', ['start-date:', 'end-date:']);
|
|||
|
|
|
|||
|
|
if (empty($options['start-date']) || empty($options['end-date'])) {
|
|||
|
|
echo "Использование: php jira_export.php --start-date=ГГГГ-ММ-ДД --end-date=ГГГГ-ММ-ДД\n";
|
|||
|
|
exit(1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$startDate = $options['start-date'];
|
|||
|
|
$endDate = $options['end-date'];
|
|||
|
|
|
|||
|
|
// Валидация формата дат
|
|||
|
|
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate) || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate)) {
|
|||
|
|
die("Ошибка: даты должны быть в формате ГГГГ-ММ-ДД\n");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Извлекаем настройки из конфига
|
|||
|
|
$jiraUrl = $config['jira']['url'];
|
|||
|
|
$apiToken = $config['jira']['apiToken'];
|
|||
|
|
$maxResults = $config['export']['maxResults'];
|
|||
|
|
|
|||
|
|
// --- 2. Инициализация клиента Guzzle ---
|
|||
|
|
$client = new Client([
|
|||
|
|
'base_uri' => $jiraUrl,
|
|||
|
|
'headers' => [
|
|||
|
|
'Authorization' => 'Bearer ' . $apiToken,
|
|||
|
|
'Accept' => 'application/json',
|
|||
|
|
],
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
foreach ($teams as $team => $teamData) {
|
|||
|
|
echo "Расчет для команды {$team}\n";
|
|||
|
|
$filename = "{$team}_tasks_{$startDate}_to_{$endDate}.csv";
|
|||
|
|
|
|||
|
|
// --- 3. Подготовка JQL-запроса ---
|
|||
|
|
// Запрос ищет задачи, у которых статус изменился на "Done" в указанный период.
|
|||
|
|
$jql = "project in (".$teamData['projects'].") AND issuetype in standardIssueTypes() AND issuetype not in (Epic, \"Admin work\") AND resolutiondate > \"{$startDate}\" AND resolutiondate < \"{$endDate}\"";
|
|||
|
|
|
|||
|
|
$params = [
|
|||
|
|
'jql' => $jql,
|
|||
|
|
'maxResults' => $maxResults,
|
|||
|
|
'fields' => 'issuetype,key,summary,assignee,resolution,aggregatetimeoriginalestimate,aggregateprogress,resolutiondate',
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// --- 4. Обработка пагинации ---
|
|||
|
|
// Если задач больше, чем maxResults, Jira вернет только часть.
|
|||
|
|
// Мы будем запрашивать страницы до тех пор, пока не получим все.
|
|||
|
|
$allIssues = [];
|
|||
|
|
$startAt = 0;
|
|||
|
|
$totalFetched = 0;
|
|||
|
|
|
|||
|
|
do {
|
|||
|
|
$response = $client->get('rest/api/2/search', [
|
|||
|
|
'query' => $params + ['startAt' => $startAt]
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
$data = json_decode($response->getBody(), true);
|
|||
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|||
|
|
throw new Exception("Ошибка декодирования JSON-ответа от Jira.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$issues = $data['issues'] ?? [];
|
|||
|
|
$allIssues = array_merge($allIssues, $issues);
|
|||
|
|
|
|||
|
|
// Обновляем счетчики для следующей итерации
|
|||
|
|
$totalFetched += count($issues);
|
|||
|
|
$startAt += count($issues);
|
|||
|
|
|
|||
|
|
echo "Получено задач: {$totalFetched} из {$data['total']}\n";
|
|||
|
|
|
|||
|
|
} while ($startAt < ($data['total'] ?? 0));
|
|||
|
|
|
|||
|
|
if (!empty($allIssues)) {
|
|||
|
|
// Убираем дубликаты, там где изменения не требуются
|
|||
|
|
$allIssues = filterByResolution($allIssues);
|
|||
|
|
|
|||
|
|
// Убираем задачи, исполнители на которых не исполнители
|
|||
|
|
$allIssues = filterByExecutors($allIssues, array_keys($teamData['executors']));
|
|||
|
|
|
|||
|
|
//file_put_contents($filename.".json", json_encode($allIssues, JSON_PRETTY_PRINT));die();
|
|||
|
|
|
|||
|
|
echo "Убрали лишнее, осталось ".count($allIssues)."\n";
|
|||
|
|
|
|||
|
|
$task = null;
|
|||
|
|
foreach ($allIssues as $i => $issue) {
|
|||
|
|
$statuses = getStatusForIssue($issue['key'], $client);
|
|||
|
|
//$allIssues[$i]['countStatuses'] = $statuses;
|
|||
|
|
$firstDateInStatus = array_map(function($value) {
|
|||
|
|
return is_array($value) && count($value) > 1 ? $value[1] : null;
|
|||
|
|
}, $statuses);
|
|||
|
|
|
|||
|
|
$countStatus = array_map(function($value) {
|
|||
|
|
return is_array($value) && count($value) > 1 ? $value[0] : null;
|
|||
|
|
}, $statuses);
|
|||
|
|
|
|||
|
|
$task = new \Nechaev\Reports\Task(
|
|||
|
|
$issue["fields"]["issuetype"]["name"],
|
|||
|
|
$issue['key'],
|
|||
|
|
$issue["fields"]["summary"],
|
|||
|
|
$issue["fields"]["assignee"]["name"],
|
|||
|
|
$issue["fields"]["aggregatetimeoriginalestimate"],
|
|||
|
|
$issue["fields"]["aggregateprogress"]["progress"],
|
|||
|
|
StatusHelper::getDataByStatus($firstDateInStatus, StatusHelper::getInWorkStatuses()),
|
|||
|
|
$issue["fields"]["resolutiondate"],
|
|||
|
|
StatusHelper::getDataByStatus($countStatus, StatusHelper::getInReviewStatuses()),
|
|||
|
|
StatusHelper::getDataByStatus($countStatus, StatusHelper::getInTestingStatuses()),
|
|||
|
|
);
|
|||
|
|
file_put_contents($filename, $task, FILE_APPEND | LOCK_EX);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
// --- 5. Сохранение в файл ---
|
|||
|
|
|
|||
|
|
|
|||
|
|
echo "Данные успешно сохранены в файл: {$filename}\n";
|
|||
|
|
} else {
|
|||
|
|
echo "За указанный период завершенных задач не найдено.\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} catch (GuzzleException | Exception $e) {
|
|||
|
|
echo "Ошибка: " . $e->getMessage() . "\n";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|