Skip to content

Commit 6fd5651

Browse files
author
Michael Peters
committed
Showing tasks nested by subtasks. This was probably more work than it's worth
1 parent 738c827 commit 6fd5651

File tree

2 files changed

+128
-14
lines changed

2 files changed

+128
-14
lines changed

src/BurndownApplication.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ public function getBaseURI() {
1010
return '/burn/';
1111
}
1212

13+
public function getIconName() {
14+
return 'slowvote';
15+
}
16+
17+
public function getShortDescription() {
18+
return 'Build burndowns';
19+
}
1320

1421
public function getRoutes() {
1522
return array(

src/BurndownData.php

Lines changed: 121 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ public function __construct($project, $viewer) {
5050

5151
if (!$start OR !$end)
5252
{
53-
// TODO make this show a real error
5453
throw new Exception("This project is not set up for Burndowns, make "
5554
."sure it has 'Sprint' in the name, and then edit it to add the sprint "
5655
."start and end date.");
@@ -176,7 +175,15 @@ public function __construct($project, $viewer) {
176175
$previous = $current;
177176
}
178177

179-
// Compute "Ideal Points remaining" column
178+
$this->computeIdealPoints();
179+
180+
}
181+
182+
/**
183+
* Compute the values for the "Ideal Points" line.
184+
*/
185+
private function computeIdealPoints() {
186+
180187
// This is a cheap hacky way to get business days, and does not account for
181188
// holidays at all.
182189
$total_business_days = 0;
@@ -371,23 +378,14 @@ public function buildBurnDownTable() {
371378
* @returns PHUIObjectBoxView
372379
*/
373380
public function buildTasksTable() {
374-
$rows = array();
375-
foreach($this->tasks as $task) {
376-
$rows[] = array(
377-
phutil_tag(
378-
'a',
379-
array(
380-
'href' => '/'.$task->getMonogram(),
381-
),
382-
$task->getMonogram().': '.$task->getTitle()),
383-
$task->getStatus(),
384-
);
385-
}
381+
382+
$rows = $this->buildTasksTree();
386383

387384
$table = id(new AphrontTableView($rows))
388385
->setHeaders(
389386
array(
390387
pht('Task'),
388+
pht('Assigned to'),
391389
pht('Status'),
392390
));
393391

@@ -398,6 +396,115 @@ public function buildTasksTable() {
398396
return $box;
399397
}
400398

399+
/**
400+
* This builds a tree of the tasks in this project. Due to the acyclic nature
401+
* of tasks, we ntake some steps to reduce and call out duplication.
402+
*
403+
* We ignore any tasks not in this sprint.
404+
*
405+
* @return array
406+
*/
407+
private function buildTasksTree() {
408+
// Shorter constants
409+
$DEPENDS_ON = PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK;
410+
$DEPENDED_ON = PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK;
411+
412+
// Load all edges of depends and depended on tasks
413+
$edges = id(new PhabricatorEdgeQuery())
414+
->withSourcePHIDs(array_keys($this->tasks))
415+
->withEdgeTypes(array($DEPENDS_ON, $DEPENDED_ON))
416+
->execute();
417+
418+
// First we build a flat map. Each task is in the map at the root level,
419+
// and lists it's parents and children.
420+
$map = array();
421+
foreach ($this->tasks as $task) {
422+
if ($parents = $edges[$task->getPHID()][$DEPENDED_ON]) {
423+
foreach ($parents as $parent) {
424+
// Make sure this task is in this sprint.
425+
if (isset($this->tasks[$parent['dst']]))
426+
$map[$task->getPHID()]['parents'][] = $parent['dst'];
427+
}
428+
}
429+
430+
if ($children = $edges[$task->getPHID()][$DEPENDS_ON]) {
431+
foreach ($children as $child) {
432+
// Make sure this task is in this sprint.
433+
if (isset($this->tasks[$child['dst']])) {
434+
$map[$task->getPHID()]['children'][] = $child['dst'];
435+
}
436+
}
437+
}
438+
}
439+
440+
// We also collect the phids we need to fetch owner information
441+
$handle_phids = array();
442+
foreach($this->tasks as $task) {
443+
// Get the owner (assigned to) phid
444+
$handle_phids[$task->getOwnerPHID()] = $task->getOwnerPHID();
445+
}
446+
447+
$handles = id(new PhabricatorHandleQuery())
448+
->setViewer($this->viewer)
449+
->withPHIDs($handle_phids)
450+
->execute();
451+
452+
// Now we loop through the tasks, and add them to the output
453+
$output = array();
454+
foreach ($this->tasks as $task) {
455+
// If parents is set, it means this task has a parent in this sprint so
456+
// skip it, the parent will handle adding this task to the output
457+
if (isset($map[$task->getPHID()]['parents'])) {
458+
continue;
459+
}
460+
461+
$this->addTaskToTree($output, $task, $map, $handles);
462+
}
463+
464+
return $output;
465+
}
466+
467+
private function addTaskToTree(&$output, $task, &$map, $handles,
468+
$depth = 0) {
469+
static $included = array();
470+
471+
// Get the owner object so we can render the owner username/link
472+
$owner = $handles[$task->getOwnerPHID()];
473+
474+
// If this task is already is this tree, this is a repeat.
475+
$repeat = isset($included[$task->getPHID()]);
476+
477+
$depth_indent='';
478+
for($i=0; $i<$depth; $i++) {
479+
$depth_indent.='&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
480+
}
481+
482+
// Build the row
483+
$output[] = array(
484+
phutil_safe_html($depth_indent.phutil_tag(
485+
'a',
486+
array(
487+
'href' => '/'.$task->getMonogram(),
488+
'class' => $task->getStatus() !== 'open'
489+
? 'phui-tag-core-closed'
490+
: '',
491+
),
492+
$task->getMonogram().': '.$task->getTitle()
493+
).($repeat? '&nbsp;&nbsp;<em title="This task is a child of more than one task in this list. Children are only shown on '.
494+
'the first occurance">[Repeat]</em>':'')),
495+
$task->getOwnerPHID() ? $owner->renderLink() : 'none assigned',
496+
$task->getStatus(),
497+
);
498+
$included[$task->getPHID()] = $task->getPHID();
499+
500+
if (isset($map[$task->getPHID()]['children'])) {
501+
foreach($map[$task->getPHID()]['children'] as $child) {
502+
$child = $this->tasks[$child];
503+
$this->addTaskToTree($output, $child, $map, $handles, $depth+1);
504+
}
505+
}
506+
}
507+
401508
/**
402509
* Format the Event data for display on the page.
403510
*

0 commit comments

Comments
 (0)