mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-30 09:20:58 +01:00
Implemented Phrequent time tracking functionality.
Summary: This differential implements Phrequent's time tracking functionality for users and hooks it up to Maniphest. It also includes a basic "Time Tracked" list for the Phrequent application, where users can review what they've spent time working on. Test Plan: Apply the patch and track some things in Maniphest. They should appear in the "Time Tracked" view of Phrequent. There is also a `phrequent.show-prompt` option which toggles whether to display a prompt when tracking time. I'm unsure of whether the prompt is useful or is more likely to cause people to click "Track Time", go off and do the task and then come back to the prompt still waiting for them to confirm. A potential solution to the "accidentally clicking the button and recording 2 seconds of time" might be to show a prompt on stop if the total time is under 10 seconds, asking whether the user wants to keep or discard the tracked time. Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Maniphest Tasks: T2857 Differential Revision: https://secure.phabricator.com/D5479
This commit is contained in:
parent
02739ef0ed
commit
e555b9025f
11 changed files with 601 additions and 2 deletions
|
@ -703,6 +703,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationPhlux' => 'applications/phlux/application/PhabricatorApplicationPhlux.php',
|
||||
'PhabricatorApplicationPholio' => 'applications/pholio/application/PhabricatorApplicationPholio.php',
|
||||
'PhabricatorApplicationPhortune' => 'applications/phortune/application/PhabricatorApplicationPhortune.php',
|
||||
'PhabricatorApplicationPhrequent' => 'applications/phrequent/application/PhabricatorApplicationPhrequent.php',
|
||||
'PhabricatorApplicationPhriction' => 'applications/phriction/application/PhabricatorApplicationPhriction.php',
|
||||
'PhabricatorApplicationPonder' => 'applications/ponder/application/PhabricatorApplicationPonder.php',
|
||||
'PhabricatorApplicationProject' => 'applications/project/application/PhabricatorApplicationProject.php',
|
||||
|
@ -1181,6 +1182,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPhabricatorOAuthConfigOptions' => 'applications/config/option/PhabricatorPhabricatorOAuthConfigOptions.php',
|
||||
'PhabricatorPhameConfigOptions' => 'applications/phame/config/PhabricatorPhameConfigOptions.php',
|
||||
'PhabricatorPholioConfigOptions' => 'applications/pholio/config/PhabricatorPholioConfigOptions.php',
|
||||
'PhabricatorPhrequentConfigOptions' => 'applications/phrequent/config/PhabricatorPhrequentConfigOptions.php',
|
||||
'PhabricatorPhrictionConfigOptions' => 'applications/phriction/config/PhabricatorPhrictionConfigOptions.php',
|
||||
'PhabricatorPinboardItemView' => 'view/layout/PhabricatorPinboardItemView.php',
|
||||
'PhabricatorPinboardView' => 'view/layout/PhabricatorPinboardView.php',
|
||||
|
@ -1556,6 +1558,14 @@ phutil_register_library_map(array(
|
|||
'PhortunePurchase' => 'applications/phortune/storage/PhortunePurchase.php',
|
||||
'PhortuneStripePaymentFormView' => 'applications/phortune/view/PhortuneStripePaymentFormView.php',
|
||||
'PhortuneUtil' => 'applications/phortune/util/PhortuneUtil.php',
|
||||
'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php',
|
||||
'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php',
|
||||
'PhrequentListController' => 'applications/phrequent/controller/PhrequentListController.php',
|
||||
'PhrequentTrackController' => 'applications/phrequent/controller/PhrequentTrackController.php',
|
||||
'PhrequentTrackableInterface' => 'applications/phrequent/interface/PhrequentTrackableInterface.php',
|
||||
'PhrequentUIEventListener' => 'applications/phrequent/event/PhrequentUIEventListener.php',
|
||||
'PhrequentUserTime' => 'applications/phrequent/storage/PhrequentUserTime.php',
|
||||
'PhrequentUserTimeQuery' => 'applications/phrequent/query/PhrequentUserTimeQuery.php',
|
||||
'PhrictionActionConstants' => 'applications/phriction/constants/PhrictionActionConstants.php',
|
||||
'PhrictionChangeType' => 'applications/phriction/constants/PhrictionChangeType.php',
|
||||
'PhrictionConstants' => 'applications/phriction/constants/PhrictionConstants.php',
|
||||
|
@ -2052,6 +2062,7 @@ phutil_register_library_map(array(
|
|||
0 => 'DifferentialDAO',
|
||||
1 => 'PhabricatorTokenReceiverInterface',
|
||||
2 => 'PhabricatorPolicyInterface',
|
||||
3 => 'PhrequentTrackableInterface',
|
||||
),
|
||||
'DifferentialRevisionCommentListView' => 'AphrontView',
|
||||
'DifferentialRevisionCommentView' => 'AphrontView',
|
||||
|
@ -2276,6 +2287,7 @@ phutil_register_library_map(array(
|
|||
1 => 'PhabricatorMarkupInterface',
|
||||
2 => 'PhabricatorPolicyInterface',
|
||||
3 => 'PhabricatorTokenReceiverInterface',
|
||||
4 => 'PhrequentTrackableInterface',
|
||||
),
|
||||
'ManiphestTaskAuxiliaryStorage' => 'ManiphestDAO',
|
||||
'ManiphestTaskDescriptionChangeController' => 'ManiphestController',
|
||||
|
@ -2354,6 +2366,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationPhlux' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationPholio' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationPhortune' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationPhrequent' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationPhriction' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationPonder' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationProject' => 'PhabricatorApplication',
|
||||
|
@ -2821,6 +2834,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPhabricatorOAuthConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorPhameConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorPholioConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorPhrequentConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorPhrictionConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorPinboardItemView' => 'AphrontView',
|
||||
'PhabricatorPinboardView' => 'AphrontView',
|
||||
|
@ -3222,6 +3236,13 @@ phutil_register_library_map(array(
|
|||
'PhortuneProductViewController' => 'PhabricatorController',
|
||||
'PhortunePurchase' => 'PhortuneDAO',
|
||||
'PhortuneStripePaymentFormView' => 'AphrontView',
|
||||
'PhrequentController' => 'PhabricatorController',
|
||||
'PhrequentDAO' => 'PhabricatorLiskDAO',
|
||||
'PhrequentListController' => 'PhrequentController',
|
||||
'PhrequentTrackController' => 'PhabricatorApplicationsController',
|
||||
'PhrequentUIEventListener' => 'PhutilEventListener',
|
||||
'PhrequentUserTime' => 'PhrequentDAO',
|
||||
'PhrequentUserTimeQuery' => 'PhabricatorOffsetPagedQuery',
|
||||
'PhrictionActionConstants' => 'PhrictionConstants',
|
||||
'PhrictionChangeType' => 'PhrictionConstants',
|
||||
'PhrictionContent' =>
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevision extends DifferentialDAO
|
||||
implements PhabricatorTokenReceiverInterface, PhabricatorPolicyInterface {
|
||||
implements
|
||||
PhabricatorTokenReceiverInterface,
|
||||
PhabricatorPolicyInterface,
|
||||
PhrequentTrackableInterface {
|
||||
|
||||
protected $title;
|
||||
protected $originalTitle;
|
||||
|
|
|
@ -7,7 +7,8 @@ final class ManiphestTask extends ManiphestDAO
|
|||
implements
|
||||
PhabricatorMarkupInterface,
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorTokenReceiverInterface {
|
||||
PhabricatorTokenReceiverInterface,
|
||||
PhrequentTrackableInterface {
|
||||
|
||||
const MARKUP_FIELD_DESCRIPTION = 'markup:desc';
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorApplicationPhrequent extends PhabricatorApplication {
|
||||
|
||||
public function getShortDescription() {
|
||||
return pht('Track Time');
|
||||
}
|
||||
|
||||
public function getBaseURI() {
|
||||
return '/phrequent/';
|
||||
}
|
||||
|
||||
public function isBeta() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getIconName() {
|
||||
return 'phrequent';
|
||||
}
|
||||
|
||||
public function getApplicationGroup() {
|
||||
return self::GROUP_ORGANIZATION;
|
||||
}
|
||||
|
||||
public function getApplicationOrder() {
|
||||
return 0.110;
|
||||
}
|
||||
|
||||
public function getEventListeners() {
|
||||
return array(
|
||||
new PhrequentUIEventListener(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/phrequent/' => array(
|
||||
'' => 'PhrequentListController',
|
||||
'track/(?P<verb>[a-z]+)/(?P<phid>[^/]+)/'
|
||||
=> 'PhrequentTrackController'
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function loadStatus(PhabricatorUser $user) {
|
||||
$status = array();
|
||||
|
||||
// TODO: Show number of timers that are currently
|
||||
// running for a user.
|
||||
|
||||
/*
|
||||
|
||||
$query = id(new ManiphestTaskQuery())
|
||||
->withStatus(ManiphestTaskQuery::STATUS_OPEN)
|
||||
->withOwners(array($user->getPHID()))
|
||||
->setLimit(1)
|
||||
->setCalculateRows(true);
|
||||
$query->execute();
|
||||
|
||||
$count = $query->getRowCount();
|
||||
$type = PhabricatorApplicationStatusView::TYPE_WARNING;
|
||||
$status[] = id(new PhabricatorApplicationStatusView())
|
||||
->setType($type)
|
||||
->setText(pht('%d Assigned Task(s)', $count))
|
||||
->setCount($count);
|
||||
|
||||
*/
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPhrequentConfigOptions
|
||||
extends PhabricatorApplicationConfigOptions {
|
||||
|
||||
public function getName() {
|
||||
return pht("Phrequent");
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return pht("Configure Phrequent.");
|
||||
}
|
||||
|
||||
public function getOptions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
abstract class PhrequentController extends PhabricatorController {
|
||||
|
||||
protected function buildNav($view) {
|
||||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI('/phrequent/'));
|
||||
|
||||
$nav->addFilter('usertime', 'Time Tracked');
|
||||
|
||||
$nav->selectFilter($view);
|
||||
|
||||
return $nav;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
final class PhrequentListController extends PhrequentController {
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$nav = $this->buildNav('usertime');
|
||||
|
||||
$query = new PhrequentUserTimeQuery();
|
||||
$query->setOrder(PhrequentUserTimeQuery::ORDER_ENDED);
|
||||
|
||||
$pager = new AphrontPagerView();
|
||||
$pager->setPageSize(500);
|
||||
$pager->setOffset($request->getInt('offset'));
|
||||
$pager->setURI($request->getRequestURI(), 'offset');
|
||||
|
||||
$logs = $query->executeWithOffsetPager($pager);
|
||||
|
||||
$title = pht('Time Tracked');
|
||||
|
||||
$header = id(new PhabricatorHeaderView())
|
||||
->setHeader($title);
|
||||
|
||||
$table = $this->buildTableView($logs);
|
||||
$table->appendChild($pager);
|
||||
|
||||
$nav->appendChild(
|
||||
array(
|
||||
$header,
|
||||
$table,
|
||||
$pager,
|
||||
));
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addCrumb(
|
||||
id(new PhabricatorCrumbView())
|
||||
->setName($title)
|
||||
->setHref($this->getApplicationURI('/')));
|
||||
$nav->setCrumbs($crumbs);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
$nav,
|
||||
array(
|
||||
'title' => $title,
|
||||
'device' => true,
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
protected function buildTableView(array $usertimes) {
|
||||
assert_instances_of($usertimes, 'PhrequentUserTime');
|
||||
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$phids = array();
|
||||
foreach ($usertimes as $usertime) {
|
||||
$phids[] = $usertime->getUserPHID();
|
||||
$phids[] = $usertime->getObjectPHID();
|
||||
}
|
||||
$handles = $this->loadViewerHandles($phids);
|
||||
|
||||
$rows = array();
|
||||
foreach ($usertimes as $usertime) {
|
||||
|
||||
if ($usertime->getDateEnded() !== null) {
|
||||
$time_spent = $usertime->getDateEnded() - $usertime->getDateStarted();
|
||||
$time_ended = phabricator_date($usertime->getDateEnded(), $user);
|
||||
} else {
|
||||
$time_spent = time() - $usertime->getDateStarted();
|
||||
$time_ended = phutil_tag(
|
||||
'em',
|
||||
array(),
|
||||
pht('Ongoing'));
|
||||
}
|
||||
|
||||
$usertime_user = $handles[$usertime->getUserPHID()];
|
||||
$usertime_object = null;
|
||||
$object = null;
|
||||
if ($usertime->getObjectPHID() !== null) {
|
||||
$usertime_object = $handles[$usertime->getObjectPHID()];
|
||||
$object = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $usertime_object->getURI()
|
||||
),
|
||||
$usertime_object->getFullName());
|
||||
} else {
|
||||
$object = phutil_tag(
|
||||
'em',
|
||||
array(),
|
||||
pht('None'));
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
$object,
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $usertime_user->getURI()
|
||||
),
|
||||
$usertime_user->getFullName()),
|
||||
phabricator_date($usertime->getDateStarted(), $user),
|
||||
$time_ended,
|
||||
$time_spent == 0 ? 'none' :
|
||||
phabricator_format_relative_time_detailed($time_spent),
|
||||
$usertime->getNote()
|
||||
);
|
||||
}
|
||||
|
||||
$table = new AphrontTableView($rows);
|
||||
$table->setDeviceReadyTable(true);
|
||||
$table->setHeaders(
|
||||
array(
|
||||
'Object',
|
||||
'User',
|
||||
'Started',
|
||||
'Ended',
|
||||
'Duration',
|
||||
'Note'
|
||||
));
|
||||
$table->setShortHeaders(
|
||||
array(
|
||||
'O',
|
||||
'U',
|
||||
'S',
|
||||
'E',
|
||||
'D',
|
||||
'Note',
|
||||
'',
|
||||
));
|
||||
$table->setColumnClasses(
|
||||
array(
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'wide'
|
||||
));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
final class PhrequentTrackController
|
||||
extends PhabricatorApplicationsController {
|
||||
|
||||
private $verb;
|
||||
private $phid;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->phid = $data['phid'];
|
||||
$this->verb = $data['verb'];
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
if (!$this->isStartingTracking() &&
|
||||
!$this->isStoppingTracking()) {
|
||||
throw new Exception('Unrecognized verb: ' . $this->verb);
|
||||
}
|
||||
|
||||
if ($this->isStartingTracking()) {
|
||||
$this->startTracking($user, $this->phid);
|
||||
} else if ($this->isStoppingTracking()) {
|
||||
$this->stopTracking($user, $this->phid);
|
||||
}
|
||||
return id(new AphrontRedirectResponse());
|
||||
}
|
||||
|
||||
private function isStartingTracking() {
|
||||
return $this->verb === 'start';
|
||||
}
|
||||
|
||||
private function isStoppingTracking() {
|
||||
return $this->verb === 'stop';
|
||||
}
|
||||
|
||||
private function startTracking($user, $phid) {
|
||||
$usertime = new PhrequentUserTime();
|
||||
$usertime->setDateStarted(time());
|
||||
$usertime->setUserPHID($user->getPHID());
|
||||
$usertime->setObjectPHID($phid);
|
||||
$usertime->save();
|
||||
}
|
||||
|
||||
private function stopTracking($user, $phid) {
|
||||
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 = UNIX_TIMESTAMP() '.
|
||||
'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(),
|
||||
$user->getPHID(),
|
||||
$phid);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
final class PhrequentUIEventListener
|
||||
extends PhutilEventListener {
|
||||
|
||||
public function register() {
|
||||
$this->listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS);
|
||||
$this->listen(PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES);
|
||||
}
|
||||
|
||||
public function handleEvent(PhutilEvent $event) {
|
||||
switch ($event->getType()) {
|
||||
case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS:
|
||||
$this->handleActionEvent($event);
|
||||
break;
|
||||
case PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES:
|
||||
$this->handlePropertyEvent($event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function handleActionEvent($event) {
|
||||
$user = $event->getUser();
|
||||
$object = $event->getValue('object');
|
||||
|
||||
if (!$object || !$object->getPHID()) {
|
||||
// No object, or the object has no PHID yet..
|
||||
return;
|
||||
}
|
||||
|
||||
if (!($object instanceof PhrequentTrackableInterface)) {
|
||||
// This object isn't a time trackable object.
|
||||
return;
|
||||
}
|
||||
|
||||
$tracking = PhrequentUserTimeQuery::isUserTrackingObject(
|
||||
$user,
|
||||
$object->getPHID());
|
||||
if (!$tracking) {
|
||||
$track_action = id(new PhabricatorActionView())
|
||||
->setName(pht('Track Time'))
|
||||
->setIcon('history')
|
||||
->setWorkflow(true)
|
||||
->setHref('/phrequent/track/start/'.$object->getPHID().'/');
|
||||
} else {
|
||||
$track_action = id(new PhabricatorActionView())
|
||||
->setName(pht('Stop Tracking'))
|
||||
->setIcon('history')
|
||||
->setWorkflow(true)
|
||||
->setHref('/phrequent/track/stop/'.$object->getPHID().'/');
|
||||
}
|
||||
|
||||
$actions = $event->getValue('actions');
|
||||
$actions[] = $track_action;
|
||||
$event->setValue('actions', $actions);
|
||||
}
|
||||
|
||||
private function handlePropertyEvent($event) {
|
||||
$user = $event->getUser();
|
||||
$object = $event->getValue('object');
|
||||
|
||||
if (!$object || !$object->getPHID()) {
|
||||
// No object, or the object has no PHID yet..
|
||||
return;
|
||||
}
|
||||
|
||||
if (!($object instanceof PhrequentTrackableInterface)) {
|
||||
// This object isn't a time trackable object.
|
||||
return;
|
||||
}
|
||||
|
||||
$time_spent = PhrequentUserTimeQuery::getTotalTimeSpentOnObject(
|
||||
$object->getPHID());
|
||||
$view = $event->getValue('view');
|
||||
$view->addProperty(
|
||||
pht('Time Spent'),
|
||||
$time_spent == 0 ? 'none' :
|
||||
phabricator_format_relative_time_detailed($time_spent));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
interface PhrequentTrackableInterface {
|
||||
|
||||
}
|
166
src/applications/phrequent/query/PhrequentUserTimeQuery.php
Normal file
166
src/applications/phrequent/query/PhrequentUserTimeQuery.php
Normal file
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
final class PhrequentUserTimeQuery extends PhabricatorOffsetPagedQuery {
|
||||
|
||||
const ORDER_ID = 'order-id';
|
||||
const ORDER_STARTED = 'order-started';
|
||||
const ORDER_ENDED = 'order-ended';
|
||||
const ORDER_DURATION = 'order-duration';
|
||||
|
||||
private $userPHIDs;
|
||||
private $objectPHIDs;
|
||||
private $order = self::ORDER_ID;
|
||||
|
||||
public function setUsers($user_phids) {
|
||||
$this->userPHIDs = $user_phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setObjects($object_phids) {
|
||||
$this->objectPHIDs = $object_phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setOrder($order) {
|
||||
$this->order = $order;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$usertime_dao = new PhrequentUserTime();
|
||||
$conn = $usertime_dao->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn,
|
||||
'SELECT usertime.* FROM %T usertime %Q %Q %Q',
|
||||
$usertime_dao->getTableName(),
|
||||
$this->buildWhereClause($conn),
|
||||
$this->buildOrderClause($conn),
|
||||
$this->buildLimitClause($conn));
|
||||
|
||||
return $usertime_dao->loadAllFromArray($data);
|
||||
}
|
||||
|
||||
private function buildWhereClause(AphrontDatabaseConnection $conn) {
|
||||
$where = array();
|
||||
|
||||
if ($this->userPHIDs) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'userPHID IN (%Ls)',
|
||||
$this->userPHIDs);
|
||||
}
|
||||
|
||||
if ($this->objectPHIDs) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'objectPHID IN (%Ls)',
|
||||
$this->objectPHIDs);
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
private function buildOrderClause(AphrontDatabaseConnection $conn) {
|
||||
switch ($this->order) {
|
||||
case self::ORDER_ID:
|
||||
return 'ORDER BY id ASC';
|
||||
case self::ORDER_STARTED:
|
||||
return 'ORDER BY dateStarted DESC';
|
||||
case self::ORDER_ENDED:
|
||||
return 'ORDER BY dateEnded IS NULL, dateEnded DESC, dateStarted DESC';
|
||||
case self::ORDER_DURATION:
|
||||
return 'ORDER BY (COALESCE(dateEnded, UNIX_TIMESTAMP() - dateStarted) '.
|
||||
'DESC';
|
||||
default:
|
||||
throw new Exception("Unknown order '{$this->order}'!");
|
||||
}
|
||||
}
|
||||
|
||||
/* -( Helper Functions ) --------------------------------------------------- */
|
||||
|
||||
public static function isUserTrackingObject(
|
||||
PhabricatorUser $user,
|
||||
$phid) {
|
||||
|
||||
$usertime_dao = new PhrequentUserTime();
|
||||
$conn = $usertime_dao->establishConnection('r');
|
||||
|
||||
$count = queryfx_one(
|
||||
$conn,
|
||||
'SELECT COUNT(usertime.id) N FROM %T usertime '.
|
||||
'WHERE usertime.userPHID = %s '.
|
||||
'AND usertime.objectPHID = %s '.
|
||||
'AND usertime.dateEnded IS NULL',
|
||||
$usertime_dao->getTableName(),
|
||||
$user->getPHID(),
|
||||
$phid);
|
||||
return $count['N'] > 0;
|
||||
}
|
||||
|
||||
public static function getTotalTimeSpentOnObject($phid) {
|
||||
$usertime_dao = new PhrequentUserTime();
|
||||
$conn = $usertime_dao->establishConnection('r');
|
||||
|
||||
// First calculate all the time spent where the
|
||||
// usertime blocks have ended.
|
||||
$sum_ended = queryfx_one(
|
||||
$conn,
|
||||
'SELECT SUM(usertime.dateEnded - usertime.dateStarted) N '.
|
||||
'FROM %T usertime '.
|
||||
'WHERE usertime.objectPHID = %s '.
|
||||
'AND usertime.dateEnded IS NOT NULL',
|
||||
$usertime_dao->getTableName(),
|
||||
$phid);
|
||||
|
||||
// Now calculate the time spent where the usertime
|
||||
// blocks have not yet ended.
|
||||
$sum_not_ended = queryfx_one(
|
||||
$conn,
|
||||
'SELECT SUM(UNIX_TIMESTAMP() - usertime.dateStarted) N '.
|
||||
'FROM %T usertime '.
|
||||
'WHERE usertime.objectPHID = %s '.
|
||||
'AND usertime.dateEnded IS NULL',
|
||||
$usertime_dao->getTableName(),
|
||||
$phid);
|
||||
|
||||
return $sum_ended['N'] + $sum_not_ended['N'];
|
||||
}
|
||||
|
||||
public static function getUserTimeSpentOnObject(
|
||||
PhabricatorUser $user,
|
||||
$phid) {
|
||||
|
||||
$usertime_dao = new PhrequentUserTime();
|
||||
$conn = $usertime_dao->establishConnection('r');
|
||||
|
||||
// First calculate all the time spent where the
|
||||
// usertime blocks have ended.
|
||||
$sum_ended = queryfx_one(
|
||||
$conn,
|
||||
'SELECT SUM(usertime.dateEnded - usertime.dateStarted) N '.
|
||||
'FROM %T usertime '.
|
||||
'WHERE usertime.userPHID = %s '.
|
||||
'AND usertime.objectPHID = %s '.
|
||||
'AND usertime.dateEnded IS NOT NULL',
|
||||
$usertime_dao->getTableName(),
|
||||
$user->getPHID(),
|
||||
$phid);
|
||||
|
||||
// Now calculate the time spent where the usertime
|
||||
// blocks have not yet ended.
|
||||
$sum_not_ended = queryfx_one(
|
||||
$conn,
|
||||
'SELECT SUM(UNIX_TIMESTAMP() - usertime.dateStarted) N '.
|
||||
'FROM %T usertime '.
|
||||
'WHERE usertime.userPHID = %s '.
|
||||
'AND usertime.objectPHID = %s '.
|
||||
'AND usertime.dateEnded IS NULL',
|
||||
$usertime_dao->getTableName(),
|
||||
$user->getPHID(),
|
||||
$phid);
|
||||
|
||||
return $sum_ended['N'] + $sum_not_ended['N'];
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue