diff --git a/src/Maintained/Application/View/project.twig b/src/Maintained/Application/View/project.twig
index ef003bf..40223cc 100644
--- a/src/Maintained/Application/View/project.twig
+++ b/src/Maintained/Application/View/project.twig
@@ -16,7 +16,11 @@
- Resolution time: {{ resolutionTime.formatLong() }}
+ Resolution time:
+ {{ resolutionTime.formatLong() }}
+
+
+ The resolution time is the median time an issue stays open.
diff --git a/src/Maintained/Statistics/StatisticsComputer.php b/src/Maintained/Statistics/StatisticsComputer.php
index 0973e50..49b96f1 100644
--- a/src/Maintained/Statistics/StatisticsComputer.php
+++ b/src/Maintained/Statistics/StatisticsComputer.php
@@ -2,7 +2,9 @@
namespace Maintained\Statistics;
-use Maintained\Diagnostic;
+use Github\Client;
+use Maintained\Issue;
+use Maintained\TimeInterval;
/**
* Computes statistics.
@@ -11,13 +13,109 @@ use Maintained\Diagnostic;
*/
class StatisticsComputer implements StatisticsProvider
{
+ /**
+ * @var Client
+ */
+ private $github;
+
+ public function __construct(Client $github)
+ {
+ $this->github = $github;
+ }
+
public function getStatistics($user, $repository)
{
- $diagnostic = new Diagnostic($user . '/' . $repository);
+ $issues = $this->fetchIssues($user, $repository);
+ $collaborators = $this->fetchCollaborators($user, $repository);
+ $issues = $this->excludeIssuesCreatedByCollaborators($issues, $collaborators);
$statistics = new Statistics();
- $statistics->resolutionTime = $diagnostic->computeMedian();
+ $statistics->resolutionTime = $this->computeResolutionTime($issues);
return $statistics;
}
+
+ /**
+ * @param Issue[] $issues
+ * @return TimeInterval
+ */
+ private function computeResolutionTime(array $issues)
+ {
+ $durations = array_map(function (Issue $issue) {
+ return $issue->getOpenedFor()->toSeconds();
+ }, $issues);
+
+ return new TimeInterval($this->median($durations));
+ }
+
+ /**
+ * @param Issue[] $issues
+ * @param string[] $collaborators
+ * @return Issue[]
+ */
+ private function excludeIssuesCreatedByCollaborators(array $issues, array $collaborators)
+ {
+ return array_filter($issues, function (Issue $issue) use ($collaborators) {
+ return !in_array($issue->getAuthor(), $collaborators);
+ });
+ }
+
+ /**
+ * @param float[] $array
+ * @return float
+ */
+ private function median(array $array) {
+ $count = count($array);
+
+ if ($count == 0) {
+ return 0;
+ }
+
+ sort($array, SORT_NUMERIC);
+
+ $middleIndex = (int) floor($count / 2);
+
+ // Handle the even case by averaging the middle 2 items
+ if ($count % 2 == 0) {
+ return ($array[$middleIndex] + $array[$middleIndex - 1]) / 2;
+ }
+
+ return $array[$middleIndex];
+ }
+
+ /**
+ * @param string $user
+ * @param string $repository
+ * @return Issue[]
+ */
+ private function fetchIssues($user, $repository)
+ {
+ /** @var \GitHub\Api\Issue $issueApi */
+ $issueApi = $this->github->api('issue');
+ $issues = $issueApi->all($user, $repository, ['state' => 'all']);
+
+ $issues = array_map(function (array $data) {
+ return Issue::fromArray($data);
+ }, $issues);
+
+ return $issues;
+ }
+
+ /**
+ * @param string $user
+ * @param string $repository
+ * @return string[]
+ */
+ private function fetchCollaborators($user, $repository)
+ {
+ /** @var \GitHub\Api\Repo $repositoryApi */
+ $repositoryApi = $this->github->api('repo');
+ $collaborators = $repositoryApi->collaborators()->all($user, $repository);
+
+ $collaborators = array_map(function ($user) {
+ return $user['login'];
+ }, $collaborators);
+
+ return $collaborators;
+ }
}
diff --git a/src/Maintained/TimeInterval.php b/src/Maintained/TimeInterval.php
index 4ab9e4e..972bfb6 100644
--- a/src/Maintained/TimeInterval.php
+++ b/src/Maintained/TimeInterval.php
@@ -46,7 +46,7 @@ class TimeInterval
case $this->seconds < self::TO_MINUTE:
return sprintf('%d s', $this->seconds);
case $this->seconds < self::TO_HOUR:
- return sprintf('%d m', $this->toMinutes());
+ return sprintf('%d min', $this->toMinutes());
case $this->seconds < self::TO_DAY:
return sprintf('%d h', $this->toHours());
default: