3 final class PhrequentUserTimeQuery
4 extends PhabricatorCursorPagedPolicyAwareQuery
{
6 const ORDER_ID_ASC
= 0;
7 const ORDER_ID_DESC
= 1;
8 const ORDER_STARTED_ASC
= 2;
9 const ORDER_STARTED_DESC
= 3;
10 const ORDER_ENDED_ASC
= 4;
11 const ORDER_ENDED_DESC
= 5;
20 private $ended = self
::ENDED_ALL
;
22 private $needPreemptingEvents;
24 public function withIDs(array $ids) {
29 public function withUserPHIDs(array $user_phids) {
30 $this->userPHIDs
= $user_phids;
34 public function withObjectPHIDs(array $object_phids) {
35 $this->objectPHIDs
= $object_phids;
39 public function withEnded($ended) {
40 $this->ended
= $ended;
44 public function setOrder($order) {
46 case self
::ORDER_ID_ASC
:
47 $this->setOrderVector(array('-id'));
49 case self
::ORDER_ID_DESC
:
50 $this->setOrderVector(array('id'));
52 case self
::ORDER_STARTED_ASC
:
53 $this->setOrderVector(array('-start', '-id'));
55 case self
::ORDER_STARTED_DESC
:
56 $this->setOrderVector(array('start', 'id'));
58 case self
::ORDER_ENDED_ASC
:
59 $this->setOrderVector(array('-end', '-id'));
61 case self
::ORDER_ENDED_DESC
:
62 $this->setOrderVector(array('end', 'id'));
65 throw new Exception(pht('Unknown order "%s".', $order));
71 public function needPreemptingEvents($need_events) {
72 $this->needPreemptingEvents
= $need_events;
76 protected function buildWhereClause(AphrontDatabaseConnection
$conn) {
79 if ($this->ids
!== null) {
86 if ($this->userPHIDs
!== null) {
93 if ($this->objectPHIDs
!== null) {
96 'objectPHID IN (%Ls)',
100 switch ($this->ended
) {
101 case self
::ENDED_ALL
:
103 case self
::ENDED_YES
:
106 'dateEnded IS NOT NULL');
111 'dateEnded IS NULL');
114 throw new Exception(pht("Unknown ended '%s'!", $this->ended
));
117 $where[] = $this->buildPagingClause($conn);
119 return $this->formatWhereClause($conn, $where);
122 public function getOrderableColumns() {
123 return parent
::getOrderableColumns() +
array(
125 'column' => 'dateStarted',
129 'column' => 'dateEnded',
136 protected function newPagingMapFromPartialObject($object) {
138 'id' => (int)$object->getID(),
139 'start' => (int)$object->getDateStarted(),
140 'end' => (int)$object->getDateEnded(),
144 protected function loadPage() {
145 $usertime = new PhrequentUserTime();
146 $conn = $usertime->establishConnection('r');
150 'SELECT usertime.* FROM %T usertime %Q %Q %Q',
151 $usertime->getTableName(),
152 $this->buildWhereClause($conn),
153 $this->buildOrderClause($conn),
154 $this->buildLimitClause($conn));
156 return $usertime->loadAllFromArray($data);
159 protected function didFilterPage(array $page) {
160 if ($this->needPreemptingEvents
) {
161 $usertime = new PhrequentUserTime();
162 $conn_r = $usertime->establishConnection('r');
165 foreach ($page as $event) {
166 $preempt[] = qsprintf(
169 (dateStarted BETWEEN %d AND %d) AND
170 (dateEnded IS NULL OR dateEnded > %d))',
171 $event->getUserPHID(),
172 $event->getDateStarted(),
173 nonempty($event->getDateEnded(), PhabricatorTime
::getNow()),
174 $event->getDateStarted());
177 $preempting_events = queryfx_all(
179 'SELECT * FROM %T WHERE %LO ORDER BY dateStarted ASC, id ASC',
180 $usertime->getTableName(),
182 $preempting_events = $usertime->loadAllFromArray($preempting_events);
184 $preempting_events = mgroup($preempting_events, 'getUserPHID');
186 foreach ($page as $event) {
187 $e_start = $event->getDateStarted();
188 $e_end = $event->getDateEnded();
191 $user_events = idx($preempting_events, $event->getUserPHID(), array());
192 foreach ($user_events as $u_event) {
193 if ($u_event->getID() == $event->getID()) {
194 // Don't allow an event to preempt itself.
198 $u_start = $u_event->getDateStarted();
199 $u_end = $u_event->getDateEnded();
201 if ($u_start < $e_start) {
202 // This event started before our event started, so it's not
207 if ($u_start == $e_start) {
208 if ($u_event->getID() < $event->getID()) {
209 // This event started at the same time as our event started,
210 // but has a lower ID, so it's not preempting us.
215 if (($e_end !== null) && ($u_start > $e_end)) {
216 // Our event has ended, and this event started after it ended.
220 if (($u_end !== null) && ($u_end < $e_start)) {
221 // This event ended before our event began.
225 $select[] = $u_event;
228 $event->attachPreemptingEvents($select);
235 /* -( Helper Functions ) --------------------------------------------------- */
237 public static function getEndedSearchOptions() {
239 self
::ENDED_ALL
=> pht('All'),
240 self
::ENDED_NO
=> pht('No'),
241 self
::ENDED_YES
=> pht('Yes'),
245 public static function getOrderSearchOptions() {
247 self
::ORDER_STARTED_ASC
=> pht('by furthest start date'),
248 self
::ORDER_STARTED_DESC
=> pht('by nearest start date'),
249 self
::ORDER_ENDED_ASC
=> pht('by furthest end date'),
250 self
::ORDER_ENDED_DESC
=> pht('by nearest end date'),
254 public static function getUserTotalObjectsTracked(
255 PhabricatorUser
$user,
256 $limit = PHP_INT_MAX
) {
258 $usertime_dao = new PhrequentUserTime();
259 $conn = $usertime_dao->establishConnection('r');
261 $count = queryfx_one(
263 'SELECT COUNT(usertime.id) N FROM %T usertime '.
264 'WHERE usertime.userPHID = %s '.
265 'AND usertime.dateEnded IS NULL '.
267 $usertime_dao->getTableName(),
273 public static function isUserTrackingObject(
274 PhabricatorUser
$user,
277 $usertime_dao = new PhrequentUserTime();
278 $conn = $usertime_dao->establishConnection('r');
280 $count = queryfx_one(
282 'SELECT COUNT(usertime.id) N FROM %T usertime '.
283 'WHERE usertime.userPHID = %s '.
284 'AND usertime.objectPHID = %s '.
285 'AND usertime.dateEnded IS NULL',
286 $usertime_dao->getTableName(),
289 return $count['N'] > 0;
292 public static function getUserTimeSpentOnObject(
293 PhabricatorUser
$user,
296 $usertime_dao = new PhrequentUserTime();
297 $conn = $usertime_dao->establishConnection('r');
299 // First calculate all the time spent where the
300 // usertime blocks have ended.
301 $sum_ended = queryfx_one(
303 'SELECT SUM(usertime.dateEnded - usertime.dateStarted) N '.
305 'WHERE usertime.userPHID = %s '.
306 'AND usertime.objectPHID = %s '.
307 'AND usertime.dateEnded IS NOT NULL',
308 $usertime_dao->getTableName(),
312 // Now calculate the time spent where the usertime
313 // blocks have not yet ended.
314 $sum_not_ended = queryfx_one(
316 'SELECT SUM(UNIX_TIMESTAMP() - usertime.dateStarted) N '.
318 'WHERE usertime.userPHID = %s '.
319 'AND usertime.objectPHID = %s '.
320 'AND usertime.dateEnded IS NULL',
321 $usertime_dao->getTableName(),
325 return $sum_ended['N'] +
$sum_not_ended['N'];
328 public function getQueryApplicationClass() {
329 return 'PhabricatorPhrequentApplication';