@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.

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

+173 -40
+2
src/__phutil_library_map__.php
··· 2550 2550 'PhrequentSearchEngine' => 'applications/phrequent/query/PhrequentSearchEngine.php', 2551 2551 'PhrequentTimeBlock' => 'applications/phrequent/storage/PhrequentTimeBlock.php', 2552 2552 'PhrequentTimeBlockTestCase' => 'applications/phrequent/storage/__tests__/PhrequentTimeBlockTestCase.php', 2553 + 'PhrequentTimeSlices' => 'applications/phrequent/storage/PhrequentTimeSlices.php', 2553 2554 'PhrequentTrackController' => 'applications/phrequent/controller/PhrequentTrackController.php', 2554 2555 'PhrequentTrackableInterface' => 'applications/phrequent/interface/PhrequentTrackableInterface.php', 2555 2556 'PhrequentTrackingEditor' => 'applications/phrequent/editor/PhrequentTrackingEditor.php', ··· 5521 5522 'PhrequentSearchEngine' => 'PhabricatorApplicationSearchEngine', 5522 5523 'PhrequentTimeBlock' => 'Phobject', 5523 5524 'PhrequentTimeBlockTestCase' => 'PhabricatorTestCase', 5525 + 'PhrequentTimeSlices' => 'Phobject', 5524 5526 'PhrequentTrackController' => 'PhrequentController', 5525 5527 'PhrequentTrackingEditor' => 'PhabricatorEditor', 5526 5528 'PhrequentUIEventListener' => 'PhabricatorEventListener',
+1
src/applications/phrequent/conduit/ConduitAPI_phrequent_tracking_Method.php
··· 31 31 $times = id(new PhrequentUserTimeQuery()) 32 32 ->setViewer($user) 33 33 ->needPreemptingEvents(true) 34 + ->withEnded(PhrequentUserTimeQuery::ENDED_NO) 34 35 ->withUserPHIDs(array($user->getPHID())) 35 36 ->execute(); 36 37
+1 -1
src/applications/phrequent/query/PhrequentUserTimeQuery.php
··· 199 199 $u_start = $u_event->getDateStarted(); 200 200 $u_end = $u_event->getDateEnded(); 201 201 202 - if (($u_start >= $e_start) && ($u_end <= $e_end) && 202 + if (($u_start >= $e_start) && 203 203 ($u_end === null || $u_end > $e_start)) { 204 204 $select[] = $u_event; 205 205 }
+48 -34
src/applications/phrequent/storage/PhrequentTimeBlock.php
··· 10 10 } 11 11 12 12 public function getTimeSpentOnObject($phid, $now) { 13 - $ranges = idx($this->getObjectTimeRanges($now), $phid, array()); 13 + $slices = idx($this->getObjectTimeRanges(), $phid); 14 14 15 - $sum = 0; 16 - foreach ($ranges as $range) { 17 - $sum += ($range[1] - $range[0]); 15 + if (!$slices) { 16 + return null; 18 17 } 19 18 20 - return $sum; 19 + return $slices->getDuration($now); 21 20 } 22 21 23 - public function getObjectTimeRanges($now) { 22 + public function getObjectTimeRanges() { 24 23 $ranges = array(); 25 24 26 25 $range_start = time(); ··· 29 28 } 30 29 31 30 $object_ranges = array(); 31 + $object_ongoing = array(); 32 32 foreach ($this->events as $event) { 33 33 34 34 // First, convert each event's preempting stack into a linear timeline ··· 42 42 ); 43 43 $timeline[] = array( 44 44 'event' => $event, 45 - 'at' => (int)nonempty($event->getDateEnded(), $now), 45 + 'at' => (int)nonempty($event->getDateEnded(), PHP_INT_MAX), 46 46 'type' => 'end', 47 47 ); 48 48 49 49 $base_phid = $event->getObjectPHID(); 50 + if (!$event->getDateEnded()) { 51 + $object_ongoing[$base_phid] = true; 52 + } 50 53 51 54 $preempts = $event->getPreemptingEvents(); 52 - 53 55 foreach ($preempts as $preempt) { 54 56 $same_object = ($preempt->getObjectPHID() == $base_phid); 55 57 $timeline[] = array( ··· 59 61 ); 60 62 $timeline[] = array( 61 63 'event' => $preempt, 62 - 'at' => (int)nonempty($preempt->getDateEnded(), $now), 64 + 'at' => (int)nonempty($preempt->getDateEnded(), PHP_INT_MAX), 63 65 'type' => $same_object ? 'end' : 'pop', 64 66 ); 65 67 } ··· 88 90 89 91 $stratum = null; 90 92 $strata = array(); 91 - 92 93 93 94 $ranges = array(); 94 95 foreach ($timeline as $timeline_event) { ··· 173 174 } 174 175 } 175 176 177 + // Filter out ranges with an indefinite start time. These occur when 178 + // popping the stack when there are multiple ongoing events. 179 + foreach ($ranges as $key => $range) { 180 + if ($range[0] == PHP_INT_MAX) { 181 + unset($ranges[$key]); 182 + } 183 + } 184 + 176 185 $object_ranges[$base_phid][] = $ranges; 177 186 } 178 187 179 - // Finally, collapse all the ranges so we don't double-count time. 180 - 188 + // Collapse all the ranges so we don't double-count time. 181 189 foreach ($object_ranges as $phid => $ranges) { 182 190 $object_ranges[$phid] = self::mergeTimeRanges(array_mergev($ranges)); 183 191 } 184 192 193 + foreach ($object_ranges as $phid => $ranges) { 194 + foreach ($ranges as $key => $range) { 195 + if ($range[1] == PHP_INT_MAX) { 196 + $ranges[$key][1] = null; 197 + } 198 + } 199 + 200 + $object_ranges[$phid] = new PhrequentTimeSlices( 201 + $phid, 202 + isset($object_ongoing[$phid]), 203 + $ranges); 204 + } 205 + 206 + // Reorder the ranges to be more stack-like, so the first item is the 207 + // top of the stack. 208 + $object_ranges = array_reverse($object_ranges, $preserve_keys = true); 209 + 185 210 return $object_ranges; 186 211 } 187 212 ··· 189 214 * Returns the current list of work. 190 215 */ 191 216 public function getCurrentWorkStack($now, $include_inactive = false) { 192 - $ranges = $this->getObjectTimeRanges($now); 217 + $ranges = $this->getObjectTimeRanges(); 193 218 194 219 $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; 220 + $active = null; 221 + foreach ($ranges as $phid => $slices) { 222 + if (!$include_inactive) { 223 + if (!$slices->getIsOngoing()) { 224 + continue; 210 225 } 211 226 } 212 227 213 - if ($include_inactive || $type !== 'inactive') { 214 - $results[] = array( 215 - 'phid' => $phid, 216 - 'time' => $total, 217 - 'type' => $type); 218 - } 228 + $results[] = array( 229 + 'phid' => $phid, 230 + 'time' => $slices->getDuration($now), 231 + 'ongoing' => $slices->getIsOngoing(), 232 + ); 219 233 } 220 234 221 235 return $results;
+37
src/applications/phrequent/storage/PhrequentTimeSlices.php
··· 1 + <?php 2 + 3 + final class PhrequentTimeSlices extends Phobject { 4 + 5 + private $objectPHID; 6 + private $isOngoing; 7 + private $ranges; 8 + 9 + public function __construct($object_phid, $is_ongoing, array $ranges) { 10 + $this->objectPHID = $object_phid; 11 + $this->isOngoing = $is_ongoing; 12 + $this->ranges = $ranges; 13 + } 14 + 15 + public function getObjectPHID() { 16 + return $this->objectPHID; 17 + } 18 + 19 + public function getDuration($now) { 20 + foreach ($this->ranges as $range) { 21 + if ($range[1] === null) { 22 + return $now - $range[0]; 23 + } else { 24 + return $range[1] - $range[0]; 25 + } 26 + } 27 + } 28 + 29 + public function getIsOngoing() { 30 + return $this->isOngoing; 31 + } 32 + 33 + public function getRanges() { 34 + return $this->ranges; 35 + } 36 + 37 + }
+84 -5
src/applications/phrequent/storage/__tests__/PhrequentTimeBlockTestCase.php
··· 86 86 87 87 $block = new PhrequentTimeBlock(array($event)); 88 88 89 - $ranges = $block->getObjectTimeRanges(1800); 89 + $ranges = $block->getObjectTimeRanges(); 90 + $ranges = $this->reduceRanges($ranges); 91 + 90 92 $this->assertEqual( 91 93 array( 92 94 'T1' => array( ··· 107 109 108 110 $block = new PhrequentTimeBlock(array($event)); 109 111 110 - $ranges = $block->getObjectTimeRanges(1000); 112 + $ranges = $block->getObjectTimeRanges(); 113 + $ranges = $this->reduceRanges($ranges); 114 + 111 115 $this->assertEqual( 112 116 array( 113 117 'T2' => array( ··· 150 154 151 155 $block = new PhrequentTimeBlock(array($event)); 152 156 153 - $ranges = $block->getObjectTimeRanges(1800); 157 + $ranges = $block->getObjectTimeRanges(); 158 + $ranges = $this->reduceRanges($ranges); 159 + 154 160 $this->assertEqual( 155 161 array( 156 162 'T1' => array( ··· 172 178 173 179 $block = new PhrequentTimeBlock(array($event)); 174 180 175 - $ranges = $block->getObjectTimeRanges(1000); 181 + $ranges = $block->getObjectTimeRanges(); 182 + $ranges = $this->reduceRanges($ranges); 176 183 177 184 $this->assertEqual( 178 185 array( ··· 198 205 199 206 $block = new PhrequentTimeBlock(array($event)); 200 207 201 - $ranges = $block->getObjectTimeRanges(1000); 208 + $ranges = $block->getObjectTimeRanges(); 209 + $ranges = $this->reduceRanges($ranges); 202 210 203 211 $this->assertEqual( 204 212 array( ··· 213 221 $ranges); 214 222 } 215 223 224 + public function testOngoing() { 225 + $event = $this->newEvent('T1', 1, null); 226 + $event->attachPreemptingEvents(array()); 227 + 228 + $block = new PhrequentTimeBlock(array($event)); 229 + 230 + $ranges = $block->getObjectTimeRanges(); 231 + $ranges = $this->reduceRanges($ranges); 232 + 233 + $this->assertEqual( 234 + array( 235 + 'T1' => array( 236 + array(1, null), 237 + ), 238 + ), 239 + $ranges); 240 + } 241 + 242 + public function testOngoingInterrupted() { 243 + $event = $this->newEvent('T1', 1, null); 244 + $event->attachPreemptingEvents( 245 + array( 246 + $this->newEvent('T2', 100, 900), 247 + )); 248 + 249 + $block = new PhrequentTimeBlock(array($event)); 250 + 251 + $ranges = $block->getObjectTimeRanges(); 252 + $ranges = $this->reduceRanges($ranges); 253 + 254 + $this->assertEqual( 255 + array( 256 + 'T1' => array( 257 + array(1, 100), 258 + array(900, null) 259 + ), 260 + ), 261 + $ranges); 262 + } 263 + 264 + public function testOngoingPreempted() { 265 + $event = $this->newEvent('T1', 1, null); 266 + $event->attachPreemptingEvents( 267 + array( 268 + $this->newEvent('T2', 100, null), 269 + )); 270 + 271 + $block = new PhrequentTimeBlock(array($event)); 272 + 273 + $ranges = $block->getObjectTimeRanges(); 274 + $ranges = $this->reduceRanges($ranges); 275 + 276 + $this->assertEqual( 277 + array( 278 + 'T1' => array( 279 + array(1, 100), 280 + ), 281 + ), 282 + $ranges); 283 + } 284 + 216 285 private function newEvent($object_phid, $start_time, $end_time) { 217 286 static $id = 0; 218 287 ··· 221 290 ->setObjectPHID($object_phid) 222 291 ->setDateStarted($start_time) 223 292 ->setDateEnded($end_time); 293 + } 294 + 295 + private function reduceRanges(array $ranges) { 296 + $results = array(); 297 + 298 + foreach ($ranges as $phid => $slices) { 299 + $results[$phid] = $slices->getRanges(); 300 + } 301 + 302 + return $results; 224 303 } 225 304 226 305 }