@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Conduit APIs to start and stop tracking time in phrequent

Summary:
This adds methods to start and stop tracking any arbitrary PHID in phrequent. Currently, this uses copy-pasted code from PhrequentTrackController. I had to do this because the code to start/stop was not abstracted into a common class.

Once the code to start/stop working is extracted into a re-usable class, the conduit API can use this as well.

Test Plan: I called the functions with a PHID of a task and ensured that the fields in the phrequent database table was being updated correctly.

Reviewers: skyronic, #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: maxhodak, erik.fercak, aran, epriestley, Korvin

Maniphest Tasks: T3569, T3970

Differential Revision: https://secure.phabricator.com/D7326

+273 -42
+10
src/__phutil_library_map__.php
··· 225 225 'ConduitAPI_phragment_Method' => 'applications/phragment/conduit/ConduitAPI_phragment_Method.php', 226 226 'ConduitAPI_phragment_getpatch_Method' => 'applications/phragment/conduit/ConduitAPI_phragment_getpatch_Method.php', 227 227 'ConduitAPI_phragment_queryfragments_Method' => 'applications/phragment/conduit/ConduitAPI_phragment_queryfragments_Method.php', 228 + 'ConduitAPI_phrequent_Method' => 'applications/phrequent/conduit/ConduitAPI_phrequent_Method.php', 229 + 'ConduitAPI_phrequent_pop_Method' => 'applications/phrequent/conduit/ConduitAPI_phrequent_pop_Method.php', 230 + 'ConduitAPI_phrequent_push_Method' => 'applications/phrequent/conduit/ConduitAPI_phrequent_push_Method.php', 231 + 'ConduitAPI_phrequent_tracking_Method' => 'applications/phrequent/conduit/ConduitAPI_phrequent_tracking_Method.php', 228 232 'ConduitAPI_phriction_Method' => 'applications/phriction/conduit/ConduitAPI_phriction_Method.php', 229 233 'ConduitAPI_phriction_edit_Method' => 'applications/phriction/conduit/ConduitAPI_phriction_edit_Method.php', 230 234 'ConduitAPI_phriction_history_Method' => 'applications/phriction/conduit/ConduitAPI_phriction_history_Method.php', ··· 2547 2551 'PhrequentTimeBlockTestCase' => 'applications/phrequent/storage/__tests__/PhrequentTimeBlockTestCase.php', 2548 2552 'PhrequentTrackController' => 'applications/phrequent/controller/PhrequentTrackController.php', 2549 2553 'PhrequentTrackableInterface' => 'applications/phrequent/interface/PhrequentTrackableInterface.php', 2554 + 'PhrequentTrackingEditor' => 'applications/phrequent/editor/PhrequentTrackingEditor.php', 2550 2555 'PhrequentUIEventListener' => 'applications/phrequent/event/PhrequentUIEventListener.php', 2551 2556 'PhrequentUserTime' => 'applications/phrequent/storage/PhrequentUserTime.php', 2552 2557 'PhrequentUserTimeQuery' => 'applications/phrequent/query/PhrequentUserTimeQuery.php', ··· 2937 2942 'ConduitAPI_phragment_Method' => 'ConduitAPIMethod', 2938 2943 'ConduitAPI_phragment_getpatch_Method' => 'ConduitAPI_phragment_Method', 2939 2944 'ConduitAPI_phragment_queryfragments_Method' => 'ConduitAPI_phragment_Method', 2945 + 'ConduitAPI_phrequent_Method' => 'ConduitAPIMethod', 2946 + 'ConduitAPI_phrequent_pop_Method' => 'ConduitAPI_phrequent_Method', 2947 + 'ConduitAPI_phrequent_push_Method' => 'ConduitAPI_phrequent_Method', 2948 + 'ConduitAPI_phrequent_tracking_Method' => 'ConduitAPI_phrequent_Method', 2940 2949 'ConduitAPI_phriction_Method' => 'ConduitAPIMethod', 2941 2950 'ConduitAPI_phriction_edit_Method' => 'ConduitAPI_phriction_Method', 2942 2951 'ConduitAPI_phriction_history_Method' => 'ConduitAPI_phriction_Method', ··· 5517 5526 'PhrequentTimeBlock' => 'Phobject', 5518 5527 'PhrequentTimeBlockTestCase' => 'PhabricatorTestCase', 5519 5528 'PhrequentTrackController' => 'PhrequentController', 5529 + 'PhrequentTrackingEditor' => 'PhabricatorEditor', 5520 5530 'PhrequentUIEventListener' => 'PhabricatorEventListener', 5521 5531 'PhrequentUserTime' => 5522 5532 array(
+11
src/applications/phrequent/conduit/ConduitAPI_phrequent_Method.php
··· 1 + <?php 2 + 3 + abstract class ConduitAPI_phrequent_Method extends ConduitAPIMethod { 4 + 5 + public function getApplication() { 6 + return PhabricatorApplication::getByClass( 7 + 'PhabricatorApplicationPhrequent'); 8 + } 9 + 10 + 11 + }
+49
src/applications/phrequent/conduit/ConduitAPI_phrequent_pop_Method.php
··· 1 + <?php 2 + 3 + final class ConduitAPI_phrequent_pop_Method 4 + extends ConduitAPI_phrequent_Method { 5 + 6 + public function getMethodDescription() { 7 + return pht('Stop tracking time on an object by popping it from the stack.'); 8 + } 9 + 10 + public function getMethodStatus() { 11 + return self::METHOD_STATUS_UNSTABLE; 12 + } 13 + 14 + public function defineParamTypes() { 15 + return array( 16 + 'objectPHID' => 'phid', 17 + 'stopTime' => 'int', 18 + 'note' => 'string' 19 + ); 20 + } 21 + 22 + public function defineReturnType() { 23 + return 'phid'; 24 + } 25 + 26 + public function defineErrorTypes() { 27 + return array( 28 + ); 29 + } 30 + 31 + protected function execute(ConduitAPIRequest $request) { 32 + $user = $request->getUser(); 33 + $object_phid = $request->getValue('objectPHID'); 34 + $timestamp = $request->getValue('stopTime'); 35 + $note = $request->getValue('note'); 36 + if ($timestamp === null) { 37 + $timestamp = time(); 38 + } 39 + 40 + $editor = new PhrequentTrackingEditor(); 41 + 42 + if (!$object_phid) { 43 + return $editor->stopTrackingTop($user, $timestamp, $note); 44 + } else { 45 + return $editor->stopTracking($user, $object_phid, $timestamp, $note); 46 + } 47 + } 48 + 49 + }
+44
src/applications/phrequent/conduit/ConduitAPI_phrequent_push_Method.php
··· 1 + <?php 2 + 3 + final class ConduitAPI_phrequent_push_Method 4 + extends ConduitAPI_phrequent_Method { 5 + 6 + public function getMethodDescription() { 7 + return pht( 8 + 'Start tracking time on an object by '. 9 + 'pushing it on the tracking stack.'); 10 + } 11 + 12 + public function getMethodStatus() { 13 + return self::METHOD_STATUS_UNSTABLE; 14 + } 15 + 16 + public function defineParamTypes() { 17 + return array( 18 + 'objectPHID' => 'required phid', 19 + 'startTime' => 'int' 20 + ); 21 + } 22 + 23 + public function defineReturnType() { 24 + return 'phid'; 25 + } 26 + 27 + public function defineErrorTypes() { 28 + return array( 29 + ); 30 + } 31 + 32 + protected function execute(ConduitAPIRequest $request) { 33 + $user = $request->getUser(); 34 + $object_phid = $request->getValue('objectPHID'); 35 + $timestamp = $request->getValue('startTime'); 36 + if ($timestamp === null) { 37 + $timestamp = time(); 38 + } 39 + 40 + $editor = new PhrequentTrackingEditor(); 41 + return $editor->startTracking($user, $object_phid, $timestamp); 42 + } 43 + 44 + }
+45
src/applications/phrequent/conduit/ConduitAPI_phrequent_tracking_Method.php
··· 1 + <?php 2 + 3 + final class ConduitAPI_phrequent_tracking_Method 4 + extends ConduitAPI_phrequent_Method { 5 + 6 + public function getMethodDescription() { 7 + return pht( 8 + 'Returns current objects being tracked in Phrequent.'); 9 + } 10 + 11 + public function getMethodStatus() { 12 + return self::METHOD_STATUS_UNSTABLE; 13 + } 14 + 15 + public function defineParamTypes() { 16 + return array(); 17 + } 18 + 19 + public function defineReturnType() { 20 + return 'array'; 21 + } 22 + 23 + public function defineErrorTypes() { 24 + return array( 25 + ); 26 + } 27 + 28 + protected function execute(ConduitAPIRequest $request) { 29 + $user = $request->getUser(); 30 + 31 + $times = id(new PhrequentUserTimeQuery()) 32 + ->setViewer($user) 33 + ->needPreemptingEvents(true) 34 + ->withUserPHIDs(array($user->getPHID())) 35 + ->execute(); 36 + 37 + $now = time(); 38 + 39 + $results = id(new PhrequentTimeBlock($times)) 40 + ->getCurrentWorkStack($now); 41 + 42 + return array('data' => $results); 43 + } 44 + 45 + }
+3 -37
src/applications/phrequent/controller/PhrequentTrackController.php
··· 14 14 public function processRequest() { 15 15 $request = $this->getRequest(); 16 16 $user = $request->getUser(); 17 + $editor = new PhrequentTrackingEditor(); 17 18 18 19 $phid = $this->phid; 19 20 $handle = id(new PhabricatorHandleQuery()) ··· 61 62 62 63 if (!$err) { 63 64 if ($this->isStartingTracking()) { 64 - $this->startTracking($user, $this->phid, $timestamp); 65 + $editor->startTracking($user, $this->phid, $timestamp); 65 66 } else if ($this->isStoppingTracking()) { 66 - $this->stopTracking($user, $this->phid, $timestamp, $note); 67 + $editor->stopTracking($user, $this->phid, $timestamp, $note); 67 68 } 68 69 return id(new AphrontRedirectResponse()); 69 70 } ··· 108 109 private function isStoppingTracking() { 109 110 return $this->verb === 'stop'; 110 111 } 111 - 112 - private function startTracking($user, $phid, $timestamp) { 113 - $usertime = new PhrequentUserTime(); 114 - $usertime->setDateStarted($timestamp); 115 - $usertime->setUserPHID($user->getPHID()); 116 - $usertime->setObjectPHID($phid); 117 - $usertime->save(); 118 - } 119 - 120 - private function stopTracking($user, $phid, $timestamp, $note) { 121 - if (!PhrequentUserTimeQuery::isUserTrackingObject($user, $phid)) { 122 - // Don't do anything, it's not being tracked. 123 - return; 124 - } 125 - 126 - $usertime_dao = new PhrequentUserTime(); 127 - $conn = $usertime_dao->establishConnection('r'); 128 - 129 - queryfx( 130 - $conn, 131 - 'UPDATE %T usertime '. 132 - 'SET usertime.dateEnded = %d, '. 133 - 'usertime.note = %s '. 134 - 'WHERE usertime.userPHID = %s '. 135 - 'AND usertime.objectPHID = %s '. 136 - 'AND usertime.dateEnded IS NULL '. 137 - 'ORDER BY usertime.dateStarted, usertime.id DESC '. 138 - 'LIMIT 1', 139 - $usertime_dao->getTableName(), 140 - $timestamp, 141 - $note, 142 - $user->getPHID(), 143 - $phid); 144 - } 145 - 146 112 }
+70
src/applications/phrequent/editor/PhrequentTrackingEditor.php
··· 1 + <?php 2 + 3 + final class PhrequentTrackingEditor extends PhabricatorEditor { 4 + 5 + public function startTracking(PhabricatorUser $user, $phid, $timestamp) { 6 + $usertime = new PhrequentUserTime(); 7 + $usertime->setDateStarted($timestamp); 8 + $usertime->setUserPHID($user->getPHID()); 9 + $usertime->setObjectPHID($phid); 10 + $usertime->save(); 11 + 12 + return $phid; 13 + } 14 + 15 + public function stopTracking( 16 + PhabricatorUser $user, 17 + $phid, 18 + $timestamp, 19 + $note) { 20 + 21 + if (!PhrequentUserTimeQuery::isUserTrackingObject($user, $phid)) { 22 + // Don't do anything, it's not being tracked. 23 + return null; 24 + } 25 + 26 + $usertime_dao = new PhrequentUserTime(); 27 + $conn = $usertime_dao->establishConnection('r'); 28 + 29 + queryfx( 30 + $conn, 31 + 'UPDATE %T usertime '. 32 + 'SET usertime.dateEnded = %d, '. 33 + 'usertime.note = %s '. 34 + 'WHERE usertime.userPHID = %s '. 35 + 'AND usertime.objectPHID = %s '. 36 + 'AND usertime.dateEnded IS NULL '. 37 + 'ORDER BY usertime.dateStarted, usertime.id DESC '. 38 + 'LIMIT 1', 39 + $usertime_dao->getTableName(), 40 + $timestamp, 41 + $note, 42 + $user->getPHID(), 43 + $phid); 44 + 45 + return $phid; 46 + } 47 + 48 + public function stopTrackingTop(PhabricatorUser $user, $timestamp, $note) { 49 + $times = id(new PhrequentUserTimeQuery()) 50 + ->setViewer($user) 51 + ->withUserPHIDs(array($user->getPHID())) 52 + ->withEnded(PhrequentUserTimeQuery::ENDED_NO) 53 + ->setOrder(PhrequentUserTimeQuery::ORDER_STARTED_DESC) 54 + ->execute(); 55 + 56 + if (count($times) === 0) { 57 + // Nothing to stop tracking. 58 + return null; 59 + } 60 + 61 + $current = head($times); 62 + 63 + return $this->stopTracking( 64 + $user, 65 + $current->getObjectPHID(), 66 + $timestamp, 67 + $note); 68 + } 69 + 70 + }
+1 -1
src/applications/phrequent/query/PhrequentSearchEngine.php
··· 186 186 $usertime->getUserPHID() === $viewer->getPHID()) { 187 187 $item->addAction( 188 188 id(new PHUIListItemView()) 189 - ->setIcon('fa-time-o') 189 + ->setIcon('fa-stop') 190 190 ->addSigil('phrequent-stop-tracking') 191 191 ->setWorkflow(true) 192 192 ->setRenderNameAsTooltip(true)
+40 -4
src/applications/phrequent/storage/PhrequentTimeBlock.php
··· 37 37 $timeline = array(); 38 38 $timeline[] = array( 39 39 'event' => $event, 40 - 'at' => $event->getDateStarted(), 40 + 'at' => (int)$event->getDateStarted(), 41 41 'type' => 'start', 42 42 ); 43 43 $timeline[] = array( 44 44 'event' => $event, 45 - 'at' => nonempty($event->getDateEnded(), $now), 45 + 'at' => (int)nonempty($event->getDateEnded(), $now), 46 46 'type' => 'end', 47 47 ); 48 48 ··· 54 54 $same_object = ($preempt->getObjectPHID() == $base_phid); 55 55 $timeline[] = array( 56 56 'event' => $preempt, 57 - 'at' => $preempt->getDateStarted(), 57 + 'at' => (int)$preempt->getDateStarted(), 58 58 'type' => $same_object ? 'start' : 'push', 59 59 ); 60 60 $timeline[] = array( 61 61 'event' => $preempt, 62 - 'at' => nonempty($preempt->getDateEnded(), $now), 62 + 'at' => (int)nonempty($preempt->getDateEnded(), $now), 63 63 'type' => $same_object ? 'end' : 'pop', 64 64 ); 65 65 } ··· 183 183 } 184 184 185 185 return $object_ranges; 186 + } 187 + 188 + /** 189 + * Returns the current list of work. 190 + */ 191 + public function getCurrentWorkStack($now, $include_inactive = false) { 192 + $ranges = $this->getObjectTimeRanges($now); 193 + 194 + $results = array(); 195 + foreach ($ranges as $phid => $blocks) { 196 + $total = 0; 197 + foreach ($blocks as $block) { 198 + $total += $block[1] - $block[0]; 199 + } 200 + 201 + $type = 'inactive'; 202 + foreach ($blocks as $block) { 203 + if ($block[1] === $now) { 204 + if ($block[0] === $block[1]) { 205 + $type = 'suspended'; 206 + } else { 207 + $type = 'active'; 208 + } 209 + break; 210 + } 211 + } 212 + 213 + if ($include_inactive || $type !== 'inactive') { 214 + $results[] = array( 215 + 'phid' => $phid, 216 + 'time' => $total, 217 + 'type' => $type); 218 + } 219 + } 220 + 221 + return $results; 186 222 } 187 223 188 224