1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/kanboard_ynh.git synced 2024-09-03 19:36:17 +02:00
kanboard_ynh/sources/app/Model/Timetable.php

357 lines
9.3 KiB
PHP
Raw Normal View History

2015-04-21 17:56:16 +02:00
<?php
namespace Model;
use DateTime;
use DateInterval;
/**
* Timetable
*
* @package model
* @author Frederic Guillot
*/
class Timetable extends Base
{
/**
* User time slots
*
* @access private
* @var array
*/
private $day;
private $week;
private $overtime;
private $timeoff;
/**
* Get a set of events by using the intersection between the timetable and the time tracking data
*
* @access public
* @param integer $user_id
* @param array $events Time tracking data
* @param string $start ISO8601 date
* @param string $end ISO8601 date
* @return array
*/
public function calculateEventsIntersect($user_id, array $events, $start, $end)
{
$start_dt = new DateTime($start);
$start_dt->setTime(0, 0);
$end_dt = new DateTime($end);
$end_dt->setTime(23, 59);
$timetable = $this->calculate($user_id, $start_dt, $end_dt);
// The user has no timetable
if (empty($this->week)) {
return $events;
}
$results = array();
foreach ($events as $event) {
$results = array_merge($results, $this->calculateEventIntersect($event, $timetable));
}
return $results;
}
/**
* Get a serie of events based on the timetable and the provided event
*
* @access public
* @param array $event
* @param array $timetable
* @return array
*/
public function calculateEventIntersect(array $event, array $timetable)
{
$events = array();
foreach ($timetable as $slot) {
$start_ts = $slot[0]->getTimestamp();
$end_ts = $slot[1]->getTimestamp();
if ($start_ts > $event['end']) {
break;
}
if ($event['start'] <= $start_ts) {
$event['start'] = $start_ts;
}
if ($event['start'] >= $start_ts && $event['start'] <= $end_ts) {
if ($event['end'] >= $end_ts) {
$events[] = array_merge($event, array('end' => $end_ts));
}
else {
$events[] = $event;
break;
}
}
}
return $events;
}
/**
* Calculate effective worked hours by taking into consideration the timetable
*
* @access public
* @param integer $user_id
* @param \DateTime $start
* @param \DateTime $end
* @return float
*/
public function calculateEffectiveDuration($user_id, DateTime $start, DateTime $end)
{
$end_timetable = clone($end);
$end_timetable->setTime(23, 59);
$timetable = $this->calculate($user_id, $start, $end_timetable);
$found_start = false;
$hours = 0;
// The user has no timetable
if (empty($this->week)) {
return $this->dateParser->getHours($start, $end);
}
foreach ($timetable as $slot) {
$isStartSlot = $this->dateParser->withinDateRange($start, $slot[0], $slot[1]);
$isEndSlot = $this->dateParser->withinDateRange($end, $slot[0], $slot[1]);
// Start and end are within the same time slot
if ($isStartSlot && $isEndSlot) {
return $this->dateParser->getHours($start, $end);
}
// We found the start slot
if (! $found_start && $isStartSlot) {
$found_start = true;
$hours = $this->dateParser->getHours($start, $slot[1]);
}
else if ($found_start) {
// We found the end slot
if ($isEndSlot) {
$hours += $this->dateParser->getHours($slot[0], $end);
break;
}
else {
// Sum hours of the intermediate time slots
$hours += $this->dateParser->getHours($slot[0], $slot[1]);
}
}
}
// The start date was not found in regular hours so we get the nearest time slot
if (! empty($timetable) && ! $found_start) {
$slot = $this->findClosestTimeSlot($start, $timetable);
if ($start < $slot[0]) {
return $this->calculateEffectiveDuration($user_id, $slot[0], $end);
}
}
return $hours;
}
/**
* Find the nearest time slot
*
* @access public
* @param DateTime $date
* @param array $timetable
* @return array
*/
public function findClosestTimeSlot(DateTime $date, array $timetable)
{
$values = array();
foreach ($timetable as $slot) {
$t1 = abs($slot[0]->getTimestamp() - $date->getTimestamp());
$t2 = abs($slot[1]->getTimestamp() - $date->getTimestamp());
$values[] = min($t1, $t2);
}
asort($values);
return $timetable[key($values)];
}
/**
* Get the timetable for a user for a given date range
*
* @access public
* @param integer $user_id
* @param \DateTime $start
* @param \DateTime $end
* @return array
*/
public function calculate($user_id, DateTime $start, DateTime $end)
{
$timetable = array();
$this->day = $this->timetableDay->getByUser($user_id);
$this->week = $this->timetableWeek->getByUser($user_id);
$this->overtime = $this->timetableExtra->getByUserAndDate($user_id, $start->format('Y-m-d'), $end->format('Y-m-d'));
$this->timeoff = $this->timetableOff->getByUserAndDate($user_id, $start->format('Y-m-d'), $end->format('Y-m-d'));
for ($today = clone($start); $today <= $end; $today->add(new DateInterval('P1D'))) {
$week_day = $today->format('N');
$timetable = array_merge($timetable, $this->getWeekSlots($today, $week_day));
$timetable = array_merge($timetable, $this->getOvertimeSlots($today, $week_day));
}
return $timetable;
}
/**
* Return worked time slots for the given day
*
* @access public
* @param \DateTime $today
* @param string $week_day
* @return array
*/
public function getWeekSlots(DateTime $today, $week_day)
{
$slots = array();
$dayoff = $this->getDayOff($today);
if (! empty($dayoff) && $dayoff['all_day'] == 1) {
return array();
}
foreach ($this->week as $slot) {
if ($week_day == $slot['day']) {
$slots = array_merge($slots, $this->getDayWorkSlots($slot, $dayoff, $today));
}
}
return $slots;
}
/**
* Get the overtime time slots for the given day
*
* @access public
* @param \DateTime $today
* @param string $week_day
* @return array
*/
public function getOvertimeSlots(DateTime $today, $week_day)
{
$slots = array();
foreach ($this->overtime as $slot) {
$day = new DateTime($slot['date']);
if ($week_day == $day->format('N')) {
if ($slot['all_day'] == 1) {
$slots = array_merge($slots, $this->getDaySlots($today));
}
else {
$slots[] = $this->getTimeSlot($slot, $day);
}
}
}
return $slots;
}
/**
* Get worked time slots and remove time off
*
* @access public
* @param array $slot
* @param array $dayoff
* @param \DateTime $today
* @return array
*/
public function getDayWorkSlots(array $slot, array $dayoff, DateTime $today)
{
$slots = array();
if (! empty($dayoff) && $dayoff['start'] < $slot['end']) {
if ($dayoff['start'] > $slot['start']) {
$slots[] = $this->getTimeSlot(array('end' => $dayoff['start']) + $slot, $today);
}
if ($dayoff['end'] < $slot['end']) {
$slots[] = $this->getTimeSlot(array('start' => $dayoff['end']) + $slot, $today);
}
}
else {
$slots[] = $this->getTimeSlot($slot, $today);
}
return $slots;
}
/**
* Get regular day work time slots
*
* @access public
* @param \DateTime $today
* @return array
*/
public function getDaySlots(DateTime $today)
{
$slots = array();
foreach ($this->day as $day) {
$slots[] = $this->getTimeSlot($day, $today);
}
return $slots;
}
/**
* Get the start and end time slot for a given day
*
* @access public
* @param array $slot
* @param \DateTime $today
* @return array
*/
public function getTimeSlot(array $slot, DateTime $today)
{
$date = $today->format('Y-m-d');
return array(
new DateTime($date.' '.$slot['start']),
new DateTime($date.' '.$slot['end']),
);
}
/**
* Return day off time slot
*
* @access public
* @param \DateTime $today
* @return array
*/
public function getDayOff(DateTime $today)
{
foreach ($this->timeoff as $day) {
if ($day['date'] === $today->format('Y-m-d')) {
return $day;
}
}
return array();
}
}