1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-24 06:20:56 +01:00

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
This commit is contained in:
James Rhodes 2014-07-12 11:42:32 +10:00
parent 9a679bf374
commit 2101c3b689
9 changed files with 273 additions and 42 deletions

View file

@ -225,6 +225,10 @@ phutil_register_library_map(array(
'ConduitAPI_phragment_Method' => 'applications/phragment/conduit/ConduitAPI_phragment_Method.php', 'ConduitAPI_phragment_Method' => 'applications/phragment/conduit/ConduitAPI_phragment_Method.php',
'ConduitAPI_phragment_getpatch_Method' => 'applications/phragment/conduit/ConduitAPI_phragment_getpatch_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_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_Method' => 'applications/phriction/conduit/ConduitAPI_phriction_Method.php',
'ConduitAPI_phriction_edit_Method' => 'applications/phriction/conduit/ConduitAPI_phriction_edit_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', 'ConduitAPI_phriction_history_Method' => 'applications/phriction/conduit/ConduitAPI_phriction_history_Method.php',
@ -2547,6 +2551,7 @@ phutil_register_library_map(array(
'PhrequentTimeBlockTestCase' => 'applications/phrequent/storage/__tests__/PhrequentTimeBlockTestCase.php', 'PhrequentTimeBlockTestCase' => 'applications/phrequent/storage/__tests__/PhrequentTimeBlockTestCase.php',
'PhrequentTrackController' => 'applications/phrequent/controller/PhrequentTrackController.php', 'PhrequentTrackController' => 'applications/phrequent/controller/PhrequentTrackController.php',
'PhrequentTrackableInterface' => 'applications/phrequent/interface/PhrequentTrackableInterface.php', 'PhrequentTrackableInterface' => 'applications/phrequent/interface/PhrequentTrackableInterface.php',
'PhrequentTrackingEditor' => 'applications/phrequent/editor/PhrequentTrackingEditor.php',
'PhrequentUIEventListener' => 'applications/phrequent/event/PhrequentUIEventListener.php', 'PhrequentUIEventListener' => 'applications/phrequent/event/PhrequentUIEventListener.php',
'PhrequentUserTime' => 'applications/phrequent/storage/PhrequentUserTime.php', 'PhrequentUserTime' => 'applications/phrequent/storage/PhrequentUserTime.php',
'PhrequentUserTimeQuery' => 'applications/phrequent/query/PhrequentUserTimeQuery.php', 'PhrequentUserTimeQuery' => 'applications/phrequent/query/PhrequentUserTimeQuery.php',
@ -2937,6 +2942,10 @@ phutil_register_library_map(array(
'ConduitAPI_phragment_Method' => 'ConduitAPIMethod', 'ConduitAPI_phragment_Method' => 'ConduitAPIMethod',
'ConduitAPI_phragment_getpatch_Method' => 'ConduitAPI_phragment_Method', 'ConduitAPI_phragment_getpatch_Method' => 'ConduitAPI_phragment_Method',
'ConduitAPI_phragment_queryfragments_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_Method' => 'ConduitAPIMethod',
'ConduitAPI_phriction_edit_Method' => 'ConduitAPI_phriction_Method', 'ConduitAPI_phriction_edit_Method' => 'ConduitAPI_phriction_Method',
'ConduitAPI_phriction_history_Method' => 'ConduitAPI_phriction_Method', 'ConduitAPI_phriction_history_Method' => 'ConduitAPI_phriction_Method',
@ -5517,6 +5526,7 @@ phutil_register_library_map(array(
'PhrequentTimeBlock' => 'Phobject', 'PhrequentTimeBlock' => 'Phobject',
'PhrequentTimeBlockTestCase' => 'PhabricatorTestCase', 'PhrequentTimeBlockTestCase' => 'PhabricatorTestCase',
'PhrequentTrackController' => 'PhrequentController', 'PhrequentTrackController' => 'PhrequentController',
'PhrequentTrackingEditor' => 'PhabricatorEditor',
'PhrequentUIEventListener' => 'PhabricatorEventListener', 'PhrequentUIEventListener' => 'PhabricatorEventListener',
'PhrequentUserTime' => 'PhrequentUserTime' =>
array( array(

View file

@ -0,0 +1,11 @@
<?php
abstract class ConduitAPI_phrequent_Method extends ConduitAPIMethod {
public function getApplication() {
return PhabricatorApplication::getByClass(
'PhabricatorApplicationPhrequent');
}
}

View file

@ -0,0 +1,49 @@
<?php
final class ConduitAPI_phrequent_pop_Method
extends ConduitAPI_phrequent_Method {
public function getMethodDescription() {
return pht('Stop tracking time on an object by popping it from the stack.');
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function defineParamTypes() {
return array(
'objectPHID' => '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);
}
}
}

View file

@ -0,0 +1,44 @@
<?php
final class ConduitAPI_phrequent_push_Method
extends ConduitAPI_phrequent_Method {
public function getMethodDescription() {
return pht(
'Start tracking time on an object by '.
'pushing it on the tracking stack.');
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function defineParamTypes() {
return array(
'objectPHID' => '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);
}
}

View file

@ -0,0 +1,45 @@
<?php
final class ConduitAPI_phrequent_tracking_Method
extends ConduitAPI_phrequent_Method {
public function getMethodDescription() {
return pht(
'Returns current objects being tracked in Phrequent.');
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function defineParamTypes() {
return array();
}
public function defineReturnType() {
return 'array';
}
public function defineErrorTypes() {
return array(
);
}
protected function execute(ConduitAPIRequest $request) {
$user = $request->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);
}
}

View file

@ -14,6 +14,7 @@ final class PhrequentTrackController
public function processRequest() { public function processRequest() {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
$editor = new PhrequentTrackingEditor();
$phid = $this->phid; $phid = $this->phid;
$handle = id(new PhabricatorHandleQuery()) $handle = id(new PhabricatorHandleQuery())
@ -61,9 +62,9 @@ final class PhrequentTrackController
if (!$err) { if (!$err) {
if ($this->isStartingTracking()) { if ($this->isStartingTracking()) {
$this->startTracking($user, $this->phid, $timestamp); $editor->startTracking($user, $this->phid, $timestamp);
} else if ($this->isStoppingTracking()) { } else if ($this->isStoppingTracking()) {
$this->stopTracking($user, $this->phid, $timestamp, $note); $editor->stopTracking($user, $this->phid, $timestamp, $note);
} }
return id(new AphrontRedirectResponse()); return id(new AphrontRedirectResponse());
} }
@ -108,39 +109,4 @@ final class PhrequentTrackController
private function isStoppingTracking() { private function isStoppingTracking() {
return $this->verb === 'stop'; 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);
}
} }

View file

@ -0,0 +1,70 @@
<?php
final class PhrequentTrackingEditor extends PhabricatorEditor {
public function startTracking(PhabricatorUser $user, $phid, $timestamp) {
$usertime = new PhrequentUserTime();
$usertime->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);
}
}

View file

@ -186,7 +186,7 @@ final class PhrequentSearchEngine
$usertime->getUserPHID() === $viewer->getPHID()) { $usertime->getUserPHID() === $viewer->getPHID()) {
$item->addAction( $item->addAction(
id(new PHUIListItemView()) id(new PHUIListItemView())
->setIcon('fa-time-o') ->setIcon('fa-stop')
->addSigil('phrequent-stop-tracking') ->addSigil('phrequent-stop-tracking')
->setWorkflow(true) ->setWorkflow(true)
->setRenderNameAsTooltip(true) ->setRenderNameAsTooltip(true)

View file

@ -37,12 +37,12 @@ final class PhrequentTimeBlock extends Phobject {
$timeline = array(); $timeline = array();
$timeline[] = array( $timeline[] = array(
'event' => $event, 'event' => $event,
'at' => $event->getDateStarted(), 'at' => (int)$event->getDateStarted(),
'type' => 'start', 'type' => 'start',
); );
$timeline[] = array( $timeline[] = array(
'event' => $event, 'event' => $event,
'at' => nonempty($event->getDateEnded(), $now), 'at' => (int)nonempty($event->getDateEnded(), $now),
'type' => 'end', 'type' => 'end',
); );
@ -54,12 +54,12 @@ final class PhrequentTimeBlock extends Phobject {
$same_object = ($preempt->getObjectPHID() == $base_phid); $same_object = ($preempt->getObjectPHID() == $base_phid);
$timeline[] = array( $timeline[] = array(
'event' => $preempt, 'event' => $preempt,
'at' => $preempt->getDateStarted(), 'at' => (int)$preempt->getDateStarted(),
'type' => $same_object ? 'start' : 'push', 'type' => $same_object ? 'start' : 'push',
); );
$timeline[] = array( $timeline[] = array(
'event' => $preempt, 'event' => $preempt,
'at' => nonempty($preempt->getDateEnded(), $now), 'at' => (int)nonempty($preempt->getDateEnded(), $now),
'type' => $same_object ? 'end' : 'pop', 'type' => $same_object ? 'end' : 'pop',
); );
} }
@ -185,6 +185,42 @@ final class PhrequentTimeBlock extends Phobject {
return $object_ranges; 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 `<start, end>` epochs) so that no * Merge a list of time ranges (pairs of `<start, end>` epochs) so that no