1
0
Fork 0
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:
epriestley 2019-04-16 11:08:59 -07:00
parent faf0a311ec
commit 02981f0add
6 changed files with 98 additions and 17 deletions

View file

@ -397,7 +397,7 @@ return array(
'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3',
'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d',
'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/owners/OwnersPathEditor.js' => '2a8b62d9',
'rsrc/js/application/owners/owners-path-editor.js' => 'ff688a7a',
@ -625,7 +625,7 @@ return array(
'javelin-behavior-icon-composer' => '38a6cedb',
'javelin-behavior-launch-icon-composer' => 'a17b84f1',
'javelin-behavior-lightbox-attachments' => 'c7e748bf',
'javelin-behavior-line-chart' => 'c8147a20',
'javelin-behavior-line-chart' => '11167911',
'javelin-behavior-linked-container' => '74446546',
'javelin-behavior-maniphest-batch-selector' => '139ef688',
'javelin-behavior-maniphest-list-editor' => 'c687e867',
@ -1007,6 +1007,12 @@ return array(
'javelin-workflow',
'phuix-icon-view',
),
11167911 => array(
'javelin-behavior',
'javelin-dom',
'javelin-vector',
'phui-chart-css',
),
'111bfd2d' => array(
'javelin-install',
),
@ -1997,12 +2003,6 @@ return array(
'phuix-icon-view',
'phabricator-busy',
),
'c8147a20' => array(
'javelin-behavior',
'javelin-dom',
'javelin-vector',
'phui-chart-css',
),
'c9749dcd' => array(
'javelin-install',
'javelin-util',

View 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};

View file

@ -3201,6 +3201,7 @@ phutil_register_library_map(array(
'PhabricatorFact' => 'applications/fact/fact/PhabricatorFact.php',
'PhabricatorFactAggregate' => 'applications/fact/storage/PhabricatorFactAggregate.php',
'PhabricatorFactApplication' => 'applications/fact/application/PhabricatorFactApplication.php',
'PhabricatorFactChart' => 'applications/fact/storage/PhabricatorFactChart.php',
'PhabricatorFactChartController' => 'applications/fact/controller/PhabricatorFactChartController.php',
'PhabricatorFactController' => 'applications/fact/controller/PhabricatorFactController.php',
'PhabricatorFactCursor' => 'applications/fact/storage/PhabricatorFactCursor.php',
@ -9229,6 +9230,10 @@ phutil_register_library_map(array(
'PhabricatorFact' => 'Phobject',
'PhabricatorFactAggregate' => 'PhabricatorFactDAO',
'PhabricatorFactApplication' => 'PhabricatorApplication',
'PhabricatorFactChart' => array(
'PhabricatorFactDAO',
'PhabricatorPolicyInterface',
),
'PhabricatorFactChartController' => 'PhabricatorFactController',
'PhabricatorFactController' => 'PhabricatorController',
'PhabricatorFactCursor' => 'PhabricatorFactDAO',

View file

@ -74,6 +74,8 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
'hardpoint' => $id,
'x' => array($x),
'y' => array($y),
'yMax' => max(0, max($y)),
'yMin' => min(0, min($y)),
'xformat' => 'epoch',
'colors' => array('#0000ff'),
));
@ -82,8 +84,9 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
->setHeaderText(pht('Count of %s', $fact->getName()))
->appendChild($chart);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Chart'));
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Chart'))
->setBorder(true);
$title = pht('Chart');

View 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;
}
}

View file

@ -8,7 +8,7 @@
JX.behavior('line-chart', function(config) {
function fn(n) {
function css_function(n) {
return n + '(' + JX.$A(arguments).slice(1).join(', ') + ')';
}
@ -50,7 +50,7 @@ JX.behavior('line-chart', function(config) {
.attr('class', 'chart');
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')
.attr('class', 'inner')
@ -73,9 +73,7 @@ JX.behavior('line-chart', function(config) {
x.domain(d3.extent(data, function(d) { return d.date; }));
var yex = d3.extent(data, function(d) { return d.count; });
yex[0] = 0;
yex[1] = yex[1] * 1.05;
y.domain(yex);
y.domain([config.yMin, config.yMax]);
g.append('path')
.datum(data)
@ -84,12 +82,12 @@ JX.behavior('line-chart', function(config) {
g.append('g')
.attr('class', 'x axis')
.attr('transform', fn('translate', 0, size.height))
.attr('transform', css_function('translate', 0, size.height))
.call(xAxis);
g.append('g')
.attr('class', 'y axis')
.attr('transform', fn('translate', 0, 0))
.attr('transform', css_function('translate', 0, 0))
.call(yAxis);
var div = d3.select('body')