1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-25 06:50:55 +01:00

Emit more usable results from phrequent.tracking

Summary:
I think this pretty much does what you would expect?

The "active" item is always at the top of the stack.

Test Plan: Called `phrequent.tracking` and got reasonable results.

Reviewers: hach-que

Reviewed By: hach-que

Subscribers: epriestley

Differential Revision: https://secure.phabricator.com/D9939
This commit is contained in:
epriestley 2014-07-16 17:12:38 -07:00
parent ae97617e36
commit ab3c17a2cd
6 changed files with 173 additions and 40 deletions

View file

@ -2550,6 +2550,7 @@ phutil_register_library_map(array(
'PhrequentSearchEngine' => 'applications/phrequent/query/PhrequentSearchEngine.php', 'PhrequentSearchEngine' => 'applications/phrequent/query/PhrequentSearchEngine.php',
'PhrequentTimeBlock' => 'applications/phrequent/storage/PhrequentTimeBlock.php', 'PhrequentTimeBlock' => 'applications/phrequent/storage/PhrequentTimeBlock.php',
'PhrequentTimeBlockTestCase' => 'applications/phrequent/storage/__tests__/PhrequentTimeBlockTestCase.php', 'PhrequentTimeBlockTestCase' => 'applications/phrequent/storage/__tests__/PhrequentTimeBlockTestCase.php',
'PhrequentTimeSlices' => 'applications/phrequent/storage/PhrequentTimeSlices.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', 'PhrequentTrackingEditor' => 'applications/phrequent/editor/PhrequentTrackingEditor.php',
@ -5521,6 +5522,7 @@ phutil_register_library_map(array(
'PhrequentSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhrequentSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhrequentTimeBlock' => 'Phobject', 'PhrequentTimeBlock' => 'Phobject',
'PhrequentTimeBlockTestCase' => 'PhabricatorTestCase', 'PhrequentTimeBlockTestCase' => 'PhabricatorTestCase',
'PhrequentTimeSlices' => 'Phobject',
'PhrequentTrackController' => 'PhrequentController', 'PhrequentTrackController' => 'PhrequentController',
'PhrequentTrackingEditor' => 'PhabricatorEditor', 'PhrequentTrackingEditor' => 'PhabricatorEditor',
'PhrequentUIEventListener' => 'PhabricatorEventListener', 'PhrequentUIEventListener' => 'PhabricatorEventListener',

View file

@ -31,6 +31,7 @@ final class ConduitAPI_phrequent_tracking_Method
$times = id(new PhrequentUserTimeQuery()) $times = id(new PhrequentUserTimeQuery())
->setViewer($user) ->setViewer($user)
->needPreemptingEvents(true) ->needPreemptingEvents(true)
->withEnded(PhrequentUserTimeQuery::ENDED_NO)
->withUserPHIDs(array($user->getPHID())) ->withUserPHIDs(array($user->getPHID()))
->execute(); ->execute();

View file

@ -199,7 +199,7 @@ final class PhrequentUserTimeQuery
$u_start = $u_event->getDateStarted(); $u_start = $u_event->getDateStarted();
$u_end = $u_event->getDateEnded(); $u_end = $u_event->getDateEnded();
if (($u_start >= $e_start) && ($u_end <= $e_end) && if (($u_start >= $e_start) &&
($u_end === null || $u_end > $e_start)) { ($u_end === null || $u_end > $e_start)) {
$select[] = $u_event; $select[] = $u_event;
} }

View file

@ -10,17 +10,16 @@ final class PhrequentTimeBlock extends Phobject {
} }
public function getTimeSpentOnObject($phid, $now) { public function getTimeSpentOnObject($phid, $now) {
$ranges = idx($this->getObjectTimeRanges($now), $phid, array()); $slices = idx($this->getObjectTimeRanges(), $phid);
$sum = 0; if (!$slices) {
foreach ($ranges as $range) { return null;
$sum += ($range[1] - $range[0]);
} }
return $sum; return $slices->getDuration($now);
} }
public function getObjectTimeRanges($now) { public function getObjectTimeRanges() {
$ranges = array(); $ranges = array();
$range_start = time(); $range_start = time();
@ -29,6 +28,7 @@ final class PhrequentTimeBlock extends Phobject {
} }
$object_ranges = array(); $object_ranges = array();
$object_ongoing = array();
foreach ($this->events as $event) { foreach ($this->events as $event) {
// First, convert each event's preempting stack into a linear timeline // First, convert each event's preempting stack into a linear timeline
@ -42,14 +42,16 @@ final class PhrequentTimeBlock extends Phobject {
); );
$timeline[] = array( $timeline[] = array(
'event' => $event, 'event' => $event,
'at' => (int)nonempty($event->getDateEnded(), $now), 'at' => (int)nonempty($event->getDateEnded(), PHP_INT_MAX),
'type' => 'end', 'type' => 'end',
); );
$base_phid = $event->getObjectPHID(); $base_phid = $event->getObjectPHID();
if (!$event->getDateEnded()) {
$object_ongoing[$base_phid] = true;
}
$preempts = $event->getPreemptingEvents(); $preempts = $event->getPreemptingEvents();
foreach ($preempts as $preempt) { foreach ($preempts as $preempt) {
$same_object = ($preempt->getObjectPHID() == $base_phid); $same_object = ($preempt->getObjectPHID() == $base_phid);
$timeline[] = array( $timeline[] = array(
@ -59,7 +61,7 @@ final class PhrequentTimeBlock extends Phobject {
); );
$timeline[] = array( $timeline[] = array(
'event' => $preempt, 'event' => $preempt,
'at' => (int)nonempty($preempt->getDateEnded(), $now), 'at' => (int)nonempty($preempt->getDateEnded(), PHP_INT_MAX),
'type' => $same_object ? 'end' : 'pop', 'type' => $same_object ? 'end' : 'pop',
); );
} }
@ -89,7 +91,6 @@ final class PhrequentTimeBlock extends Phobject {
$stratum = null; $stratum = null;
$strata = array(); $strata = array();
$ranges = array(); $ranges = array();
foreach ($timeline as $timeline_event) { foreach ($timeline as $timeline_event) {
$id = $timeline_event['event']->getID(); $id = $timeline_event['event']->getID();
@ -173,15 +174,39 @@ final class PhrequentTimeBlock extends Phobject {
} }
} }
// Filter out ranges with an indefinite start time. These occur when
// popping the stack when there are multiple ongoing events.
foreach ($ranges as $key => $range) {
if ($range[0] == PHP_INT_MAX) {
unset($ranges[$key]);
}
}
$object_ranges[$base_phid][] = $ranges; $object_ranges[$base_phid][] = $ranges;
} }
// Finally, collapse all the ranges so we don't double-count time. // Collapse all the ranges so we don't double-count time.
foreach ($object_ranges as $phid => $ranges) { foreach ($object_ranges as $phid => $ranges) {
$object_ranges[$phid] = self::mergeTimeRanges(array_mergev($ranges)); $object_ranges[$phid] = self::mergeTimeRanges(array_mergev($ranges));
} }
foreach ($object_ranges as $phid => $ranges) {
foreach ($ranges as $key => $range) {
if ($range[1] == PHP_INT_MAX) {
$ranges[$key][1] = null;
}
}
$object_ranges[$phid] = new PhrequentTimeSlices(
$phid,
isset($object_ongoing[$phid]),
$ranges);
}
// Reorder the ranges to be more stack-like, so the first item is the
// top of the stack.
$object_ranges = array_reverse($object_ranges, $preserve_keys = true);
return $object_ranges; return $object_ranges;
} }
@ -189,33 +214,22 @@ final class PhrequentTimeBlock extends Phobject {
* Returns the current list of work. * Returns the current list of work.
*/ */
public function getCurrentWorkStack($now, $include_inactive = false) { public function getCurrentWorkStack($now, $include_inactive = false) {
$ranges = $this->getObjectTimeRanges($now); $ranges = $this->getObjectTimeRanges();
$results = array(); $results = array();
foreach ($ranges as $phid => $blocks) { $active = null;
$total = 0; foreach ($ranges as $phid => $slices) {
foreach ($blocks as $block) { if (!$include_inactive) {
$total += $block[1] - $block[0]; if (!$slices->getIsOngoing()) {
} continue;
$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( $results[] = array(
'phid' => $phid, 'phid' => $phid,
'time' => $total, 'time' => $slices->getDuration($now),
'type' => $type); 'ongoing' => $slices->getIsOngoing(),
} );
} }
return $results; return $results;

View file

@ -0,0 +1,37 @@
<?php
final class PhrequentTimeSlices extends Phobject {
private $objectPHID;
private $isOngoing;
private $ranges;
public function __construct($object_phid, $is_ongoing, array $ranges) {
$this->objectPHID = $object_phid;
$this->isOngoing = $is_ongoing;
$this->ranges = $ranges;
}
public function getObjectPHID() {
return $this->objectPHID;
}
public function getDuration($now) {
foreach ($this->ranges as $range) {
if ($range[1] === null) {
return $now - $range[0];
} else {
return $range[1] - $range[0];
}
}
}
public function getIsOngoing() {
return $this->isOngoing;
}
public function getRanges() {
return $this->ranges;
}
}

View file

@ -86,7 +86,9 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
$block = new PhrequentTimeBlock(array($event)); $block = new PhrequentTimeBlock(array($event));
$ranges = $block->getObjectTimeRanges(1800); $ranges = $block->getObjectTimeRanges();
$ranges = $this->reduceRanges($ranges);
$this->assertEqual( $this->assertEqual(
array( array(
'T1' => array( 'T1' => array(
@ -107,7 +109,9 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
$block = new PhrequentTimeBlock(array($event)); $block = new PhrequentTimeBlock(array($event));
$ranges = $block->getObjectTimeRanges(1000); $ranges = $block->getObjectTimeRanges();
$ranges = $this->reduceRanges($ranges);
$this->assertEqual( $this->assertEqual(
array( array(
'T2' => array( 'T2' => array(
@ -150,7 +154,9 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
$block = new PhrequentTimeBlock(array($event)); $block = new PhrequentTimeBlock(array($event));
$ranges = $block->getObjectTimeRanges(1800); $ranges = $block->getObjectTimeRanges();
$ranges = $this->reduceRanges($ranges);
$this->assertEqual( $this->assertEqual(
array( array(
'T1' => array( 'T1' => array(
@ -172,7 +178,8 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
$block = new PhrequentTimeBlock(array($event)); $block = new PhrequentTimeBlock(array($event));
$ranges = $block->getObjectTimeRanges(1000); $ranges = $block->getObjectTimeRanges();
$ranges = $this->reduceRanges($ranges);
$this->assertEqual( $this->assertEqual(
array( array(
@ -198,7 +205,8 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
$block = new PhrequentTimeBlock(array($event)); $block = new PhrequentTimeBlock(array($event));
$ranges = $block->getObjectTimeRanges(1000); $ranges = $block->getObjectTimeRanges();
$ranges = $this->reduceRanges($ranges);
$this->assertEqual( $this->assertEqual(
array( array(
@ -213,6 +221,67 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
$ranges); $ranges);
} }
public function testOngoing() {
$event = $this->newEvent('T1', 1, null);
$event->attachPreemptingEvents(array());
$block = new PhrequentTimeBlock(array($event));
$ranges = $block->getObjectTimeRanges();
$ranges = $this->reduceRanges($ranges);
$this->assertEqual(
array(
'T1' => array(
array(1, null),
),
),
$ranges);
}
public function testOngoingInterrupted() {
$event = $this->newEvent('T1', 1, null);
$event->attachPreemptingEvents(
array(
$this->newEvent('T2', 100, 900),
));
$block = new PhrequentTimeBlock(array($event));
$ranges = $block->getObjectTimeRanges();
$ranges = $this->reduceRanges($ranges);
$this->assertEqual(
array(
'T1' => array(
array(1, 100),
array(900, null)
),
),
$ranges);
}
public function testOngoingPreempted() {
$event = $this->newEvent('T1', 1, null);
$event->attachPreemptingEvents(
array(
$this->newEvent('T2', 100, null),
));
$block = new PhrequentTimeBlock(array($event));
$ranges = $block->getObjectTimeRanges();
$ranges = $this->reduceRanges($ranges);
$this->assertEqual(
array(
'T1' => array(
array(1, 100),
),
),
$ranges);
}
private function newEvent($object_phid, $start_time, $end_time) { private function newEvent($object_phid, $start_time, $end_time) {
static $id = 0; static $id = 0;
@ -223,4 +292,14 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
->setDateEnded($end_time); ->setDateEnded($end_time);
} }
private function reduceRanges(array $ranges) {
$results = array();
foreach ($ranges as $phid => $slices) {
$results[$phid] = $slices->getRanges();
}
return $results;
}
} }