Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/fact/chart/PhabricatorChartDataset.php b/src/applications/fact/chart/PhabricatorChartDataset.php
index a794834229..093a742077 100644
--- a/src/applications/fact/chart/PhabricatorChartDataset.php
+++ b/src/applications/fact/chart/PhabricatorChartDataset.php
@@ -1,109 +1,102 @@
<?php
abstract class PhabricatorChartDataset
extends Phobject {
private $functions;
final public function getDatasetTypeKey() {
return $this->getPhobjectClassConstant('DATASETKEY', 32);
}
final public function getFunctions() {
return $this->functions;
}
final public function setFunctions(array $functions) {
assert_instances_of($functions, 'PhabricatorComposeChartFunction');
$this->functions = $functions;
return $this;
}
final public static function getAllDatasetTypes() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getDatasetTypeKey')
->execute();
}
final public static function newFromDictionary(array $map) {
PhutilTypeSpec::checkMap(
$map,
array(
'type' => 'string',
'functions' => 'list<wild>',
));
$types = self::getAllDatasetTypes();
$dataset_type = $map['type'];
if (!isset($types[$dataset_type])) {
throw new Exception(
pht(
'Trying to construct a dataset of type "%s", but this type is '.
'unknown. Supported types are: %s.',
$dataset_type,
implode(', ', array_keys($types))));
}
$dataset = id(clone $types[$dataset_type]);
$functions = array();
foreach ($map['functions'] as $map) {
$functions[] = PhabricatorChartFunction::newFromDictionary($map);
}
$dataset->setFunctions($functions);
return $dataset;
}
- final public function toDictionary() {
- return array(
- 'type' => $this->getDatasetTypeKey(),
- 'functions' => mpull($this->getFunctions(), 'toDictionary'),
- );
- }
-
final public function getChartDisplayData(
PhabricatorChartDataQuery $data_query) {
return $this->newChartDisplayData($data_query);
}
abstract protected function newChartDisplayData(
PhabricatorChartDataQuery $data_query);
final public function getTabularDisplayData(
PhabricatorChartDataQuery $data_query) {
$results = array();
$functions = $this->getFunctions();
foreach ($functions as $function) {
$datapoints = $function->newDatapoints($data_query);
$refs = $function->getDataRefs(ipull($datapoints, 'x'));
foreach ($datapoints as $key => $point) {
$x = $point['x'];
if (isset($refs[$x])) {
$xrefs = $refs[$x];
} else {
$xrefs = array();
}
$datapoints[$key]['refs'] = $xrefs;
}
$results[] = array(
'data' => $datapoints,
);
}
return id(new PhabricatorChartDisplayData())
->setWireData($results);
}
}
diff --git a/src/applications/fact/chart/PhabricatorChartFunction.php b/src/applications/fact/chart/PhabricatorChartFunction.php
index ef5c5641bf..3ddcd6aec0 100644
--- a/src/applications/fact/chart/PhabricatorChartFunction.php
+++ b/src/applications/fact/chart/PhabricatorChartFunction.php
@@ -1,254 +1,251 @@
<?php
abstract class PhabricatorChartFunction
extends Phobject {
private $argumentParser;
private $functionLabel;
final public function getFunctionKey() {
return $this->getPhobjectClassConstant('FUNCTIONKEY', 32);
}
final public static function getAllFunctions() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getFunctionKey')
->execute();
}
final public function setArguments(array $arguments) {
$parser = $this->getArgumentParser();
$parser->setRawArguments($arguments);
$specs = $this->newArguments();
if (!is_array($specs)) {
throw new Exception(
pht(
'Expected "newArguments()" in class "%s" to return a list of '.
'argument specifications, got %s.',
get_class($this),
phutil_describe_type($specs)));
}
assert_instances_of($specs, 'PhabricatorChartFunctionArgument');
foreach ($specs as $spec) {
$parser->addArgument($spec);
}
$parser->setHaveAllArguments(true);
$parser->parseArguments();
return $this;
}
public function setFunctionLabel(PhabricatorChartFunctionLabel $label) {
$this->functionLabel = $label;
return $this;
}
public function getFunctionLabel() {
if (!$this->functionLabel) {
$this->functionLabel = id(new PhabricatorChartFunctionLabel())
->setName(pht('Unlabeled Function'))
->setColor('rgba(255, 0, 0, 1)')
->setFillColor('rgba(255, 0, 0, 0.15)');
}
return $this->functionLabel;
}
+ final public function getKey() {
+ return $this->getFunctionLabel()->getKey();
+ }
+
final public static function newFromDictionary(array $map) {
PhutilTypeSpec::checkMap(
$map,
array(
'function' => 'string',
'arguments' => 'list<wild>',
));
$functions = self::getAllFunctions();
$function_name = $map['function'];
if (!isset($functions[$function_name])) {
throw new Exception(
pht(
'Attempting to build function "%s" from dictionary, but that '.
'function is unknown. Known functions are: %s.',
$function_name,
implode(', ', array_keys($functions))));
}
$function = id(clone $functions[$function_name])
->setArguments($map['arguments']);
return $function;
}
- public function toDictionary() {
- return array(
- 'function' => $this->getFunctionKey(),
- 'arguments' => $this->getArgumentParser()->getRawArguments(),
- );
- }
-
public function getSubfunctions() {
$result = array();
$result[] = $this;
foreach ($this->getFunctionArguments() as $argument) {
foreach ($argument->getSubfunctions() as $subfunction) {
$result[] = $subfunction;
}
}
return $result;
}
public function getFunctionArguments() {
$results = array();
$parser = $this->getArgumentParser();
foreach ($parser->getAllArguments() as $argument) {
if ($argument->getType() !== 'function') {
continue;
}
$name = $argument->getName();
$value = $this->getArgument($name);
if (!is_array($value)) {
$results[] = $value;
} else {
foreach ($value as $arg_value) {
$results[] = $arg_value;
}
}
}
return $results;
}
public function newDatapoints(PhabricatorChartDataQuery $query) {
$xv = $this->newInputValues($query);
if ($xv === null) {
$xv = $this->newDefaultInputValues($query);
}
$xv = $query->selectInputValues($xv);
$n = count($xv);
$yv = $this->evaluateFunction($xv);
$points = array();
for ($ii = 0; $ii < $n; $ii++) {
$y = $yv[$ii];
if ($y === null) {
continue;
}
$points[] = array(
'x' => $xv[$ii],
'y' => $y,
);
}
return $points;
}
abstract protected function newArguments();
final protected function newArgument() {
return new PhabricatorChartFunctionArgument();
}
final protected function getArgument($key) {
return $this->getArgumentParser()->getArgumentValue($key);
}
final protected function getArgumentParser() {
if (!$this->argumentParser) {
$parser = id(new PhabricatorChartFunctionArgumentParser())
->setFunction($this);
$this->argumentParser = $parser;
}
return $this->argumentParser;
}
abstract public function evaluateFunction(array $xv);
abstract public function getDataRefs(array $xv);
abstract public function loadRefs(array $refs);
public function getDomain() {
return null;
}
public function newInputValues(PhabricatorChartDataQuery $query) {
return null;
}
public function loadData() {
return;
}
protected function newDefaultInputValues(PhabricatorChartDataQuery $query) {
$x_min = $query->getMinimumValue();
$x_max = $query->getMaximumValue();
$limit = $query->getLimit();
return $this->newLinearSteps($x_min, $x_max, $limit);
}
protected function newLinearSteps($src, $dst, $count) {
$count = (int)$count;
$src = (int)$src;
$dst = (int)$dst;
if ($count === 0) {
throw new Exception(
pht('Can not generate zero linear steps between two values!'));
}
if ($src === $dst) {
return array($src);
}
if ($count === 1) {
return array($src);
}
$is_reversed = ($src > $dst);
if ($is_reversed) {
$min = (double)$dst;
$max = (double)$src;
} else {
$min = (double)$src;
$max = (double)$dst;
}
$step = (double)($max - $min) / (double)($count - 1);
$steps = array();
for ($cursor = $min; $cursor <= $max; $cursor += $step) {
$x = (int)round($cursor);
if (isset($steps[$x])) {
continue;
}
$steps[$x] = $x;
}
$steps = array_values($steps);
if ($is_reversed) {
$steps = array_reverse($steps);
}
return $steps;
}
}
diff --git a/src/applications/fact/chart/PhabricatorChartFunctionLabel.php b/src/applications/fact/chart/PhabricatorChartFunctionLabel.php
index ad85c49b71..fa3f65aa67 100644
--- a/src/applications/fact/chart/PhabricatorChartFunctionLabel.php
+++ b/src/applications/fact/chart/PhabricatorChartFunctionLabel.php
@@ -1,56 +1,67 @@
<?php
final class PhabricatorChartFunctionLabel
extends Phobject {
+ private $key;
private $name;
private $color;
private $icon;
private $fillColor;
+ public function setKey($key) {
+ $this->key = $key;
+ return $this;
+ }
+
+ public function getKey() {
+ return $this->key;
+ }
+
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setColor($color) {
$this->color = $color;
return $this;
}
public function getColor() {
return $this->color;
}
public function setIcon($icon) {
$this->icon = $icon;
return $this;
}
public function getIcon() {
return $this->icon;
}
public function setFillColor($fill_color) {
$this->fillColor = $fill_color;
return $this;
}
public function getFillColor() {
return $this->fillColor;
}
public function toWireFormat() {
return array(
+ 'key' => $this->getKey(),
'name' => $this->getName(),
'color' => $this->getColor(),
'icon' => $this->getIcon(),
'fillColor' => $this->getFillColor(),
);
}
}
diff --git a/src/applications/fact/chart/PhabricatorChartStackedAreaDataset.php b/src/applications/fact/chart/PhabricatorChartStackedAreaDataset.php
index 4d30cd767b..2ba08ea1c9 100644
--- a/src/applications/fact/chart/PhabricatorChartStackedAreaDataset.php
+++ b/src/applications/fact/chart/PhabricatorChartStackedAreaDataset.php
@@ -1,226 +1,300 @@
<?php
final class PhabricatorChartStackedAreaDataset
extends PhabricatorChartDataset {
const DATASETKEY = 'stacked-area';
- protected function newChartDisplayData(
- PhabricatorChartDataQuery $data_query) {
- $functions = $this->getFunctions();
+ private $stacks;
- $reversed_functions = array_reverse($functions, true);
+ public function setStacks(array $stacks) {
+ $this->stacks = $stacks;
+ return $this;
+ }
- $function_points = array();
- foreach ($reversed_functions as $function_idx => $function) {
- $function_points[$function_idx] = array();
+ public function getStacks() {
+ return $this->stacks;
+ }
- $datapoints = $function->newDatapoints($data_query);
- foreach ($datapoints as $point) {
- $x_value = $point['x'];
- $function_points[$function_idx][$x_value] = $point;
- }
- }
+ protected function newChartDisplayData(
+ PhabricatorChartDataQuery $data_query) {
- $raw_points = $function_points;
+ $functions = $this->getFunctions();
+ $functions = mpull($functions, null, 'getKey');
- // We need to define every function we're drawing at every point where
- // any of the functions we're drawing are defined. If we don't, we'll
- // end up with weird gaps or overlaps between adjacent areas, and won't
- // know how much we need to lift each point above the baseline when
- // stacking the functions on top of one another.
+ $stacks = $this->getStacks();
- $must_define = array();
- foreach ($function_points as $function_idx => $points) {
- foreach ($points as $x => $point) {
- $must_define[$x] = $x;
- }
+ if (!$stacks) {
+ $stacks = array(
+ array_reverse(array_keys($functions), true),
+ );
}
- ksort($must_define);
- foreach ($reversed_functions as $function_idx => $function) {
- $missing = array();
- foreach ($must_define as $x) {
- if (!isset($function_points[$function_idx][$x])) {
- $missing[$x] = true;
+ $series = array();
+ $raw_points = array();
+
+ foreach ($stacks as $stack) {
+ $stack_functions = array_select_keys($functions, $stack);
+
+ $function_points = $this->getFunctionDatapoints(
+ $data_query,
+ $stack_functions);
+
+ $stack_points = $function_points;
+
+ $function_points = $this->getGeometry(
+ $data_query,
+ $function_points);
+
+ $baseline = array();
+ foreach ($function_points as $function_idx => $points) {
+ $bounds = array();
+ foreach ($points as $x => $point) {
+ if (!isset($baseline[$x])) {
+ $baseline[$x] = 0;
+ }
+
+ $y0 = $baseline[$x];
+ $baseline[$x] += $point['y'];
+ $y1 = $baseline[$x];
+
+ $bounds[] = array(
+ 'x' => $x,
+ 'y0' => $y0,
+ 'y1' => $y1,
+ );
+
+ if (isset($stack_points[$function_idx][$x])) {
+ $stack_points[$function_idx][$x]['y1'] = $y1;
+ }
}
- }
- if (!$missing) {
- continue;
+ $series[$function_idx] = $bounds;
}
- $points = $function_points[$function_idx];
-
- $values = array_keys($points);
- $cursor = -1;
- $length = count($values);
-
- foreach ($missing as $x => $ignored) {
- // Move the cursor forward until we find the last point before "x"
- // which is defined.
- while ($cursor + 1 < $length && $values[$cursor + 1] < $x) {
- $cursor++;
- }
-
- // If this new point is to the left of all defined points, we'll
- // assume the value is 0. If the point is to the right of all defined
- // points, we assume the value is the same as the last known value.
-
- // If it's between two defined points, we average them.
-
- if ($cursor < 0) {
- $y = 0;
- } else if ($cursor + 1 < $length) {
- $xmin = $values[$cursor];
- $xmax = $values[$cursor + 1];
-
- $ymin = $points[$xmin]['y'];
- $ymax = $points[$xmax]['y'];
-
- // Fill in the missing point by creating a linear interpolation
- // between the two adjacent points.
- $distance = ($x - $xmin) / ($xmax - $xmin);
- $y = $ymin + (($ymax - $ymin) * $distance);
- } else {
- $xmin = $values[$cursor];
- $y = $function_points[$function_idx][$xmin]['y'];
- }
+ $raw_points += $stack_points;
+ }
- $function_points[$function_idx][$x] = array(
- 'x' => $x,
- 'y' => $y,
- );
- }
+ $series = array_select_keys($series, array_keys($functions));
+ $series = array_values($series);
- ksort($function_points[$function_idx]);
- }
+ $raw_points = array_select_keys($raw_points, array_keys($functions));
+ $raw_points = array_values($raw_points);
$range_min = null;
$range_max = null;
- $series = array();
- $baseline = array();
- foreach ($function_points as $function_idx => $points) {
- $below = idx($function_points, $function_idx - 1);
-
- $bounds = array();
- foreach ($points as $x => $point) {
- if (!isset($baseline[$x])) {
- $baseline[$x] = 0;
- }
-
- $y0 = $baseline[$x];
- $baseline[$x] += $point['y'];
- $y1 = $baseline[$x];
-
- $bounds[] = array(
- 'x' => $x,
- 'y0' => $y0,
- 'y1' => $y1,
- );
-
- if (isset($raw_points[$function_idx][$x])) {
- $raw_points[$function_idx][$x]['y1'] = $y1;
- }
+ foreach ($series as $geometry_list) {
+ foreach ($geometry_list as $geometry_item) {
+ $y0 = $geometry_item['y0'];
+ $y1 = $geometry_item['y1'];
if ($range_min === null) {
$range_min = $y0;
}
$range_min = min($range_min, $y0, $y1);
if ($range_max === null) {
$range_max = $y1;
}
$range_max = max($range_max, $y0, $y1);
}
-
- $series[] = $bounds;
}
- $series = array_reverse($series);
-
// We're going to group multiple events into a single point if they have
// X values that are very close to one another.
//
// If the Y values are also close to one another (these points are near
// one another in a horizontal line), it can be hard to select any
// individual point with the mouse.
//
// Even if the Y values are not close together (the points are on a
// fairly steep slope up or down), it's usually better to be able to
// mouse over a single point at the top or bottom of the slope and get
// a summary of what's going on.
$domain_max = $data_query->getMaximumValue();
$domain_min = $data_query->getMinimumValue();
$resolution = ($domain_max - $domain_min) / 100;
$events = array();
foreach ($raw_points as $function_idx => $points) {
$event_list = array();
$event_group = array();
$head_event = null;
foreach ($points as $point) {
$x = $point['x'];
if ($head_event === null) {
// We don't have any points yet, so start a new group.
$head_event = $x;
$event_group[] = $point;
} else if (($x - $head_event) <= $resolution) {
// This point is close to the first point in this group, so
// add it to the existing group.
$event_group[] = $point;
} else {
// This point is not close to the first point in the group,
// so create a new group.
$event_list[] = $event_group;
$head_event = $x;
$event_group = array($point);
}
}
if ($event_group) {
$event_list[] = $event_group;
}
$event_spec = array();
foreach ($event_list as $key => $event_points) {
// NOTE: We're using the last point as the representative point so
// that you can learn about a section of a chart by hovering over
// the point to right of the section, which is more intuitive than
// other points.
$event = last($event_points);
$event = $event + array(
'n' => count($event_points),
);
$event_list[$key] = $event;
}
$events[] = $event_list;
}
$wire_labels = array();
foreach ($functions as $function_key => $function) {
$label = $function->getFunctionLabel();
$wire_labels[] = $label->toWireFormat();
}
$result = array(
'type' => $this->getDatasetTypeKey(),
'data' => $series,
'events' => $events,
'labels' => $wire_labels,
);
return id(new PhabricatorChartDisplayData())
->setWireData($result)
->setRange(new PhabricatorChartInterval($range_min, $range_max));
}
+ private function getAllXValuesAsMap(
+ PhabricatorChartDataQuery $data_query,
+ array $point_lists) {
+
+ // We need to define every function we're drawing at every point where
+ // any of the functions we're drawing are defined. If we don't, we'll
+ // end up with weird gaps or overlaps between adjacent areas, and won't
+ // know how much we need to lift each point above the baseline when
+ // stacking the functions on top of one another.
+
+ $must_define = array();
+
+ $min = $data_query->getMinimumValue();
+ $max = $data_query->getMaximumValue();
+ $must_define[$max] = $max;
+ $must_define[$min] = $min;
+
+ foreach ($point_lists as $point_list) {
+ foreach ($point_list as $x => $point) {
+ $must_define[$x] = $x;
+ }
+ }
+
+ ksort($must_define);
+
+ return $must_define;
+ }
+
+ private function getFunctionDatapoints(
+ PhabricatorChartDataQuery $data_query,
+ array $functions) {
+
+ assert_instances_of($functions, 'PhabricatorChartFunction');
+
+ $points = array();
+ foreach ($functions as $idx => $function) {
+ $points[$idx] = array();
+
+ $datapoints = $function->newDatapoints($data_query);
+ foreach ($datapoints as $point) {
+ $x_value = $point['x'];
+ $points[$idx][$x_value] = $point;
+ }
+ }
+
+ return $points;
+ }
+
+ private function getGeometry(
+ PhabricatorChartDataQuery $data_query,
+ array $point_lists) {
+
+ $must_define = $this->getAllXValuesAsMap($data_query, $point_lists);
+
+ foreach ($point_lists as $idx => $points) {
+
+ $missing = array();
+ foreach ($must_define as $x) {
+ if (!isset($points[$x])) {
+ $missing[$x] = true;
+ }
+ }
+
+ if (!$missing) {
+ continue;
+ }
+
+ $values = array_keys($points);
+ $cursor = -1;
+ $length = count($values);
+
+ foreach ($missing as $x => $ignored) {
+ // Move the cursor forward until we find the last point before "x"
+ // which is defined.
+ while ($cursor + 1 < $length && $values[$cursor + 1] < $x) {
+ $cursor++;
+ }
+
+ // If this new point is to the left of all defined points, we'll
+ // assume the value is 0. If the point is to the right of all defined
+ // points, we assume the value is the same as the last known value.
+
+ // If it's between two defined points, we average them.
+
+ if ($cursor < 0) {
+ $y = 0;
+ } else if ($cursor + 1 < $length) {
+ $xmin = $values[$cursor];
+ $xmax = $values[$cursor + 1];
+
+ $ymin = $points[$xmin]['y'];
+ $ymax = $points[$xmax]['y'];
+
+ // Fill in the missing point by creating a linear interpolation
+ // between the two adjacent points.
+ $distance = ($x - $xmin) / ($xmax - $xmin);
+ $y = $ymin + (($ymax - $ymin) * $distance);
+ } else {
+ $xmin = $values[$cursor];
+ $y = $points[$xmin]['y'];
+ }
+
+ $point_lists[$idx][$x] = array(
+ 'x' => $x,
+ 'y' => $y,
+ );
+ }
+
+ ksort($point_lists[$idx]);
+ }
+
+ return $point_lists;
+ }
}
diff --git a/src/applications/project/chart/PhabricatorProjectBurndownChartEngine.php b/src/applications/project/chart/PhabricatorProjectBurndownChartEngine.php
index 2dbb2a6a16..8dda7cde16 100644
--- a/src/applications/project/chart/PhabricatorProjectBurndownChartEngine.php
+++ b/src/applications/project/chart/PhabricatorProjectBurndownChartEngine.php
@@ -1,130 +1,168 @@
<?php
final class PhabricatorProjectBurndownChartEngine
extends PhabricatorChartEngine {
const CHARTENGINEKEY = 'project.burndown';
public function setProjects(array $projects) {
assert_instances_of($projects, 'PhabricatorProject');
$project_phids = mpull($projects, 'getPHID');
return $this->setEngineParameter('projectPHIDs', $project_phids);
}
protected function newChart(PhabricatorFactChart $chart, array $map) {
$viewer = $this->getViewer();
$map = $map + array(
'projectPHIDs' => array(),
);
if ($map['projectPHIDs']) {
$projects = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withPHIDs($map['projectPHIDs'])
->execute();
$project_phids = mpull($projects, 'getPHID');
} else {
$project_phids = array();
}
$functions = array();
+ $stacks = array();
+
if ($project_phids) {
foreach ($project_phids as $project_phid) {
$function = $this->newFunction(
array(
'accumulate',
array(
'compose',
array('fact', 'tasks.open-count.assign.project', $project_phid),
array('min', 0),
),
));
$function->getFunctionLabel()
+ ->setKey('moved-in')
->setName(pht('Tasks Moved Into Project'))
->setColor('rgba(128, 128, 200, 1)')
->setFillColor('rgba(128, 128, 200, 0.15)');
$functions[] = $function;
+ $function = $this->newFunction(
+ array(
+ 'accumulate',
+ array(
+ 'compose',
+ array('fact', 'tasks.open-count.status.project', $project_phid),
+ array('min', 0),
+ ),
+ ));
+
+ $function->getFunctionLabel()
+ ->setKey('reopened')
+ ->setName(pht('Tasks Reopened'))
+ ->setColor('rgba(128, 128, 200, 1)')
+ ->setFillColor('rgba(128, 128, 200, 0.15)');
+
+ $functions[] = $function;
+
$function = $this->newFunction(
array(
'accumulate',
array('fact', 'tasks.open-count.create.project', $project_phid),
));
$function->getFunctionLabel()
+ ->setKey('created')
->setName(pht('Tasks Created'))
->setColor('rgba(0, 0, 200, 1)')
->setFillColor('rgba(0, 0, 200, 0.15)');
$functions[] = $function;
$function = $this->newFunction(
array(
'accumulate',
array(
'compose',
- array('fact', 'tasks.open-count.assign.project', $project_phid),
+ array('fact', 'tasks.open-count.status.project', $project_phid),
array('max', 0),
),
));
$function->getFunctionLabel()
- ->setName(pht('Tasks Moved Out of Project'))
- ->setColor('rgba(128, 200, 128, 1)')
- ->setFillColor('rgba(128, 200, 128, 0.15)');
+ ->setKey('closed')
+ ->setName(pht('Tasks Closed'))
+ ->setColor('rgba(0, 200, 0, 1)')
+ ->setFillColor('rgba(0, 200, 0, 0.15)');
$functions[] = $function;
$function = $this->newFunction(
array(
'accumulate',
- array('fact', 'tasks.open-count.status.project', $project_phid),
+ array(
+ 'compose',
+ array('fact', 'tasks.open-count.assign.project', $project_phid),
+ array('max', 0),
+ ),
));
$function->getFunctionLabel()
- ->setName(pht('Tasks Closed'))
- ->setColor('rgba(0, 200, 0, 1)')
- ->setFillColor('rgba(0, 200, 0, 0.15)');
+ ->setKey('moved-out')
+ ->setName(pht('Tasks Moved Out of Project'))
+ ->setColor('rgba(128, 200, 128, 1)')
+ ->setFillColor('rgba(128, 200, 128, 0.15)');
$functions[] = $function;
+
+ $stacks[] = array('created', 'reopened', 'moved-in');
+ $stacks[] = array('closed', 'moved-out');
}
} else {
$function = $this->newFunction(
array(
'accumulate',
array('fact', 'tasks.open-count.create'),
));
$function->getFunctionLabel()
- ->setName(pht('Tasks Created'))
+ ->setKey('open')
+ ->setName(pht('Open Tasks'))
->setColor('rgba(0, 0, 200, 1)')
->setFillColor('rgba(0, 0, 200, 0.15)');
$functions[] = $function;
$function = $this->newFunction(
array(
'accumulate',
array('fact', 'tasks.open-count.status'),
));
$function->getFunctionLabel()
- ->setName(pht('Tasks Closed'))
+ ->setKey('closed')
+ ->setName(pht('Closed Tasks'))
->setColor('rgba(0, 200, 0, 1)')
->setFillColor('rgba(0, 200, 0, 0.15)');
$functions[] = $function;
}
$datasets = array();
- $datasets[] = id(new PhabricatorChartStackedAreaDataset())
+ $dataset = id(new PhabricatorChartStackedAreaDataset())
->setFunctions($functions);
+ if ($stacks) {
+ $dataset->setStacks($stacks);
+ }
+
+ $datasets[] = $dataset;
$chart->attachDatasets($datasets);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 3, 7:38 PM (5 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
166278
Default Alt Text
(28 KB)

Event Timeline