mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-24 14:30:56 +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:
parent
ae97617e36
commit
ab3c17a2cd
6 changed files with 173 additions and 40 deletions
|
@ -2550,6 +2550,7 @@ phutil_register_library_map(array(
|
|||
'PhrequentSearchEngine' => 'applications/phrequent/query/PhrequentSearchEngine.php',
|
||||
'PhrequentTimeBlock' => 'applications/phrequent/storage/PhrequentTimeBlock.php',
|
||||
'PhrequentTimeBlockTestCase' => 'applications/phrequent/storage/__tests__/PhrequentTimeBlockTestCase.php',
|
||||
'PhrequentTimeSlices' => 'applications/phrequent/storage/PhrequentTimeSlices.php',
|
||||
'PhrequentTrackController' => 'applications/phrequent/controller/PhrequentTrackController.php',
|
||||
'PhrequentTrackableInterface' => 'applications/phrequent/interface/PhrequentTrackableInterface.php',
|
||||
'PhrequentTrackingEditor' => 'applications/phrequent/editor/PhrequentTrackingEditor.php',
|
||||
|
@ -5521,6 +5522,7 @@ phutil_register_library_map(array(
|
|||
'PhrequentSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhrequentTimeBlock' => 'Phobject',
|
||||
'PhrequentTimeBlockTestCase' => 'PhabricatorTestCase',
|
||||
'PhrequentTimeSlices' => 'Phobject',
|
||||
'PhrequentTrackController' => 'PhrequentController',
|
||||
'PhrequentTrackingEditor' => 'PhabricatorEditor',
|
||||
'PhrequentUIEventListener' => 'PhabricatorEventListener',
|
||||
|
|
|
@ -31,6 +31,7 @@ final class ConduitAPI_phrequent_tracking_Method
|
|||
$times = id(new PhrequentUserTimeQuery())
|
||||
->setViewer($user)
|
||||
->needPreemptingEvents(true)
|
||||
->withEnded(PhrequentUserTimeQuery::ENDED_NO)
|
||||
->withUserPHIDs(array($user->getPHID()))
|
||||
->execute();
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ final class PhrequentUserTimeQuery
|
|||
$u_start = $u_event->getDateStarted();
|
||||
$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)) {
|
||||
$select[] = $u_event;
|
||||
}
|
||||
|
|
|
@ -10,17 +10,16 @@ final class PhrequentTimeBlock extends Phobject {
|
|||
}
|
||||
|
||||
public function getTimeSpentOnObject($phid, $now) {
|
||||
$ranges = idx($this->getObjectTimeRanges($now), $phid, array());
|
||||
$slices = idx($this->getObjectTimeRanges(), $phid);
|
||||
|
||||
$sum = 0;
|
||||
foreach ($ranges as $range) {
|
||||
$sum += ($range[1] - $range[0]);
|
||||
if (!$slices) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $sum;
|
||||
return $slices->getDuration($now);
|
||||
}
|
||||
|
||||
public function getObjectTimeRanges($now) {
|
||||
public function getObjectTimeRanges() {
|
||||
$ranges = array();
|
||||
|
||||
$range_start = time();
|
||||
|
@ -29,6 +28,7 @@ final class PhrequentTimeBlock extends Phobject {
|
|||
}
|
||||
|
||||
$object_ranges = array();
|
||||
$object_ongoing = array();
|
||||
foreach ($this->events as $event) {
|
||||
|
||||
// First, convert each event's preempting stack into a linear timeline
|
||||
|
@ -42,14 +42,16 @@ final class PhrequentTimeBlock extends Phobject {
|
|||
);
|
||||
$timeline[] = array(
|
||||
'event' => $event,
|
||||
'at' => (int)nonempty($event->getDateEnded(), $now),
|
||||
'at' => (int)nonempty($event->getDateEnded(), PHP_INT_MAX),
|
||||
'type' => 'end',
|
||||
);
|
||||
|
||||
$base_phid = $event->getObjectPHID();
|
||||
if (!$event->getDateEnded()) {
|
||||
$object_ongoing[$base_phid] = true;
|
||||
}
|
||||
|
||||
$preempts = $event->getPreemptingEvents();
|
||||
|
||||
foreach ($preempts as $preempt) {
|
||||
$same_object = ($preempt->getObjectPHID() == $base_phid);
|
||||
$timeline[] = array(
|
||||
|
@ -59,7 +61,7 @@ final class PhrequentTimeBlock extends Phobject {
|
|||
);
|
||||
$timeline[] = array(
|
||||
'event' => $preempt,
|
||||
'at' => (int)nonempty($preempt->getDateEnded(), $now),
|
||||
'at' => (int)nonempty($preempt->getDateEnded(), PHP_INT_MAX),
|
||||
'type' => $same_object ? 'end' : 'pop',
|
||||
);
|
||||
}
|
||||
|
@ -89,7 +91,6 @@ final class PhrequentTimeBlock extends Phobject {
|
|||
$stratum = null;
|
||||
$strata = array();
|
||||
|
||||
|
||||
$ranges = array();
|
||||
foreach ($timeline as $timeline_event) {
|
||||
$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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
$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;
|
||||
}
|
||||
|
||||
|
@ -189,33 +214,22 @@ final class PhrequentTimeBlock extends Phobject {
|
|||
* Returns the current list of work.
|
||||
*/
|
||||
public function getCurrentWorkStack($now, $include_inactive = false) {
|
||||
$ranges = $this->getObjectTimeRanges($now);
|
||||
$ranges = $this->getObjectTimeRanges();
|
||||
|
||||
$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;
|
||||
$active = null;
|
||||
foreach ($ranges as $phid => $slices) {
|
||||
if (!$include_inactive) {
|
||||
if (!$slices->getIsOngoing()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($include_inactive || $type !== 'inactive') {
|
||||
$results[] = array(
|
||||
'phid' => $phid,
|
||||
'time' => $total,
|
||||
'type' => $type);
|
||||
}
|
||||
'time' => $slices->getDuration($now),
|
||||
'ongoing' => $slices->getIsOngoing(),
|
||||
);
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
|
37
src/applications/phrequent/storage/PhrequentTimeSlices.php
Normal file
37
src/applications/phrequent/storage/PhrequentTimeSlices.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -86,7 +86,9 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
|
|||
|
||||
$block = new PhrequentTimeBlock(array($event));
|
||||
|
||||
$ranges = $block->getObjectTimeRanges(1800);
|
||||
$ranges = $block->getObjectTimeRanges();
|
||||
$ranges = $this->reduceRanges($ranges);
|
||||
|
||||
$this->assertEqual(
|
||||
array(
|
||||
'T1' => array(
|
||||
|
@ -107,7 +109,9 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
|
|||
|
||||
$block = new PhrequentTimeBlock(array($event));
|
||||
|
||||
$ranges = $block->getObjectTimeRanges(1000);
|
||||
$ranges = $block->getObjectTimeRanges();
|
||||
$ranges = $this->reduceRanges($ranges);
|
||||
|
||||
$this->assertEqual(
|
||||
array(
|
||||
'T2' => array(
|
||||
|
@ -150,7 +154,9 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
|
|||
|
||||
$block = new PhrequentTimeBlock(array($event));
|
||||
|
||||
$ranges = $block->getObjectTimeRanges(1800);
|
||||
$ranges = $block->getObjectTimeRanges();
|
||||
$ranges = $this->reduceRanges($ranges);
|
||||
|
||||
$this->assertEqual(
|
||||
array(
|
||||
'T1' => array(
|
||||
|
@ -172,7 +178,8 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
|
|||
|
||||
$block = new PhrequentTimeBlock(array($event));
|
||||
|
||||
$ranges = $block->getObjectTimeRanges(1000);
|
||||
$ranges = $block->getObjectTimeRanges();
|
||||
$ranges = $this->reduceRanges($ranges);
|
||||
|
||||
$this->assertEqual(
|
||||
array(
|
||||
|
@ -198,7 +205,8 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
|
|||
|
||||
$block = new PhrequentTimeBlock(array($event));
|
||||
|
||||
$ranges = $block->getObjectTimeRanges(1000);
|
||||
$ranges = $block->getObjectTimeRanges();
|
||||
$ranges = $this->reduceRanges($ranges);
|
||||
|
||||
$this->assertEqual(
|
||||
array(
|
||||
|
@ -213,6 +221,67 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
|
|||
$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) {
|
||||
static $id = 0;
|
||||
|
||||
|
@ -223,4 +292,14 @@ final class PhrequentTimeBlockTestCase extends PhabricatorTestCase {
|
|||
->setDateEnded($end_time);
|
||||
}
|
||||
|
||||
private function reduceRanges(array $ranges) {
|
||||
$results = array();
|
||||
|
||||
foreach ($ranges as $phid => $slices) {
|
||||
$results[$phid] = $slices->getRanges();
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue