mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-30 02:32:42 +01:00
(stable) Promote 2019 Week 38
This commit is contained in:
commit
f0f352ffd0
32 changed files with 924 additions and 371 deletions
|
@ -141,7 +141,7 @@ return array(
|
||||||
'rsrc/css/phui/phui-big-info-view.css' => '362ad37b',
|
'rsrc/css/phui/phui-big-info-view.css' => '362ad37b',
|
||||||
'rsrc/css/phui/phui-box.css' => '5ed3b8cb',
|
'rsrc/css/phui/phui-box.css' => '5ed3b8cb',
|
||||||
'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30',
|
'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30',
|
||||||
'rsrc/css/phui/phui-chart.css' => '10135a9d',
|
'rsrc/css/phui/phui-chart.css' => '14df9ae3',
|
||||||
'rsrc/css/phui/phui-cms.css' => '8c05c41e',
|
'rsrc/css/phui/phui-cms.css' => '8c05c41e',
|
||||||
'rsrc/css/phui/phui-comment-form.css' => '68a2d99a',
|
'rsrc/css/phui/phui-comment-form.css' => '68a2d99a',
|
||||||
'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0',
|
'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0',
|
||||||
|
@ -390,7 +390,7 @@ return array(
|
||||||
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123',
|
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123',
|
||||||
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a',
|
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a',
|
||||||
'rsrc/js/application/drydock/drydock-live-operation-status.js' => '47a0728b',
|
'rsrc/js/application/drydock/drydock-live-operation-status.js' => '47a0728b',
|
||||||
'rsrc/js/application/fact/Chart.js' => 'eec96de0',
|
'rsrc/js/application/fact/Chart.js' => '52e3ff03',
|
||||||
'rsrc/js/application/fact/ChartCurtainView.js' => '86954222',
|
'rsrc/js/application/fact/ChartCurtainView.js' => '86954222',
|
||||||
'rsrc/js/application/fact/ChartFunctionLabel.js' => '81de1dab',
|
'rsrc/js/application/fact/ChartFunctionLabel.js' => '81de1dab',
|
||||||
'rsrc/js/application/files/behavior-document-engine.js' => '243d6c22',
|
'rsrc/js/application/files/behavior-document-engine.js' => '243d6c22',
|
||||||
|
@ -699,7 +699,7 @@ return array(
|
||||||
'javelin-behavior-user-menu' => '60cd9241',
|
'javelin-behavior-user-menu' => '60cd9241',
|
||||||
'javelin-behavior-view-placeholder' => 'a9942052',
|
'javelin-behavior-view-placeholder' => 'a9942052',
|
||||||
'javelin-behavior-workflow' => '9623adc1',
|
'javelin-behavior-workflow' => '9623adc1',
|
||||||
'javelin-chart' => 'eec96de0',
|
'javelin-chart' => '52e3ff03',
|
||||||
'javelin-chart-curtain-view' => '86954222',
|
'javelin-chart-curtain-view' => '86954222',
|
||||||
'javelin-chart-function-label' => '81de1dab',
|
'javelin-chart-function-label' => '81de1dab',
|
||||||
'javelin-color' => '78f811c9',
|
'javelin-color' => '78f811c9',
|
||||||
|
@ -828,7 +828,7 @@ return array(
|
||||||
'phui-calendar-day-css' => '9597d706',
|
'phui-calendar-day-css' => '9597d706',
|
||||||
'phui-calendar-list-css' => 'ccd7e4e2',
|
'phui-calendar-list-css' => 'ccd7e4e2',
|
||||||
'phui-calendar-month-css' => 'cb758c42',
|
'phui-calendar-month-css' => 'cb758c42',
|
||||||
'phui-chart-css' => '10135a9d',
|
'phui-chart-css' => '14df9ae3',
|
||||||
'phui-cms-css' => '8c05c41e',
|
'phui-cms-css' => '8c05c41e',
|
||||||
'phui-comment-form-css' => '68a2d99a',
|
'phui-comment-form-css' => '68a2d99a',
|
||||||
'phui-comment-panel-css' => 'ec4e31c0',
|
'phui-comment-panel-css' => 'ec4e31c0',
|
||||||
|
@ -1369,6 +1369,12 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-fx',
|
'javelin-fx',
|
||||||
),
|
),
|
||||||
|
'52e3ff03' => array(
|
||||||
|
'phui-chart-css',
|
||||||
|
'd3',
|
||||||
|
'javelin-chart-curtain-view',
|
||||||
|
'javelin-chart-function-label',
|
||||||
|
),
|
||||||
'541f81c3' => array(
|
'541f81c3' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
),
|
),
|
||||||
|
@ -2127,12 +2133,6 @@ return array(
|
||||||
'phabricator-keyboard-shortcut',
|
'phabricator-keyboard-shortcut',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
),
|
),
|
||||||
'eec96de0' => array(
|
|
||||||
'phui-chart-css',
|
|
||||||
'd3',
|
|
||||||
'javelin-chart-curtain-view',
|
|
||||||
'javelin-chart-function-label',
|
|
||||||
),
|
|
||||||
'ef836bf2' => array(
|
'ef836bf2' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
|
|
@ -3104,6 +3104,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorDefaultRequestExceptionHandler' => 'aphront/handler/PhabricatorDefaultRequestExceptionHandler.php',
|
'PhabricatorDefaultRequestExceptionHandler' => 'aphront/handler/PhabricatorDefaultRequestExceptionHandler.php',
|
||||||
'PhabricatorDefaultSyntaxStyle' => 'infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php',
|
'PhabricatorDefaultSyntaxStyle' => 'infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php',
|
||||||
'PhabricatorDefaultUnlockEngine' => 'applications/system/engine/PhabricatorDefaultUnlockEngine.php',
|
'PhabricatorDefaultUnlockEngine' => 'applications/system/engine/PhabricatorDefaultUnlockEngine.php',
|
||||||
|
'PhabricatorDemoChartEngine' => 'applications/fact/engine/PhabricatorDemoChartEngine.php',
|
||||||
'PhabricatorDestructibleCodex' => 'applications/system/codex/PhabricatorDestructibleCodex.php',
|
'PhabricatorDestructibleCodex' => 'applications/system/codex/PhabricatorDestructibleCodex.php',
|
||||||
'PhabricatorDestructibleCodexInterface' => 'applications/system/interface/PhabricatorDestructibleCodexInterface.php',
|
'PhabricatorDestructibleCodexInterface' => 'applications/system/interface/PhabricatorDestructibleCodexInterface.php',
|
||||||
'PhabricatorDestructibleInterface' => 'applications/system/interface/PhabricatorDestructibleInterface.php',
|
'PhabricatorDestructibleInterface' => 'applications/system/interface/PhabricatorDestructibleInterface.php',
|
||||||
|
@ -4223,6 +4224,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProfileMenuItemView' => 'applications/search/engine/PhabricatorProfileMenuItemView.php',
|
'PhabricatorProfileMenuItemView' => 'applications/search/engine/PhabricatorProfileMenuItemView.php',
|
||||||
'PhabricatorProfileMenuItemViewList' => 'applications/search/engine/PhabricatorProfileMenuItemViewList.php',
|
'PhabricatorProfileMenuItemViewList' => 'applications/search/engine/PhabricatorProfileMenuItemViewList.php',
|
||||||
'PhabricatorProject' => 'applications/project/storage/PhabricatorProject.php',
|
'PhabricatorProject' => 'applications/project/storage/PhabricatorProject.php',
|
||||||
|
'PhabricatorProjectActivityChartEngine' => 'applications/project/chart/PhabricatorProjectActivityChartEngine.php',
|
||||||
'PhabricatorProjectAddHeraldAction' => 'applications/project/herald/PhabricatorProjectAddHeraldAction.php',
|
'PhabricatorProjectAddHeraldAction' => 'applications/project/herald/PhabricatorProjectAddHeraldAction.php',
|
||||||
'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php',
|
'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php',
|
||||||
'PhabricatorProjectArchiveController' => 'applications/project/controller/PhabricatorProjectArchiveController.php',
|
'PhabricatorProjectArchiveController' => 'applications/project/controller/PhabricatorProjectArchiveController.php',
|
||||||
|
@ -4422,6 +4424,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsWatchersSearchEngineAttachment.php',
|
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsWatchersSearchEngineAttachment.php',
|
||||||
'PhabricatorPronounSetting' => 'applications/settings/setting/PhabricatorPronounSetting.php',
|
'PhabricatorPronounSetting' => 'applications/settings/setting/PhabricatorPronounSetting.php',
|
||||||
'PhabricatorProtocolLog' => 'infrastructure/log/PhabricatorProtocolLog.php',
|
'PhabricatorProtocolLog' => 'infrastructure/log/PhabricatorProtocolLog.php',
|
||||||
|
'PhabricatorPureChartFunction' => 'applications/fact/chart/PhabricatorPureChartFunction.php',
|
||||||
'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php',
|
'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php',
|
||||||
'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php',
|
'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php',
|
||||||
'PhabricatorQueryConstraint' => 'infrastructure/query/constraint/PhabricatorQueryConstraint.php',
|
'PhabricatorQueryConstraint' => 'infrastructure/query/constraint/PhabricatorQueryConstraint.php',
|
||||||
|
@ -8300,7 +8303,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAccessLog' => 'Phobject',
|
'PhabricatorAccessLog' => 'Phobject',
|
||||||
'PhabricatorAccessLogConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorAccessLogConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
'PhabricatorAccessibilitySetting' => 'PhabricatorSelectSetting',
|
'PhabricatorAccessibilitySetting' => 'PhabricatorSelectSetting',
|
||||||
'PhabricatorAccumulateChartFunction' => 'PhabricatorChartFunction',
|
'PhabricatorAccumulateChartFunction' => 'PhabricatorHigherOrderChartFunction',
|
||||||
'PhabricatorActionListView' => 'AphrontTagView',
|
'PhabricatorActionListView' => 'AphrontTagView',
|
||||||
'PhabricatorActionView' => 'AphrontView',
|
'PhabricatorActionView' => 'AphrontView',
|
||||||
'PhabricatorActivitySettingsPanel' => 'PhabricatorSettingsPanel',
|
'PhabricatorActivitySettingsPanel' => 'PhabricatorSettingsPanel',
|
||||||
|
@ -9155,7 +9158,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorConpherenceWidgetVisibleSetting' => 'PhabricatorInternalSetting',
|
'PhabricatorConpherenceWidgetVisibleSetting' => 'PhabricatorInternalSetting',
|
||||||
'PhabricatorConsoleApplication' => 'PhabricatorApplication',
|
'PhabricatorConsoleApplication' => 'PhabricatorApplication',
|
||||||
'PhabricatorConsoleContentSource' => 'PhabricatorContentSource',
|
'PhabricatorConsoleContentSource' => 'PhabricatorContentSource',
|
||||||
'PhabricatorConstantChartFunction' => 'PhabricatorChartFunction',
|
'PhabricatorConstantChartFunction' => 'PhabricatorPureChartFunction',
|
||||||
'PhabricatorContactNumbersSettingsPanel' => 'PhabricatorSettingsPanel',
|
'PhabricatorContactNumbersSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||||
'PhabricatorContentSource' => 'Phobject',
|
'PhabricatorContentSource' => 'Phobject',
|
||||||
'PhabricatorContentSourceModule' => 'PhabricatorConfigModule',
|
'PhabricatorContentSourceModule' => 'PhabricatorConfigModule',
|
||||||
|
@ -9167,7 +9170,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorCoreCreateTransaction' => 'PhabricatorCoreTransactionType',
|
'PhabricatorCoreCreateTransaction' => 'PhabricatorCoreTransactionType',
|
||||||
'PhabricatorCoreTransactionType' => 'PhabricatorModularTransactionType',
|
'PhabricatorCoreTransactionType' => 'PhabricatorModularTransactionType',
|
||||||
'PhabricatorCoreVoidTransaction' => 'PhabricatorModularTransactionType',
|
'PhabricatorCoreVoidTransaction' => 'PhabricatorModularTransactionType',
|
||||||
'PhabricatorCosChartFunction' => 'PhabricatorChartFunction',
|
'PhabricatorCosChartFunction' => 'PhabricatorPureChartFunction',
|
||||||
'PhabricatorCountFact' => 'PhabricatorFact',
|
'PhabricatorCountFact' => 'PhabricatorFact',
|
||||||
'PhabricatorCountdown' => array(
|
'PhabricatorCountdown' => array(
|
||||||
'PhabricatorCountdownDAO',
|
'PhabricatorCountdownDAO',
|
||||||
|
@ -9433,6 +9436,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorDefaultRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
'PhabricatorDefaultRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||||
'PhabricatorDefaultSyntaxStyle' => 'PhabricatorSyntaxStyle',
|
'PhabricatorDefaultSyntaxStyle' => 'PhabricatorSyntaxStyle',
|
||||||
'PhabricatorDefaultUnlockEngine' => 'PhabricatorUnlockEngine',
|
'PhabricatorDefaultUnlockEngine' => 'PhabricatorUnlockEngine',
|
||||||
|
'PhabricatorDemoChartEngine' => 'PhabricatorChartEngine',
|
||||||
'PhabricatorDestructibleCodex' => 'Phobject',
|
'PhabricatorDestructibleCodex' => 'Phobject',
|
||||||
'PhabricatorDestructionEngine' => 'Phobject',
|
'PhabricatorDestructionEngine' => 'Phobject',
|
||||||
'PhabricatorDestructionEngineExtension' => 'Phobject',
|
'PhabricatorDestructionEngineExtension' => 'Phobject',
|
||||||
|
@ -10068,7 +10072,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMarkupInterface',
|
'PhabricatorMarkupInterface',
|
||||||
),
|
),
|
||||||
'PhabricatorMarkupPreviewController' => 'PhabricatorController',
|
'PhabricatorMarkupPreviewController' => 'PhabricatorController',
|
||||||
'PhabricatorMaxChartFunction' => 'PhabricatorChartFunction',
|
'PhabricatorMaxChartFunction' => 'PhabricatorPureChartFunction',
|
||||||
'PhabricatorMemeEngine' => 'Phobject',
|
'PhabricatorMemeEngine' => 'Phobject',
|
||||||
'PhabricatorMemeRemarkupRule' => 'PhutilRemarkupRule',
|
'PhabricatorMemeRemarkupRule' => 'PhutilRemarkupRule',
|
||||||
'PhabricatorMentionRemarkupRule' => 'PhutilRemarkupRule',
|
'PhabricatorMentionRemarkupRule' => 'PhutilRemarkupRule',
|
||||||
|
@ -10135,7 +10139,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMetronome' => 'Phobject',
|
'PhabricatorMetronome' => 'Phobject',
|
||||||
'PhabricatorMetronomeTestCase' => 'PhabricatorTestCase',
|
'PhabricatorMetronomeTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock',
|
'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock',
|
||||||
'PhabricatorMinChartFunction' => 'PhabricatorChartFunction',
|
'PhabricatorMinChartFunction' => 'PhabricatorPureChartFunction',
|
||||||
'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction',
|
'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
'PhabricatorModularTransactionType' => 'Phobject',
|
'PhabricatorModularTransactionType' => 'Phobject',
|
||||||
'PhabricatorMonogramDatasourceEngineExtension' => 'PhabricatorDatasourceEngineExtension',
|
'PhabricatorMonogramDatasourceEngineExtension' => 'PhabricatorDatasourceEngineExtension',
|
||||||
|
@ -10730,6 +10734,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorSpacesInterface',
|
'PhabricatorSpacesInterface',
|
||||||
'PhabricatorEditEngineSubtypeInterface',
|
'PhabricatorEditEngineSubtypeInterface',
|
||||||
),
|
),
|
||||||
|
'PhabricatorProjectActivityChartEngine' => 'PhabricatorChartEngine',
|
||||||
'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction',
|
'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction',
|
||||||
'PhabricatorProjectApplication' => 'PhabricatorApplication',
|
'PhabricatorProjectApplication' => 'PhabricatorApplication',
|
||||||
'PhabricatorProjectArchiveController' => 'PhabricatorProjectController',
|
'PhabricatorProjectArchiveController' => 'PhabricatorProjectController',
|
||||||
|
@ -10950,6 +10955,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||||
'PhabricatorPronounSetting' => 'PhabricatorSelectSetting',
|
'PhabricatorPronounSetting' => 'PhabricatorSelectSetting',
|
||||||
'PhabricatorProtocolLog' => 'Phobject',
|
'PhabricatorProtocolLog' => 'Phobject',
|
||||||
|
'PhabricatorPureChartFunction' => 'PhabricatorChartFunction',
|
||||||
'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck',
|
'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck',
|
||||||
'PhabricatorQuery' => 'Phobject',
|
'PhabricatorQuery' => 'Phobject',
|
||||||
'PhabricatorQueryConstraint' => 'Phobject',
|
'PhabricatorQueryConstraint' => 'Phobject',
|
||||||
|
@ -11208,7 +11214,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
),
|
),
|
||||||
'PhabricatorSavedQueryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorSavedQueryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'PhabricatorScaleChartFunction' => 'PhabricatorChartFunction',
|
'PhabricatorScaleChartFunction' => 'PhabricatorPureChartFunction',
|
||||||
'PhabricatorScheduleTaskTriggerAction' => 'PhabricatorTriggerAction',
|
'PhabricatorScheduleTaskTriggerAction' => 'PhabricatorTriggerAction',
|
||||||
'PhabricatorScopedEnv' => 'Phobject',
|
'PhabricatorScopedEnv' => 'Phobject',
|
||||||
'PhabricatorSearchAbstractDocument' => 'Phobject',
|
'PhabricatorSearchAbstractDocument' => 'Phobject',
|
||||||
|
@ -11299,12 +11305,12 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorSetupIssue' => 'Phobject',
|
'PhabricatorSetupIssue' => 'Phobject',
|
||||||
'PhabricatorSetupIssueUIExample' => 'PhabricatorUIExample',
|
'PhabricatorSetupIssueUIExample' => 'PhabricatorUIExample',
|
||||||
'PhabricatorSetupIssueView' => 'AphrontView',
|
'PhabricatorSetupIssueView' => 'AphrontView',
|
||||||
'PhabricatorShiftChartFunction' => 'PhabricatorChartFunction',
|
'PhabricatorShiftChartFunction' => 'PhabricatorPureChartFunction',
|
||||||
'PhabricatorShortSite' => 'PhabricatorSite',
|
'PhabricatorShortSite' => 'PhabricatorSite',
|
||||||
'PhabricatorShowFiletreeSetting' => 'PhabricatorSelectSetting',
|
'PhabricatorShowFiletreeSetting' => 'PhabricatorSelectSetting',
|
||||||
'PhabricatorSignDocumentsUserLogType' => 'PhabricatorUserLogType',
|
'PhabricatorSignDocumentsUserLogType' => 'PhabricatorUserLogType',
|
||||||
'PhabricatorSimpleEditType' => 'PhabricatorEditType',
|
'PhabricatorSimpleEditType' => 'PhabricatorEditType',
|
||||||
'PhabricatorSinChartFunction' => 'PhabricatorChartFunction',
|
'PhabricatorSinChartFunction' => 'PhabricatorPureChartFunction',
|
||||||
'PhabricatorSite' => 'AphrontSite',
|
'PhabricatorSite' => 'AphrontSite',
|
||||||
'PhabricatorSlackAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
'PhabricatorSlackAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
||||||
'PhabricatorSlowvoteApplication' => 'PhabricatorApplication',
|
'PhabricatorSlowvoteApplication' => 'PhabricatorApplication',
|
||||||
|
|
|
@ -22,15 +22,10 @@ final class PhabricatorFactApplication extends PhabricatorApplication {
|
||||||
return self::GROUP_UTILITIES;
|
return self::GROUP_UTILITIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isPrototype() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRoutes() {
|
public function getRoutes() {
|
||||||
return array(
|
return array(
|
||||||
'/fact/' => array(
|
'/fact/' => array(
|
||||||
'' => 'PhabricatorFactHomeController',
|
'' => 'PhabricatorFactHomeController',
|
||||||
'chart/' => 'PhabricatorFactChartController',
|
|
||||||
'chart/(?P<chartKey>[^/]+)/(?:(?P<mode>draw)/)?' =>
|
'chart/(?P<chartKey>[^/]+)/(?:(?P<mode>draw)/)?' =>
|
||||||
'PhabricatorFactChartController',
|
'PhabricatorFactChartController',
|
||||||
'object/(?<phid>[^/]+)/' => 'PhabricatorFactObjectController',
|
'object/(?<phid>[^/]+)/' => 'PhabricatorFactObjectController',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorAccumulateChartFunction
|
final class PhabricatorAccumulateChartFunction
|
||||||
extends PhabricatorChartFunction {
|
extends PhabricatorHigherOrderChartFunction {
|
||||||
|
|
||||||
const FUNCTIONKEY = 'accumulate';
|
const FUNCTIONKEY = 'accumulate';
|
||||||
|
|
||||||
|
@ -13,14 +13,6 @@ final class PhabricatorAccumulateChartFunction
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDomain() {
|
|
||||||
return $this->getArgument('x')->getDomain();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function newInputValues(PhabricatorChartDataQuery $query) {
|
|
||||||
return $this->getArgument('x')->newInputValues($query);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function evaluateFunction(array $xv) {
|
public function evaluateFunction(array $xv) {
|
||||||
// First, we're going to accumulate the underlying function. Then
|
// First, we're going to accumulate the underlying function. Then
|
||||||
// we'll map the inputs through the accumulation.
|
// we'll map the inputs through the accumulation.
|
||||||
|
|
|
@ -59,13 +59,6 @@ abstract class PhabricatorChartDataset
|
||||||
return $dataset;
|
return $dataset;
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function toDictionary() {
|
|
||||||
return array(
|
|
||||||
'type' => $this->getDatasetTypeKey(),
|
|
||||||
'functions' => mpull($this->getFunctions(), 'toDictionary'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final public function getChartDisplayData(
|
final public function getChartDisplayData(
|
||||||
PhabricatorChartDataQuery $data_query) {
|
PhabricatorChartDataQuery $data_query) {
|
||||||
return $this->newChartDisplayData($data_query);
|
return $this->newChartDisplayData($data_query);
|
||||||
|
@ -75,4 +68,35 @@ abstract class PhabricatorChartDataset
|
||||||
PhabricatorChartDataQuery $data_query);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,10 @@ abstract class PhabricatorChartFunction
|
||||||
return $this->functionLabel;
|
return $this->functionLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function getKey() {
|
||||||
|
return $this->getFunctionLabel()->getKey();
|
||||||
|
}
|
||||||
|
|
||||||
final public static function newFromDictionary(array $map) {
|
final public static function newFromDictionary(array $map) {
|
||||||
PhutilTypeSpec::checkMap(
|
PhutilTypeSpec::checkMap(
|
||||||
$map,
|
$map,
|
||||||
|
@ -86,13 +90,6 @@ abstract class PhabricatorChartFunction
|
||||||
return $function;
|
return $function;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toDictionary() {
|
|
||||||
return array(
|
|
||||||
'function' => $this->getFunctionKey(),
|
|
||||||
'arguments' => $this->getArgumentParser()->getRawArguments(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSubfunctions() {
|
public function getSubfunctions() {
|
||||||
$result = array();
|
$result = array();
|
||||||
$result[] = $this;
|
$result[] = $this;
|
||||||
|
@ -180,6 +177,8 @@ abstract class PhabricatorChartFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract public function evaluateFunction(array $xv);
|
abstract public function evaluateFunction(array $xv);
|
||||||
|
abstract public function getDataRefs(array $xv);
|
||||||
|
abstract public function loadRefs(array $refs);
|
||||||
|
|
||||||
public function getDomain() {
|
public function getDomain() {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -3,11 +3,21 @@
|
||||||
final class PhabricatorChartFunctionLabel
|
final class PhabricatorChartFunctionLabel
|
||||||
extends Phobject {
|
extends Phobject {
|
||||||
|
|
||||||
|
private $key;
|
||||||
private $name;
|
private $name;
|
||||||
private $color;
|
private $color;
|
||||||
private $icon;
|
private $icon;
|
||||||
private $fillColor;
|
private $fillColor;
|
||||||
|
|
||||||
|
public function setKey($key) {
|
||||||
|
$this->key = $key;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey() {
|
||||||
|
return $this->key;
|
||||||
|
}
|
||||||
|
|
||||||
public function setName($name) {
|
public function setName($name) {
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -46,6 +56,7 @@ final class PhabricatorChartFunctionLabel
|
||||||
|
|
||||||
public function toWireFormat() {
|
public function toWireFormat() {
|
||||||
return array(
|
return array(
|
||||||
|
'key' => $this->getKey(),
|
||||||
'name' => $this->getName(),
|
'name' => $this->getName(),
|
||||||
'color' => $this->getColor(),
|
'color' => $this->getColor(),
|
||||||
'icon' => $this->getIcon(),
|
'icon' => $this->getIcon(),
|
||||||
|
|
|
@ -5,24 +5,187 @@ final class PhabricatorChartStackedAreaDataset
|
||||||
|
|
||||||
const DATASETKEY = 'stacked-area';
|
const DATASETKEY = 'stacked-area';
|
||||||
|
|
||||||
|
private $stacks;
|
||||||
|
|
||||||
|
public function setStacks(array $stacks) {
|
||||||
|
$this->stacks = $stacks;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStacks() {
|
||||||
|
return $this->stacks;
|
||||||
|
}
|
||||||
|
|
||||||
protected function newChartDisplayData(
|
protected function newChartDisplayData(
|
||||||
PhabricatorChartDataQuery $data_query) {
|
PhabricatorChartDataQuery $data_query) {
|
||||||
|
|
||||||
$functions = $this->getFunctions();
|
$functions = $this->getFunctions();
|
||||||
|
$functions = mpull($functions, null, 'getKey');
|
||||||
|
|
||||||
$reversed_functions = array_reverse($functions, true);
|
$stacks = $this->getStacks();
|
||||||
|
|
||||||
$function_points = array();
|
if (!$stacks) {
|
||||||
foreach ($reversed_functions as $function_idx => $function) {
|
$stacks = array(
|
||||||
$function_points[$function_idx] = array();
|
array_reverse(array_keys($functions), true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$datapoints = $function->newDatapoints($data_query);
|
$series = array();
|
||||||
foreach ($datapoints as $point) {
|
$raw_points = array();
|
||||||
$x = $point['x'];
|
|
||||||
$function_points[$function_idx][$x] = $point;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$series[$function_idx] = $bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
$raw_points += $stack_points;
|
||||||
|
}
|
||||||
|
|
||||||
|
$series = array_select_keys($series, array_keys($functions));
|
||||||
|
$series = array_values($series);
|
||||||
|
|
||||||
|
$raw_points = array_select_keys($raw_points, array_keys($functions));
|
||||||
|
$raw_points = array_values($raw_points);
|
||||||
|
|
||||||
|
$range_min = null;
|
||||||
|
$range_max = null;
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$raw_points = $function_points;
|
// 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
|
// 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
|
// any of the functions we're drawing are defined. If we don't, we'll
|
||||||
|
@ -31,17 +194,54 @@ final class PhabricatorChartStackedAreaDataset
|
||||||
// stacking the functions on top of one another.
|
// stacking the functions on top of one another.
|
||||||
|
|
||||||
$must_define = array();
|
$must_define = array();
|
||||||
foreach ($function_points as $function_idx => $points) {
|
|
||||||
foreach ($points as $x => $point) {
|
$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;
|
$must_define[$x] = $x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ksort($must_define);
|
ksort($must_define);
|
||||||
|
|
||||||
foreach ($reversed_functions as $function_idx => $function) {
|
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();
|
$missing = array();
|
||||||
foreach ($must_define as $x) {
|
foreach ($must_define as $x) {
|
||||||
if (!isset($function_points[$function_idx][$x])) {
|
if (!isset($points[$x])) {
|
||||||
$missing[$x] = true;
|
$missing[$x] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,8 +250,6 @@ final class PhabricatorChartStackedAreaDataset
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$points = $function_points[$function_idx];
|
|
||||||
|
|
||||||
$values = array_keys($points);
|
$values = array_keys($points);
|
||||||
$cursor = -1;
|
$cursor = -1;
|
||||||
$length = count($values);
|
$length = count($values);
|
||||||
|
@ -84,88 +282,19 @@ final class PhabricatorChartStackedAreaDataset
|
||||||
$y = $ymin + (($ymax - $ymin) * $distance);
|
$y = $ymin + (($ymax - $ymin) * $distance);
|
||||||
} else {
|
} else {
|
||||||
$xmin = $values[$cursor];
|
$xmin = $values[$cursor];
|
||||||
$y = $function_points[$function_idx][$xmin]['y'];
|
$y = $points[$xmin]['y'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$function_points[$function_idx][$x] = array(
|
$point_lists[$idx][$x] = array(
|
||||||
'x' => $x,
|
'x' => $x,
|
||||||
'y' => $y,
|
'y' => $y,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ksort($function_points[$function_idx]);
|
ksort($point_lists[$idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$range_min = null;
|
return $point_lists;
|
||||||
$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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
$events = array();
|
|
||||||
foreach ($raw_points as $function_idx => $points) {
|
|
||||||
$event_list = array();
|
|
||||||
foreach ($points as $point) {
|
|
||||||
$event_list[] = $point;
|
|
||||||
}
|
|
||||||
$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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,4 +70,22 @@ final class PhabricatorComposeChartFunction
|
||||||
return $yv;
|
return $yv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDataRefs(array $xv) {
|
||||||
|
// TODO: This is not entirely correct. The correct implementation would
|
||||||
|
// map "x -> y" at each stage of composition and pull and aggregate all
|
||||||
|
// the datapoint refs. In practice, we currently never compose functions
|
||||||
|
// with a data function somewhere in the middle, so just grabbing the first
|
||||||
|
// result is close enough.
|
||||||
|
|
||||||
|
// In the future, we may: notably, "x -> shift(-1 month) -> ..." to
|
||||||
|
// generate a month-over-month overlay is a sensible operation which will
|
||||||
|
// source data from the middle of a function composition.
|
||||||
|
|
||||||
|
foreach ($this->getFunctionArguments() as $function) {
|
||||||
|
return $function->getDataRefs($xv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorConstantChartFunction
|
final class PhabricatorConstantChartFunction
|
||||||
extends PhabricatorChartFunction {
|
extends PhabricatorPureChartFunction {
|
||||||
|
|
||||||
const FUNCTIONKEY = 'constant';
|
const FUNCTIONKEY = 'constant';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorCosChartFunction
|
final class PhabricatorCosChartFunction
|
||||||
extends PhabricatorChartFunction {
|
extends PhabricatorPureChartFunction {
|
||||||
|
|
||||||
const FUNCTIONKEY = 'cos';
|
const FUNCTIONKEY = 'cos';
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ final class PhabricatorFactChartFunction
|
||||||
|
|
||||||
private $fact;
|
private $fact;
|
||||||
private $map;
|
private $map;
|
||||||
|
private $refs;
|
||||||
|
|
||||||
protected function newArguments() {
|
protected function newArguments() {
|
||||||
$key_argument = $this->newArgument()
|
$key_argument = $this->newArgument()
|
||||||
|
@ -51,13 +52,15 @@ final class PhabricatorFactChartFunction
|
||||||
|
|
||||||
$data = queryfx_all(
|
$data = queryfx_all(
|
||||||
$conn,
|
$conn,
|
||||||
'SELECT value, epoch FROM %T WHERE %LA ORDER BY epoch ASC',
|
'SELECT id, value, epoch FROM %T WHERE %LA ORDER BY epoch ASC',
|
||||||
$table_name,
|
$table_name,
|
||||||
$where);
|
$where);
|
||||||
|
|
||||||
$map = array();
|
$map = array();
|
||||||
|
$refs = array();
|
||||||
if ($data) {
|
if ($data) {
|
||||||
foreach ($data as $row) {
|
foreach ($data as $row) {
|
||||||
|
$ref = (string)$row['id'];
|
||||||
$value = (int)$row['value'];
|
$value = (int)$row['value'];
|
||||||
$epoch = (int)$row['epoch'];
|
$epoch = (int)$row['epoch'];
|
||||||
|
|
||||||
|
@ -66,10 +69,17 @@ final class PhabricatorFactChartFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
$map[$epoch] += $value;
|
$map[$epoch] += $value;
|
||||||
|
|
||||||
|
if (!isset($refs[$epoch])) {
|
||||||
|
$refs[$epoch] = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$refs[$epoch][] = $ref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->map = $map;
|
$this->map = $map;
|
||||||
|
$this->refs = $refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDomain() {
|
public function getDomain() {
|
||||||
|
@ -99,4 +109,60 @@ final class PhabricatorFactChartFunction
|
||||||
return $yv;
|
return $yv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDataRefs(array $xv) {
|
||||||
|
return array_select_keys($this->refs, $xv);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadRefs(array $refs) {
|
||||||
|
$fact = $this->fact;
|
||||||
|
|
||||||
|
$datapoint_table = $fact->newDatapoint();
|
||||||
|
$conn = $datapoint_table->establishConnection('r');
|
||||||
|
|
||||||
|
$dimension_table = new PhabricatorFactObjectDimension();
|
||||||
|
|
||||||
|
$where = array();
|
||||||
|
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'p.id IN (%Ld)',
|
||||||
|
$refs);
|
||||||
|
|
||||||
|
|
||||||
|
$rows = queryfx_all(
|
||||||
|
$conn,
|
||||||
|
'SELECT
|
||||||
|
p.id id,
|
||||||
|
p.value,
|
||||||
|
od.objectPHID objectPHID,
|
||||||
|
dd.objectPHID dimensionPHID
|
||||||
|
FROM %R p
|
||||||
|
LEFT JOIN %R od ON od.id = p.objectID
|
||||||
|
LEFT JOIN %R dd ON dd.id = p.dimensionID
|
||||||
|
WHERE %LA',
|
||||||
|
$datapoint_table,
|
||||||
|
$dimension_table,
|
||||||
|
$dimension_table,
|
||||||
|
$where);
|
||||||
|
$rows = ipull($rows, null, 'id');
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
|
||||||
|
foreach ($refs as $ref) {
|
||||||
|
if (!isset($rows[$ref])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = $rows[$ref];
|
||||||
|
|
||||||
|
$results[$ref] = array(
|
||||||
|
'objectPHID' => $row['objectPHID'],
|
||||||
|
'dimensionPHID' => $row['dimensionPHID'],
|
||||||
|
'value' => (float)$row['value'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,4 +32,38 @@ abstract class PhabricatorHigherOrderChartFunction
|
||||||
return array_keys($map);
|
return array_keys($map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDataRefs(array $xv) {
|
||||||
|
$refs = array();
|
||||||
|
|
||||||
|
foreach ($this->getFunctionArguments() as $function) {
|
||||||
|
$function_refs = $function->getDataRefs($xv);
|
||||||
|
|
||||||
|
$function_refs = array_select_keys($function_refs, $xv);
|
||||||
|
if (!$function_refs) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($function_refs as $x => $ref_list) {
|
||||||
|
if (!isset($refs[$x])) {
|
||||||
|
$refs[$x] = array();
|
||||||
|
}
|
||||||
|
foreach ($ref_list as $ref) {
|
||||||
|
$refs[$x][] = $ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $refs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadRefs(array $refs) {
|
||||||
|
$results = array();
|
||||||
|
|
||||||
|
foreach ($this->getFunctionArguments() as $function) {
|
||||||
|
$results += $function->loadRefs($refs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,27 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorMaxChartFunction
|
final class PhabricatorMaxChartFunction
|
||||||
extends PhabricatorChartFunction {
|
extends PhabricatorPureChartFunction {
|
||||||
|
|
||||||
const FUNCTIONKEY = 'max';
|
const FUNCTIONKEY = 'max';
|
||||||
|
|
||||||
protected function newArguments() {
|
protected function newArguments() {
|
||||||
return array(
|
return array(
|
||||||
$this->newArgument()
|
|
||||||
->setName('x')
|
|
||||||
->setType('function'),
|
|
||||||
$this->newArgument()
|
$this->newArgument()
|
||||||
->setName('max')
|
->setName('max')
|
||||||
->setType('number'),
|
->setType('number'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDomain() {
|
|
||||||
return $this->getArgument('x')->getDomain();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function newInputValues(PhabricatorChartDataQuery $query) {
|
|
||||||
return $this->getArgument('x')->newInputValues($query);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function evaluateFunction(array $xv) {
|
public function evaluateFunction(array $xv) {
|
||||||
$yv = $this->getArgument('x')->evaluateFunction($xv);
|
|
||||||
$max = $this->getArgument('max');
|
$max = $this->getArgument('max');
|
||||||
|
|
||||||
foreach ($yv as $k => $y) {
|
$yv = array();
|
||||||
if ($y > $max) {
|
foreach ($xv as $x) {
|
||||||
$yv[$k] = null;
|
if ($x > $max) {
|
||||||
|
$yv[] = null;
|
||||||
|
} else {
|
||||||
|
$yv[] = $x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,36 +1,27 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorMinChartFunction
|
final class PhabricatorMinChartFunction
|
||||||
extends PhabricatorChartFunction {
|
extends PhabricatorPureChartFunction {
|
||||||
|
|
||||||
const FUNCTIONKEY = 'min';
|
const FUNCTIONKEY = 'min';
|
||||||
|
|
||||||
protected function newArguments() {
|
protected function newArguments() {
|
||||||
return array(
|
return array(
|
||||||
$this->newArgument()
|
|
||||||
->setName('x')
|
|
||||||
->setType('function'),
|
|
||||||
$this->newArgument()
|
$this->newArgument()
|
||||||
->setName('min')
|
->setName('min')
|
||||||
->setType('number'),
|
->setType('number'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDomain() {
|
|
||||||
return $this->getArgument('x')->getDomain();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function newInputValues(PhabricatorChartDataQuery $query) {
|
|
||||||
return $this->getArgument('x')->newInputValues($query);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function evaluateFunction(array $xv) {
|
public function evaluateFunction(array $xv) {
|
||||||
$yv = $this->getArgument('x')->evaluateFunction($xv);
|
|
||||||
$min = $this->getArgument('min');
|
$min = $this->getArgument('min');
|
||||||
|
|
||||||
foreach ($yv as $k => $y) {
|
$yv = array();
|
||||||
if ($y < $min) {
|
foreach ($xv as $x) {
|
||||||
$yv[$k] = null;
|
if ($x < $min) {
|
||||||
|
$yv[] = null;
|
||||||
|
} else {
|
||||||
|
$yv[] = $x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
src/applications/fact/chart/PhabricatorPureChartFunction.php
Normal file
14
src/applications/fact/chart/PhabricatorPureChartFunction.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorPureChartFunction
|
||||||
|
extends PhabricatorChartFunction {
|
||||||
|
|
||||||
|
public function getDataRefs(array $xv) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadRefs(array $refs) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorScaleChartFunction
|
final class PhabricatorScaleChartFunction
|
||||||
extends PhabricatorChartFunction {
|
extends PhabricatorPureChartFunction {
|
||||||
|
|
||||||
const FUNCTIONKEY = 'scale';
|
const FUNCTIONKEY = 'scale';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorShiftChartFunction
|
final class PhabricatorShiftChartFunction
|
||||||
extends PhabricatorChartFunction {
|
extends PhabricatorPureChartFunction {
|
||||||
|
|
||||||
const FUNCTIONKEY = 'shift';
|
const FUNCTIONKEY = 'shift';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorSinChartFunction
|
final class PhabricatorSinChartFunction
|
||||||
extends PhabricatorChartFunction {
|
extends PhabricatorPureChartFunction {
|
||||||
|
|
||||||
const FUNCTIONKEY = 'sin';
|
const FUNCTIONKEY = 'sin';
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
<?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();
|
||||||
|
|
||||||
$chart_key = $request->getURIData('chartKey');
|
$chart_key = $request->getURIData('chartKey');
|
||||||
if ($chart_key === null) {
|
if (!$chart_key) {
|
||||||
return $this->newDemoChart();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$engine = id(new PhabricatorChartRenderingEngine())
|
$engine = id(new PhabricatorChartRenderingEngine())
|
||||||
|
@ -24,15 +25,24 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
$mode = $request->getURIData('mode');
|
$mode = $request->getURIData('mode');
|
||||||
$is_draw_mode = ($mode === 'draw');
|
$is_draw_mode = ($mode === 'draw');
|
||||||
|
|
||||||
// TODO: For now, always pull the data. We'll throw it away if we're just
|
$want_data = $is_draw_mode;
|
||||||
// drawing the frame, but this makes errors easier to debug.
|
|
||||||
$chart_data = $engine->newChartData();
|
|
||||||
|
|
||||||
if ($is_draw_mode) {
|
// In developer mode, always pull the data in the main request. We'll
|
||||||
return id(new AphrontAjaxResponse())->setContent($chart_data);
|
// throw it away if we're just drawing the chart frame, but this currently
|
||||||
|
// makes errors quite a bit easier to debug.
|
||||||
|
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
|
||||||
|
$want_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($want_data) {
|
||||||
|
$chart_data = $engine->newChartData();
|
||||||
|
if ($is_draw_mode) {
|
||||||
|
return id(new AphrontAjaxResponse())->setContent($chart_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$chart_view = $engine->newChartView();
|
$chart_view = $engine->newChartView();
|
||||||
|
|
||||||
return $this->newChartResponse($chart_view);
|
return $this->newChartResponse($chart_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,58 +60,10 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setTitle($title)
|
->setTitle($title)
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->appendChild($box);
|
->appendChild(
|
||||||
}
|
|
||||||
|
|
||||||
private function newDemoChart() {
|
|
||||||
$viewer = $this->getViewer();
|
|
||||||
|
|
||||||
$argvs = array();
|
|
||||||
|
|
||||||
$argvs[] = array('fact', 'tasks.count.create');
|
|
||||||
|
|
||||||
$argvs[] = array('constant', 360);
|
|
||||||
|
|
||||||
$argvs[] = array('fact', 'tasks.open-count.create');
|
|
||||||
|
|
||||||
$argvs[] = array(
|
|
||||||
'sum',
|
|
||||||
array(
|
|
||||||
'accumulate',
|
|
||||||
array('fact', 'tasks.count.create'),
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'accumulate',
|
|
||||||
array('fact', 'tasks.open-count.create'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
$argvs[] = array(
|
|
||||||
'compose',
|
|
||||||
array('scale', 0.001),
|
|
||||||
array('cos'),
|
|
||||||
array('scale', 100),
|
|
||||||
array('shift', 800),
|
|
||||||
);
|
|
||||||
|
|
||||||
$datasets = array();
|
|
||||||
foreach ($argvs as $argv) {
|
|
||||||
$datasets[] = PhabricatorChartDataset::newFromDictionary(
|
|
||||||
array(
|
array(
|
||||||
'function' => $argv,
|
$box,
|
||||||
));
|
));
|
||||||
}
|
|
||||||
|
|
||||||
$chart = id(new PhabricatorFactChart())
|
|
||||||
->setDatasets($datasets);
|
|
||||||
|
|
||||||
$engine = id(new PhabricatorChartRenderingEngine())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->setChart($chart);
|
|
||||||
|
|
||||||
$chart = $engine->getStoredChart();
|
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())->setURI($chart->getURI());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +1,20 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorFactHomeController extends PhabricatorFactController {
|
final class PhabricatorFactHomeController
|
||||||
|
extends PhabricatorFactController {
|
||||||
|
|
||||||
public function shouldAllowPublic() {
|
public function shouldAllowPublic() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$viewer = $request->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
$chart = id(new PhabricatorDemoChartEngine())
|
||||||
$uri = new PhutilURI('/fact/chart/');
|
->setViewer($viewer)
|
||||||
$uri->replaceQueryParam('y1', $request->getStr('y1'));
|
->newStoredChart();
|
||||||
return id(new AphrontRedirectResponse())->setURI($uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
$chart_form = $this->buildChartForm();
|
return id(new AphrontRedirectResponse())->setURI($chart->getURI());
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
|
||||||
$crumbs->addTextCrumb(pht('Home'));
|
|
||||||
|
|
||||||
$title = pht('Facts');
|
|
||||||
|
|
||||||
return $this->newPage()
|
|
||||||
->setTitle($title)
|
|
||||||
->setCrumbs($crumbs)
|
|
||||||
->appendChild(
|
|
||||||
array(
|
|
||||||
$chart_form,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function buildChartForm() {
|
|
||||||
$request = $this->getRequest();
|
|
||||||
$viewer = $request->getUser();
|
|
||||||
|
|
||||||
$specs = PhabricatorFact::getAllFacts();
|
|
||||||
$options = mpull($specs, 'getName', 'getKey');
|
|
||||||
|
|
||||||
$form = id(new AphrontFormView())
|
|
||||||
->setUser($viewer)
|
|
||||||
->appendChild(
|
|
||||||
id(new AphrontFormSelectControl())
|
|
||||||
->setLabel(pht('Y-Axis'))
|
|
||||||
->setName('y1')
|
|
||||||
->setOptions($options))
|
|
||||||
->appendChild(
|
|
||||||
id(new AphrontFormSubmitControl())
|
|
||||||
->setValue(pht('Plot Chart')));
|
|
||||||
|
|
||||||
$panel = new PHUIObjectBoxView();
|
|
||||||
$panel->setForm($form);
|
|
||||||
$panel->setHeaderText(pht('Plot Chart'));
|
|
||||||
|
|
||||||
return $panel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ abstract class PhabricatorChartEngine
|
||||||
|
|
||||||
abstract protected function newChart(PhabricatorFactChart $chart, array $map);
|
abstract protected function newChart(PhabricatorFactChart $chart, array $map);
|
||||||
|
|
||||||
final public function buildChartPanel() {
|
final public function newStoredChart() {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$parameters = $this->getEngineParameters();
|
$parameters = $this->getEngineParameters();
|
||||||
|
@ -76,7 +76,11 @@ abstract class PhabricatorChartEngine
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->setChart($chart);
|
->setChart($chart);
|
||||||
|
|
||||||
$chart = $rendering_engine->getStoredChart();
|
return $rendering_engine->getStoredChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function buildChartPanel() {
|
||||||
|
$chart = $this->newStoredChart();
|
||||||
|
|
||||||
$panel_type = id(new PhabricatorDashboardChartPanelType())
|
$panel_type = id(new PhabricatorDashboardChartPanelType())
|
||||||
->getPanelTypeKey();
|
->getPanelTypeKey();
|
||||||
|
@ -91,7 +95,7 @@ abstract class PhabricatorChartEngine
|
||||||
final protected function newFunction($name /* , ... */) {
|
final protected function newFunction($name /* , ... */) {
|
||||||
$argv = func_get_args();
|
$argv = func_get_args();
|
||||||
return id(new PhabricatorComposeChartFunction())
|
return id(new PhabricatorComposeChartFunction())
|
||||||
->setArguments(array($argv));
|
->setArguments($argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,146 @@ final class PhabricatorChartRenderingEngine
|
||||||
return $chart_view;
|
return $chart_view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function newTabularView() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$tabular_data = $this->newTabularData();
|
||||||
|
|
||||||
|
$ref_keys = array();
|
||||||
|
foreach ($tabular_data['datasets'] as $tabular_dataset) {
|
||||||
|
foreach ($tabular_dataset as $function) {
|
||||||
|
foreach ($function['data'] as $point) {
|
||||||
|
foreach ($point['refs'] as $ref) {
|
||||||
|
$ref_keys[$ref] = $ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$chart = $this->getStoredChart();
|
||||||
|
|
||||||
|
$ref_map = array();
|
||||||
|
foreach ($chart->getDatasets() as $dataset) {
|
||||||
|
foreach ($dataset->getFunctions() as $function) {
|
||||||
|
// If we aren't looking for anything else, bail out.
|
||||||
|
if (!$ref_keys) {
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
$function_refs = $function->loadRefs($ref_keys);
|
||||||
|
|
||||||
|
$ref_map += $function_refs;
|
||||||
|
|
||||||
|
// Remove the ref keys that we found data for from the list of keys
|
||||||
|
// we are looking for. If any function gives us data for a given ref,
|
||||||
|
// that's satisfactory.
|
||||||
|
foreach ($function_refs as $ref_key => $ref_data) {
|
||||||
|
unset($ref_keys[$ref_key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$phids = array();
|
||||||
|
foreach ($ref_map as $ref => $ref_data) {
|
||||||
|
if (isset($ref_data['objectPHID'])) {
|
||||||
|
$phids[] = $ref_data['objectPHID'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$handles = $viewer->loadHandles($phids);
|
||||||
|
|
||||||
|
$tabular_view = array();
|
||||||
|
foreach ($tabular_data['datasets'] as $tabular_data) {
|
||||||
|
foreach ($tabular_data as $function) {
|
||||||
|
$rows = array();
|
||||||
|
foreach ($function['data'] as $point) {
|
||||||
|
$ref_views = array();
|
||||||
|
|
||||||
|
$xv = date('Y-m-d h:i:s', $point['x']);
|
||||||
|
$yv = $point['y'];
|
||||||
|
|
||||||
|
$point_refs = array();
|
||||||
|
foreach ($point['refs'] as $ref) {
|
||||||
|
if (!isset($ref_map[$ref])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$point_refs[$ref] = $ref_map[$ref];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$point_refs) {
|
||||||
|
$rows[] = array(
|
||||||
|
$xv,
|
||||||
|
$yv,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
foreach ($point_refs as $ref => $ref_data) {
|
||||||
|
$ref_value = $ref_data['value'];
|
||||||
|
$ref_link = $handles[$ref_data['objectPHID']]
|
||||||
|
->renderLink();
|
||||||
|
|
||||||
|
$view_uri = urisprintf(
|
||||||
|
'/fact/object/%s/',
|
||||||
|
$ref_data['objectPHID']);
|
||||||
|
|
||||||
|
$ref_button = id(new PHUIButtonView())
|
||||||
|
->setIcon('fa-table')
|
||||||
|
->setTag('a')
|
||||||
|
->setColor('grey')
|
||||||
|
->setHref($view_uri)
|
||||||
|
->setText(pht('View Data'));
|
||||||
|
|
||||||
|
$rows[] = array(
|
||||||
|
$xv,
|
||||||
|
$yv,
|
||||||
|
$ref_value,
|
||||||
|
$ref_link,
|
||||||
|
$ref_button,
|
||||||
|
);
|
||||||
|
|
||||||
|
$xv = null;
|
||||||
|
$yv = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = id(new AphrontTableView($rows))
|
||||||
|
->setHeaders(
|
||||||
|
array(
|
||||||
|
pht('X'),
|
||||||
|
pht('Y'),
|
||||||
|
pht('Raw'),
|
||||||
|
pht('Refs'),
|
||||||
|
null,
|
||||||
|
))
|
||||||
|
->setColumnClasses(
|
||||||
|
array(
|
||||||
|
'n',
|
||||||
|
'n',
|
||||||
|
'n',
|
||||||
|
'wide',
|
||||||
|
null,
|
||||||
|
));
|
||||||
|
|
||||||
|
$tabular_view[] = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Function'))
|
||||||
|
->setTable($table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tabular_view;
|
||||||
|
}
|
||||||
|
|
||||||
public function newChartData() {
|
public function newChartData() {
|
||||||
|
return $this->newWireData(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newTabularData() {
|
||||||
|
return $this->newWireData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newWireData($is_tabular) {
|
||||||
$chart = $this->getStoredChart();
|
$chart = $this->getStoredChart();
|
||||||
$chart_key = $chart->getChartKey();
|
$chart_key = $chart->getChartKey();
|
||||||
|
|
||||||
|
@ -151,7 +290,11 @@ final class PhabricatorChartRenderingEngine
|
||||||
$wire_datasets = array();
|
$wire_datasets = array();
|
||||||
$ranges = array();
|
$ranges = array();
|
||||||
foreach ($datasets as $dataset) {
|
foreach ($datasets as $dataset) {
|
||||||
$display_data = $dataset->getChartDisplayData($data_query);
|
if ($is_tabular) {
|
||||||
|
$display_data = $dataset->getTabularDisplayData($data_query);
|
||||||
|
} else {
|
||||||
|
$display_data = $dataset->getChartDisplayData($data_query);
|
||||||
|
}
|
||||||
|
|
||||||
$ranges[] = $display_data->getRange();
|
$ranges[] = $display_data->getRange();
|
||||||
$wire_datasets[] = $display_data->getWireData();
|
$wire_datasets[] = $display_data->getWireData();
|
||||||
|
|
46
src/applications/fact/engine/PhabricatorDemoChartEngine.php
Normal file
46
src/applications/fact/engine/PhabricatorDemoChartEngine.php
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorDemoChartEngine
|
||||||
|
extends PhabricatorChartEngine {
|
||||||
|
|
||||||
|
const CHARTENGINEKEY = 'facts.demo';
|
||||||
|
|
||||||
|
protected function newChart(PhabricatorFactChart $chart, array $map) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$functions = array();
|
||||||
|
|
||||||
|
$function = $this->newFunction(
|
||||||
|
array('scale', 0.0001),
|
||||||
|
array('cos'),
|
||||||
|
array('scale', 128),
|
||||||
|
array('shift', 256));
|
||||||
|
|
||||||
|
$function->getFunctionLabel()
|
||||||
|
->setKey('cos-x')
|
||||||
|
->setName(pht('cos(x)'))
|
||||||
|
->setColor('rgba(0, 200, 0, 1)')
|
||||||
|
->setFillColor('rgba(0, 200, 0, 0.15)');
|
||||||
|
|
||||||
|
$functions[] = $function;
|
||||||
|
|
||||||
|
$function = $this->newFunction(
|
||||||
|
array('constant', 345));
|
||||||
|
|
||||||
|
$function->getFunctionLabel()
|
||||||
|
->setKey('constant-345')
|
||||||
|
->setName(pht('constant(345)'))
|
||||||
|
->setColor('rgba(0, 0, 200, 1)')
|
||||||
|
->setFillColor('rgba(0, 0, 200, 0.15)');
|
||||||
|
|
||||||
|
$functions[] = $function;
|
||||||
|
|
||||||
|
$datasets = array();
|
||||||
|
|
||||||
|
$datasets[] = id(new PhabricatorChartStackedAreaDataset())
|
||||||
|
->setFunctions($functions);
|
||||||
|
|
||||||
|
$chart->attachDatasets($datasets);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -35,8 +35,12 @@ final class ManiphestReportController extends ManiphestController {
|
||||||
$nav->addLabel(pht('Open Tasks'));
|
$nav->addLabel(pht('Open Tasks'));
|
||||||
$nav->addFilter('user', pht('By User'));
|
$nav->addFilter('user', pht('By User'));
|
||||||
$nav->addFilter('project', pht('By Project'));
|
$nav->addFilter('project', pht('By Project'));
|
||||||
$nav->addLabel(pht('Burnup'));
|
|
||||||
$nav->addFilter('burn', pht('Burnup Rate'));
|
$class = 'PhabricatorFactApplication';
|
||||||
|
if (PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) {
|
||||||
|
$nav->addLabel(pht('Burnup'));
|
||||||
|
$nav->addFilter('burn', pht('Burnup Rate'));
|
||||||
|
}
|
||||||
|
|
||||||
$this->view = $nav->selectFilter($this->view, 'user');
|
$this->view = $nav->selectFilter($this->view, 'user');
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorProjectActivityChartEngine
|
||||||
|
extends PhabricatorChartEngine {
|
||||||
|
|
||||||
|
const CHARTENGINEKEY = 'project.activity';
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
$project_phid = head($project_phids);
|
||||||
|
|
||||||
|
$functions = array();
|
||||||
|
$stacks = array();
|
||||||
|
|
||||||
|
$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.status.project', $project_phid),
|
||||||
|
array('max', 0),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
$function->getFunctionLabel()
|
||||||
|
->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(
|
||||||
|
'compose',
|
||||||
|
array('fact', 'tasks.open-count.assign.project', $project_phid),
|
||||||
|
array('max', 0),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
$function->getFunctionLabel()
|
||||||
|
->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');
|
||||||
|
|
||||||
|
$datasets = array();
|
||||||
|
|
||||||
|
$dataset = id(new PhabricatorChartStackedAreaDataset())
|
||||||
|
->setFunctions($functions)
|
||||||
|
->setStacks($stacks);
|
||||||
|
|
||||||
|
$datasets[] = $dataset;
|
||||||
|
$chart->attachDatasets($datasets);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -30,97 +30,78 @@ final class PhabricatorProjectBurndownChartEngine
|
||||||
|
|
||||||
$functions = array();
|
$functions = array();
|
||||||
if ($project_phids) {
|
if ($project_phids) {
|
||||||
foreach ($project_phids as $project_phid) {
|
$open_function = $this->newFunction(
|
||||||
$function = $this->newFunction(
|
array(
|
||||||
'min',
|
'accumulate',
|
||||||
array(
|
array(
|
||||||
'accumulate',
|
'sum',
|
||||||
array('fact', 'tasks.open-count.assign.project', $project_phid),
|
$this->newFactSum(
|
||||||
|
'tasks.open-count.create.project', $project_phids),
|
||||||
|
$this->newFactSum(
|
||||||
|
'tasks.open-count.status.project', $project_phids),
|
||||||
|
$this->newFactSum(
|
||||||
|
'tasks.open-count.assign.project', $project_phids),
|
||||||
),
|
),
|
||||||
0);
|
));
|
||||||
|
|
||||||
$function->getFunctionLabel()
|
$closed_function = $this->newFunction(
|
||||||
->setName(pht('Tasks Moved Into Project'))
|
array(
|
||||||
->setColor('rgba(0, 200, 200, 1)')
|
'accumulate',
|
||||||
->setFillColor('rgba(0, 200, 200, 0.15)');
|
$this->newFactSum('tasks.open-count.status.project', $project_phids),
|
||||||
|
));
|
||||||
$functions[] = $function;
|
|
||||||
|
|
||||||
$function = $this->newFunction(
|
|
||||||
'min',
|
|
||||||
array(
|
|
||||||
'accumulate',
|
|
||||||
array('fact', 'tasks.open-count.status.project', $project_phid),
|
|
||||||
),
|
|
||||||
0);
|
|
||||||
|
|
||||||
$function->getFunctionLabel()
|
|
||||||
->setName(pht('Tasks Reopened'))
|
|
||||||
->setColor('rgba(200, 0, 200, 1)')
|
|
||||||
->setFillColor('rgba(200, 0, 200, 0.15)');
|
|
||||||
|
|
||||||
$functions[] = $function;
|
|
||||||
|
|
||||||
$function = $this->newFunction(
|
|
||||||
'sum',
|
|
||||||
array(
|
|
||||||
'accumulate',
|
|
||||||
array('fact', 'tasks.open-count.create.project', $project_phid),
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'max',
|
|
||||||
array(
|
|
||||||
'accumulate',
|
|
||||||
array('fact', 'tasks.open-count.status.project', $project_phid),
|
|
||||||
),
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'max',
|
|
||||||
array(
|
|
||||||
'accumulate',
|
|
||||||
array('fact', 'tasks.open-count.assign.project', $project_phid),
|
|
||||||
),
|
|
||||||
0,
|
|
||||||
));
|
|
||||||
|
|
||||||
$function->getFunctionLabel()
|
|
||||||
->setName(pht('Tasks Created'))
|
|
||||||
->setColor('rgba(0, 0, 200, 1)')
|
|
||||||
->setFillColor('rgba(0, 0, 200, 0.15)');
|
|
||||||
|
|
||||||
$functions[] = $function;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
$function = $this->newFunction(
|
$open_function = $this->newFunction(
|
||||||
'accumulate',
|
array(
|
||||||
array('fact', 'tasks.open-count.create'));
|
'accumulate',
|
||||||
|
array('fact', 'tasks.open-count.create'),
|
||||||
|
));
|
||||||
|
|
||||||
$function->getFunctionLabel()
|
$closed_function = $this->newFunction(
|
||||||
->setName(pht('Tasks Created'))
|
array(
|
||||||
->setColor('rgba(0, 200, 200, 1)')
|
'accumulate',
|
||||||
->setFillColor('rgba(0, 200, 200, 0.15)');
|
array('fact', 'tasks.open-count.status'),
|
||||||
|
));
|
||||||
$functions[] = $function;
|
|
||||||
|
|
||||||
$function = $this->newFunction(
|
|
||||||
'accumulate',
|
|
||||||
array('fact', 'tasks.open-count.status'));
|
|
||||||
|
|
||||||
$function->getFunctionLabel()
|
|
||||||
->setName(pht('Tasks Closed / Reopened'))
|
|
||||||
->setColor('rgba(200, 0, 200, 1)')
|
|
||||||
->setFillColor('rgba(200, 0, 200, 0.15)');
|
|
||||||
|
|
||||||
$functions[] = $function;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$open_function->getFunctionLabel()
|
||||||
|
->setKey('open')
|
||||||
|
->setName(pht('Open Tasks'))
|
||||||
|
->setColor('rgba(0, 0, 200, 1)')
|
||||||
|
->setFillColor('rgba(0, 0, 200, 0.15)');
|
||||||
|
|
||||||
|
$closed_function->getFunctionLabel()
|
||||||
|
->setKey('closed')
|
||||||
|
->setName(pht('Closed Tasks'))
|
||||||
|
->setColor('rgba(0, 200, 0, 1)')
|
||||||
|
->setFillColor('rgba(0, 200, 0, 0.15)');
|
||||||
|
|
||||||
$datasets = array();
|
$datasets = array();
|
||||||
|
|
||||||
$datasets[] = id(new PhabricatorChartStackedAreaDataset())
|
$dataset = id(new PhabricatorChartStackedAreaDataset())
|
||||||
->setFunctions($functions);
|
->setFunctions(
|
||||||
|
array(
|
||||||
|
$open_function,
|
||||||
|
$closed_function,
|
||||||
|
))
|
||||||
|
->setStacks(
|
||||||
|
array(
|
||||||
|
array('open'),
|
||||||
|
array('closed'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$datasets[] = $dataset;
|
||||||
$chart->attachDatasets($datasets);
|
$chart->attachDatasets($datasets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function newFactSum($fact_key, array $phids) {
|
||||||
|
$result = array();
|
||||||
|
$result[] = 'sum';
|
||||||
|
|
||||||
|
foreach ($phids as $phid) {
|
||||||
|
$result[] = array('fact', $fact_key, $phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,10 +44,24 @@ final class PhabricatorProjectReportsController
|
||||||
->setParentPanelPHIDs(array())
|
->setParentPanelPHIDs(array())
|
||||||
->renderPanel();
|
->renderPanel();
|
||||||
|
|
||||||
|
$activity_panel = id(new PhabricatorProjectActivityChartEngine())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setProjects(array($project))
|
||||||
|
->buildChartPanel();
|
||||||
|
|
||||||
|
$activity_panel->setName(pht('%s: Activity', $project->getName()));
|
||||||
|
|
||||||
|
$activity_view = id(new PhabricatorDashboardPanelRenderingEngine())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setPanel($activity_panel)
|
||||||
|
->setParentPanelPHIDs(array())
|
||||||
|
->renderPanel();
|
||||||
|
|
||||||
$view = id(new PHUITwoColumnView())
|
$view = id(new PHUITwoColumnView())
|
||||||
->setFooter(
|
->setFooter(
|
||||||
array(
|
array(
|
||||||
$chart_view,
|
$chart_view,
|
||||||
|
$activity_view,
|
||||||
));
|
));
|
||||||
|
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
|
|
|
@ -34,6 +34,11 @@ final class PhabricatorProjectReportsProfileMenuItem
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$class = 'PhabricatorFactApplication';
|
||||||
|
if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||||
|
|
||||||
$real_address = your_custom_parsing_function($raw_header);
|
$real_address = your_custom_parsing_function($raw_header);
|
||||||
|
|
||||||
$_SERVER['REMOTE_ADDR'] = $raw_header;
|
$_SERVER['REMOTE_ADDR'] = $real_address;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -36,16 +36,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart .point {
|
.chart .point {
|
||||||
fill: {$lightblue};
|
fill: #ffffff;
|
||||||
stroke: {$blue};
|
stroke: {$blue};
|
||||||
stroke-width: 1px;
|
stroke-width: 2px;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-tooltip {
|
.chart-tooltip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 120px;
|
width: 120px;
|
||||||
height: 16px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
background: {$lightbluebackground};
|
background: {$lightbluebackground};
|
||||||
|
|
37
webroot/rsrc/js/application/fact/Chart.js
vendored
37
webroot/rsrc/js/application/fact/Chart.js
vendored
|
@ -133,18 +133,33 @@ JX.install('Chart', {
|
||||||
},
|
},
|
||||||
|
|
||||||
_newStackedArea: function(g, dataset, x, y, div, curtain) {
|
_newStackedArea: function(g, dataset, x, y, div, curtain) {
|
||||||
|
var ii;
|
||||||
|
|
||||||
var to_date = JX.bind(this, this._newDate);
|
var to_date = JX.bind(this, this._newDate);
|
||||||
|
|
||||||
var area = d3.area()
|
var area = d3.area()
|
||||||
.x(function(d) { return x(to_date(d.x)); })
|
.x(function(d) { return x(to_date(d.x)); })
|
||||||
.y0(function(d) { return y(d.y0); })
|
.y0(function(d) {
|
||||||
|
// When the area is positive, draw it above the X axis. When the area
|
||||||
|
// is negative, draw it below the X axis. We currently avoid having
|
||||||
|
// functions which cross the X axis by clever construction.
|
||||||
|
if (d.y0 >= 0 && d.y1 >= 0) {
|
||||||
|
return y(d.y0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.y0 <= 0 && d.y1 <= 0) {
|
||||||
|
return y(d.y0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return y(0);
|
||||||
|
})
|
||||||
.y1(function(d) { return y(d.y1); });
|
.y1(function(d) { return y(d.y1); });
|
||||||
|
|
||||||
var line = d3.line()
|
var line = d3.line()
|
||||||
.x(function(d) { return x(to_date(d.x)); })
|
.x(function(d) { return x(to_date(d.x)); })
|
||||||
.y(function(d) { return y(d.y1); });
|
.y(function(d) { return y(d.y1); });
|
||||||
|
|
||||||
for (var ii = 0; ii < dataset.data.length; ii++) {
|
for (ii = 0; ii < dataset.data.length; ii++) {
|
||||||
var label = new JX.ChartFunctionLabel(dataset.labels[ii]);
|
var label = new JX.ChartFunctionLabel(dataset.labels[ii]);
|
||||||
|
|
||||||
var fill_color = label.getFillColor() || label.getColor();
|
var fill_color = label.getFillColor() || label.getColor();
|
||||||
|
@ -160,6 +175,11 @@ JX.install('Chart', {
|
||||||
.style('stroke', stroke_color)
|
.style('stroke', stroke_color)
|
||||||
.attr('d', line(dataset.data[ii]));
|
.attr('d', line(dataset.data[ii]));
|
||||||
|
|
||||||
|
curtain.addFunctionLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we've drawn all the areas and lines, draw the dots.
|
||||||
|
for (ii = 0; ii < dataset.data.length; ii++) {
|
||||||
g.selectAll('dot')
|
g.selectAll('dot')
|
||||||
.data(dataset.events[ii])
|
.data(dataset.events[ii])
|
||||||
.enter()
|
.enter()
|
||||||
|
@ -178,8 +198,16 @@ JX.install('Chart', {
|
||||||
|
|
||||||
var d_d = dd.getDate();
|
var d_d = dd.getDate();
|
||||||
|
|
||||||
|
var y = parseInt(d.y1);
|
||||||
|
|
||||||
|
var label = d.n + ' Points';
|
||||||
|
|
||||||
|
var view =
|
||||||
|
d_y + '-' + d_m + '-' + d_d + ': ' + y + '<br />' +
|
||||||
|
label;
|
||||||
|
|
||||||
div
|
div
|
||||||
.html(d_y + '-' + d_m + '-' + d_d + ': ' + d.y1)
|
.html(view)
|
||||||
.style('opacity', 0.9)
|
.style('opacity', 0.9)
|
||||||
.style('left', (d3.event.pageX - 60) + 'px')
|
.style('left', (d3.event.pageX - 60) + 'px')
|
||||||
.style('top', (d3.event.pageY - 38) + 'px');
|
.style('top', (d3.event.pageY - 38) + 'px');
|
||||||
|
@ -187,9 +215,8 @@ JX.install('Chart', {
|
||||||
.on('mouseout', function() {
|
.on('mouseout', function() {
|
||||||
div.style('opacity', 0);
|
div.style('opacity', 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
curtain.addFunctionLabel(label);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_newDate: function(epoch) {
|
_newDate: function(epoch) {
|
||||||
|
|
Loading…
Reference in a new issue