From 252ae7455a01a18325dd6c58cce614e84fd09d1c Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 17 Feb 2014 17:19:32 +0200 Subject: [PATCH 01/10] Auto-refresh button for dashboards. --- rd_ui/app/scripts/controllers.js | 39 ++++++++++++++++++++++++++++---- rd_ui/app/views/dashboard.html | 2 ++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/rd_ui/app/scripts/controllers.js b/rd_ui/app/scripts/controllers.js index 0a6eef304..3b76fae21 100644 --- a/rd_ui/app/scripts/controllers.js +++ b/rd_ui/app/scripts/controllers.js @@ -1,8 +1,37 @@ (function () { - var DashboardCtrl = function ($scope, $routeParams, $http, Dashboard) { - $scope.dashboard = Dashboard.get({slug: $routeParams.dashboardSlug}, function(dashboard) { - $scope.$parent.pageTitle = dashboard.name; - }); + var DashboardCtrl = function ($scope, $routeParams, $http, $timeout, Dashboard) { + $scope.refreshEnabled = false; + $scope.refreshRate = 60; + + var refreshDashboard = function() { + $scope.dashboard = Dashboard.get({slug: $routeParams.dashboardSlug}, function(dashboard) { + $scope.$parent.pageTitle = dashboard.name; + }); + + autoRefresh(); + }; + + var autoRefresh = function() { + if ($scope.refreshEnabled) { + $timeout(refreshDashboard, $scope.refreshRate); + }; + } + + $scope.triggerRefresh = function(){ + $scope.refreshEnabled = !$scope.refreshEnabled; + + if ($scope.refreshEnabled) { + var refreshRate = _.min(_.flatten($scope.dashboard.widgets), function(widget) { + return widget.visualization.query.ttl; + }).visualization.query.ttl; + + $scope.refreshRate = _.max([120, refreshRate * 2])*1000; + + autoRefresh(); + } + }; + + refreshDashboard(); }; var WidgetCtrl = function ($scope, $http, $location, Query) { @@ -390,7 +419,7 @@ } angular.module('redash.controllers', []) - .controller('DashboardCtrl', ['$scope', '$routeParams', '$http', 'Dashboard', DashboardCtrl]) + .controller('DashboardCtrl', ['$scope', '$routeParams', '$http', '$timeout', 'Dashboard', DashboardCtrl]) .controller('WidgetCtrl', ['$scope', '$http', '$location', 'Query', WidgetCtrl]) .controller('QueriesCtrl', ['$scope', '$http', '$location', '$filter', 'Query', QueriesCtrl]) .controller('QueryFiddleCtrl', ['$scope', '$window', '$location', '$routeParams', '$http', '$location', 'growl', 'notifications', 'Query', 'Visualization', QueryFiddleCtrl]) diff --git a/rd_ui/app/views/dashboard.html b/rd_ui/app/views/dashboard.html index bfd5841d1..9ee76f7f7 100644 --- a/rd_ui/app/views/dashboard.html +++ b/rd_ui/app/views/dashboard.html @@ -4,6 +4,8 @@

{{dashboard.name}} + + From 574f75b2932724bc5d5ba02deaf3f88c1c528eba Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 17 Feb 2014 17:19:48 +0200 Subject: [PATCH 02/10] Option to set every minute refresh rate. --- rd_ui/app/scripts/controllers.js | 1 + 1 file changed, 1 insertion(+) diff --git a/rd_ui/app/scripts/controllers.js b/rd_ui/app/scripts/controllers.js index 3b76fae21..b3e99695b 100644 --- a/rd_ui/app/scripts/controllers.js +++ b/rd_ui/app/scripts/controllers.js @@ -172,6 +172,7 @@ $scope.refreshOptions = [ {value: -1, name: 'No Refresh'}, + {value: 60, name: 'Every minute'}, ] _.each(_.range(1, 13), function(i) { From 30b86ea781183c0c54f46f2011380218a70aafa2 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 17 Feb 2014 17:57:26 +0200 Subject: [PATCH 03/10] Only refresh widgets that have their query data updated. --- rd_ui/app/scripts/controllers.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/rd_ui/app/scripts/controllers.js b/rd_ui/app/scripts/controllers.js index b3e99695b..19a3b59f5 100644 --- a/rd_ui/app/scripts/controllers.js +++ b/rd_ui/app/scripts/controllers.js @@ -2,18 +2,29 @@ var DashboardCtrl = function ($scope, $routeParams, $http, $timeout, Dashboard) { $scope.refreshEnabled = false; $scope.refreshRate = 60; - - var refreshDashboard = function() { - $scope.dashboard = Dashboard.get({slug: $routeParams.dashboardSlug}, function(dashboard) { - $scope.$parent.pageTitle = dashboard.name; - }); - - autoRefresh(); - }; + $scope.dashboard = Dashboard.get({slug: $routeParams.dashboardSlug}, function(dashboard) { + $scope.$parent.pageTitle = dashboard.name; + }); var autoRefresh = function() { if ($scope.refreshEnabled) { - $timeout(refreshDashboard, $scope.refreshRate); + $timeout(function() { + Dashboard.get({slug: $routeParams.dashboardSlug}, function(dashboard) { + var newWidgets = _.groupBy(_.flatten(dashboard.widgets), 'id'); + + _.each($scope.dashboard.widgets, function(row) { + _.each(row, function(widget, i) { + var newWidget = newWidgets[widget.id]; + if (newWidget && newWidget[0].visualization.query.latest_query_data_id != widget.visualization.query.latest_query_data_id ) { + row[i] = newWidget[0]; + } + }); + }); + + autoRefresh(); + }); + + }, $scope.refreshRate); }; } @@ -30,8 +41,6 @@ autoRefresh(); } }; - - refreshDashboard(); }; var WidgetCtrl = function ($scope, $http, $location, Query) { From 07455e5821d21c988c7c5fcda9345e99355eb4e7 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 17 Feb 2014 18:01:44 +0200 Subject: [PATCH 04/10] Use database number from redis url if available. --- redash/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/redash/__init__.py b/redash/__init__.py index 6309e01ff..28bbe7dd7 100644 --- a/redash/__init__.py +++ b/redash/__init__.py @@ -33,7 +33,11 @@ def json_representation(data, code, headers=None): redis_url = urlparse.urlparse(settings.REDIS_URL) -redis_connection = redis.StrictRedis(host=redis_url.hostname, port=redis_url.port, db=0, password=redis_url.password) +if redis_url.path: + redis_db = redis_url.path[1] +else: + redis_db = 0 +redis_connection = redis.StrictRedis(host=redis_url.hostname, port=redis_url.port, db=redis_db, password=redis_url.password) from redash import data data_manager = data.Manager(redis_connection, db) From 2d0998a9955528f23f8d55e3067af581414f368f Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Mon, 24 Feb 2014 14:40:47 +0200 Subject: [PATCH 05/10] Update Getting Started instructions. --- README.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 380953b49..1dd045b8c 100644 --- a/README.md +++ b/README.md @@ -46,14 +46,8 @@ It's very likely that in the future we will switch to [D3.js](http://d3js.org/) ## Getting Started -1. Download the [latest release](https://github.com/everythingme/redash/releases). -2. Make sure you have `Python` v2.7, `pip`, PostgreSQL and Redis installed. -3. Install Python requirements: `pip install -r requirements.txt`. -4. Make a copy of the examples settings file: `cp redash/settings_example.py redash/settings.py` and edit the relevant settings. -5. Create database: `./manage.py database create_tables`. -6. Start the web server: `./manage.py runserver`. -7. Start the worker: `./manage.py runworker`. -8. Open `http://localhost:5000/` and query away. +* [Setting up re:dash on Heroku in 5 minutes](https://github.com/EverythingMe/redash/wiki/Setting-up-re:dash-on-Heroku-in-5-minutes) +* [Setting re:dash on your own server (Ubuntu)](https://github.com/EverythingMe/redash/wiki/Setting-re:dash-on-your-own-server-(Ubuntu)) **Need help setting re:dash or one of the dependencies up?** Ping @arikfr on the IRC #redash channel or send a message to the [mailing list](https://groups.google.com/forum/#!forum/redash-users), and he will gladly help. From b81c3ba614b0256eba368c2252088bb8e2a602f5 Mon Sep 17 00:00:00 2001 From: Eran Kampf Date: Mon, 24 Feb 2014 16:44:08 +0200 Subject: [PATCH 06/10] Fixed MySQL Errors --- redash/data/query_runner_mysql.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/redash/data/query_runner_mysql.py b/redash/data/query_runner_mysql.py index de9b7d7bf..a479322e9 100644 --- a/redash/data/query_runner_mysql.py +++ b/redash/data/query_runner_mysql.py @@ -43,7 +43,10 @@ def mysql(connection_string): cursor.close() except MySQLdb.Error, e: json_data = None - error = e.message + error = e.args[1] + except KeyboardInterrupt: + error = "Query cancelled by user." + json_data = None except Exception as e: raise sys.exc_info()[1], None, sys.exc_info()[2] finally: From cbd1cf7c259e595c1f92122fdea2d7bbc2e0885f Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 25 Feb 2014 08:16:30 +0200 Subject: [PATCH 07/10] Make sure visualization don't overflow --- rd_ui/app/styles/redash.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rd_ui/app/styles/redash.css b/rd_ui/app/styles/redash.css index 2da630180..7a0b72841 100644 --- a/rd_ui/app/styles/redash.css +++ b/rd_ui/app/styles/redash.css @@ -213,4 +213,8 @@ to add those CSS styles here. */ color: white; background-color: #FF8080; border-radius: 50%; +} + +visualization-renderer > div { + overflow: scroll; } \ No newline at end of file From a0662d53237a4bfbaa5d0aa58b8266b9666a19c7 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 25 Feb 2014 08:17:16 +0200 Subject: [PATCH 08/10] Remove outdated vagrant file --- Berksfile | 3 --- Vagrantfile | 60 ----------------------------------------------------- 2 files changed, 63 deletions(-) delete mode 100644 Berksfile delete mode 100644 Vagrantfile diff --git a/Berksfile b/Berksfile deleted file mode 100644 index acc59cb36..000000000 --- a/Berksfile +++ /dev/null @@ -1,3 +0,0 @@ -cookbook 'apt' -cookbook 'postgresql' -cookbook 'redash', git: 'git@github.com:EverythingMe/chef-redash.git' diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index 415006f58..000000000 --- a/Vagrantfile +++ /dev/null @@ -1,60 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! -VAGRANTFILE_API_VERSION = '2' - -POSTGRES_PASSWORD = 'securepass' - -# Currently, chef postgress cookbook works with cleartext paswords, -# unless the password begins with 'md5' -# See https://github.com/hw-cookbooks/postgresql/issues/95 -require "digest/md5" -postgres_password_md5 = 'md5'+Digest::MD5.hexdigest(POSTGRES_PASSWORD+'postgres') - -# After starting the vagrant machine, the application is accessible via the URL -# http://localhost:9999 -HOST_PORT_TO_FORWARD_TO_REDASH = 9999 - -# Deploy direcly the code in parent dir; Don't download a release tarball -live_testing_deployment = true - -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - config.vm.box = 'ubuntu-precise-cloudimg-amd64' - config.vm.box_url = 'http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-amd64-vagrant-disk1.box' - - if config.respond_to? :cache - config.cache.auto_detect = true - end - - config.berkshelf.enabled = true - config.omnibus.chef_version = :latest - - config.vm.network 'forwarded_port', guest: 8888, host: HOST_PORT_TO_FORWARD_TO_REDASH - - if live_testing_deployment - config.vm.synced_folder "..", "/opt/redash" - end - - config.vm.provision :chef_solo do |chef| - # run apt-get update before anything else (specifically postgresql).. - chef.add_recipe 'apt' - chef.add_recipe 'redash::redis_for_redash' - chef.add_recipe 'postgresql::client' - chef.add_recipe 'postgresql::server' - chef.add_recipe 'redash::redash_pg_schema' - chef.add_recipe 'redash::redash' - # chef.log_level = :debug - chef.json = { - 'apt' => { 'compiletime' => true }, - 'postgresql' => { 'password' => {'postgres' => postgres_password_md5 } }, - 'redash' => { 'db' => {'host' => 'localhost', - 'user' => 'postgres', - 'password' => POSTGRES_PASSWORD }, - 'allow' => {'google_app_domain' => 'gmail.com', - 'admins' => ['joe@egmail.com','jack@gmail.com']}, - 'install_tarball' => !live_testing_deployment, - 'user' => live_testing_deployment ? 'vagrant' : 'redash'} - } - end -end From 6d14c5c55522a65756574823770246cf887fc62a Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 25 Feb 2014 08:36:59 +0200 Subject: [PATCH 09/10] Fix graphite settings example --- redash/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redash/settings.py b/redash/settings.py index f5df483b6..c20032851 100644 --- a/redash/settings.py +++ b/redash/settings.py @@ -39,7 +39,7 @@ CONNECTION_ADAPTER = os.environ.get("REDASH_CONNECTION_ADAPTER", "pg") # Connection string for the database that is used to run queries against. Examples: # -- mysql: CONNECTION_STRING = "Server=;User=;Pwd=;Database=" # -- pg: CONNECTION_STRING = "user= password= host= port=5439 dbname=" -# -- graphite: CONNECTION_STRING = {'url': 'https://graphite.yourcompany.com', 'auth': ('user', 'password'), 'verify': True} +# -- graphite: CONNECTION_STRING = {"url": "https://graphite.yourcompany.com", "auth": ["user", "password"], "verify": true} CONNECTION_STRING = os.environ.get("REDASH_CONNECTION_STRING", "user= password= host= port=5439 dbname=") # Connection settings for re:dash's own database (where we store the queries, results, etc) From edcf0661a6aee84b731d435164a2c29d20561530 Mon Sep 17 00:00:00 2001 From: Arik Fraimovich Date: Tue, 25 Feb 2014 08:37:19 +0200 Subject: [PATCH 10/10] Fix: add parsing of graphite settings --- redash/data/manager.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/redash/data/manager.py b/redash/data/manager.py index d787f2e1c..5061dff0f 100644 --- a/redash/data/manager.py +++ b/redash/data/manager.py @@ -158,7 +158,12 @@ class Manager(object): runner = query_runner_mysql.mysql(connection_string) elif connection_type == 'graphite': from redash.data import query_runner_graphite - runner = query_runner_graphite.graphite(connection_string) + connection_params = json.loads(connection_string) + if connection_params['auth']: + connection_params['auth'] = tuple(connection_params['auth']) + else: + connection_params['auth'] = None + runner = query_runner_graphite.graphite(connection_params) else: from redash.data import query_runner runner = query_runner.redshift(connection_string)