mirror of
https://github.com/getredash/redash.git
synced 2025-12-25 01:03:20 -05:00
Compare commits
10 Commits
v0.3.1+b91
...
v0.3.1+b98
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2ec77668e | ||
|
|
f8302ab65a | ||
|
|
e632cf1c42 | ||
|
|
640557df4f | ||
|
|
9b7227a88b | ||
|
|
aabc912862 | ||
|
|
02d6567347 | ||
|
|
6f8767d1fc | ||
|
|
bc787efc86 | ||
|
|
e0d46c3942 |
@@ -20,8 +20,9 @@
|
||||
});
|
||||
};
|
||||
|
||||
$scope.open = function(query) {
|
||||
$scope.open = function(query, visualization) {
|
||||
$location.path('/queries/' + query.id);
|
||||
$location.hash(visualization.id);
|
||||
}
|
||||
|
||||
$scope.query = new Query($scope.widget.visualization.query);
|
||||
|
||||
@@ -65,7 +65,8 @@
|
||||
'Line': 'line',
|
||||
'Column': 'column',
|
||||
'Area': 'area',
|
||||
'Scatter': 'scatter'
|
||||
'Scatter': 'scatter',
|
||||
'Pie': 'pie'
|
||||
};
|
||||
|
||||
if (!scope.vis) {
|
||||
@@ -79,24 +80,22 @@
|
||||
'type': Visualization.prototype.TYPES.CHART,
|
||||
'name': '',
|
||||
'description': q.description || '',
|
||||
'options': newOptions()
|
||||
'options': newOptions(Visualization.prototype.TYPES.CHART)
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
function newOptions(chartType) {
|
||||
if (chartType === Visualization.prototype.TYPES.COHORT) {
|
||||
// empty config at the moment
|
||||
return {};
|
||||
if (chartType === Visualization.prototype.TYPES.CHART) {
|
||||
return {
|
||||
'series': {
|
||||
'type': 'column'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Chart
|
||||
return {
|
||||
'series': {
|
||||
'type': scope.seriesTypes[0]
|
||||
}
|
||||
};
|
||||
return {};
|
||||
}
|
||||
|
||||
scope.$watch('vis.type', function(type) {
|
||||
|
||||
@@ -33,10 +33,20 @@
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var s = "<b>" + this.points[0].key + "</b>";
|
||||
$.each(this.points, function (i, point) {
|
||||
s += '<br/><span style="color:' + point.series.color + '">' + point.series.name + '</span>: ' +
|
||||
Highcharts.numberFormat(point.y);
|
||||
var points = this.points;
|
||||
var name = points[0].key || points[0].name;
|
||||
|
||||
var s = "<b>" + name + "</b>";
|
||||
|
||||
$.each(points, function (i, point) {
|
||||
if (points.length > 1) {
|
||||
s += '<br/><span style="color:' + point.series.color + '">' + point.series.name + '</span>: ' + Highcharts.numberFormat(point.y);
|
||||
} else {
|
||||
s += ": " + Highcharts.numberFormat(point.y);
|
||||
if (point.percentage < 100) {
|
||||
s += ' (' +Highcharts.numberFormat(point.percentage) + '%)';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -95,15 +105,28 @@
|
||||
},
|
||||
line: {
|
||||
marker: {
|
||||
radius: 3,
|
||||
radius: 1
|
||||
},
|
||||
lineWidth: 1,
|
||||
lineWidth: 2,
|
||||
states: {
|
||||
hover: {
|
||||
lineWidth: 2
|
||||
lineWidth: 2,
|
||||
marker: {
|
||||
radius: 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
pie: {
|
||||
allowPointSelect: true,
|
||||
cursor: 'pointer',
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
color: '#000000',
|
||||
connectorColor: '#000000',
|
||||
format: '<b>{point.name}</b>: {point.percentage:.1f} %'
|
||||
}
|
||||
},
|
||||
scatter: {
|
||||
marker: {
|
||||
radius: 5,
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title" style="cursor: pointer;" ng-click="open(query)">
|
||||
<h3 class="panel-title" style="cursor: pointer;" ng-click="open(query, widget.visualization)">
|
||||
<p>
|
||||
<span ng-bind="query.name"></span>
|
||||
</p>
|
||||
@@ -37,7 +37,7 @@
|
||||
tooltip-placement="bottom">Updated: <span am-time-ago="queryResult.getUpdatedAt()"></span></span>
|
||||
|
||||
<span class="pull-right">
|
||||
<a class="btn btn-default btn-xs" ng-href="/queries/{{query.id}}"><span class="glyphicon glyphicon-link"></span></a>
|
||||
<a class="btn btn-default btn-xs" ng-href="/queries/{{query.id}}#{{widget.visualization.id}}"><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>
|
||||
</span>
|
||||
|
||||
|
||||
@@ -56,9 +56,9 @@
|
||||
<rd-tab id="pivot" name="Pivot Table"></rd-tab>
|
||||
<!-- hide the table visualization -->
|
||||
<rd-tab id="{{vis.id}}" name="{{vis.name}}" ng-hide="vis.type=='TABLE'" ng-repeat="vis in query.visualizations">
|
||||
<span class="remove" ng-click="deleteVisualization($event, vis)"> ×</span>
|
||||
<span class="remove" ng-click="deleteVisualization($event, vis)" ng-show="currentUser.canEdit(query)"> ×</span>
|
||||
</rd-tab>
|
||||
<rd-tab id="add" name="+New" removeable="true"></rd-tab>
|
||||
<rd-tab id="add" name="+New" removeable="true" ng-show="currentUser.canEdit(query)"></rd-tab>
|
||||
</ul>
|
||||
|
||||
<div class="col-lg-12" ng-show="selectedTab == 'table'">
|
||||
@@ -70,12 +70,16 @@
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12" ng-show="selectedTab == vis.id" ng-repeat="vis in query.visualizations">
|
||||
<div class="row">
|
||||
<div class="row" ng-show="currentUser.canEdit(query)">
|
||||
<p>
|
||||
<div class="col-lg-6">
|
||||
<div class="col-lg-12">
|
||||
<edit-visulatization-form vis="vis" query="query"></edit-visulatization-form>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p>
|
||||
<div class="col-lg-12">
|
||||
<visualization-renderer visualization="vis" query-result="queryResult"></visualization-renderer>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
@@ -153,9 +153,12 @@ class Manager(object):
|
||||
if self.workers:
|
||||
return self.workers
|
||||
|
||||
if getattr(settings, 'CONNECTION_ADAPTER', None) == "mysql":
|
||||
if connection_type == 'mysql':
|
||||
from redash.data import query_runner_mysql
|
||||
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)
|
||||
else:
|
||||
from redash.data import query_runner
|
||||
runner = query_runner.redshift(connection_string)
|
||||
|
||||
46
redash/data/query_runner_graphite.py
Normal file
46
redash/data/query_runner_graphite.py
Normal file
@@ -0,0 +1,46 @@
|
||||
"""
|
||||
QueryRunner for Graphite.
|
||||
"""
|
||||
import json
|
||||
import datetime
|
||||
import requests
|
||||
from redash.utils import JSONEncoder
|
||||
|
||||
|
||||
def graphite(connection_params):
|
||||
def transform_result(response):
|
||||
columns = [{'name': 'Time::x'}, {'name': 'value::y'}, {'name': 'name::series'}]
|
||||
rows = []
|
||||
|
||||
for series in response.json():
|
||||
for values in series['datapoints']:
|
||||
timestamp = datetime.datetime.fromtimestamp(int(values[1]))
|
||||
rows.append({'Time::x': timestamp, 'name::series': series['target'], 'value::y': values[0]})
|
||||
|
||||
data = {'columns': columns, 'rows': rows}
|
||||
return json.dumps(data, cls=JSONEncoder)
|
||||
|
||||
def query_runner(query):
|
||||
base_url = "%s/render?format=json&" % connection_params['url']
|
||||
url = "%s%s" % (base_url, "&".join(query.split("\n")))
|
||||
error = None
|
||||
data = None
|
||||
|
||||
try:
|
||||
response = requests.get(url, auth=connection_params['auth'],
|
||||
verify=connection_params['verify'])
|
||||
|
||||
if response.status_code == 200:
|
||||
data = transform_result(response)
|
||||
else:
|
||||
error = "Failed getting results (%d)" % response.status_code
|
||||
|
||||
except Exception, ex:
|
||||
data = None
|
||||
error = ex.message
|
||||
|
||||
return data, error
|
||||
|
||||
query_runner.annotate_query = False
|
||||
|
||||
return query_runner
|
||||
@@ -234,8 +234,11 @@ class Worker(threading.Thread):
|
||||
start_time = time.time()
|
||||
self.set_title("running query %s" % job_id)
|
||||
|
||||
annotated_query = "/* Pid: %s, Job Id: %s, Query hash: %s, Priority: %s */ %s" % \
|
||||
(pid, job.id, job.query_hash, job.priority, job.query)
|
||||
if getattr(self.query_runner, 'annotate_query', True):
|
||||
annotated_query = "/* Pid: %s, Job Id: %s, Query hash: %s, Priority: %s */ %s" % \
|
||||
(pid, job.id, job.query_hash, job.priority, job.query)
|
||||
else:
|
||||
annotated_query = job.query
|
||||
|
||||
# TODO: here's the part that needs to be forked, not all of the worker process...
|
||||
data, error = self.query_runner(annotated_query)
|
||||
|
||||
@@ -3,11 +3,12 @@ Example settings module. You should make your own copy as settings.py and enter
|
||||
"""
|
||||
REDIS_URL = "redis://localhost:6379"
|
||||
|
||||
# Either "pg" or "mysql"
|
||||
# "pg", "graphite" or "mysql"
|
||||
CONNECTION_ADAPTER = "pg"
|
||||
# Connection string for the database that is used to run queries against
|
||||
# -- example mysql CONNECTION_STRING = "Server=;User=;Pwd=;Database="
|
||||
# -- example pg CONNECTION_STRING = "user= password= host= port=5439 dbname="
|
||||
# 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}
|
||||
CONNECTION_STRING = "user= password= host= port=5439 dbname="
|
||||
# Connection settings for re:dash's own database (where we store the queries, results, etc)
|
||||
DATABASE_CONFIG = {
|
||||
|
||||
Reference in New Issue
Block a user