Changeset View
Changeset View
Standalone View
Standalone View
src/applications/fact/controller/PhabricatorFactChartController.php
<?php | <?php | ||||
final class PhabricatorFactChartController extends PhabricatorFactController { | final class PhabricatorFactChartController extends PhabricatorFactController { | ||||
public function handleRequest(AphrontRequest $request) { | public function handleRequest(AphrontRequest $request) { | ||||
$viewer = $request->getViewer(); | $viewer = $request->getViewer(); | ||||
// When drawing a chart, we send down a placeholder piece of HTML first, | // When drawing a chart, we send down a placeholder piece of HTML first, | ||||
// then fetch the data via async request. Determine if we're drawing | // then fetch the data via async request. Determine if we're drawing | ||||
// the structure or actually pulling the data. | // the structure or actually pulling the data. | ||||
$mode = $request->getURIData('mode'); | $mode = $request->getURIData('mode'); | ||||
$is_chart_mode = ($mode === 'chart'); | $is_chart_mode = ($mode === 'chart'); | ||||
$is_draw_mode = ($mode === 'draw'); | $is_draw_mode = ($mode === 'draw'); | ||||
$series = $request->getStr('y1'); | $functions = array(); | ||||
$facts = PhabricatorFact::getAllFacts(); | $functions[] = id(new PhabricatorFactChartFunction()) | ||||
$fact = idx($facts, $series); | ->setArguments(array('tasks.count.create')); | ||||
if (!$fact) { | $functions[] = id(new PhabricatorFactChartFunction()) | ||||
return new Aphront404Response(); | ->setArguments(array('tasks.open-count.create')); | ||||
} | |||||
$key_id = id(new PhabricatorFactKeyDimension()) | |||||
->newDimensionID($fact->getKey()); | |||||
if (!$key_id) { | |||||
return new Aphront404Response(); | |||||
} | |||||
if ($is_chart_mode) { | if ($is_chart_mode) { | ||||
return $this->newChartResponse(); | return $this->newChartResponse(); | ||||
} | } | ||||
$table = $fact->newDatapoint(); | |||||
$conn_r = $table->establishConnection('r'); | |||||
$table_name = $table->getTableName(); | |||||
$data = queryfx_all( | |||||
$conn_r, | |||||
'SELECT value, epoch FROM %T WHERE keyID = %d ORDER BY epoch ASC', | |||||
$table_name, | |||||
$key_id); | |||||
$points = array(); | |||||
$sum = 0; | |||||
foreach ($data as $key => $row) { | |||||
$sum += (int)$row['value']; | |||||
$points[(int)$row['epoch']] = $sum; | |||||
} | |||||
if (!$points) { | |||||
throw new Exception('No data to show!'); | |||||
} | |||||
// Limit amount of data passed to browser. | |||||
$count = count($points); | |||||
$limit = 2000; | |||||
if ($count > $limit) { | |||||
$i = 0; | |||||
$every = ceil($count / $limit); | |||||
foreach ($points as $epoch => $sum) { | |||||
$i++; | |||||
if ($i % $every && $i != $count) { | |||||
unset($points[$epoch]); | |||||
} | |||||
} | |||||
} | |||||
$datasets = array(); | $datasets = array(); | ||||
foreach ($functions as $function) { | |||||
$function->loadData(); | |||||
$datasets[] = array( | $points = $function->getDatapoints(2000); | ||||
amckinley: I'm pretty much just not going to comment about hard-coded stuff like this because everything… | |||||
'x' => array_keys($points), | |||||
'y' => array_values($points), | |||||
'color' => '#ff0000', | |||||
); | |||||
// Add a dummy "y = x" dataset to prove we can draw multiple datasets. | $x = array(); | ||||
$x_min = min(array_keys($points)); | $y = array(); | ||||
$x_max = max(array_keys($points)); | |||||
$x_range = ($x_max - $x_min) / 4; | foreach ($points as $point) { | ||||
$linear = array(); | $x[] = $point['x']; | ||||
foreach ($points as $x => $y) { | $y[] = $point['y']; | ||||
$linear[$x] = round(count($points) * (($x - $x_min) / $x_range)); | |||||
} | } | ||||
$datasets[] = array( | $datasets[] = array( | ||||
'x' => array_keys($linear), | 'x' => $x, | ||||
'y' => array_values($linear), | 'y' => $y, | ||||
'color' => '#0000ff', | 'color' => '#ff00ff', | ||||
); | ); | ||||
} | |||||
$y_min = 0; | $y_min = 0; | ||||
$y_max = 0; | $y_max = 0; | ||||
$x_min = null; | $x_min = null; | ||||
$x_max = 0; | $x_max = 0; | ||||
foreach ($datasets as $dataset) { | foreach ($datasets as $dataset) { | ||||
if (!$dataset['y']) { | if (!$dataset['y']) { | ||||
continue; | continue; | ||||
▲ Show 20 Lines • Show All 66 Lines • Show Last 20 Lines |
I'm pretty much just not going to comment about hard-coded stuff like this because everything is so much in flux. I'm assuming you're on top of vaguely-defined "scalability" stuff like how to downsample extremely frequent datasets, caching chart results (or CDN'ing them), etc etc.