mirror of
https://github.com/valitydev/redash.git
synced 2024-11-07 09:28:51 +00:00
Merge pull request #222 from EverythingMe/feature_download_from_dashboard
Feature download from dashboard
This commit is contained in:
commit
5b998269b3
@ -107,19 +107,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scope.filters = $scope.queryResult.getFilters();
|
$scope.filters = $scope.queryResult.getFilters();
|
||||||
|
|
||||||
if ($scope.queryResult.getId() == null) {
|
|
||||||
$scope.dataUri = "";
|
|
||||||
} else {
|
|
||||||
$scope.dataUri =
|
|
||||||
'/api/queries/' + $scope.query.id + '/results/' +
|
|
||||||
$scope.queryResult.getId() + '.csv';
|
|
||||||
|
|
||||||
$scope.dataFilename =
|
|
||||||
$scope.query.name.replace(" ", "_") +
|
|
||||||
moment($scope.queryResult.getUpdatedAt()).format("_YYYY_MM_DD") +
|
|
||||||
".csv";
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.$watch("queryResult && queryResult.getStatus()", function(status) {
|
$scope.$watch("queryResult && queryResult.getStatus()", function(status) {
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
(function() {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var directives = angular.module('redash.directives', []);
|
var directives = angular.module('redash.directives', []);
|
||||||
|
|
||||||
directives.directive('alertUnsavedChanges', ['$window', function($window) {
|
directives.directive('alertUnsavedChanges', ['$window', function ($window) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
replace: true,
|
replace: true,
|
||||||
scope: {
|
scope: {
|
||||||
'isDirty': '='
|
'isDirty': '='
|
||||||
},
|
},
|
||||||
link: function($scope) {
|
link: function ($scope) {
|
||||||
var
|
var
|
||||||
|
|
||||||
unloadMessage = "You will lose your changes if you leave",
|
unloadMessage = "You will lose your changes if you leave",
|
||||||
@ -19,11 +19,11 @@
|
|||||||
// store original handler (if any)
|
// store original handler (if any)
|
||||||
_onbeforeunload = $window.onbeforeunload;
|
_onbeforeunload = $window.onbeforeunload;
|
||||||
|
|
||||||
$window.onbeforeunload = function() {
|
$window.onbeforeunload = function () {
|
||||||
return $scope.isDirty ? unloadMessage : null;
|
return $scope.isDirty ? unloadMessage : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.$on('$locationChangeStart', function(event, next, current) {
|
$scope.$on('$locationChangeStart', function (event, next, current) {
|
||||||
if (next.split("#")[0] == current.split("#")[0]) {
|
if (next.split("#")[0] == current.split("#")[0]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -33,14 +33,14 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.$on('$destroy', function() {
|
$scope.$on('$destroy', function () {
|
||||||
$window.onbeforeunload = _onbeforeunload;
|
$window.onbeforeunload = _onbeforeunload;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
directives.directive('rdTab', function() {
|
directives.directive('rdTab', function () {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
@ -50,15 +50,17 @@
|
|||||||
transclude: true,
|
transclude: true,
|
||||||
template: '<li class="rd-tab" ng-class="{active: tabId==selectedTab}"><a href="#{{tabId}}">{{name}}<span ng-transclude></span></a></li>',
|
template: '<li class="rd-tab" ng-class="{active: tabId==selectedTab}"><a href="#{{tabId}}">{{name}}<span ng-transclude></span></a></li>',
|
||||||
replace: true,
|
replace: true,
|
||||||
link: function(scope) {
|
link: function (scope) {
|
||||||
scope.$watch(function(){return scope.$parent.selectedTab}, function(tab) {
|
scope.$watch(function () {
|
||||||
|
return scope.$parent.selectedTab
|
||||||
|
}, function (tab) {
|
||||||
scope.selectedTab = tab;
|
scope.selectedTab = tab;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
directives.directive('rdTabs', ['$location', function($location) {
|
directives.directive('rdTabs', ['$location', function ($location) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
@ -67,12 +69,16 @@
|
|||||||
},
|
},
|
||||||
template: '<ul class="nav nav-tabs"><li ng-class="{active: tab==selectedTab}" ng-repeat="tab in tabsCollection"><a href="#{{tab.key}}">{{tab.name}}</a></li></ul>',
|
template: '<ul class="nav nav-tabs"><li ng-class="{active: tab==selectedTab}" ng-repeat="tab in tabsCollection"><a href="#{{tab.key}}">{{tab.name}}</a></li></ul>',
|
||||||
replace: true,
|
replace: true,
|
||||||
link: function($scope, element, attrs) {
|
link: function ($scope, element, attrs) {
|
||||||
$scope.selectTab = function(tabKey) {
|
$scope.selectTab = function (tabKey) {
|
||||||
$scope.selectedTab = _.find($scope.tabsCollection, function(tab) { return tab.key == tabKey; });
|
$scope.selectedTab = _.find($scope.tabsCollection, function (tab) {
|
||||||
|
return tab.key == tabKey;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.$watch(function() { return $location.hash()}, function(hash) {
|
$scope.$watch(function () {
|
||||||
|
return $location.hash()
|
||||||
|
}, function (hash) {
|
||||||
if (hash) {
|
if (hash) {
|
||||||
$scope.selectTab($location.hash());
|
$scope.selectTab($location.hash());
|
||||||
} else {
|
} else {
|
||||||
@ -93,7 +99,7 @@
|
|||||||
editable: '=',
|
editable: '=',
|
||||||
done: '='
|
done: '='
|
||||||
},
|
},
|
||||||
template: function(tElement, tAttrs) {
|
template: function (tElement, tAttrs) {
|
||||||
var elType = tAttrs.editor || 'input';
|
var elType = tAttrs.editor || 'input';
|
||||||
var placeholder = tAttrs.placeholder || 'Click to edit';
|
var placeholder = tAttrs.placeholder || 'Click to edit';
|
||||||
return '<span ng-click="editable && edit()" ng-bind="value" ng-class="{editable: editable}"></span>' +
|
return '<span ng-click="editable && edit()" ng-bind="value" ng-class="{editable: editable}"></span>' +
|
||||||
@ -139,18 +145,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(inputElement).keydown(function(e) {
|
$(inputElement).keydown(function (e) {
|
||||||
// 'return' or 'enter' key pressed
|
// 'return' or 'enter' key pressed
|
||||||
// allow 'shift' to break lines
|
// allow 'shift' to break lines
|
||||||
if (e.which === 13 && !e.shiftKey) {
|
if (e.which === 13 && !e.shiftKey) {
|
||||||
save();
|
save();
|
||||||
} else if (e.which === 27) {
|
} else if (e.which === 27) {
|
||||||
$scope.value = $scope.oldValue;
|
$scope.value = $scope.oldValue;
|
||||||
$scope.$apply(function() {
|
$scope.$apply(function () {
|
||||||
$(inputElement[0]).blur();
|
$(inputElement[0]).blur();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).blur(function() {
|
}).blur(function () {
|
||||||
save();
|
save();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -158,21 +164,23 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// http://stackoverflow.com/a/17904092/1559840
|
// http://stackoverflow.com/a/17904092/1559840
|
||||||
directives.directive('jsonText', function() {
|
directives.directive('jsonText', function () {
|
||||||
return {
|
return {
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
require: 'ngModel',
|
require: 'ngModel',
|
||||||
link: function(scope, element, attr, ngModel) {
|
link: function (scope, element, attr, ngModel) {
|
||||||
function into(input) {
|
function into(input) {
|
||||||
return JSON.parse(input);
|
return JSON.parse(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
function out(data) {
|
function out(data) {
|
||||||
return JSON.stringify(data, undefined, 2);
|
return JSON.stringify(data, undefined, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModel.$parsers.push(into);
|
ngModel.$parsers.push(into);
|
||||||
ngModel.$formatters.push(out);
|
ngModel.$formatters.push(out);
|
||||||
|
|
||||||
scope.$watch(attr.ngModel, function(newValue) {
|
scope.$watch(attr.ngModel, function (newValue) {
|
||||||
element[0].value = out(newValue);
|
element[0].value = out(newValue);
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
@ -184,12 +192,12 @@
|
|||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: { timestamp: '=' },
|
scope: { timestamp: '=' },
|
||||||
template: '{{currentTime}}',
|
template: '{{currentTime}}',
|
||||||
controller: ['$scope' ,function ($scope) {
|
controller: ['$scope' , function ($scope) {
|
||||||
$scope.currentTime = "00:00:00";
|
$scope.currentTime = "00:00:00";
|
||||||
|
|
||||||
// We're using setInterval directly instead of $timeout, to avoid using $apply, to
|
// We're using setInterval directly instead of $timeout, to avoid using $apply, to
|
||||||
// prevent the digest loop being run every second.
|
// prevent the digest loop being run every second.
|
||||||
var currentTimer = setInterval(function() {
|
var currentTimer = setInterval(function () {
|
||||||
$scope.currentTime = moment(moment() - moment($scope.timestamp)).utc().format("HH:mm:ss");
|
$scope.currentTime = moment(moment() - moment($scope.timestamp)).utc().format("HH:mm:ss");
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@ -204,7 +212,7 @@
|
|||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
directives.directive('rdTimeAgo', function() {
|
directives.directive('rdTimeAgo', function () {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
|
@ -38,6 +38,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function queryResultCSVLink() {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
link: function (scope, element) {
|
||||||
|
scope.$watch('queryResult && queryResult.getData()', function(data) {
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope.queryResult.getId() == null) {
|
||||||
|
element.attr('href', '');
|
||||||
|
} else {
|
||||||
|
element.attr('href', '/api/queries/' + scope.query.id + '/results/' + scope.queryResult.getId() + '.csv');
|
||||||
|
element.attr('download', scope.query.name.replace(" ", "_") + moment(scope.queryResult.getUpdatedAt()).format("_YYYY_MM_DD") + ".csv");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function queryEditor() {
|
function queryEditor() {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
@ -135,6 +155,7 @@
|
|||||||
angular.module('redash.directives')
|
angular.module('redash.directives')
|
||||||
.directive('queryLink', queryLink)
|
.directive('queryLink', queryLink)
|
||||||
.directive('querySourceLink', querySourceLink)
|
.directive('querySourceLink', querySourceLink)
|
||||||
|
.directive('queryResultLink', queryResultCSVLink)
|
||||||
.directive('queryEditor', queryEditor)
|
.directive('queryEditor', queryEditor)
|
||||||
.directive('queryRefreshSelect', queryRefreshSelect)
|
.directive('queryRefreshSelect', queryRefreshSelect)
|
||||||
.directive('queryFormatter', ['$http', queryFormatter]);
|
.directive('queryFormatter', ['$http', queryFormatter]);
|
||||||
|
@ -44,6 +44,12 @@
|
|||||||
<a class="btn btn-default btn-xs" ng-href="/queries/{{query.id}}#{{widget.visualization.id}}" ng-show="currentUser.hasPermission('view_query')"><span class="glyphicon glyphicon-link"></span></a>
|
<a class="btn btn-default btn-xs" ng-href="/queries/{{query.id}}#{{widget.visualization.id}}" ng-show="currentUser.hasPermission('view_query')"><span class="glyphicon glyphicon-link"></span></a>
|
||||||
<button type="button" class="btn btn-default btn-xs" ng-show="dashboard.canEdit()" ng-click="deleteWidget()" title="Remove Widget"><span class="glyphicon glyphicon-trash"></span></button>
|
<button type="button" class="btn btn-default btn-xs" ng-show="dashboard.canEdit()" ng-click="deleteWidget()" title="Remove Widget"><span class="glyphicon glyphicon-trash"></span></button>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span class="pull-right">
|
||||||
|
<a class="btn btn-default btn-xs" ng-disabled="!queryResult.getData()" query-result-link target="_self">
|
||||||
|
<span class="glyphicon glyphicon-cloud-download"></span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@
|
|||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-primary btn-sm" ng-disabled="queryExecuting || !queryResult.getData()" ng-href="{{dataUri}}" download="{{dataFilename}}" target="_self">
|
<a class="btn btn-primary btn-sm" ng-disabled="queryExecuting || !queryResult.getData()" query-result-link target="_self">
|
||||||
<span class="glyphicon glyphicon-cloud-download"></span>
|
<span class="glyphicon glyphicon-cloud-download"></span>
|
||||||
<span class="rd-hidden-xs">Download Dataset</span>
|
<span class="rd-hidden-xs">Download Dataset</span>
|
||||||
</a>
|
</a>
|
||||||
|
Loading…
Reference in New Issue
Block a user