From ad3b399b96a88fd552f62a7ff34535b24481f57c Mon Sep 17 00:00:00 2001
From: Matthieu Napoli
Date: Tue, 9 Sep 2014 22:33:31 +0700
Subject: [PATCH] Added the "opened issues" badge
---
app/config/routes.php | 2 +-
.../Controller/BadgeController.php | 63 +++++++++---
.../Application/Controller/HomeController.php | 20 +++-
.../Controller/ProjectController.php | 1 +
.../Application/Twig/TwigExtension.php | 4 +-
src/Maintained/Application/View/home.twig | 99 +++++--------------
src/Maintained/Application/View/project.twig | 19 +++-
src/Maintained/Issue.php | 8 ++
src/Maintained/Statistics/Statistics.php | 9 ++
.../Statistics/StatisticsComputer.php | 23 +++++
10 files changed, 155 insertions(+), 93 deletions(-)
diff --git a/app/config/routes.php b/app/config/routes.php
index 6a58a01..189d66f 100644
--- a/app/config/routes.php
+++ b/app/config/routes.php
@@ -14,7 +14,7 @@ return [
'controller' => ProjectController::class,
],
'badge' => [
- 'pattern' => '/badge/{user}/{repository}.svg',
+ 'pattern' => '/badge/{badge}/{user}/{repository}.svg',
'controller' => BadgeController::class,
],
];
diff --git a/src/Maintained/Application/Controller/BadgeController.php b/src/Maintained/Application/Controller/BadgeController.php
index 18f96fd..ea246b3 100644
--- a/src/Maintained/Application/Controller/BadgeController.php
+++ b/src/Maintained/Application/Controller/BadgeController.php
@@ -4,7 +4,9 @@ namespace Maintained\Application\Controller;
use DI\Annotation\Inject;
use Github\Exception\ApiLimitExceedException;
+use Maintained\Statistics\Statistics;
use Maintained\Statistics\StatisticsProvider;
+use PUGX\Poser\Image;
use PUGX\Poser\Poser;
/**
@@ -12,6 +14,9 @@ use PUGX\Poser\Poser;
*/
class BadgeController
{
+ const BADGE_RESOLUTION = 'resolution';
+ const BADGE_OPEN_RATIO = 'opened';
+
const COLOR_OK = '18bc9c';
const COLOR_WARNING = 'CC9237';
const COLOR_DANGER = '9C3838';
@@ -28,22 +33,20 @@ class BadgeController
*/
private $poser;
- public function __invoke($user, $repository)
+ public function __invoke($badge, $user, $repository)
{
try {
$statistics = $this->statisticsProvider->getStatistics($user, $repository);
- $days = $statistics->resolutionTime->toDays();
-
- if ($days < 2) {
- $color = self::COLOR_OK;
- } elseif ($days < 8) {
- $color = self::COLOR_WARNING;
- } else {
- $color = self::COLOR_DANGER;
+ switch ($badge) {
+ case self::BADGE_OPEN_RATIO:
+ $badge = $this->createOpenRatioBadge($statistics);
+ break;
+ case self::BADGE_RESOLUTION:
+ default:
+ $badge = $this->createResolutionBadge($statistics);
+ break;
}
-
- $badge = $this->poser->generate('resolution', $statistics->resolutionTime, $color, 'svg');
} catch (ApiLimitExceedException $e) {
$badge = $this->poser->generate('github-api', 'limit', self::COLOR_DANGER, 'svg');
}
@@ -51,4 +54,42 @@ class BadgeController
header('Content-type: image/svg+xml');
echo $badge;
}
+
+ /**
+ * @param Statistics $statistics
+ * @return Image
+ */
+ private function createResolutionBadge(Statistics $statistics)
+ {
+ $days = $statistics->resolutionTime->toDays();
+
+ if ($days < 2) {
+ $color = self::COLOR_OK;
+ } elseif ($days < 8) {
+ $color = self::COLOR_WARNING;
+ } else {
+ $color = self::COLOR_DANGER;
+ }
+
+ return $this->poser->generate('resolution', $statistics->resolutionTime->formatShort(), $color, 'svg');
+ }
+
+ /**
+ * @param Statistics $statistics
+ * @return Image
+ */
+ private function createOpenRatioBadge(Statistics $statistics)
+ {
+ $ratio = $statistics->openIssuesRatio;
+
+ if ($ratio < 0.1) {
+ $color = self::COLOR_OK;
+ } elseif ($ratio < 0.2) {
+ $color = self::COLOR_WARNING;
+ } else {
+ $color = self::COLOR_DANGER;
+ }
+
+ return $this->poser->generate('opened issues', round($ratio * 100) . '%', $color, 'svg');
+ }
}
diff --git a/src/Maintained/Application/Controller/HomeController.php b/src/Maintained/Application/Controller/HomeController.php
index 0640314..167c8fd 100644
--- a/src/Maintained/Application/Controller/HomeController.php
+++ b/src/Maintained/Application/Controller/HomeController.php
@@ -2,13 +2,29 @@
namespace Maintained\Application\Controller;
+use Twig_Environment;
+
/**
* @author Matthieu Napoli
*/
class HomeController
{
- public function __invoke(\Twig_Environment $twig)
+ public function __invoke(Twig_Environment $twig)
{
- echo $twig->render('home.twig');
+ $demoProjects = [
+ 'symfony/symfony' => 'Symfony',
+ 'Ocramius/ProxyManager' => 'ProxyManager',
+ 'Atlantic18/DoctrineExtensions' => 'DoctrineExtensions',
+ 'schmittjoh/serializer' => 'JMS Serializer',
+ 'PHPOffice/PHPExcel' => 'PHPExcel',
+ 'composer/composer' => 'Composer',
+ 'Behat/Behat' => 'Behat',
+ 'kriswallsmith/assetic' => 'Assetic',
+ 'sebastianbergmann/phpunit' => 'PHPUnit',
+ ];
+
+ echo $twig->render('home.twig', [
+ 'projects' => $demoProjects,
+ ]);
}
}
diff --git a/src/Maintained/Application/Controller/ProjectController.php b/src/Maintained/Application/Controller/ProjectController.php
index 6995276..bd2d208 100644
--- a/src/Maintained/Application/Controller/ProjectController.php
+++ b/src/Maintained/Application/Controller/ProjectController.php
@@ -28,6 +28,7 @@ class ProjectController
echo $this->twig->render('project.twig', [
'repository' => $user . '/' . $repository,
'resolutionTime' => $statistics->resolutionTime,
+ 'openedIssues' => round($statistics->openIssuesRatio * 100),
]);
}
}
diff --git a/src/Maintained/Application/Twig/TwigExtension.php b/src/Maintained/Application/Twig/TwigExtension.php
index 5a3065f..d193200 100644
--- a/src/Maintained/Application/Twig/TwigExtension.php
+++ b/src/Maintained/Application/Twig/TwigExtension.php
@@ -24,10 +24,10 @@ class TwigExtension extends Twig_Extension
];
}
- public function generateBadge($repository)
+ public function generateBadge($repository, $type = 'resolution')
{
return <<
+
HTML;
}
}
diff --git a/src/Maintained/Application/View/home.twig b/src/Maintained/Application/View/home.twig
index b6188ee..da45d3c 100644
--- a/src/Maintained/Application/View/home.twig
+++ b/src/Maintained/Application/View/home.twig
@@ -66,13 +66,19 @@
Resolution time
- Median time needed to close an issue.
+ Median time needed to close an issue or pull request.
+
+
+ Issues or PR opened by the collaborators are ignored.
Closed percentage
- Percentage of closed issues.
+ Percentage of closed issues and pull requests.
+
+
+ Issues or PR opened by the collaborators are ignored.
@@ -84,80 +90,21 @@
-
-
-
-
-
-
-
-
-
-
-
- Behat
-
-
- {{ badge('Behat/Behat') }}
-
-
-
-
- Assetic
-
-
- {{ badge('kriswallsmith/assetic') }}
-
-
-
-
- PHPUnit
-
-
- {{ badge('sebastianbergmann/phpunit') }}
-
+ {% for repository, name in projects %}
+
+
+
+ {{ name }}
+
+
+
+ {{ badge(repository, 'resolution') }}
+
+
+ {{ badge(repository, 'opened') }}
+
+
+ {% endfor %}
diff --git a/src/Maintained/Application/View/project.twig b/src/Maintained/Application/View/project.twig
index e10d098..6ddc9c8 100644
--- a/src/Maintained/Application/View/project.twig
+++ b/src/Maintained/Application/View/project.twig
@@ -23,7 +23,24 @@
{{ badge(repository) }}
- The resolution time is the median time an issue stays open.
+ The resolution time is the median time an issue or pull request stays open.
+
+
+ Issues or PR opened by the collaborators are ignored.
+
+
+
+ Open issues:
+ {{ openedIssues }}%
+
+
+ {{ badge(repository, 'opened') }}
+
+
+ The percentage of open issues and pull requests.
+
+
+ Issues or PR opened by the collaborators are ignored.
diff --git a/src/Maintained/Issue.php b/src/Maintained/Issue.php
index 8431fa9..9bdcfbe 100644
--- a/src/Maintained/Issue.php
+++ b/src/Maintained/Issue.php
@@ -78,4 +78,12 @@ class Issue
{
return $this->openedFor;
}
+
+ /**
+ * @return bool
+ */
+ public function isOpen()
+ {
+ return $this->open;
+ }
}
diff --git a/src/Maintained/Statistics/Statistics.php b/src/Maintained/Statistics/Statistics.php
index 5446cc6..3bec3b4 100644
--- a/src/Maintained/Statistics/Statistics.php
+++ b/src/Maintained/Statistics/Statistics.php
@@ -12,7 +12,16 @@ use Maintained\TimeInterval;
class Statistics
{
/**
+ * Average time for closing an issue.
+ *
* @var TimeInterval
*/
public $resolutionTime;
+
+ /**
+ * Ratio of open issues.
+ *
+ * @var float
+ */
+ public $openIssuesRatio;
}
diff --git a/src/Maintained/Statistics/StatisticsComputer.php b/src/Maintained/Statistics/StatisticsComputer.php
index 49b96f1..5e15464 100644
--- a/src/Maintained/Statistics/StatisticsComputer.php
+++ b/src/Maintained/Statistics/StatisticsComputer.php
@@ -27,10 +27,12 @@ class StatisticsComputer implements StatisticsProvider
{
$issues = $this->fetchIssues($user, $repository);
$collaborators = $this->fetchCollaborators($user, $repository);
+
$issues = $this->excludeIssuesCreatedByCollaborators($issues, $collaborators);
$statistics = new Statistics();
$statistics->resolutionTime = $this->computeResolutionTime($issues);
+ $statistics->openIssuesRatio = $this->computeOpenIssueRatio($issues);
return $statistics;
}
@@ -48,6 +50,27 @@ class StatisticsComputer implements StatisticsProvider
return new TimeInterval($this->median($durations));
}
+ /**
+ * @param Issue[] $issues
+ * @return float
+ */
+ private function computeOpenIssueRatio(array $issues)
+ {
+ if (empty($issues)) {
+ return 0;
+ }
+
+ $openIssues = 0;
+
+ foreach ($issues as $issue) {
+ if ($issue->isOpen()) {
+ $openIssues++;
+ }
+ }
+
+ return $openIssues / count($issues);
+ }
+
/**
* @param Issue[] $issues
* @param string[] $collaborators