dashboard widgets are configurable via config (#269)
This commit is contained in:
@@ -76,21 +76,11 @@ class ActivityRepository extends AbstractRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* Return global statistic data for all user.
|
||||
*
|
||||
* @return ActivityStatistic
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
* @return int
|
||||
*/
|
||||
public function getGlobalStatistics()
|
||||
public function countActivity()
|
||||
{
|
||||
$countAll = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(a.id) FROM ' . Activity::class . ' a')
|
||||
->getSingleScalarResult();
|
||||
|
||||
$stats = new ActivityStatistic();
|
||||
$stats->setCount($countAll);
|
||||
|
||||
return $stats;
|
||||
return $this->count([]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,21 +32,11 @@ class CustomerRepository extends AbstractRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* Return statistic data for all customer.
|
||||
*
|
||||
* @return CustomerStatistic
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
* @return int
|
||||
*/
|
||||
public function getGlobalStatistics()
|
||||
public function countCustomer()
|
||||
{
|
||||
$countAll = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(c.id) FROM ' . Customer::class . ' c')
|
||||
->getSingleScalarResult();
|
||||
|
||||
$stats = new CustomerStatistic();
|
||||
$stats->setCount($countAll);
|
||||
|
||||
return $stats;
|
||||
return $this->count([]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,21 +31,11 @@ class ProjectRepository extends AbstractRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* Return statistic data for all user.
|
||||
*
|
||||
* @return ProjectStatistic
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
* @return int
|
||||
*/
|
||||
public function getGlobalStatistics()
|
||||
public function countProject()
|
||||
{
|
||||
$countAll = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(p.id) FROM ' . Project::class . ' p')
|
||||
->getSingleScalarResult();
|
||||
|
||||
$stats = new ProjectStatistic();
|
||||
$stats->setCount($countAll);
|
||||
|
||||
return $stats;
|
||||
return $this->count([]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,7 +14,6 @@ use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Model\Statistic\Month;
|
||||
use App\Model\Statistic\Year;
|
||||
use App\Model\TimesheetGlobalStatistic;
|
||||
use App\Model\TimesheetStatistic;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use DateTime;
|
||||
@@ -22,11 +21,15 @@ use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
|
||||
/**
|
||||
* Class TimesheetRepository
|
||||
*/
|
||||
class TimesheetRepository extends AbstractRepository
|
||||
{
|
||||
public const STATS_QUERY_DURATION = 'duration';
|
||||
public const STATS_QUERY_RATE = 'rate';
|
||||
public const STATS_QUERY_USER = 'users';
|
||||
public const STATS_QUERY_AMOUNT = 'amount';
|
||||
public const STATS_QUERY_ACTIVE = 'active';
|
||||
public const STATS_QUERY_MONTHLY = 'monthly';
|
||||
|
||||
/**
|
||||
* @param Timesheet $entry
|
||||
* @return bool
|
||||
@@ -67,45 +70,91 @@ class TimesheetRepository extends AbstractRepository
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @param User|null $user
|
||||
* @return int|mixed
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getStatistic(string $type, ?DateTime $begin, ?DateTime $end, ?User $user)
|
||||
{
|
||||
switch ($type) {
|
||||
case self::STATS_QUERY_ACTIVE:
|
||||
return count($this->getActiveEntries($user));
|
||||
break;
|
||||
case self::STATS_QUERY_MONTHLY:
|
||||
return $this->getMonthlyStats($user, $begin, $end);
|
||||
break;
|
||||
case self::STATS_QUERY_DURATION:
|
||||
$what = 'SUM(t.duration)';
|
||||
break;
|
||||
case self::STATS_QUERY_RATE:
|
||||
$what = 'SUM(t.rate)';
|
||||
break;
|
||||
case self::STATS_QUERY_USER:
|
||||
$what = 'COUNT(DISTINCT(t.user))';
|
||||
break;
|
||||
case self::STATS_QUERY_AMOUNT:
|
||||
$what = 'COUNT(t.id)';
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid query type: ' . $type);
|
||||
}
|
||||
|
||||
return $this->queryTimeRange($what, $begin, $end, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $select
|
||||
* @param User|null $user
|
||||
* @return \Doctrine\ORM\QueryBuilder
|
||||
* @return int
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
protected function queryThisMonth($select, User $user = null)
|
||||
protected function queryThisMonth($select, ?User $user)
|
||||
{
|
||||
$end = new DateTime('last day of this month');
|
||||
$end->setTime(23, 59, 59);
|
||||
$begin = new DateTime('first day of this month');
|
||||
$begin->setTime(0, 0, 0);
|
||||
$begin = new DateTime('first day of this month 00:00:00');
|
||||
$end = new DateTime('last day of this month 23:59:59');
|
||||
|
||||
return $this->queryTimeRange($select, $begin, $end, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $select
|
||||
* @param DateTime $begin
|
||||
* @param DateTime $end
|
||||
* @param string $select
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @param User|null $user
|
||||
* @return \Doctrine\ORM\QueryBuilder
|
||||
* @return int|mixed
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
protected function queryTimeRange($select, DateTime $begin, DateTime $end, User $user = null)
|
||||
protected function queryTimeRange(string $select, ?DateTime $begin, ?DateTime $end, ?User $user)
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select($select)
|
||||
->from(Timesheet::class, 't')
|
||||
->where($qb->expr()->gt('t.begin', ':from'))
|
||||
->andWhere($qb->expr()->lt('t.end', ':to'))
|
||||
->setParameter('from', $begin, Type::DATETIME)
|
||||
->setParameter('to', $end, Type::DATETIME);
|
||||
->from(Timesheet::class, 't');
|
||||
|
||||
if (!empty($begin)) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->gt('t.begin', ':from'))
|
||||
->setParameter('from', $begin, Type::DATETIME);
|
||||
}
|
||||
|
||||
if (!empty($end)) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->lt('t.end', ':to'))
|
||||
->setParameter('to', $end, Type::DATETIME);
|
||||
}
|
||||
|
||||
if (null !== $user) {
|
||||
$qb->andWhere('t.user = :user')
|
||||
->setParameter('user', $user);
|
||||
}
|
||||
|
||||
return $qb;
|
||||
$result = $qb->getQuery()->getSingleScalarResult();
|
||||
|
||||
return empty($result) ? 0 : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,24 +166,11 @@ class TimesheetRepository extends AbstractRepository
|
||||
*/
|
||||
public function getUserStatistics(User $user)
|
||||
{
|
||||
$durationTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT SUM(t.duration) FROM ' . Timesheet::class . ' t WHERE t.user = :user')
|
||||
->setParameter('user', $user)
|
||||
->getSingleScalarResult();
|
||||
$recordsTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(t.id) FROM ' . Timesheet::class . ' t WHERE t.user = :user')
|
||||
->setParameter('user', $user)
|
||||
->getSingleScalarResult();
|
||||
$rateTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT SUM(t.rate) FROM ' . Timesheet::class . ' t WHERE t.user = :user')
|
||||
->setParameter('user', $user)
|
||||
->getSingleScalarResult();
|
||||
$amountMonth = $this->queryThisMonth('SUM(t.rate)', $user)
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
$durationMonth = $this->queryThisMonth('SUM(t.duration)', $user)
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
$durationTotal = $this->getStatistic(self::STATS_QUERY_DURATION, null, null, $user);
|
||||
$recordsTotal = $this->getStatistic(self::STATS_QUERY_AMOUNT, null, null, $user);
|
||||
$rateTotal = $this->getStatistic(self::STATS_QUERY_RATE, null, null, $user);
|
||||
$amountMonth = $this->queryThisMonth('SUM(t.rate)', $user);
|
||||
$durationMonth = $this->queryThisMonth('SUM(t.duration)', $user);
|
||||
$firstEntry = $this->getEntityManager()
|
||||
->createQuery('SELECT MIN(t.begin) FROM ' . Timesheet::class . ' t WHERE t.user = :user')
|
||||
->setParameter('user', $user)
|
||||
@@ -155,23 +191,40 @@ class TimesheetRepository extends AbstractRepository
|
||||
* Returns an array of Year statistics.
|
||||
*
|
||||
* @param User|null $user
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @return Year[]
|
||||
*/
|
||||
public function getMonthlyStats(User $user = null)
|
||||
public function getMonthlyStats(User $user = null, ?DateTime $begin = null, ?DateTime $end = null)
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select('SUM(t.rate) as rate, SUM(t.duration) as duration, MONTH(t.begin) as month, YEAR(t.begin) as year')
|
||||
->from(Timesheet::class, 't')
|
||||
->where($qb->expr()->gt('t.begin', '0'))
|
||||
->andWhere($qb->expr()->isNotNull('t.end'))
|
||||
->where($qb->expr()->gt('t.begin', ':from'))
|
||||
;
|
||||
|
||||
if (!empty($begin)) {
|
||||
$qb->setParameter('from', $begin, Type::DATETIME);
|
||||
} else {
|
||||
$qb->setParameter('from', 0);
|
||||
}
|
||||
|
||||
if (!empty($end)) {
|
||||
$qb->andWhere($qb->expr()->lt('t.end', ':to'))
|
||||
->setParameter('to', $end, Type::DATETIME);
|
||||
} else {
|
||||
$qb->andWhere($qb->expr()->isNotNull('t.end'));
|
||||
}
|
||||
|
||||
$qb
|
||||
->orderBy('year', 'DESC')
|
||||
->addOrderBy('month', 'ASC')
|
||||
->groupBy('year')
|
||||
->addGroupBy('month');
|
||||
|
||||
if (null !== $user) {
|
||||
$qb->where('t.user = :user')
|
||||
$qb->andWhere('t.user = :user')
|
||||
->setParameter('user', $user);
|
||||
}
|
||||
|
||||
@@ -198,52 +251,6 @@ class TimesheetRepository extends AbstractRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch statistic data for all user.
|
||||
*
|
||||
* @return TimesheetGlobalStatistic
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getGlobalStatistics()
|
||||
{
|
||||
$durationTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT SUM(t.duration) FROM ' . Timesheet::class . ' t')
|
||||
->getSingleScalarResult();
|
||||
$recordsTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(t.id) FROM ' . Timesheet::class . ' t')
|
||||
->getSingleScalarResult();
|
||||
$rateTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT SUM(t.rate) FROM ' . Timesheet::class . ' t')
|
||||
->getSingleScalarResult();
|
||||
$userTotal = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(DISTINCT(t.user)) FROM ' . Timesheet::class . ' t')
|
||||
->getSingleScalarResult();
|
||||
$activeNow = $this->getActiveEntries();
|
||||
$amountMonth = $this->queryThisMonth('SUM(t.rate)')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
$durationMonth = $this->queryThisMonth('SUM(t.duration)')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
$activeMonth = $this->queryThisMonth('COUNT(DISTINCT(t.user))')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
|
||||
$stats = new TimesheetGlobalStatistic();
|
||||
$stats->setAmountTotal($rateTotal);
|
||||
$stats->setDurationTotal($durationTotal);
|
||||
$stats->setActiveTotal($userTotal);
|
||||
$stats->setActiveCurrently(count($activeNow));
|
||||
$stats->setActiveThisMonth($activeMonth);
|
||||
$stats->setAmountThisMonth($amountMonth);
|
||||
$stats->setDurationThisMonth($durationMonth);
|
||||
$stats->setRecordsTotal($recordsTotal);
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO replace me by a findByQuery() call
|
||||
*
|
||||
* @param User $user
|
||||
* @return Timesheet[]|null
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Model\UserStatistic;
|
||||
use App\Repository\Query\UserQuery;
|
||||
use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;
|
||||
|
||||
@@ -29,21 +28,11 @@ class UserRepository extends AbstractRepository implements UserLoaderInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Return statistic data for all user.
|
||||
*
|
||||
* @return UserStatistic
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
* @return int
|
||||
*/
|
||||
public function getGlobalStatistics()
|
||||
public function countUser()
|
||||
{
|
||||
$countAll = $this->getEntityManager()
|
||||
->createQuery('SELECT COUNT(u.id) FROM ' . User::class . ' u')
|
||||
->getSingleScalarResult();
|
||||
|
||||
$stats = new UserStatistic();
|
||||
$stats->setTotalAmount($countAll);
|
||||
|
||||
return $stats;
|
||||
return $this->count([]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
82
src/Repository/WidgetRepository.php
Normal file
82
src/Repository/WidgetRepository.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Model\Widget;
|
||||
|
||||
class WidgetRepository
|
||||
{
|
||||
/**
|
||||
* @var TimesheetRepository
|
||||
*/
|
||||
protected $repository;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $widgets = [];
|
||||
|
||||
/**
|
||||
* @param TimesheetRepository $repository
|
||||
* @param array $widgets
|
||||
*/
|
||||
public function __construct(TimesheetRepository $repository, array $widgets)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->widgets = $widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $name)
|
||||
{
|
||||
return isset($this->widgets[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param User|null $user
|
||||
* @return Widget
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function get(string $name, ?User $user)
|
||||
{
|
||||
if (!$this->has($name)) {
|
||||
throw new \InvalidArgumentException('Cannot find widget: ' . $name);
|
||||
}
|
||||
|
||||
$widget = $this->widgets[$name];
|
||||
|
||||
$begin = !empty($widget['begin']) ? new \DateTime($widget['begin']) : null;
|
||||
$end = !empty($widget['end']) ? new \DateTime($widget['end']) : null;
|
||||
$theUser = $widget['user'] ? $user : null;
|
||||
$type = isset($widget['type']) ? $widget['type'] : Widget::TYPE_COUNTER;
|
||||
|
||||
$data = $this->repository->getStatistic($widget['query'], $begin, $end, $theUser);
|
||||
|
||||
$model = new Widget($widget['title'], $data);
|
||||
$model
|
||||
->setColor($widget['color'])
|
||||
->setIcon($widget['icon'])
|
||||
->setType($type)
|
||||
;
|
||||
|
||||
if ($widget['query'] == TimesheetRepository::STATS_QUERY_DURATION) {
|
||||
$model->setDataType(Widget::DATA_TYPE_DURATION);
|
||||
} elseif ($widget['query'] == TimesheetRepository::STATS_QUERY_AMOUNT) {
|
||||
$model->setDataType(Widget::DATA_TYPE_MONEY);
|
||||
}
|
||||
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user