|
|
<?php |
|
|
|
|
|
namespace Kanboard\Model; |
|
|
|
|
|
use DateTime; |
|
|
use Kanboard\Core\Base; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SubtaskTimeTrackingModel extends Base |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const TABLE = 'subtask_time_tracking'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function getTimerQuery($user_id) |
|
|
{ |
|
|
$sql = $this->db |
|
|
->table(self::TABLE) |
|
|
->columns('start') |
|
|
->eq($this->db->escapeIdentifier('user_id',self::TABLE), $user_id) |
|
|
->eq($this->db->escapeIdentifier('end',self::TABLE), 0) |
|
|
->eq($this->db->escapeIdentifier('subtask_id',self::TABLE), SubtaskModel::TABLE.'.id') |
|
|
->limit(1) |
|
|
->buildSelectQuery(); |
|
|
|
|
|
|
|
|
$sql = substr_replace($sql, $user_id, strpos($sql, '?'), 1); |
|
|
$sql = substr_replace($sql, 0, strpos($sql, '?'), 1); |
|
|
$sql = substr_replace($sql, SubtaskModel::TABLE.'.id', strpos($sql, '?'), 1); |
|
|
return $sql; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function getUserQuery($user_id) |
|
|
{ |
|
|
return $this->db |
|
|
->table(self::TABLE) |
|
|
->columns( |
|
|
self::TABLE.'.id', |
|
|
self::TABLE.'.subtask_id', |
|
|
self::TABLE.'.end', |
|
|
self::TABLE.'.start', |
|
|
self::TABLE.'.time_spent', |
|
|
SubtaskModel::TABLE.'.task_id', |
|
|
SubtaskModel::TABLE.'.title AS subtask_title', |
|
|
TaskModel::TABLE.'.title AS task_title', |
|
|
TaskModel::TABLE.'.project_id', |
|
|
TaskModel::TABLE.'.color_id' |
|
|
) |
|
|
->join(SubtaskModel::TABLE, 'id', 'subtask_id') |
|
|
->join(TaskModel::TABLE, 'id', 'task_id', SubtaskModel::TABLE) |
|
|
->eq(self::TABLE.'.user_id', $user_id); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function getTaskQuery($task_id) |
|
|
{ |
|
|
return $this->db |
|
|
->table(self::TABLE) |
|
|
->columns( |
|
|
self::TABLE.'.id', |
|
|
self::TABLE.'.subtask_id', |
|
|
self::TABLE.'.end', |
|
|
self::TABLE.'.start', |
|
|
self::TABLE.'.time_spent', |
|
|
self::TABLE.'.user_id', |
|
|
SubtaskModel::TABLE.'.task_id', |
|
|
SubtaskModel::TABLE.'.title AS subtask_title', |
|
|
TaskModel::TABLE.'.project_id', |
|
|
UserModel::TABLE.'.username', |
|
|
UserModel::TABLE.'.name AS user_fullname' |
|
|
) |
|
|
->join(SubtaskModel::TABLE, 'id', 'subtask_id') |
|
|
->join(TaskModel::TABLE, 'id', 'task_id', SubtaskModel::TABLE) |
|
|
->join(UserModel::TABLE, 'id', 'user_id', self::TABLE) |
|
|
->eq(TaskModel::TABLE.'.id', $task_id); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function getUserTimesheet($user_id) |
|
|
{ |
|
|
return $this->db |
|
|
->table(self::TABLE) |
|
|
->eq('user_id', $user_id) |
|
|
->findAll(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function hasTimer($subtask_id, $user_id) |
|
|
{ |
|
|
return $this->db->table(self::TABLE)->eq('subtask_id', $subtask_id)->eq('user_id', $user_id)->eq('end', 0)->exists(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function toggleTimer($subtask_id, $user_id, $status) |
|
|
{ |
|
|
if ($this->configModel->get('subtask_time_tracking') == 1) { |
|
|
if ($status == SubtaskModel::STATUS_INPROGRESS) { |
|
|
return $this->subtaskTimeTrackingModel->logStartTime($subtask_id, $user_id); |
|
|
} elseif ($status == SubtaskModel::STATUS_DONE) { |
|
|
return $this->subtaskTimeTrackingModel->logEndTime($subtask_id, $user_id); |
|
|
} |
|
|
} |
|
|
|
|
|
return false; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function logStartTime($subtask_id, $user_id) |
|
|
{ |
|
|
return |
|
|
! $this->hasTimer($subtask_id, $user_id) && |
|
|
$this->db |
|
|
->table(self::TABLE) |
|
|
->insert(array('subtask_id' => $subtask_id, 'user_id' => $user_id, 'start' => time(), 'end' => 0)); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function logEndTime($subtask_id, $user_id) |
|
|
{ |
|
|
$time_spent = $this->getTimeSpent($subtask_id, $user_id); |
|
|
|
|
|
if ($time_spent > 0) { |
|
|
$this->updateSubtaskTimeSpent($subtask_id, $time_spent); |
|
|
} |
|
|
|
|
|
return $this->db |
|
|
->table(self::TABLE) |
|
|
->eq('subtask_id', $subtask_id) |
|
|
->eq('user_id', $user_id) |
|
|
->eq('end', 0) |
|
|
->update(array( |
|
|
'end' => time(), |
|
|
'time_spent' => $time_spent, |
|
|
)); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function getTimeSpent($subtask_id, $user_id) |
|
|
{ |
|
|
$hook = 'model:subtask-time-tracking:calculate:time-spent'; |
|
|
$start_time = $this->db |
|
|
->table(self::TABLE) |
|
|
->eq('subtask_id', $subtask_id) |
|
|
->eq('user_id', $user_id) |
|
|
->eq('end', 0) |
|
|
->findOneColumn('start'); |
|
|
|
|
|
if (empty($start_time)) { |
|
|
return 0; |
|
|
} |
|
|
|
|
|
$end = new DateTime; |
|
|
$start = new DateTime; |
|
|
$start->setTimestamp($start_time); |
|
|
|
|
|
if ($this->hook->exists($hook)) { |
|
|
return $this->hook->first($hook, array( |
|
|
'user_id' => $user_id, |
|
|
'start' => $start, |
|
|
'end' => $end, |
|
|
)); |
|
|
} |
|
|
|
|
|
return $this->dateParser->getHours($start, $end); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function updateSubtaskTimeSpent($subtask_id, $time_spent) |
|
|
{ |
|
|
$subtask = $this->subtaskModel->getById($subtask_id); |
|
|
|
|
|
return $this->subtaskModel->update(array( |
|
|
'id' => $subtask['id'], |
|
|
'time_spent' => $subtask['time_spent'] + $time_spent, |
|
|
'task_id' => $subtask['task_id'], |
|
|
), false); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function updateTaskTimeTracking($task_id) |
|
|
{ |
|
|
$values = $this->calculateSubtaskTime($task_id); |
|
|
|
|
|
return $this->db |
|
|
->table(TaskModel::TABLE) |
|
|
->eq('id', $task_id) |
|
|
->update($values); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function calculateSubtaskTime($task_id) |
|
|
{ |
|
|
return $this->db |
|
|
->table(SubtaskModel::TABLE) |
|
|
->eq('task_id', $task_id) |
|
|
->columns( |
|
|
'SUM(time_spent) AS time_spent', |
|
|
'SUM(time_estimated) AS time_estimated' |
|
|
) |
|
|
->findOne(); |
|
|
} |
|
|
} |
|
|
|