Project page

This commit is contained in:
Matthieu Napoli
2014-09-09 18:49:35 +07:00
parent fa44efe905
commit 4ed90181ec
13 changed files with 219 additions and 14 deletions

View File

@@ -8,6 +8,9 @@ use Interop\Container\ContainerInterface;
use Maintained\Badge\BadgeGenerator;
use Maintained\Badge\BadgeProvider;
use Maintained\Badge\CachedBadgeProvider;
use Maintained\Statistics\CachedStatisticsProvider;
use Maintained\Statistics\StatisticsComputer;
use Maintained\Statistics\StatisticsProvider;
use PUGX\Poser\Poser;
use PUGX\Poser\Render\SvgRender;
use function DI\factory;
@@ -39,8 +42,12 @@ return [
}),
Cache::class => object(FilesystemCache::class)
->constructor(__DIR__ . '/../../app/cache/badges'),
->constructor(__DIR__ . '/../../app/cache/app')
->method('setNamespace', 'Maintained'),
BadgeProvider::class => object(CachedBadgeProvider::class)
->constructorParameter('wrapped', link(BadgeGenerator::class)),
StatisticsProvider::class => object(CachedStatisticsProvider::class)
->constructorParameter('wrapped', link(StatisticsComputer::class)),
];

View File

@@ -2,13 +2,18 @@
use Maintained\Application\Controller\BadgeController;
use Maintained\Application\Controller\HomeController;
use Maintained\Application\Controller\ProjectController;
return [
'home' => [
'home' => [
'pattern' => '/',
'controller' => HomeController::class,
],
'badge' => [
'project' => [
'pattern' => '/project/{user}/{repository}',
'controller' => ProjectController::class,
],
'badge' => [
'pattern' => '/badge/{user}/{repository}.svg',
'controller' => BadgeController::class,
],

View File

@@ -0,0 +1,33 @@
<?php
namespace Maintained\Application\Controller;
use Maintained\Statistics\StatisticsProvider;
/**
* @author Matthieu Napoli <matthieu@mnapoli.fr>
*/
class ProjectController
{
/**
* @Inject
* @var \Twig_Environment
*/
private $twig;
/**
* @Inject
* @var StatisticsProvider
*/
private $statisticsProvider;
public function __invoke($user, $repository)
{
$statistics = $this->statisticsProvider->getStatistics($user, $repository);
echo $this->twig->render('project.twig', [
'repository' => $user . '/' . $repository,
'resolutionTime' => $statistics->resolutionTime,
]);
}
}

View File

@@ -32,25 +32,27 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#page-top">Is it maintained?</a>
<a class="navbar-brand" href="/#page-top">Is it maintained?</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li class="hidden">
<a href="#page-top"></a>
<a href="/#page-top"></a>
</li>
<li class="page-scroll">
<a href="#check">Check a project</a>
<a href="/#check">Check a project</a>
</li>
<li class="page-scroll">
<a href="#metrics">Metrics</a>
<a href="/#metrics">Metrics</a>
</li>
<li class="page-scroll">
<a href="#about">About</a>
<a href="/#about">About</a>
</li>
<li class="page-scroll">
<a href="https://github.com/mnapoli/Maintained"><i class="fa fa-github"></i> GitHub</a>
<li>
<a href="https://github.com/mnapoli/Maintained">
<i class="fa fa-github"></i> GitHub
</a>
</li>
</ul>
</div>

View File

@@ -0,0 +1,28 @@
{% extends 'layout.twig' %}
{% block content %}
<section>
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h2>{{ repository }}</h2>
<hr class="star-primary">
</div>
</div>
<div class="row">
<div class="col-lg-8 col-lg-offset-2 text-center">
<p>
Resolution time: <strong>{{ resolutionTime.formatLong() }}</strong>
</p>
</div>
</div>
</div>
</section>
{% endblock %}

View File

@@ -11,7 +11,7 @@ use Doctrine\Common\Cache\Cache;
*/
class CachedBadgeProvider implements BadgeProvider
{
const CACHE_NAMESPACE = 'maintained/';
const CACHE_NAMESPACE = 'badge/';
/**
* @var Cache

View File

@@ -55,7 +55,7 @@ class Diagnostic
}
/**
* @return float
* @return TimeInterval
*/
public function computeAverage()
{
@@ -67,6 +67,9 @@ class Diagnostic
return new TimeInterval($average);
}
/**
* @return TimeInterval
*/
public function computeMedian()
{
$durations = array_map(function (Issue $issue) {

View File

@@ -0,0 +1,46 @@
<?php
namespace Maintained\Statistics;
use Doctrine\Common\Cache\Cache;
/**
* Wraps and caches another provider.
*
* @author Matthieu Napoli <matthieu@mnapoli.fr>
*/
class CachedStatisticsProvider implements StatisticsProvider
{
const CACHE_NAMESPACE = 'statistics/';
/**
* @var Cache
*/
private $cache;
/**
* @var StatisticsProvider
*/
private $wrapped;
public function __construct(Cache $cache, StatisticsProvider $wrapped)
{
$this->cache = $cache;
$this->wrapped = $wrapped;
}
public function getStatistics($user, $repository)
{
$key = self::CACHE_NAMESPACE . $user . '/' . $repository;
$statistics = $this->cache->fetch($key);
if ($statistics === false) {
$statistics = $this->wrapped->getStatistics($user, $repository);
$this->cache->save($key, $statistics);
}
return $statistics;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Maintained\Statistics;
use Maintained\TimeInterval;
/**
* Statistics of an open source project.
*
* @author Matthieu Napoli <matthieu@mnapoli.fr>
*/
class Statistics
{
/**
* @var TimeInterval
*/
public $resolutionTime;
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Maintained\Statistics;
use Maintained\Diagnostic;
/**
* Computes statistics.
*
* @author Matthieu Napoli <matthieu@mnapoli.fr>
*/
class StatisticsComputer implements StatisticsProvider
{
public function getStatistics($user, $repository)
{
$diagnostic = new Diagnostic($user . '/' . $repository);
$statistics = new Statistics();
$statistics->resolutionTime = $diagnostic->computeMedian();
return $statistics;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Maintained\Statistics;
/**
* Provides statistics.
*
* @author Matthieu Napoli <matthieu@mnapoli.fr>
*/
interface StatisticsProvider
{
/**
* @param string $user
* @param string $repository
* @return Statistics
*/
public function getStatistics($user, $repository);
}

View File

@@ -54,6 +54,25 @@ class TimeInterval
}
}
/**
* Format to a long display string.
*
* @return string
*/
public function formatLong()
{
switch (true) {
case $this->seconds < self::TO_MINUTE:
return sprintf('%d second(s)', $this->seconds);
case $this->seconds < self::TO_HOUR:
return sprintf('%d minute(s)', $this->toMinutes());
case $this->seconds < self::TO_DAY:
return sprintf('%d hour(s)', $this->toHours());
default:
return sprintf('%d day(s)', $this->toDays());
}
}
/**
* @return int
*/

View File

@@ -27,6 +27,7 @@ body {
line-height: 1.42857143;
color: #2c3e50;
background-color: #ffffff;
padding-top: 50px;
}
p {
@@ -107,7 +108,7 @@ header {
}
header .container {
padding-top: 100px;
padding-top: 40px;
padding-bottom: 50px;
}
@@ -130,8 +131,10 @@ header .intro-text .skills {
}
@media(min-width:768px) {
body {
padding-top: 100px;
}
header .container {
padding-top: 140px;
padding-bottom: 100px;
}