Last active
December 10, 2015 11:38
-
-
Save ohader/4428305 to your computer and use it in GitHub Desktop.
Forge Issue Analysis
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Usage: | |
* php forge-analyse.php <date-from> [<date-until>] | |
* php forge-analyse.php 2012-12-24 2012-12-31 | |
* | |
* @author Oliver Hader <[email protected]> | |
* @license GPL v2 or any later version | |
* @see http://forge.typo3.org/projects/typo3v4-core/wiki/FriendlyGhost | |
*/ | |
class AnalyseIssues { | |
const ISSUES_Limit = 50; | |
const ISSUES_AllUrl = 'http://forge.typo3.org/projects/typo3v4-core/issues.json?status_id=*&limit=%d&offset=%d&sort=updated_on:desc'; | |
const ISSUES_DetailUrl = 'http://forge.typo3.org/projects/typo3v4-core/issues/%d.json?include=journals,changesets'; | |
const DATETIME_Addition = 'T00:00:00+01:00'; | |
const STATUS_Patched = '8'; | |
const STATUS_Resolved = '3'; | |
/** | |
* @var DateTime | |
*/ | |
protected $from; | |
/** | |
* @var DateTime | |
*/ | |
protected $until; | |
/** | |
* @var boolean | |
*/ | |
protected $verbose = FALSE; | |
/** | |
* @var array | |
*/ | |
protected $issues = array( | |
'created' => array(), | |
'patched' => array(), | |
'resolved' => array(), | |
); | |
/** | |
* @var array | |
*/ | |
protected $projects = array(); | |
public function __construct(DateTime $from, DateTime $until = NULL) { | |
$this->from = $from; | |
$this->until = $until; | |
} | |
public function setVerbose($verbose) { | |
$this->verbose = (bool) $verbose; | |
} | |
public function execute() { | |
$this->getIssues(); | |
$this->render(); | |
} | |
protected function render() { | |
$issuesInProjects = array(); | |
foreach ($this->issues as $status => $issues) { | |
foreach ($issues as $issue) { | |
$projectId = $issue['project']['id']; | |
if (!isset($this->projects[$projectId])) { | |
$this->projects[$projectId] = $issue['project']['name']; | |
$issuesInProjects[$projectId] = array( | |
'created' => array(), | |
'patched' => array(), | |
'resolved' => array(), | |
); | |
} | |
$issuesInProjects[$projectId][$status][] = $issue; | |
} | |
} | |
ksort($issuesInProjects); | |
echo PHP_EOL; | |
echo str_repeat('=', 70) . PHP_EOL; | |
echo 'Issues from ' . $this->from->format('Y-m-d') . ($this->until ? ' until ' . $this->until->format('Y-m-d') : '') . PHP_EOL; | |
echo str_repeat('=', 70) . PHP_EOL; | |
foreach ($issuesInProjects as $projectId => $issuesByStatus) { | |
echo PHP_EOL; | |
echo $this->projects[$projectId] . PHP_EOL; | |
echo str_repeat('-', 70) . PHP_EOL; | |
foreach ($issuesByStatus as $status => $issues) { | |
echo ' * ' . $status . ': ' . count($issues) . PHP_EOL; | |
} | |
} | |
} | |
protected function getIssues() { | |
$offset = 0; | |
do { | |
$continue = FALSE; | |
$json = file_get_contents($this->getIssuesAllUrl(self::ISSUES_Limit, $offset)); | |
$data = json_decode($json, TRUE); | |
if (empty($data['issues']) || !is_array($data['issues'])) { | |
break; | |
} | |
foreach ($data['issues'] as $issue) { | |
$id = $issue['id']; | |
$createdOn = new DateTime($issue['created_on']); | |
$updatedOn = new DateTime($issue['updated_on']); | |
$issue['updated_on'] = $updatedOn; | |
$issue['created_on'] = $createdOn; | |
// Collect if created in date range | |
if ($createdOn >= $this->from && ($this->until == NULL || $createdOn <= $this->until)) { | |
$this->issues['created'][$id] = $issue; | |
$continue = TRUE; | |
} | |
// Don't limit to range, since update might be ongoing, | |
// thus only use recent status on the accordant range | |
if ($updatedOn >= $this->from) { | |
$recentStatus = $this->getRecentStatus($id); | |
if ($recentStatus === self::STATUS_Resolved) { | |
$this->issues['resolved'][$id] = $issue; | |
$continue = TRUE; | |
} | |
if ($recentStatus === self::STATUS_Patched) { | |
$this->issues['patched'][$id] = $issue; | |
$continue = TRUE; | |
} | |
} | |
} | |
$offset += self::ISSUES_Limit; | |
} while ($continue); | |
} | |
/** | |
* @param integer $id | |
* @return NULL|array | |
*/ | |
protected function getRecentStatus($id) { | |
$recentStatus = NULL; | |
$json = file_get_contents($this->getIssuesDetailUrl($id)); | |
$data = json_decode($json, TRUE); | |
if (!empty($data['issue']['journals']) && is_array($data['issue']['journals'])) { | |
foreach ($data['issue']['journals'] as $journal) { | |
$createdOn = new DateTime($journal['created_on']); | |
if ($createdOn >= $this->from && ($this->until == NULL || $createdOn <= $this->until)) { | |
$statusDetail = $this->findInJournalDetails($journal['details'], 'status_id'); | |
if ($statusDetail !== NULL && !empty($statusDetail['new_value'])) { | |
$recentStatus = $statusDetail['new_value']; | |
} | |
} | |
} | |
} | |
return $recentStatus; | |
} | |
protected function findInJournalDetails(array $details, $name) { | |
$result = NULL; | |
foreach ($details as $detail) { | |
if ($detail['name'] === $name) { | |
$result = $detail; | |
break; | |
} | |
} | |
return $result; | |
} | |
protected function getIssuesAllUrl($limit, $offset) { | |
$url = sprintf(self::ISSUES_AllUrl, $limit, $offset); | |
if ($this->verbose) { | |
echo '[FETCH] ' . $url . PHP_EOL; | |
} | |
return $url; | |
} | |
protected function getIssuesDetailUrl($id) { | |
$url = sprintf(self::ISSUES_DetailUrl, $id); | |
if ($this->verbose) { | |
echo '[FETCH] ' . $url . PHP_EOL; | |
} | |
return $url; | |
} | |
} | |
if (empty($argv[1])) { | |
echo 'Usage:' . PHP_EOL; | |
echo $argv[0] . ' <date-from> [<date-until>]' . PHP_EOL; | |
echo 'Example:' . PHP_EOL; | |
echo $argv[0] . ' 2012-12-24' . PHP_EOL; | |
} else { | |
$from = new DateTime($argv[1] . AnalyseIssues::DATETIME_Addition); | |
if (!empty($argv[2])) { | |
$until = new DateTime($argv[2] . AnalyseIssues::DATETIME_Addition); | |
} else { | |
$until = NULL; | |
} | |
$analyse = new AnalyseIssues($from, $until); | |
$analyse->setVerbose(FALSE); | |
$analyse->execute(); | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
http://forge.typo3.org/projects/typo3v4-core/ is now http://forge.typo3.org/projects/typo3cms-core/