mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
Fix negative chart values, add storage for charts
Summary: Ref T13279. I think I'm going to fling some stuff at the wall for a bit here and hope most of it sticks, so this series of changes may not be terribly cohesive or focused. Here: The range of the chart is locked to "[0, 105% of max]". This is trying to make a pleasing extra margin above the maximum value, but currently just breaks charts with negative values. Later: - I'll probably let users customize this. - We should likely select 0 as the automatic minimum for charts with no negative values. - For charts with positive values, it would be nice to automatically pick a pleasantly round number (25, 100, 1000) as a maximum by default. We don't have any storage for charts yet. Add some. This works like queries, where every possible configuration gets a short URL slug. Nothing writes or reads this yet. Rename `fn()` to `css_function()`. This builds CSS functions for D3. The JS is likely to get substantial structural rewrites later on, `fn()` was just particularly offensive. Test Plan: Viewed a fact series with negative values. Ran `bin/storage upgrade`. Reviewers: amckinley Reviewed By: amckinley Subscribers: yelirekim, PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13279 Differential Revision: https://secure.phabricator.com/D20438
This commit is contained in:
parent
faf0a311ec
commit
02981f0add
6 changed files with 98 additions and 17 deletions
|
@ -397,7 +397,7 @@ return array(
|
||||||
'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3',
|
'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3',
|
||||||
'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d',
|
'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d',
|
||||||
'rsrc/js/application/maniphest/behavior-batch-selector.js' => '139ef688',
|
'rsrc/js/application/maniphest/behavior-batch-selector.js' => '139ef688',
|
||||||
'rsrc/js/application/maniphest/behavior-line-chart.js' => 'c8147a20',
|
'rsrc/js/application/maniphest/behavior-line-chart.js' => '11167911',
|
||||||
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'c687e867',
|
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'c687e867',
|
||||||
'rsrc/js/application/owners/OwnersPathEditor.js' => '2a8b62d9',
|
'rsrc/js/application/owners/OwnersPathEditor.js' => '2a8b62d9',
|
||||||
'rsrc/js/application/owners/owners-path-editor.js' => 'ff688a7a',
|
'rsrc/js/application/owners/owners-path-editor.js' => 'ff688a7a',
|
||||||
|
@ -625,7 +625,7 @@ return array(
|
||||||
'javelin-behavior-icon-composer' => '38a6cedb',
|
'javelin-behavior-icon-composer' => '38a6cedb',
|
||||||
'javelin-behavior-launch-icon-composer' => 'a17b84f1',
|
'javelin-behavior-launch-icon-composer' => 'a17b84f1',
|
||||||
'javelin-behavior-lightbox-attachments' => 'c7e748bf',
|
'javelin-behavior-lightbox-attachments' => 'c7e748bf',
|
||||||
'javelin-behavior-line-chart' => 'c8147a20',
|
'javelin-behavior-line-chart' => '11167911',
|
||||||
'javelin-behavior-linked-container' => '74446546',
|
'javelin-behavior-linked-container' => '74446546',
|
||||||
'javelin-behavior-maniphest-batch-selector' => '139ef688',
|
'javelin-behavior-maniphest-batch-selector' => '139ef688',
|
||||||
'javelin-behavior-maniphest-list-editor' => 'c687e867',
|
'javelin-behavior-maniphest-list-editor' => 'c687e867',
|
||||||
|
@ -1007,6 +1007,12 @@ return array(
|
||||||
'javelin-workflow',
|
'javelin-workflow',
|
||||||
'phuix-icon-view',
|
'phuix-icon-view',
|
||||||
),
|
),
|
||||||
|
11167911 => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-vector',
|
||||||
|
'phui-chart-css',
|
||||||
|
),
|
||||||
'111bfd2d' => array(
|
'111bfd2d' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
),
|
),
|
||||||
|
@ -1997,12 +2003,6 @@ return array(
|
||||||
'phuix-icon-view',
|
'phuix-icon-view',
|
||||||
'phabricator-busy',
|
'phabricator-busy',
|
||||||
),
|
),
|
||||||
'c8147a20' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-vector',
|
|
||||||
'phui-chart-css',
|
|
||||||
),
|
|
||||||
'c9749dcd' => array(
|
'c9749dcd' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
|
6
resources/sql/autopatches/20190416.chart.01.storage.sql
Normal file
6
resources/sql/autopatches/20190416.chart.01.storage.sql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_fact.fact_chart (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
chartKey BINARY(12) NOT NULL,
|
||||||
|
chartParameters LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
UNIQUE KEY `key_chart` (chartKey)
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -3201,6 +3201,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFact' => 'applications/fact/fact/PhabricatorFact.php',
|
'PhabricatorFact' => 'applications/fact/fact/PhabricatorFact.php',
|
||||||
'PhabricatorFactAggregate' => 'applications/fact/storage/PhabricatorFactAggregate.php',
|
'PhabricatorFactAggregate' => 'applications/fact/storage/PhabricatorFactAggregate.php',
|
||||||
'PhabricatorFactApplication' => 'applications/fact/application/PhabricatorFactApplication.php',
|
'PhabricatorFactApplication' => 'applications/fact/application/PhabricatorFactApplication.php',
|
||||||
|
'PhabricatorFactChart' => 'applications/fact/storage/PhabricatorFactChart.php',
|
||||||
'PhabricatorFactChartController' => 'applications/fact/controller/PhabricatorFactChartController.php',
|
'PhabricatorFactChartController' => 'applications/fact/controller/PhabricatorFactChartController.php',
|
||||||
'PhabricatorFactController' => 'applications/fact/controller/PhabricatorFactController.php',
|
'PhabricatorFactController' => 'applications/fact/controller/PhabricatorFactController.php',
|
||||||
'PhabricatorFactCursor' => 'applications/fact/storage/PhabricatorFactCursor.php',
|
'PhabricatorFactCursor' => 'applications/fact/storage/PhabricatorFactCursor.php',
|
||||||
|
@ -9229,6 +9230,10 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFact' => 'Phobject',
|
'PhabricatorFact' => 'Phobject',
|
||||||
'PhabricatorFactAggregate' => 'PhabricatorFactDAO',
|
'PhabricatorFactAggregate' => 'PhabricatorFactDAO',
|
||||||
'PhabricatorFactApplication' => 'PhabricatorApplication',
|
'PhabricatorFactApplication' => 'PhabricatorApplication',
|
||||||
|
'PhabricatorFactChart' => array(
|
||||||
|
'PhabricatorFactDAO',
|
||||||
|
'PhabricatorPolicyInterface',
|
||||||
|
),
|
||||||
'PhabricatorFactChartController' => 'PhabricatorFactController',
|
'PhabricatorFactChartController' => 'PhabricatorFactController',
|
||||||
'PhabricatorFactController' => 'PhabricatorController',
|
'PhabricatorFactController' => 'PhabricatorController',
|
||||||
'PhabricatorFactCursor' => 'PhabricatorFactDAO',
|
'PhabricatorFactCursor' => 'PhabricatorFactDAO',
|
||||||
|
|
|
@ -74,6 +74,8 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
'hardpoint' => $id,
|
'hardpoint' => $id,
|
||||||
'x' => array($x),
|
'x' => array($x),
|
||||||
'y' => array($y),
|
'y' => array($y),
|
||||||
|
'yMax' => max(0, max($y)),
|
||||||
|
'yMin' => min(0, min($y)),
|
||||||
'xformat' => 'epoch',
|
'xformat' => 'epoch',
|
||||||
'colors' => array('#0000ff'),
|
'colors' => array('#0000ff'),
|
||||||
));
|
));
|
||||||
|
@ -82,8 +84,9 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
->setHeaderText(pht('Count of %s', $fact->getName()))
|
->setHeaderText(pht('Count of %s', $fact->getName()))
|
||||||
->appendChild($chart);
|
->appendChild($chart);
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs()
|
||||||
$crumbs->addTextCrumb(pht('Chart'));
|
->addTextCrumb(pht('Chart'))
|
||||||
|
->setBorder(true);
|
||||||
|
|
||||||
$title = pht('Chart');
|
$title = pht('Chart');
|
||||||
|
|
||||||
|
|
69
src/applications/fact/storage/PhabricatorFactChart.php
Normal file
69
src/applications/fact/storage/PhabricatorFactChart.php
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorFactChart
|
||||||
|
extends PhabricatorFactDAO
|
||||||
|
implements PhabricatorPolicyInterface {
|
||||||
|
|
||||||
|
protected $chartKey;
|
||||||
|
protected $chartParameters = array();
|
||||||
|
|
||||||
|
protected function getConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::CONFIG_SERIALIZATION => array(
|
||||||
|
'chartParameters' => self::SERIALIZATION_JSON,
|
||||||
|
),
|
||||||
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
|
'chartKey' => 'bytes12',
|
||||||
|
),
|
||||||
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
'key_chart' => array(
|
||||||
|
'columns' => array('chartKey'),
|
||||||
|
'unique' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) + parent::getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setChartParameter($key, $value) {
|
||||||
|
$this->chartParameters[$key] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getChartParameter($key, $default = null) {
|
||||||
|
return idx($this->chartParameters, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save() {
|
||||||
|
if ($this->getID()) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Chart configurations are not mutable. You can not update or '.
|
||||||
|
'overwrite an existing chart configuration.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$digest = serialize($this->chartParameters);
|
||||||
|
$digest = PhabricatorHash::digestForIndex($digest);
|
||||||
|
|
||||||
|
$this->chartKey = $digest;
|
||||||
|
|
||||||
|
return parent::save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
public function getCapabilities() {
|
||||||
|
return array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPolicy($capability) {
|
||||||
|
return PhabricatorPolicies::getMostOpenPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
JX.behavior('line-chart', function(config) {
|
JX.behavior('line-chart', function(config) {
|
||||||
|
|
||||||
function fn(n) {
|
function css_function(n) {
|
||||||
return n + '(' + JX.$A(arguments).slice(1).join(', ') + ')';
|
return n + '(' + JX.$A(arguments).slice(1).join(', ') + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ JX.behavior('line-chart', function(config) {
|
||||||
.attr('class', 'chart');
|
.attr('class', 'chart');
|
||||||
|
|
||||||
var g = svg.append('g')
|
var g = svg.append('g')
|
||||||
.attr('transform', fn('translate', padding.left, padding.top));
|
.attr('transform', css_function('translate', padding.left, padding.top));
|
||||||
|
|
||||||
g.append('rect')
|
g.append('rect')
|
||||||
.attr('class', 'inner')
|
.attr('class', 'inner')
|
||||||
|
@ -73,9 +73,7 @@ JX.behavior('line-chart', function(config) {
|
||||||
x.domain(d3.extent(data, function(d) { return d.date; }));
|
x.domain(d3.extent(data, function(d) { return d.date; }));
|
||||||
|
|
||||||
var yex = d3.extent(data, function(d) { return d.count; });
|
var yex = d3.extent(data, function(d) { return d.count; });
|
||||||
yex[0] = 0;
|
y.domain([config.yMin, config.yMax]);
|
||||||
yex[1] = yex[1] * 1.05;
|
|
||||||
y.domain(yex);
|
|
||||||
|
|
||||||
g.append('path')
|
g.append('path')
|
||||||
.datum(data)
|
.datum(data)
|
||||||
|
@ -84,12 +82,12 @@ JX.behavior('line-chart', function(config) {
|
||||||
|
|
||||||
g.append('g')
|
g.append('g')
|
||||||
.attr('class', 'x axis')
|
.attr('class', 'x axis')
|
||||||
.attr('transform', fn('translate', 0, size.height))
|
.attr('transform', css_function('translate', 0, size.height))
|
||||||
.call(xAxis);
|
.call(xAxis);
|
||||||
|
|
||||||
g.append('g')
|
g.append('g')
|
||||||
.attr('class', 'y axis')
|
.attr('class', 'y axis')
|
||||||
.attr('transform', fn('translate', 0, 0))
|
.attr('transform', css_function('translate', 0, 0))
|
||||||
.call(yAxis);
|
.call(yAxis);
|
||||||
|
|
||||||
var div = d3.select('body')
|
var div = d3.select('body')
|
||||||
|
|
Loading…
Reference in a new issue