Christopher Johnson (WMDE) has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/172664

Change subject: Refactor BurndownDataView
......................................................................

Refactor BurndownDataView

Change-Id: I9f1b6b5c0fa46e58ff362aa0009ce2b8da6b4438
---
A scruntizer.yml
M src/__phutil_library_map__.php
M src/storage/SprintBuildStats.php
M src/util/BurndownDataDate.php
M src/view/BurndownDataView.php
A src/view/EventTableView.php
A src/view/SprintTableView.php
A src/view/TasksTableView.php
8 files changed, 447 insertions(+), 349 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/phabricator/extensions/Sprint 
refs/changes/64/172664/1

diff --git a/scruntizer.yml b/scruntizer.yml
new file mode 100644
index 0000000..d7450ca
--- /dev/null
+++ b/scruntizer.yml
@@ -0,0 +1,3 @@
+# .scrutinizer.yml
+tools:
+    external_code_coverage: true
\ No newline at end of file
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index b9da3d2..d7728ef 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -20,6 +20,7 @@
     'BurndownListController' => 'controller/BurndownListController.php',
     'BurndownTestDataGenerator' => '__tests__/BurndownTestDataGenerator.php',
     'CeleritySprintResources' => 'celerity/CeleritySprintResources.php',
+    'EventTableView' => 'view/EventTableView.php',
     'OpenTasksView' => 'view/OpenTasksView.php',
     'ProjectOpenTasksView' => 'view/ProjectOpenTasksView.php',
     'SprintBeginDateField' => 'customfield/SprintBeginDateField.php',
@@ -32,10 +33,12 @@
     'SprintReportBurndownView' => 'view/SprintReportBurndownView.php',
     'SprintReportController' => 'controller/SprintReportController.php',
     'SprintReportOpenTasksView' => 'view/SprintReportOpenTasksView.php',
+    'SprintTableView' => 'view/SprintTableView.php',
     'SprintTaskStoryPointsField' => 
'customfield/SprintTaskStoryPointsField.php',
     'SprintTestCase' => '__tests__/SprintTestCase.php',
     'SprintTransaction' => 'storage/SprintTransaction.php',
     'SprintView' => 'view/SprintView.php',
+    'TasksTableView' => 'view/TasksTableView.php',
     'UserOpenTasksView' => 'view/UserOpenTasksView.php',
     'ViewController' => 'controller/ViewController.php',
   ),
diff --git a/src/storage/SprintBuildStats.php b/src/storage/SprintBuildStats.php
index 263c3bc..f5c63aa 100644
--- a/src/storage/SprintBuildStats.php
+++ b/src/storage/SprintBuildStats.php
@@ -2,6 +2,8 @@
 
 final class SprintBuildStats {
   private $timezone;
+  private $task_open_status_sum;
+  private $task_closed_status_sum;
 
   public function setTimezone ($viewer) {
     $this->timezone = new DateTimeZone($viewer->getTimezoneIdentifier());
@@ -40,7 +42,7 @@
   // up the relevant columns
   public function sumSprintStats($dates) {
     $previous = null;
-    foreach ($dates as $current) {
+    foreach ($dates as $key => $current) {
       $current->setTasksTotal($current->getTasksAddedToday());
       $current->setPointsTotal($current->getPointsAddedToday());
       
$current->setTasksRemaining($current->getTasksAddedToday()-$current->getTasksClosedToday());
@@ -122,4 +124,5 @@
     $task_closed_status_sum += $points;
     return $task_closed_status_sum;
   }
+
 }
diff --git a/src/util/BurndownDataDate.php b/src/util/BurndownDataDate.php
index ef227ea..9cfa89d 100644
--- a/src/util/BurndownDataDate.php
+++ b/src/util/BurndownDataDate.php
@@ -90,8 +90,8 @@
     return $this->tasks_total;
   }
 
-  public function setTasksRemaining($tasks_added_today) {
-    $this->tasks_remaining = $tasks_added_today;
+  public function setTasksRemaining($tasks_remaining) {
+    $this->tasks_remaining = $tasks_remaining;
     return $this->tasks_remaining;
   }
 
diff --git a/src/view/BurndownDataView.php b/src/view/BurndownDataView.php
index 452339c..4828e75 100644
--- a/src/view/BurndownDataView.php
+++ b/src/view/BurndownDataView.php
@@ -4,7 +4,8 @@
  * @license GPL version 3
  */
 
-final class BurndownDataView extends SprintView {
+final class BurndownDataView extends SprintView
+{
 
   private $request;
   private $timeseries;
@@ -13,37 +14,47 @@
   private $viewer;
   private $tasks;
   private $xactions;
-  private $task_open_status_sum;
-  private $task_closed_status_sum;
+  private $tasks_table;
 
-  public function setProject ($project) {
+  public function setProject($project)
+  {
     $this->project = $project;
     return $this;
   }
 
-  public function setViewer ($viewer) {
+  public function setViewer($viewer)
+  {
     $this->viewer = $viewer;
     return $this;
   }
 
-  public function setRequest ($request) {
-    $this->request =  $request;
+  public function setRequest($request)
+  {
+    $this->request = $request;
     return $this;
   }
 
-  public function render() {
+  public function render()
+  {
     $chart = $this->buildC3Chart();
-    $tasks_table = $this->buildTasksTable();
+    $tasks_table = id(new TasksTableView())
+        ->setProject($this->project)
+        ->setViewer($this->viewer)
+        ->setRequest($this->request);
+    $tasks_table = $tasks_table->buildTasksTable();
     $pie = $this->buildC3Pie();
-    $burndown_table = $this->buildBurnDownTable();
-    $event_table = $this->buildEventTable();
-    return array ($chart, $tasks_table, $pie, $burndown_table, $event_table);
+    $sprint_table = new SprintTableView();
+    $burndown_table = $sprint_table->buildBurnDownTable($this->sprint_data);
+    $event_table = new EventTableView();
+    $event_table = $event_table->buildEventTable($this->project, 
$this->viewer);
+    return array($chart, $tasks_table, $pie, $burndown_table, $event_table);
   }
 
-  private function buildChartDataSet() {
+  private function buildChartDataSet()
+  {
     $query = id(new SprintQuery())
-         ->setProject($this->project)
-         ->setViewer($this->viewer);
+        ->setProject($this->project)
+        ->setViewer($this->viewer);
     $aux_fields = $query->getAuxFields();
     $start = $query->getStartDate($aux_fields);
     $end = $query->getEndDate($aux_fields);
@@ -71,14 +82,16 @@
     return $data;
   }
 
-  private function setSprintData($dates) {
+  private function setSprintData($dates)
+  {
     $stats = id(new SprintBuildStats());
     $dates = $stats->sumSprintStats($dates);
     $sprint_data = $stats->computeIdealPoints($dates);
     return $sprint_data;
-}
+  }
 
-  private function transposeArray($array) {
+  private function transposeArray($array)
+  {
     $transposed_array = array();
     if ($array) {
       foreach ($array as $row_key => $row) {
@@ -92,9 +105,10 @@
       }
     }
     return $transposed_array;
-   }
+  }
 
-  private function buildC3Chart() {
+  private function buildC3Chart()
+  {
     $data = $this->buildChartDataSet();
     $totalpoints = $data[0];
     $remainingpoints = $data[1];
@@ -102,9 +116,9 @@
     $pointstoday = $data[3];
     $timeseries = $this->timeseries;
 
-    require_celerity_resource('d3','sprint');
-    require_celerity_resource('c3-css','sprint');
-    require_celerity_resource('c3','sprint');
+    require_celerity_resource('d3', 'sprint');
+    require_celerity_resource('c3-css', 'sprint');
+    require_celerity_resource('c3', 'sprint');
 
     $id = 'chart';
     Javelin::initBehavior('c3-chart', array(
@@ -112,13 +126,13 @@
         'timeseries' => $timeseries,
         'totalpoints' => $totalpoints,
         'remainingpoints' => $remainingpoints,
-        'idealpoints' =>   $idealpoints,
-        'pointstoday' =>   $pointstoday
+        'idealpoints' => $idealpoints,
+        'pointstoday' => $pointstoday
     ), 'sprint');
 
-    $chart= id(new PHUIObjectBoxView())
+    $chart = id(new PHUIObjectBoxView())
         ->setHeaderText(pht('Burndown for ' . $this->project->getName()))
-         ->appendChild(phutil_tag('div',
+        ->appendChild(phutil_tag('div',
             array(
                 'id' => 'chart',
                 'style' => 'width: 100%; height:400px'
@@ -128,12 +142,17 @@
   }
 
   private function buildC3Pie() {
-    $task_open_status_sum = $this->task_open_status_sum;
-    $task_closed_status_sum = $this->task_closed_status_sum;
+    $tasks_table = id(new TasksTableView())
+        ->setProject($this->project)
+        ->setViewer($this->viewer)
+        ->setRequest($this->request);
+    $tasks_table->setStatusPoints();
+    $task_open_status_sum = $tasks_table->getOpenStatusSum();
+    $task_closed_status_sum = $tasks_table->getClosedStatusSum();
 
-    require_celerity_resource('d3','sprint');
-    require_celerity_resource('c3-css','sprint');
-    require_celerity_resource('c3','sprint');
+    require_celerity_resource('d3', 'sprint');
+    require_celerity_resource('c3-css', 'sprint');
+    require_celerity_resource('c3', 'sprint');
 
     $id = 'pie';
     Javelin::initBehavior('c3-pie', array(
@@ -142,7 +161,7 @@
         'resolved' => $task_closed_status_sum,
     ), 'sprint');
 
-    $pie= id(new PHUIObjectBoxView())
+    $pie = id(new PHUIObjectBoxView())
         ->setHeaderText(pht('Task Status Report for ' . 
$this->project->getName()))
         ->appendChild(phutil_tag('div',
             array(
@@ -152,316 +171,4 @@
 
     return $pie;
   }
-
-  /**
-   * Format the Burndown data for display on the page.
-   *
-   * @returns PHUIObjectBoxView
-   */
-  private function buildBurnDownTable() {
-    $data = array();
-
-    foreach ($this->sprint_data as $date) {
-      $data[] = array(
-          $date->getDate(),
-          $date->getTasksTotal(),
-          $date->getTasksRemaining(),
-          $date->getPointsTotal(),
-          $date->getPointsRemaining(),
-          $date->getPointsIdealRemaining(),
-          $date->getPointsClosedToday(),
-      );
-    }
-
-    $table = id(new AphrontTableView($data))
-        ->setHeaders(
-            array(
-                pht('Date'),
-                pht('Total Tasks'),
-                pht('Remaining Tasks'),
-                pht('Total Points'),
-                pht('Remaining Points'),
-                pht('Ideal Remaining Points'),
-                pht('Points Completed Today'),
-            ));
-
-    $box = id(new PHUIObjectBoxView())
-        ->setHeaderText(pht('DATA'))
-        ->appendChild($table);
-
-    return $box;
-  }
-
-  /**
-   * Format the tasks data for display on the page.
-   *
-   * @returns PHUIObjectBoxView
-   */
-  private function buildTasksTable() {
-    $order = $this->request->getStr('order', 'name');
-    list($order, $reverse) = AphrontTableView::parseSort($order);
-    $rows = $this->buildTasksTree($order, $reverse);
-    $table = id(new AphrontTableView($rows))
-        ->setHeaders(
-            array(
-                pht('Task'),
-                pht('Assigned to'),
-                pht('Priority'),
-                pht('Points'),
-                pht('Status'),
-            ));
-    $table->makeSortable(
-        $this->request->getRequestURI(),
-        'order',
-        $order,
-        $reverse,
-        array(
-            'Task',
-            'Assigned to',
-            'Priority',
-            'Points',
-            'Status'
-         )
-    );
-
-    $box = id(new PHUIObjectBoxView())
-        ->setHeaderText(pht('Tasks in this Sprint'))
-        ->appendChild($table);
-
-    return $box;
-  }
-
-  private function setSortOrder ($row, $order, $task, $assigned_to, $priority,
-                                 $points, $status) {
-    switch ($order) {
-      case 'Task':
-        $row['sort'] = $task;
-        break;
-      case 'Assigned to':
-        $row['sort'] = $assigned_to;
-        break;
-      case 'Priority':
-        $row['sort'] = $priority;
-        break;
-      case 'Points':
-        $row['sort'] = $points;
-        break;
-      case 'Status':
-      default:
-        $row['sort'] = $status;
-        break;
-    }
-    return $row['sort'];
-  }
-
-
-  private function buildTaskMap ($edges) {
-    $map = array();
-    foreach ($this->tasks as $task) {
-      if ($parents =
-          
$edges[$task->getPHID()][PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK]) 
{
-        foreach ($parents as $parent) {
-          // Make sure this task is in this sprint.
-          if (isset($this->tasks[$parent['dst']]))
-            $map[$task->getPHID()]['parents'][] = $parent['dst'];
-        }
-      }
-
-      if ($children =
-          
$edges[$task->getPHID()][PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK]) {
-        foreach ($children as $child) {
-          // Make sure this task is in this sprint.
-          if (isset($this->tasks[$child['dst']])) {
-            $map[$task->getPHID()]['children'][] = $child['dst'];
-          }
-        }
-      }
-    }
-    return $map;
-  }
-
-  /**
-   * This builds a tree of the tasks in this project. Due to the acyclic nature
-   * of tasks, we ntake some steps to reduce and call out duplication.
-   *
-   * We ignore any tasks not in this sprint.
-   *
-   * @return array
-   */
-  private function buildTasksTree($order, $reverse) {
-    $query = id(new SprintQuery());
-    $edges = $query->getEdges($this->tasks);
-    $map = $this->buildTaskMap($edges);
-
-    // We also collect the phids we need to fetch owner information
-    $handle_phids = array();
-    foreach ($this->tasks as $task) {
-      // Get the owner (assigned to) phid
-      $handle_phids[$task->getOwnerPHID()] = $task->getOwnerPHID();
-    }
-    $handles = $query->getViewerHandles($this->request, $handle_phids);
-
-    // Now we loop through the tasks, and add them to the output
-    $output = array();
-    $rows = array();
-    foreach ($this->tasks as $task) {
-      // If parents is set, it means this task has a parent in this sprint so
-      // skip it, the parent will handle adding this task to the output
-      if (isset($map[$task->getPHID()]['parents'])) {
-        continue;
-      }
-
-      $row = $this->addTaskToTree($output, $task, $map, $handles);
-      list ($task, $assigned_to, $priority,$points, $status) = $row[0];
-      $row['sort'] = $this->setSortOrder($row, $order, $task, $assigned_to, 
$priority,$points, $status);
-      $rows[] = $row;
-    }
-    $rows = isort($rows, 'sort');
-
-    foreach ($rows as $k => $row) {
-      unset($rows[$k]['sort']);
-    }
-
-    if ($reverse) {
-      $rows = array_reverse($rows);
-    }
-    $rows = array_map( function( $a ) { return $a['0']; }, $rows );
-    return $rows;
-  }
-
-  private function addTaskToTree($output, $task, $map, $handles, $depth = 0) {
-    static $included = array();
-    $query = id(new SprintQuery())
-        ->setProject($this->project)
-        ->setViewer($this->viewer);
-
-    // Get the owner object so we can render the owner username/link
-    $owner = $handles[$task->getOwnerPHID()];
-
-    // If this task is already is this tree, this is a repeat.
-    $repeat = isset($included[$task->getPHID()]);
-
-    $data = $query->getXactionData(SprintConstants::CUSTOMFIELD_TYPE_STATUS);
-    $points = $this->getTaskStoryPoints($task->getPHID(),$data);
-    $points = trim($points, '"');
-
-    $priority_name = new ManiphestTaskPriority();
-    $status = $this->setTaskStatus($task);
-    $this->sumPointsbyStatus($status, $points);
-    $depth_indent = '';
-    for ($i = 0; $i < $depth; $i++) {
-      $depth_indent .= '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
-    }
-
-    // Build the row
-    $output[] = array(
-        phutil_safe_html($depth_indent . phutil_tag(
-                'a',
-                array(
-                    'href' => '/' . $task->getMonogram(),
-                    'class' => $task->getStatus() !== 'open'
-                        ? 'phui-tag-core-closed'
-                        : '',
-                ),
-                $task->getMonogram() . ': ' . $task->getTitle()
-            ) . ($repeat ? '&nbsp;&nbsp;<em title="This task is a child of 
more than one task in this list. Children are only shown on ' .
-                'the first occurance">[Repeat]</em>' : '')),
-        $task->getOwnerPHID() ? $owner->renderLink() : 'none assigned',
-        $priority_name->getTaskPriorityName($task->getPriority()),
-        $points,
-        $status,
-    );
-    $included[$task->getPHID()] = $task->getPHID();
-
-    if (isset($map[$task->getPHID()]['children'])) {
-      foreach ($map[$task->getPHID()]['children'] as $child) {
-        $child = $this->tasks[$child];
-        $this->addTaskToTree($output, $child, $map, $handles, $depth + 1);
-      }
-    }
-    return $output;
-  }
-
-  /**
-   * Format the Event data for display on the page.
-   *
-   * @returns PHUIObjectBoxView
-   */
-  private function buildEventTable() {
-    $query = id(new SprintQuery())
-        ->setProject($this->project)
-        ->setViewer($this->viewer);
-    $aux_fields = $query->getAuxFields();
-    $start = $query->getStartDate($aux_fields);
-    $end = $query->getEndDate($aux_fields);
-
-    $tasks = $query->getTasks();
-
-    $query->checkNull($start, $end, $tasks);
-
-    $xactions = $query->getXactions($tasks);
-
-    $events = $query->getEvents($xactions, $tasks);
-    $rows = array();
-    foreach ($events as $event) {
-      $task_phid = $this->xactions[$event['transactionPHID']]->getObjectPHID();
-      $task = $this->tasks[$task_phid];
-
-      $rows[] = array(
-          phabricator_datetime($event['epoch'], $this->viewer),
-          phutil_tag(
-              'a',
-              array(
-                  'href' => '/' . $task->getMonogram(),
-              ),
-              $task->getMonogram() . ': ' . $task->getTitle()),
-          $event['title'],
-      );
-    }
-
-    $table = id(new AphrontTableView($rows))
-        ->setHeaders(
-            array(
-                pht('When'),
-                pht('Task'),
-                pht('Action'),
-            ))
-        ->setColumnClasses(
-            array(
-                '',
-                '',
-                'wide',
-            ));
-
-    $box = id(new PHUIObjectBoxView())
-        ->setHeaderText(pht('Events related to this sprint'))
-        ->appendChild($table);
-
-    return $box;
-  }
-
-  private function getTaskStoryPoints($task,$points_data) {
-    $storypoints = array();
-       foreach ($points_data as $k=>$subarray) {
-         if (isset ($subarray['objectPHID']) && $subarray['objectPHID'] == 
$task) {
-           $points_data[$k] = $subarray;
-           $storypoints = $subarray['newValue'];
-         }
-       }
-    return $storypoints;
-  }
-
-  private function setTaskStatus($task) {
-    $status = $task->getStatus();
-   return $status;
-  }
-
-  private function sumPointsbyStatus ($status, $points) {
-    $stats = id(new SprintBuildStats());
-    if ($status == 'open') {
-      $this->task_open_status_sum = 
$stats->setTaskOpenStatusSum($this->task_open_status_sum, $points);
-    } elseif ($status == 'resolved') {
-      $this->task_closed_status_sum = 
$stats->setTaskClosedStatusSum($this->task_closed_status_sum, $points);
-    }
-   }
-}
\ No newline at end of file
+}
diff --git a/src/view/EventTableView.php b/src/view/EventTableView.php
new file mode 100644
index 0000000..a122142
--- /dev/null
+++ b/src/view/EventTableView.php
@@ -0,0 +1,61 @@
+<?php
+
+final class EventTableView {
+  /**
+   * Format the Event data for display on the page.
+   *
+   * @returns PHUIObjectBoxView
+   */
+  public function buildEventTable($project, $viewer) {
+    $query = id(new SprintQuery())
+        ->setProject($project)
+        ->setViewer($viewer);
+    $aux_fields = $query->getAuxFields();
+    $start = $query->getStartDate($aux_fields);
+    $end = $query->getEndDate($aux_fields);
+
+    $tasks = $query->getTasks();
+    $query->checkNull($start, $end, $tasks);
+    $xactions = $query->getXactions($tasks);
+    $xactions = mpull($xactions, null, 'getPHID');
+    $tasks = mpull($tasks, null, 'getPHID');
+    $events = $query->getEvents($xactions, $tasks);
+    $rows = array();
+    foreach ($events as $event) {
+      $task_phid = $xactions[$event['transactionPHID']]->getObjectPHID();
+      $task = $tasks[$task_phid];
+
+      $rows[] = array(
+          phabricator_datetime($event['epoch'], $viewer),
+          phutil_tag(
+              'a',
+              array(
+                  'href' => '/' . $task->getMonogram(),
+              ),
+              $task->getMonogram() . ': ' . $task->getTitle()),
+          $event['title'],
+      );
+    }
+
+    $table = id(new AphrontTableView($rows))
+        ->setHeaders(
+            array(
+                pht('When'),
+                pht('Task'),
+                pht('Action'),
+            ))
+        ->setColumnClasses(
+            array(
+                '',
+                '',
+                'wide',
+            ));
+
+    $box = id(new PHUIObjectBoxView())
+        ->setHeaderText(pht('Events related to this sprint'))
+        ->appendChild($table);
+
+    return $box;
+  }
+
+}
\ No newline at end of file
diff --git a/src/view/SprintTableView.php b/src/view/SprintTableView.php
new file mode 100644
index 0000000..6423ccd
--- /dev/null
+++ b/src/view/SprintTableView.php
@@ -0,0 +1,45 @@
+<?php
+
+final class SprintTableView
+{
+
+  /**
+   * Format the Burndown data for display on the page.
+   *
+   * @returns PHUIObjectBoxView
+   */
+  public function buildBurnDownTable($sprint_data)
+  {
+    $data = array();
+
+    foreach ($sprint_data as $date) {
+      $data[] = array(
+          $date->getDate(),
+          $date->getTasksTotal(),
+          $date->getTasksRemaining(),
+          $date->getPointsTotal(),
+          $date->getPointsRemaining(),
+          $date->getPointsIdealRemaining(),
+          $date->getPointsClosedToday(),
+      );
+    }
+
+    $table = id(new AphrontTableView($data))
+        ->setHeaders(
+            array(
+                pht('Date'),
+                pht('Total Tasks'),
+                pht('Remaining Tasks'),
+                pht('Total Points'),
+                pht('Remaining Points'),
+                pht('Ideal Remaining Points'),
+                pht('Points Completed Today'),
+            ));
+
+    $box = id(new PHUIObjectBoxView())
+        ->setHeaderText(pht('DATA'))
+        ->appendChild($table);
+
+    return $box;
+  }
+}
diff --git a/src/view/TasksTableView.php b/src/view/TasksTableView.php
new file mode 100644
index 0000000..2829a71
--- /dev/null
+++ b/src/view/TasksTableView.php
@@ -0,0 +1,276 @@
+<?php
+
+final class TasksTableView {
+
+  private $project;
+  private $viewer;
+  private $request;
+  private $task_open_status_sum;
+  private $task_closed_status_sum;
+
+  public function setProject ($project) {
+    $this->project = $project;
+    return $this;
+  }
+
+  public function setViewer ($viewer) {
+    $this->viewer = $viewer;
+    return $this;
+  }
+
+  public function setRequest ($request) {
+    $this->request =  $request;
+    return $this;
+  }
+
+  public function getTaskOpenStatusSum () {
+    return $this->task_open_status_sum;
+  }
+
+  public function getTaskClosedStatusSum () {
+    return $this->task_closed_status_sum;
+  }
+  /**
+   * Format the tasks data for display on the page.
+   *
+   * @returns PHUIObjectBoxView
+   */
+  public function buildTasksTable() {
+    $order = $this->request->getStr('order', 'name');
+    list($order, $reverse) = AphrontTableView::parseSort($order);
+    $rows = $this->buildTasksTree($order, $reverse);
+    $table = id(new AphrontTableView($rows))
+        ->setHeaders(
+            array(
+                pht('Task'),
+                pht('Assigned to'),
+                pht('Priority'),
+                pht('Points'),
+                pht('Status'),
+            ));
+    $table->makeSortable(
+        $this->request->getRequestURI(),
+        'order',
+        $order,
+        $reverse,
+        array(
+            'Task',
+            'Assigned to',
+            'Priority',
+            'Points',
+            'Status'
+        )
+    );
+
+    $box = id(new PHUIObjectBoxView())
+        ->setHeaderText(pht('Tasks in this Sprint'))
+        ->appendChild($table);
+
+    return $box;
+  }
+
+   /**
+   * This builds a tree of the tasks in this project. Due to the acyclic nature
+   * of tasks, we ntake some steps to reduce and call out duplication.
+   *
+   * We ignore any tasks not in this sprint.
+   *
+   * @return array
+   */
+  private function buildTasksTree($order, $reverse) {
+    $query = id(new SprintQuery())
+        ->setProject($this->project)
+        ->setViewer($this->viewer);
+    $tasks = $query->getTasks();
+    $tasks = mpull($tasks, null, 'getPHID');
+    $edges = $query->getEdges($tasks);
+    $map = $this->buildTaskMap($edges, $tasks);
+
+    // We also collect the phids we need to fetch owner information
+    $handle_phids = array();
+    foreach ($tasks as $task) {
+      // Get the owner (assigned to) phid
+      $handle_phids[$task->getOwnerPHID()] = $task->getOwnerPHID();
+    }
+    $handles = $query->getViewerHandles($this->request, $handle_phids);
+
+    // Now we loop through the tasks, and add them to the output
+    $output = array();
+    $rows = array();
+    foreach ($tasks as $task) {
+      // If parents is set, it means this task has a parent in this sprint so
+      // skip it, the parent will handle adding this task to the output
+      if (isset($map[$task->getPHID()]['parents'])) {
+        continue;
+      }
+
+      $row = $this->addTaskToTree($output, $task, $tasks, $map, $handles);
+      list ($task, $assigned_to, $priority,$points, $status) = $row[0];
+      $row['sort'] = $this->setSortOrder($row, $order, $task, $assigned_to, 
$priority,$points, $status);
+      $rows[] = $row;
+    }
+    $rows = isort($rows, 'sort');
+
+    foreach ($rows as $k => $row) {
+      unset($rows[$k]['sort']);
+    }
+
+    if ($reverse) {
+      $rows = array_reverse($rows);
+    }
+    $rows = array_map( function( $a ) { return $a['0']; }, $rows );
+    return $rows;
+  }
+
+  private function setSortOrder ($row, $order, $task, $assigned_to, $priority,
+                                 $points, $status) {
+    switch ($order) {
+      case 'Task':
+        $row['sort'] = $task;
+        break;
+      case 'Assigned to':
+        $row['sort'] = $assigned_to;
+        break;
+      case 'Priority':
+        $row['sort'] = $priority;
+        break;
+      case 'Points':
+        $row['sort'] = $points;
+        break;
+      case 'Status':
+      default:
+        $row['sort'] = $status;
+        break;
+    }
+    return $row['sort'];
+  }
+
+  private function buildTaskMap ($edges, $tasks) {
+    $map = array();
+    foreach ($tasks as $task) {
+      if ($parents =
+          
$edges[$task->getPHID()][PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK]) 
{
+        foreach ($parents as $parent) {
+          // Make sure this task is in this sprint.
+          if (isset($tasks[$parent['dst']]))
+            $map[$task->getPHID()]['parents'][] = $parent['dst'];
+        }
+      }
+
+      if ($children =
+          
$edges[$task->getPHID()][PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK]) {
+        foreach ($children as $child) {
+          // Make sure this task is in this sprint.
+          if (isset($tasks[$child['dst']])) {
+            $map[$task->getPHID()]['children'][] = $child['dst'];
+          }
+        }
+      }
+    }
+    return $map;
+  }
+
+  private function getTaskPoints($task) {
+    $query = id(new SprintQuery())
+        ->setProject($this->project)
+        ->setViewer($this->viewer);
+    $data = $query->getXactionData(SprintConstants::CUSTOMFIELD_TYPE_STATUS);
+    $points = $this->getTaskStoryPoints($task->getPHID(),$data);
+    $points = trim($points, '"');
+    return $points;
+  }
+
+  private function addTaskToTree($output, $task, $tasks, $map, $handles, 
$depth = 0) {
+    static $included = array();
+
+    // Get the owner object so we can render the owner username/link
+    $owner = $handles[$task->getOwnerPHID()];
+
+    // If this task is already in this tree, this is a repeat.
+    $repeat = isset($included[$task->getPHID()]);
+
+    $points = $this->getTaskPoints($task);
+    $priority_name = new ManiphestTaskPriority();
+    $status = $this->setTaskStatus($task);
+    $depth_indent = '';
+    for ($i = 0; $i < $depth; $i++) {
+      $depth_indent .= '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
+    }
+
+    // Build the row
+    $output[] = array(
+        phutil_safe_html($depth_indent . phutil_tag(
+                'a',
+                array(
+                    'href' => '/' . $task->getMonogram(),
+                    'class' => $task->getStatus() !== 'open'
+                        ? 'phui-tag-core-closed'
+                        : '',
+                ),
+                $task->getMonogram() . ': ' . $task->getTitle()
+            ) . ($repeat ? '&nbsp;&nbsp;<em title="This task is a child of 
more than one task in this list. Children are only shown on ' .
+                'the first occurance">[Repeat]</em>' : '')),
+        $task->getOwnerPHID() ? $owner->renderLink() : 'none assigned',
+        $priority_name->getTaskPriorityName($task->getPriority()),
+        $points,
+        $status,
+    );
+    $included[$task->getPHID()] = $task->getPHID();
+
+    if (isset($map[$task->getPHID()]['children'])) {
+      foreach ($map[$task->getPHID()]['children'] as $child) {
+        $child = $tasks[$child];
+        $this->addTaskToTree($output, $child, $map, $handles, $depth + 1);
+      }
+    }
+    return $output;
+  }
+
+  private function getTaskStoryPoints($task,$points_data) {
+    $storypoints = array();
+    foreach ($points_data as $k=>$subarray) {
+      if (isset ($subarray['objectPHID']) && $subarray['objectPHID'] == $task) 
{
+        $points_data[$k] = $subarray;
+        $storypoints = $subarray['newValue'];
+      }
+    }
+    return $storypoints;
+  }
+
+  private function setTaskStatus($task) {
+    $status = $task->getStatus();
+    return $status;
+  }
+
+  private function sumPointsbyStatus ($task) {
+    $stats = id(new SprintBuildStats());
+    $status = $this->setTaskStatus($task);
+    $points = $this->getTaskPoints($task);
+    if ($status == 'open') {
+      $this->task_open_status_sum = 
$stats->setTaskOpenStatusSum($this->task_open_status_sum, $points);
+    } elseif ($status == 'resolved') {
+      $this->task_closed_status_sum = 
$stats->setTaskClosedStatusSum($this->task_closed_status_sum, $points);
+    }
+    return;
+  }
+
+  public function setStatusPoints () {
+    $query = id(new SprintQuery())
+        ->setProject($this->project)
+        ->setViewer($this->viewer);
+    $tasks = $query->getTasks();
+    $tasks = mpull($tasks, null, 'getPHID');
+    foreach ($tasks as $task) {
+      $this->sumPointsbyStatus($task);
+    }
+   return;
+  }
+
+  public function getOpenStatusSum() {
+    return $this->task_open_status_sum;
+  }
+
+  public function getClosedStatusSum() {
+    return $this->task_closed_status_sum;
+  }
+}
\ No newline at end of file

-- 
To view, visit https://gerrit.wikimedia.org/r/172664
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I9f1b6b5c0fa46e58ff362aa0009ce2b8da6b4438
Gerrit-PatchSet: 1
Gerrit-Project: phabricator/extensions/Sprint
Gerrit-Branch: master
Gerrit-Owner: Christopher Johnson (WMDE) <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to