respect users timezone when populating working-time dashboard widget (#988)

This commit is contained in:
Kevin Papst
2019-07-30 23:57:20 +02:00
committed by GitHub
parent 3f51437569
commit 654ffda454
4 changed files with 92 additions and 32 deletions

View File

@@ -275,37 +275,86 @@ class TimesheetRepository extends EntityRepository
* @param User|null $user * @param User|null $user
* @return mixed * @return mixed
*/ */
public function getDailyData(DateTime $begin, DateTime $end, ?User $user = null) protected function getDailyData(DateTime $begin, DateTime $end, ?User $user = null)
{ {
$qb = $this->getEntityManager()->createQueryBuilder(); $query = new TimesheetQuery();
$query
->setBegin($begin)
->setEnd($end)
->setUser($user)
->setState(TimesheetQuery::STATE_STOPPED)
;
$timesheets = $this->getTimesheetsForQuery($query);
$qb $results = [];
->addSelect('SUM(t.rate) as rate') /** @var Timesheet $result */
->addSelect('SUM(t.duration) as duration') foreach ($timesheets as $result) {
->addSelect('MONTH(t.begin) as month') $timezone = new \DateTimeZone($result->getTimezone());
->addSelect('YEAR(t.begin) as year') /** @var \DateTime $beginTmp */
->addSelect('DAY(t.begin) as day') $beginTmp = $result->getBegin();
->from(Timesheet::class, 't') $beginTmp->setTimezone($timezone);
->andWhere($qb->expr()->gte('t.begin', ':from')) /** @var DateTime $endTmp */
->setParameter('from', $begin, Type::DATETIME) $endTmp = $result->getEnd();
->andWhere($qb->expr()->lte('t.end', ':to')) $endTmp->setTimezone($timezone);
->setParameter('to', $end, Type::DATETIME); $dateKeyEnd = $endTmp->format('Ymd');
if (null !== $user) { do {
$qb->andWhere('t.user = :user') $dateKey = $beginTmp->format('Ymd');
->setParameter('user', $user);
if (!isset($results[$dateKey])) {
$results[$dateKey] = [
'rate' => 0,
'duration' => 0,
'month' => $beginTmp->format('n'),
'year' => $beginTmp->format('Y'),
'day' => $beginTmp->format('j'),
'details' => []
];
} }
$qb if ($dateKey !== $dateKeyEnd) {
->addGroupBy('year') $newDateBegin = clone $beginTmp;
->addGroupBy('month') $newDateBegin->add(new \DateInterval('P1D'));
->addGroupBy('day') $newDateBegin->setTime(0, 0, 0);
->addOrderBy('year', 'DESC') } else {
->addOrderBy('month', 'ASC') $newDateBegin = clone $endTmp;
->addOrderBy('day', 'ASC') }
;
return $qb->getQuery()->execute(); $duration = $newDateBegin->getTimestamp() - $beginTmp->getTimestamp();
$durationPercent = $duration / $result->getDuration();
$rate = $result->getRate() * $durationPercent;
$results[$dateKey]['rate'] += $rate;
$results[$dateKey]['duration'] += $duration;
$detailsId = $result->getProject()->getCustomer()->getId() . '_' . $result->getProject()->getId();
if (!isset($results[$dateKey]['details'][$detailsId])) {
$results[$dateKey]['details'][$detailsId] = [
'project' => $result->getProject(),
'activity' => $result->getActivity(),
'duration' => 0,
'rate' => 0,
];
$results[$dateKey]['details'][$detailsId]['duration'] += $duration;
$results[$dateKey]['details'][$detailsId]['rate'] += $rate;
}
$beginTmp = $newDateBegin;
if ((int) $end->format('Ymd') < (int) $newDateBegin->format('Ymd')) {
break 1;
}
} while ($dateKey !== $dateKeyEnd);
}
ksort($results);
foreach ($results as $key => $value) {
$results[$key]['details'] = array_values($results[$key]['details']);
}
$results = array_values($results);
return $results;
} }
/** /**
@@ -334,7 +383,9 @@ class TimesheetRepository extends EntityRepository
foreach ($results as $statRow) { foreach ($results as $statRow) {
$dateTime = new DateTime(); $dateTime = new DateTime();
$dateTime->setDate($statRow['year'], $statRow['month'], $statRow['day']); $dateTime->setDate($statRow['year'], $statRow['month'], $statRow['day']);
$days[$dateTime->format('Ymd')] = new Day($dateTime, (int) $statRow['duration'], (float) $statRow['rate']); $dateTime->setTime(0, 0, 0);
$day = new Day($dateTime, (int) $statRow['duration'], (float) $statRow['rate']);
$days[$dateTime->format('Ymd')] = $day;
} }
ksort($days); ksort($days);

View File

@@ -11,6 +11,7 @@ namespace App\Widget\Type;
use App\Repository\TimesheetRepository; use App\Repository\TimesheetRepository;
use App\Security\CurrentUser; use App\Security\CurrentUser;
use App\Timesheet\UserDateTimeFactory;
use DateTime; use DateTime;
class DailyWorkingTimeChart extends SimpleWidget class DailyWorkingTimeChart extends SimpleWidget
@@ -21,10 +22,15 @@ class DailyWorkingTimeChart extends SimpleWidget
* @var TimesheetRepository * @var TimesheetRepository
*/ */
protected $repository; protected $repository;
/**
* @var UserDateTimeFactory
*/
private $dateTimeFactory;
public function __construct(TimesheetRepository $repository, CurrentUser $user) public function __construct(TimesheetRepository $repository, CurrentUser $user, UserDateTimeFactory $dateTime)
{ {
$this->repository = $repository; $this->repository = $repository;
$this->dateTimeFactory = $dateTime;
$this->setId('DailyWorkingTimeChart'); $this->setId('DailyWorkingTimeChart');
$this->setTitle('stats.yourWorkingHours'); $this->setTitle('stats.yourWorkingHours');
$this->setOptions([ $this->setOptions([
@@ -57,8 +63,8 @@ class DailyWorkingTimeChart extends SimpleWidget
$options = $this->getOptions($options); $options = $this->getOptions($options);
$user = $options['user']; $user = $options['user'];
$begin = new DateTime($options['begin']); $begin = new DateTime($options['begin'], $this->dateTimeFactory->getTimezone());
$end = new DateTime($options['end']); $end = new DateTime($options['end'], $this->dateTimeFactory->getTimezone());
return $this->repository->getDailyStats($user, $begin, $end); return $this->repository->getDailyStats($user, $begin, $end);
} }

View File

@@ -35,7 +35,7 @@ document.addEventListener('kimai.initialized', function() {
data: { data: {
labels: [ labels: [
{% for day in data -%} {% for day in data -%}
moment('{{ day.day|date_format(constant('\DateTime::ISO8601')) }}').format('ll') moment('{{ day.day|date_format('Y-m-d') }}').format('ll')
{% if not loop.last %},{% endif -%} {% if not loop.last %},{% endif -%}
{%- endfor %} {%- endfor %}
], ],

View File

@@ -13,6 +13,7 @@ use App\Entity\User;
use App\Model\Statistic\Day; use App\Model\Statistic\Day;
use App\Repository\TimesheetRepository; use App\Repository\TimesheetRepository;
use App\Security\CurrentUser; use App\Security\CurrentUser;
use App\Tests\Mocks\Security\UserDateTimeFactoryFactory;
use App\Widget\Type\AbstractWidgetType; use App\Widget\Type\AbstractWidgetType;
use App\Widget\Type\DailyWorkingTimeChart; use App\Widget\Type\DailyWorkingTimeChart;
use App\Widget\Type\SimpleWidget; use App\Widget\Type\SimpleWidget;
@@ -31,8 +32,9 @@ class DailyWorkingTimeChartTest extends TestCase
$repository = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock(); $repository = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock();
$user = $this->getMockBuilder(CurrentUser::class)->disableOriginalConstructor()->setMethods(['getUser'])->getMock(); $user = $this->getMockBuilder(CurrentUser::class)->disableOriginalConstructor()->setMethods(['getUser'])->getMock();
$user->expects($this->once())->method('getUser')->willReturn(new User()); $user->expects($this->once())->method('getUser')->willReturn(new User());
$mockFactory = new UserDateTimeFactoryFactory($this);
return new DailyWorkingTimeChart($repository, $user); return new DailyWorkingTimeChart($repository, $user, $mockFactory->create('Europe/Berlin'));
} }
public function testExtendsSimpleWidget() public function testExtendsSimpleWidget()
@@ -106,8 +108,9 @@ class DailyWorkingTimeChartTest extends TestCase
}); });
$user = $this->getMockBuilder(CurrentUser::class)->disableOriginalConstructor()->setMethods(['getUser'])->getMock(); $user = $this->getMockBuilder(CurrentUser::class)->disableOriginalConstructor()->setMethods(['getUser'])->getMock();
$user->expects($this->once())->method('getUser')->willReturn((new User())->setUsername('tralalala')); $user->expects($this->once())->method('getUser')->willReturn((new User())->setUsername('tralalala'));
$mockFactory = new UserDateTimeFactoryFactory($this);
$sut = new DailyWorkingTimeChart($repository, $user); $sut = new DailyWorkingTimeChart($repository, $user, $mockFactory->create('Europe/Berlin'));
$data = $sut->getData([]); $data = $sut->getData([]);
self::assertCount(7, $data); self::assertCount(7, $data);
foreach ($data as $statObj) { foreach ($data as $statObj) {