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

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

Change subject: Added d3 js chart loaded with celerity/javelin
......................................................................

Added d3 js chart loaded with celerity/javelin

Change-Id: I03e0755911d583678e1607d509af9ad848747029
---
M src/query/SprintQuery.php
M src/view/BurndownDataView.php
M src/view/SprintReportBurndownView.php
3 files changed, 308 insertions(+), 173 deletions(-)


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

diff --git a/src/query/SprintQuery.php b/src/query/SprintQuery.php
index b8c9aae..c312643 100644
--- a/src/query/SprintQuery.php
+++ b/src/query/SprintQuery.php
@@ -22,6 +22,13 @@
     return $this;
   }
 
+  public function getViewerHandles($request, array $phids) {
+    return id(new PhabricatorHandleQuery())
+        ->setViewer($request->getUser())
+        ->withPHIDs($phids)
+        ->execute();
+  }
+
   public function getAuxFields() {
     $field_list = PhabricatorCustomField::getObjectFields($this->project, 
PhabricatorCustomField::ROLE_EDIT);
     $field_list->setViewer($this->viewer);
diff --git a/src/view/BurndownDataView.php b/src/view/BurndownDataView.php
index ee05124..5e962df 100644
--- a/src/view/BurndownDataView.php
+++ b/src/view/BurndownDataView.php
@@ -40,7 +40,7 @@
 
   public function render() {
 
-    $chart = $this->buildBurnDownChart();
+    $chart = $this->buildC3Chart();
     $tasks_table = $this->buildTasksTable();
     $burndown_table = $this->buildBurnDownTable();
     $event_table = $this->buildEventTable();
@@ -78,7 +78,6 @@
 
 
     $data = array(array(
-        pht('Date'),
         pht('Total Points'),
         pht('Remaining Points'),
         pht('Ideal Points'),
@@ -91,7 +90,6 @@
         $future = new DateTime($date->getDate()) > id(new 
DateTime())->setTime(0, 0);
       }
       $data[] = array(
-          $date->getDate(),
           $future ? null : $date->points_total,
           $future ? null : $date->points_remaining,
           $date->points_ideal_remaining,
@@ -99,9 +97,25 @@
       );
 
     }
+    $data = $this->transposeArray($data);
     return $data;
-
   }
+
+  private function transposeArray($array) {
+    $transposed_array = array();
+    if ($array) {
+      foreach ($array as $row_key => $row) {
+        if (is_array($row) && !empty($row)) { //check to see if there is a 
second dimension
+          foreach ($row as $column_key => $element) {
+            $transposed_array[$column_key][$row_key] = $element;
+          }
+        } else {
+          $transposed_array[0][$row_key] = $row;
+        }
+      }
+     return $transposed_array;
+     }
+   }
 
   // Now loop through the events and build the data for each day
   private function buildDailyData($events, $start, $end) {
@@ -198,6 +212,39 @@
     }
   }
 
+  private function buildC3Chart() {
+    $this->data = $this->buildChartDataSet();
+    $totalpoints = $this->data[0];
+    $remainingpoints = $this->data[1];
+    $idealpoints = $this->data[2];
+    $pointstoday = $this->data[3];
+    $timeseries = array_keys($this->dates);
+
+    require_celerity_resource('d3');
+    require_celerity_resource('c3-css');
+    require_celerity_resource('c3');
+
+    $id = 'chart';
+    Javelin::initBehavior('c3-chart', array(
+        'hardpoint' => $id,
+        'timeseries' => $timeseries,
+        'totalpoints' => $totalpoints,
+        'remainingpoints' => $remainingpoints,
+        'idealpoints' =>   $idealpoints,
+        'pointstoday' =>   $pointstoday
+    ));
+
+    $chart= id(new PHUIObjectBoxView())
+        ->setHeaderText(pht('Burndown for ' . $this->project->getName()))
+         ->appendChild(phutil_tag('div',
+            array(
+                'id' => 'chart',
+                'style' => 'width: 100%; height:400px'
+            ), ''));
+
+    return $chart;
+  }
+
   private function buildBurnDownChart() {
 
     $this->data = $this->buildChartDataSet();
diff --git a/src/view/SprintReportBurndownView.php 
b/src/view/SprintReportBurndownView.php
index 5494d82..eda52eb 100644
--- a/src/view/SprintReportBurndownView.php
+++ b/src/view/SprintReportBurndownView.php
@@ -16,198 +16,265 @@
 
   public function render() {
 
-      $handle = null;
+    $filter = $this->BuildFilter();
+    $chart = $this->buildBurnDownChart();
+    $table = $this->buildStatsTable();
+    return array($filter, $chart, $table);
+  }
 
-      $project_phid = $this->request->getStr('project');
-      if ($project_phid) {
-        $phids = array($project_phid);
-        $handles = $this->loadViewerHandles($phids);
-        $handle = $handles[$project_phid];
-      }
+  private function getXactionData($project_phid) {
+     $query = id(new SprintQuery())
+        ->setPHID($project_phid);
+      $data = $query->getXactionData(ManiphestTransaction::TYPE_STATUS);
+    return $data;
+  }
 
-      $query = id(new SprintQuery())
+  private function buildFilter() {
+    $handle = null;
+    $project_phid = $this->request->getStr('project');
+    if ($project_phid) {
+      $phids = array($project_phid);
+      $handle = $this->getProjectHandle ($phids,$project_phid);
+    }
+    $tokens = array();
+    if ($handle) {
+      $tokens = $this->getTokens($handle);
+    }
+    $filter = parent::renderReportFilters($tokens, $has_window = false);
+    return $filter;
+  }
+
+  private function getTokens($handle) {
+    $tokens = array($handle);
+    return $tokens;
+  }
+
+  private function getProjectHandle($phids,$project_phid) {
+
+    $query = id(new SprintQuery())
         ->setPHID($project_phid);
 
-      $data = $query->getXactionData(ManiphestTransaction::TYPE_STATUS);
+    $handles = $query->getViewerHandles($this->request, $phids);
+    $handle = $handles[$project_phid];
+    return $handle;
+  }
 
-      $stats = array();
+ private function addTaskStatustoData ($data) {
+   foreach ($data as $key => $row) {
+
+     // NOTE: Hack to avoid json_decode().
+     $oldv = trim($row['oldValue'], '"');
+     $newv = trim($row['newValue'], '"');
+
+     if ($oldv == 'null') {
+       $old_is_open = false;
+     } else {
+       $old_is_open = ManiphestTaskStatus::isOpenStatus($oldv);
+     }
+
+     $new_is_open = ManiphestTaskStatus::isOpenStatus($newv);
+
+     $is_open = ($new_is_open && !$old_is_open);
+     $is_close = ($old_is_open && !$new_is_open);
+
+     $data[$key]['_is_open'] = $is_open;
+     $data[$key]['_is_close'] = $is_close;
+
+     if (!$is_open && !$is_close) {
+       // This is either some kind of bogus events, or a resolution change
+       // (e.g., resolved -> invalid). Just skip it.
+       continue;
+     }
+   }
+   return $data;
+ }
+
+  private function buildStatsfromEvents ($data) {
+
+   $stats = array();
+   $data = $this->addTaskStatustoData ($data);
+
+    foreach ($data as $key => $row) {
+
+     $day_bucket = phabricator_format_local_time(
+          $row['dateCreated'],
+          $this->user,
+          'Yz');
+
+      if (empty($stats[$day_bucket])) {
+        $stats[$day_bucket] = array(
+            'open' => 0,
+            'close' => 0,
+        );
+      }
+
+      $stats[$day_bucket][$data[$key]['_is_close'] ? 'close' : 'open']++;
+    }
+    return $stats;
+  }
+
+  private function buildDayBucketsfromEvents ($data) {
       $day_buckets = array();
-
       foreach ($data as $key => $row) {
 
-        // NOTE: Hack to avoid json_decode().
-        $oldv = trim($row['oldValue'], '"');
-        $newv = trim($row['newValue'], '"');
-
-        if ($oldv == 'null') {
-          $old_is_open = false;
-        } else {
-          $old_is_open = ManiphestTaskStatus::isOpenStatus($oldv);
-        }
-
-        $new_is_open = ManiphestTaskStatus::isOpenStatus($newv);
-
-        $is_open  = ($new_is_open && !$old_is_open);
-        $is_close = ($old_is_open && !$new_is_open);
-
-        $data[$key]['_is_open'] = $is_open;
-        $data[$key]['_is_close'] = $is_close;
-
-        if (!$is_open && !$is_close) {
-          // This is either some kind of bogus events, or a resolution change
-          // (e.g., resolved -> invalid). Just skip it.
-          continue;
-        }
-
-        $day_bucket = phabricator_format_local_time(
+       $day_bucket = phabricator_format_local_time(
             $row['dateCreated'],
             $this->user,
             'Yz');
-        $day_buckets[$day_bucket] = $row['dateCreated'];
-        if (empty($stats[$day_bucket])) {
-          $stats[$day_bucket] = array(
-              'open'  => 0,
-              'close' => 0,
-          );
+       $day_buckets[$day_bucket] = $row['dateCreated'];
+       }
+    return $day_buckets;
+  }
+
+  private function buildStatsTable() {
+
+    $handle = null;
+    $project_phid = $this->request->getStr('project');
+
+    if ($project_phid) {
+      $phids = array($project_phid);
+      $handle = $this->getProjectHandle ($phids,$project_phid);
+    }
+
+    $data = $this->getXactionData($project_phid);
+
+    $stats = $this->buildStatsfromEvents($data);
+    $day_buckets = $this->buildDayBucketsfromEvents($data);
+
+    $template = array(
+        'open' => 0,
+        'close' => 0,
+    );
+
+    $rows = array();
+    $rowc = array();
+    $last_month = null;
+    $last_month_epoch = null;
+    $last_week = null;
+    $last_week_epoch = null;
+    $week = null;
+    $month = null;
+
+    $period = $template;
+
+    foreach ($stats as $bucket => $info) {
+      $epoch = $day_buckets[$bucket];
+
+      $week_bucket = phabricator_format_local_time(
+          $epoch,
+          $this->user,
+          'YW');
+      if ($week_bucket != $last_week) {
+        if ($week) {
+          $rows[] = $this->formatBurnRow(
+              'Week of ' . phabricator_date($last_week_epoch, $this->user),
+              $week);
+          $rowc[] = 'week';
         }
-        $stats[$day_bucket][$is_close ? 'close' : 'open']++;
+        $week = $template;
+        $last_week = $week_bucket;
+        $last_week_epoch = $epoch;
       }
 
-      $template = array(
-          'open'  => 0,
-          'close' => 0,
-      );
-
-      $rows = array();
-      $rowc = array();
-      $last_month = null;
-      $last_month_epoch = null;
-      $last_week = null;
-      $last_week_epoch = null;
-      $week = null;
-      $month = null;
-
-      $period = $template;
-
-      foreach ($stats as $bucket => $info) {
-        $epoch = $day_buckets[$bucket];
-
-        $week_bucket = phabricator_format_local_time(
-            $epoch,
-            $this->user,
-            'YW');
-        if ($week_bucket != $last_week) {
-          if ($week) {
-            $rows[] = $this->formatBurnRow(
-                'Week of '.phabricator_date($last_week_epoch, $this->user),
-                $week);
-            $rowc[] = 'week';
-          }
-          $week = $template;
-          $last_week = $week_bucket;
-          $last_week_epoch = $epoch;
+      $month_bucket = phabricator_format_local_time(
+          $epoch,
+          $this->user,
+          'Ym');
+      if ($month_bucket != $last_month) {
+        if ($month) {
+          $rows[] = $this->formatBurnRow(
+              phabricator_format_local_time($last_month_epoch, $this->user, 
'F, Y'),
+              $month);
+          $rowc[] = 'month';
         }
-
-        $month_bucket = phabricator_format_local_time(
-            $epoch,
-            $this->user,
-            'Ym');
-        if ($month_bucket != $last_month) {
-          if ($month) {
-            $rows[] = $this->formatBurnRow(
-                phabricator_format_local_time($last_month_epoch, $this->user, 
'F, Y'),
-                $month);
-            $rowc[] = 'month';
-          }
-          $month = $template;
-          $last_month = $month_bucket;
-          $last_month_epoch = $epoch;
-        }
-
-        $rows[] = $this->formatBurnRow(phabricator_date($epoch, $this->user), 
$info);
-        $rowc[] = null;
-        $week['open'] += $info['open'];
-        $week['close'] += $info['close'];
-        $month['open'] += $info['open'];
-        $month['close'] += $info['close'];
-        $period['open'] += $info['open'];
-        $period['close'] += $info['close'];
+        $month = $template;
+        $last_month = $month_bucket;
+        $last_month_epoch = $epoch;
       }
 
-      if ($week) {
-        $rows[] = $this->formatBurnRow(
-            pht('Week To Date'),
-            $week);
-        $rowc[] = 'week';
-      }
+      $rows[] = $this->formatBurnRow(phabricator_date($epoch, $this->user), 
$info);
+      $rowc[] = null;
+      $week['open'] += $info['open'];
+      $week['close'] += $info['close'];
+      $month['open'] += $info['open'];
+      $month['close'] += $info['close'];
+      $period['open'] += $info['open'];
+      $period['close'] += $info['close'];
+    }
 
-      if ($month) {
-        $rows[] = $this->formatBurnRow(
-            pht('Month To Date'),
-            $month);
-        $rowc[] = 'month';
-      }
-
+    if ($week) {
       $rows[] = $this->formatBurnRow(
-          pht('All Time'),
-          $period);
-      $rowc[] = 'aggregate';
+          pht('Week To Date'),
+          $week);
+      $rowc[] = 'week';
+    }
 
-      $rows = array_reverse($rows);
-      $rowc = array_reverse($rowc);
+    if ($month) {
+      $rows[] = $this->formatBurnRow(
+          pht('Month To Date'),
+          $month);
+      $rowc[] = 'month';
+    }
 
-      $table = new AphrontTableView($rows);
-      $table->setRowClasses($rowc);
-      $table->setHeaders(
-          array(
-              pht('Period'),
-              pht('Opened'),
-              pht('Closed'),
-              pht('Change'),
-          ));
-      $table->setColumnClasses(
-          array(
-              'left narrow',
-              'center narrow',
-              'center narrow',
-              'center narrow',
-          ));
+    $rows[] = $this->formatBurnRow(
+        pht('All Time'),
+        $period);
+    $rowc[] = 'aggregate';
 
-      if ($handle) {
-        $inst = pht(
-            'NOTE: This table reflects tasks currently in '.
-            'the project. If a task was opened in the past but added to '.
-            'the project recently, it is counted on the day it was '.
-            'opened, not the day it was categorized. If a task was part '.
-            'of this project in the past but no longer is, it is not '.
-            'counted at all.');
-        $header = pht('Task Burn Rate for Project %s', $handle->renderLink());
-        $caption = phutil_tag('p', array(), $inst);
-      } else {
-        $header = pht('Task Burn Rate for All Tasks');
-        $caption = null;
-      }
+    $rows = array_reverse($rows);
+    $rowc = array_reverse($rowc);
 
-      if ($caption) {
-        $caption = id(new AphrontErrorView())
-            ->appendChild($caption)
-            ->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
-      }
+    $table = new AphrontTableView($rows);
+    $table->setRowClasses($rowc);
+    $table->setHeaders(
+        array(
+            pht('Period'),
+            pht('Opened'),
+            pht('Closed'),
+            pht('Change'),
+        ));
+    $table->setColumnClasses(
+        array(
+            'left narrow',
+            'center narrow',
+            'center narrow',
+            'center narrow',
+        ));
 
-      $panel = new PHUIObjectBoxView();
-      $panel->setHeaderText($header);
-      if ($caption) {
-        $panel->setErrorView($caption);
-      }
-      $panel->appendChild($table);
+    if ($handle) {
+      $inst = pht(
+          'NOTE: This table reflects tasks currently in ' .
+          'the project. If a task was opened in the past but added to ' .
+          'the project recently, it is counted on the day it was ' .
+          'opened, not the day it was categorized. If a task was part ' .
+          'of this project in the past but no longer is, it is not ' .
+          'counted at all.');
+      $header = pht('Task Burn Rate for Project %s', $handle->renderLink());
+      $caption = phutil_tag('p', array(), $inst);
+    } else {
+      $header = pht('Task Burn Rate for All Tasks');
+      $caption = null;
+    }
 
-      $tokens = array();
-      if ($handle) {
-        $tokens = array($handle);
-      }
+    if ($caption) {
+      $caption = id(new AphrontErrorView())
+          ->appendChild($caption)
+          ->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
+    }
 
-      $filter = parent::renderReportFilters($tokens, $has_window = false);
+    $panel = new PHUIObjectBoxView();
+    $panel->setHeaderText($header);
+    if ($caption) {
+      $panel->setErrorView($caption);
+    }
+    $panel->appendChild($table);
 
+    return $panel;
+  }
+
+  private function buildBurnDownChart() {
+    $project_phid = $this->request->getStr('project');
+    $data = $this->getXactionData($project_phid);
       $id = celerity_generate_unique_node_id();
       $chart = phutil_tag(
           'div',
@@ -238,14 +305,28 @@
           'yformat' => 'int',
       ));
 
-      return array($filter, $chart, $panel);
+      return $chart;
     }
 
-    private function buildSeries(array $data) {
-      $out = array();
+  private function buildPointSeries(array $data) {
+    $out = array();
 
+    foreach ($data as $row) {
+      $t = (int)$row['dateCreated'];
+      $newv = trim($row['newValue'], '"');
+      if ($row['newValue']) {
+            $out[$t] = $newv;
+      }
+    }
+
+    return array(array_keys($out), array_values($out));
+  }
+
+  private function buildSeries(array $data) {
+      $out = array();
+      $data = $this->addTaskStatustoData ($data);
       $counter = 0;
-      foreach ($data as $row) {
+      foreach ($data as $key => $row) {
         $t = (int)$row['dateCreated'];
         if ($row['_is_close']) {
           --$counter;

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I03e0755911d583678e1607d509af9ad848747029
Gerrit-PatchSet: 1
Gerrit-Project: phabricator/extensions/Sprint
Gerrit-Branch: master
Gerrit-Owner: Christopher Johnson (WMDE) <christopher.john...@wikimedia.de>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to