diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -225,6 +225,10 @@ 'ConduitAPI_phragment_Method' => 'applications/phragment/conduit/ConduitAPI_phragment_Method.php', 'ConduitAPI_phragment_getpatch_Method' => 'applications/phragment/conduit/ConduitAPI_phragment_getpatch_Method.php', 'ConduitAPI_phragment_queryfragments_Method' => 'applications/phragment/conduit/ConduitAPI_phragment_queryfragments_Method.php', + 'ConduitAPI_phrequent_Method' => 'applications/phrequent/conduit/ConduitAPI_phrequent_Method.php', + 'ConduitAPI_phrequent_pop_Method' => 'applications/phrequent/conduit/ConduitAPI_phrequent_pop_Method.php', + 'ConduitAPI_phrequent_push_Method' => 'applications/phrequent/conduit/ConduitAPI_phrequent_push_Method.php', + 'ConduitAPI_phrequent_tracking_Method' => 'applications/phrequent/conduit/ConduitAPI_phrequent_tracking_Method.php', 'ConduitAPI_phriction_Method' => 'applications/phriction/conduit/ConduitAPI_phriction_Method.php', 'ConduitAPI_phriction_edit_Method' => 'applications/phriction/conduit/ConduitAPI_phriction_edit_Method.php', 'ConduitAPI_phriction_history_Method' => 'applications/phriction/conduit/ConduitAPI_phriction_history_Method.php', @@ -2547,6 +2551,7 @@ 'PhrequentTimeBlockTestCase' => 'applications/phrequent/storage/__tests__/PhrequentTimeBlockTestCase.php', 'PhrequentTrackController' => 'applications/phrequent/controller/PhrequentTrackController.php', 'PhrequentTrackableInterface' => 'applications/phrequent/interface/PhrequentTrackableInterface.php', + 'PhrequentTrackingEditor' => 'applications/phrequent/editor/PhrequentTrackingEditor.php', 'PhrequentUIEventListener' => 'applications/phrequent/event/PhrequentUIEventListener.php', 'PhrequentUserTime' => 'applications/phrequent/storage/PhrequentUserTime.php', 'PhrequentUserTimeQuery' => 'applications/phrequent/query/PhrequentUserTimeQuery.php', @@ -2937,6 +2942,10 @@ 'ConduitAPI_phragment_Method' => 'ConduitAPIMethod', 'ConduitAPI_phragment_getpatch_Method' => 'ConduitAPI_phragment_Method', 'ConduitAPI_phragment_queryfragments_Method' => 'ConduitAPI_phragment_Method', + 'ConduitAPI_phrequent_Method' => 'ConduitAPIMethod', + 'ConduitAPI_phrequent_pop_Method' => 'ConduitAPI_phrequent_Method', + 'ConduitAPI_phrequent_push_Method' => 'ConduitAPI_phrequent_Method', + 'ConduitAPI_phrequent_tracking_Method' => 'ConduitAPI_phrequent_Method', 'ConduitAPI_phriction_Method' => 'ConduitAPIMethod', 'ConduitAPI_phriction_edit_Method' => 'ConduitAPI_phriction_Method', 'ConduitAPI_phriction_history_Method' => 'ConduitAPI_phriction_Method', @@ -5517,6 +5526,7 @@ 'PhrequentTimeBlock' => 'Phobject', 'PhrequentTimeBlockTestCase' => 'PhabricatorTestCase', 'PhrequentTrackController' => 'PhrequentController', + 'PhrequentTrackingEditor' => 'PhabricatorEditor', 'PhrequentUIEventListener' => 'PhabricatorEventListener', 'PhrequentUserTime' => array( diff --git a/src/applications/phrequent/conduit/ConduitAPI_phrequent_Method.php b/src/applications/phrequent/conduit/ConduitAPI_phrequent_Method.php new file mode 100644 --- /dev/null +++ b/src/applications/phrequent/conduit/ConduitAPI_phrequent_Method.php @@ -0,0 +1,11 @@ + 'phid', + 'stopTime' => 'int', + 'note' => 'string' + ); + } + + public function defineReturnType() { + return 'phid'; + } + + public function defineErrorTypes() { + return array( + ); + } + + protected function execute(ConduitAPIRequest $request) { + $user = $request->getUser(); + $object_phid = $request->getValue('objectPHID'); + $timestamp = $request->getValue('stopTime'); + $note = $request->getValue('note'); + if ($timestamp === null) { + $timestamp = time(); + } + + $editor = new PhrequentTrackingEditor(); + + if (!$object_phid) { + return $editor->stopTrackingTop($user, $timestamp, $note); + } else { + return $editor->stopTracking($user, $object_phid, $timestamp, $note); + } + } + +} diff --git a/src/applications/phrequent/conduit/ConduitAPI_phrequent_push_Method.php b/src/applications/phrequent/conduit/ConduitAPI_phrequent_push_Method.php new file mode 100644 --- /dev/null +++ b/src/applications/phrequent/conduit/ConduitAPI_phrequent_push_Method.php @@ -0,0 +1,44 @@ + 'required phid', + 'startTime' => 'int' + ); + } + + public function defineReturnType() { + return 'phid'; + } + + public function defineErrorTypes() { + return array( + ); + } + + protected function execute(ConduitAPIRequest $request) { + $user = $request->getUser(); + $object_phid = $request->getValue('objectPHID'); + $timestamp = $request->getValue('startTime'); + if ($timestamp === null) { + $timestamp = time(); + } + + $editor = new PhrequentTrackingEditor(); + return $editor->startTracking($user, $object_phid, $timestamp); + } + +} diff --git a/src/applications/phrequent/conduit/ConduitAPI_phrequent_tracking_Method.php b/src/applications/phrequent/conduit/ConduitAPI_phrequent_tracking_Method.php new file mode 100644 --- /dev/null +++ b/src/applications/phrequent/conduit/ConduitAPI_phrequent_tracking_Method.php @@ -0,0 +1,45 @@ +getUser(); + + $times = id(new PhrequentUserTimeQuery()) + ->setViewer($user) + ->needPreemptingEvents(true) + ->withUserPHIDs(array($user->getPHID())) + ->execute(); + + $now = time(); + + $results = id(new PhrequentTimeBlock($times)) + ->getCurrentWorkStack($now); + + return array('data' => $results); + } + +} diff --git a/src/applications/phrequent/controller/PhrequentTrackController.php b/src/applications/phrequent/controller/PhrequentTrackController.php --- a/src/applications/phrequent/controller/PhrequentTrackController.php +++ b/src/applications/phrequent/controller/PhrequentTrackController.php @@ -14,6 +14,7 @@ public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); + $editor = new PhrequentTrackingEditor(); $phid = $this->phid; $handle = id(new PhabricatorHandleQuery()) @@ -61,9 +62,9 @@ if (!$err) { if ($this->isStartingTracking()) { - $this->startTracking($user, $this->phid, $timestamp); + $editor->startTracking($user, $this->phid, $timestamp); } else if ($this->isStoppingTracking()) { - $this->stopTracking($user, $this->phid, $timestamp, $note); + $editor->stopTracking($user, $this->phid, $timestamp, $note); } return id(new AphrontRedirectResponse()); } @@ -108,39 +109,4 @@ private function isStoppingTracking() { return $this->verb === 'stop'; } - - private function startTracking($user, $phid, $timestamp) { - $usertime = new PhrequentUserTime(); - $usertime->setDateStarted($timestamp); - $usertime->setUserPHID($user->getPHID()); - $usertime->setObjectPHID($phid); - $usertime->save(); - } - - private function stopTracking($user, $phid, $timestamp, $note) { - if (!PhrequentUserTimeQuery::isUserTrackingObject($user, $phid)) { - // Don't do anything, it's not being tracked. - return; - } - - $usertime_dao = new PhrequentUserTime(); - $conn = $usertime_dao->establishConnection('r'); - - queryfx( - $conn, - 'UPDATE %T usertime '. - 'SET usertime.dateEnded = %d, '. - 'usertime.note = %s '. - 'WHERE usertime.userPHID = %s '. - 'AND usertime.objectPHID = %s '. - 'AND usertime.dateEnded IS NULL '. - 'ORDER BY usertime.dateStarted, usertime.id DESC '. - 'LIMIT 1', - $usertime_dao->getTableName(), - $timestamp, - $note, - $user->getPHID(), - $phid); - } - } diff --git a/src/applications/phrequent/editor/PhrequentTrackingEditor.php b/src/applications/phrequent/editor/PhrequentTrackingEditor.php new file mode 100644 --- /dev/null +++ b/src/applications/phrequent/editor/PhrequentTrackingEditor.php @@ -0,0 +1,70 @@ +setDateStarted($timestamp); + $usertime->setUserPHID($user->getPHID()); + $usertime->setObjectPHID($phid); + $usertime->save(); + + return $phid; + } + + public function stopTracking( + PhabricatorUser $user, + $phid, + $timestamp, + $note) { + + if (!PhrequentUserTimeQuery::isUserTrackingObject($user, $phid)) { + // Don't do anything, it's not being tracked. + return null; + } + + $usertime_dao = new PhrequentUserTime(); + $conn = $usertime_dao->establishConnection('r'); + + queryfx( + $conn, + 'UPDATE %T usertime '. + 'SET usertime.dateEnded = %d, '. + 'usertime.note = %s '. + 'WHERE usertime.userPHID = %s '. + 'AND usertime.objectPHID = %s '. + 'AND usertime.dateEnded IS NULL '. + 'ORDER BY usertime.dateStarted, usertime.id DESC '. + 'LIMIT 1', + $usertime_dao->getTableName(), + $timestamp, + $note, + $user->getPHID(), + $phid); + + return $phid; + } + + public function stopTrackingTop(PhabricatorUser $user, $timestamp, $note) { + $times = id(new PhrequentUserTimeQuery()) + ->setViewer($user) + ->withUserPHIDs(array($user->getPHID())) + ->withEnded(PhrequentUserTimeQuery::ENDED_NO) + ->setOrder(PhrequentUserTimeQuery::ORDER_STARTED_DESC) + ->execute(); + + if (count($times) === 0) { + // Nothing to stop tracking. + return null; + } + + $current = head($times); + + return $this->stopTracking( + $user, + $current->getObjectPHID(), + $timestamp, + $note); + } + +} diff --git a/src/applications/phrequent/query/PhrequentSearchEngine.php b/src/applications/phrequent/query/PhrequentSearchEngine.php --- a/src/applications/phrequent/query/PhrequentSearchEngine.php +++ b/src/applications/phrequent/query/PhrequentSearchEngine.php @@ -186,7 +186,7 @@ $usertime->getUserPHID() === $viewer->getPHID()) { $item->addAction( id(new PHUIListItemView()) - ->setIcon('fa-time-o') + ->setIcon('fa-stop') ->addSigil('phrequent-stop-tracking') ->setWorkflow(true) ->setRenderNameAsTooltip(true) diff --git a/src/applications/phrequent/storage/PhrequentTimeBlock.php b/src/applications/phrequent/storage/PhrequentTimeBlock.php --- a/src/applications/phrequent/storage/PhrequentTimeBlock.php +++ b/src/applications/phrequent/storage/PhrequentTimeBlock.php @@ -37,12 +37,12 @@ $timeline = array(); $timeline[] = array( 'event' => $event, - 'at' => $event->getDateStarted(), + 'at' => (int)$event->getDateStarted(), 'type' => 'start', ); $timeline[] = array( 'event' => $event, - 'at' => nonempty($event->getDateEnded(), $now), + 'at' => (int)nonempty($event->getDateEnded(), $now), 'type' => 'end', ); @@ -54,12 +54,12 @@ $same_object = ($preempt->getObjectPHID() == $base_phid); $timeline[] = array( 'event' => $preempt, - 'at' => $preempt->getDateStarted(), + 'at' => (int)$preempt->getDateStarted(), 'type' => $same_object ? 'start' : 'push', ); $timeline[] = array( 'event' => $preempt, - 'at' => nonempty($preempt->getDateEnded(), $now), + 'at' => (int)nonempty($preempt->getDateEnded(), $now), 'type' => $same_object ? 'end' : 'pop', ); } @@ -185,6 +185,42 @@ return $object_ranges; } + /** + * Returns the current list of work. + */ + public function getCurrentWorkStack($now, $include_inactive = false) { + $ranges = $this->getObjectTimeRanges($now); + + $results = array(); + foreach ($ranges as $phid => $blocks) { + $total = 0; + foreach ($blocks as $block) { + $total += $block[1] - $block[0]; + } + + $type = 'inactive'; + foreach ($blocks as $block) { + if ($block[1] === $now) { + if ($block[0] === $block[1]) { + $type = 'suspended'; + } else { + $type = 'active'; + } + break; + } + } + + if ($include_inactive || $type !== 'inactive') { + $results[] = array( + 'phid' => $phid, + 'time' => $total, + 'type' => $type); + } + } + + return $results; + } + /** * Merge a list of time ranges (pairs of `` epochs) so that no