Skip to content

Commit

Permalink
Merge with upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
arikfr committed Sep 2, 2019
2 parents bec6132 + db9ce37 commit f82dedb
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 101 deletions.
6 changes: 0 additions & 6 deletions client/app/assets/less/ant.less
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,6 @@
}
}

// Fix for Ant dropdowns when they are used in Boootstrap modals
// ANGULAR_REMOVE_ME Remove when all dialogs will be migrated to React (also search and remove usages)
.ant-dropdown-in-bootstrap-modal {
z-index: 1050;
}

// Button overrides
.@{btn-prefix-cls} {
transition-duration: 150ms;
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/EditParameterSettingsDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ function EditParameterSettingsDialog(props) {
</Select>
</Form.Item>
{param.type === 'enum' && (
<Form.Item label="Values" help="Dropdown list values (newline delimeted)" {...formItemProps}>
<Form.Item label="Values" help="Dropdown list values (newline delimited)" {...formItemProps}>
<Input.TextArea
rows={3}
value={param.enumOptions}
Expand Down
1 change: 0 additions & 1 deletion client/app/components/ParameterValueInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ class ParameterValueInput extends React.Component {
value={value}
onChange={this.onSelect}
dropdownMatchSelectWidth={false}
dropdownClassName="ant-dropdown-in-bootstrap-modal"
showSearch
showArrow
style={{ minWidth: 60 }}
Expand Down
1 change: 0 additions & 1 deletion client/app/components/QueryBasedParameterInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ export class QueryBasedParameterInput extends React.Component {
value={isArray(value) ? value : toString(value)}
onChange={onSelect}
dropdownMatchSelectWidth={false}
dropdownClassName="ant-dropdown-in-bootstrap-modal"
optionFilterProp="children"
showSearch
showArrow
Expand Down
32 changes: 18 additions & 14 deletions client/app/components/dashboards/widget.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,26 @@
</div>
</div>

<div class="body-row clearfix tile__bottom-control">
<a class="refresh-button hidden-print btn btn-sm btn-default btn-transparent" ng-click="$ctrl.refresh(1)" ng-if="!$ctrl.public && !!$ctrl.widget.getQueryResult()" data-test="RefreshButton">
<i class="zmdi zmdi-refresh" ng-class="{ 'zmdi-hc-spin': $ctrl.refreshClickButtonId === 1}"></i>
<span am-time-ago="$ctrl.widget.getQueryResult().getUpdatedAt()"></span>
</a>
<span class="small hidden-print" ng-if="$ctrl.public">
<i class="zmdi zmdi-time-restore"></i> <span am-time-ago="$ctrl.widget.getQueryResult().getUpdatedAt()"></span>
</span>
<span class="visible-print">
<i class="zmdi zmdi-time-restore"></i> {{$ctrl.widget.getQueryResult().getUpdatedAt() | dateTime}}
<div class="body-row tile__bottom-control">
<span>
<a class="refresh-button hidden-print btn btn-sm btn-default btn-transparent" ng-click="$ctrl.refresh(1)" ng-if="!$ctrl.public && !!$ctrl.widget.getQueryResult()" data-test="RefreshButton">
<i class="zmdi zmdi-refresh" ng-class="{ 'zmdi-hc-spin': $ctrl.refreshClickButtonId === 1}"></i>
<span am-time-ago="$ctrl.widget.getQueryResult().getUpdatedAt()"></span>
</a>
<span class="small hidden-print" ng-if="$ctrl.public">
<i class="zmdi zmdi-time-restore"></i> <span am-time-ago="$ctrl.widget.getQueryResult().getUpdatedAt()"></span>
</span>
<span class="visible-print">
<i class="zmdi zmdi-time-restore"></i> {{$ctrl.widget.getQueryResult().getUpdatedAt() | dateTime}}
</span>
</span>

<button class="btn btn-sm btn-default pull-right hidden-print btn-transparent btn__refresh" ng-click="$ctrl.refresh(2)" ng-if="!$ctrl.public">
<i class="zmdi zmdi-refresh" ng-class="{ 'zmdi-hc-spin': $ctrl.refreshClickButtonId === 2}"></i>
</button>
<button class="btn btn-sm btn-default pull-right hidden-print btn-transparent btn__refresh" ng-click="$ctrl.expandVisualization()"><i class="zmdi zmdi-fullscreen"></i></button>
<span>
<button class="btn btn-sm btn-default hidden-print btn-transparent btn__refresh" ng-click="$ctrl.expandVisualization()"><i class="zmdi zmdi-fullscreen"></i></button>
<button class="btn btn-sm btn-default hidden-print btn-transparent btn__refresh" ng-click="$ctrl.refresh(2)" ng-if="!$ctrl.public">
<i class="zmdi zmdi-refresh" ng-class="{ 'zmdi-hc-spin': $ctrl.refreshClickButtonId === 2}"></i>
</button>
</span>
</div>
</div>

Expand Down
20 changes: 15 additions & 5 deletions client/app/components/dashboards/widget.less
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,6 @@ visualization-name {
}
}

.refresh-button {
margin-left: -6px;
}

.tile {
.widget-menu-regular, .btn__refresh {
opacity: 0 !important;
Expand Down Expand Up @@ -249,7 +245,21 @@ visualization-name {

.tile__bottom-control {
padding: 10px 15px;
line-height: 2;
display: flex;
justify-content: space-between;
align-items: center;

.btn-transparent {
&:first-child {
margin-left: -10px;
}

&:last-child {
margin-right: -10px;
}
}



a {
color: fade(@redash-black, 65%);
Expand Down
65 changes: 32 additions & 33 deletions client/app/components/queries/visualization-embed.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,40 +24,39 @@ <h3>
</visualization-renderer>
</div>

<div class="clearfix tile__bottom-control">
<div class="row">
<div class="col-xs-6">
<a class="small hidden-print" ng-click="$ctrl.refreshQueryResults()">
<i ng-class='{"zmdi-hc-spin": $ctrl.loading}' class="zmdi zmdi-refresh"></i>
<span am-time-ago="$ctrl.queryResult.getUpdatedAt()" ng-if="!$ctrl.loading"></span>
<rd-timer from="$ctrl.refreshStartedAt" ng-if="$ctrl.loading"></rd-timer>
</a>
<span class="small visible-print"><i class="zmdi zmdi-time-restore"></i> {{$ctrl.queryResult.getUpdatedAt() | dateTime}} UTC</span>
</div>
<div class="col-xs-6 text-right hidden-print" ng-if="!$ctrl.hideQueryLink">
<a class="btn btn-default btn-sm" ng-href="{{$ctrl.query.getUrl()}}" target="_blank" tooltip="Open in Redash">
<span class="zmdi zmdi-link"></span>
</a>
<div class="tile__bottom-control">
<span>
<a class="small hidden-print" ng-click="$ctrl.refreshQueryResults()">
<i ng-class='{"zmdi-hc-spin": $ctrl.loading}' class="zmdi zmdi-refresh"></i>
<span am-time-ago="$ctrl.queryResult.getUpdatedAt()" ng-if="!$ctrl.loading"></span>
<rd-timer from="$ctrl.refreshStartedAt" ng-if="$ctrl.loading"></rd-timer>
</a>
<span class="small visible-print"><i class="zmdi zmdi-time-restore"></i> {{$ctrl.queryResult.getUpdatedAt() | dateTime}} UTC</span>
</span>

<span class="hidden-print" ng-if="!$ctrl.hideQueryLink">
<a class="btn btn-default btn-sm" ng-href="{{$ctrl.query.getUrl()}}" target="_blank" tooltip="Open in Redash">
<span class="zmdi zmdi-link"></span>
</a>

<div class="btn-group dropup" uib-dropdown ng-if="!$ctrl.query.hasParameters()">
<button type="button" class="btn btn-default btn-sm dropdown-toggle" aria-haspopup="true" uib-dropdown-toggle
aria-expanded="false">
Download Dataset <span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right" uib-dropdown-menu>
<li>
<a ng-href="{{$ctrl.queryResult.getLink($ctrl.query.id, 'csv', $ctrl.apiKey)}}" download="{{$ctrl.queryResult.getName($ctrl.query.name, 'csv')}}" target="_self">
<span class="fa fa-file-o"></span> Download as CSV File
</a>
</li>
<li>
<a ng-href="{{$ctrl.queryResult.getLink($ctrl.query.id, 'xlsx', $ctrl.apiKey)}}" download="{{$ctrl.queryResult.getName($ctrl.query.name, 'xlsx')}}" target="_self">
<span class="fa fa-file-excel-o"></span> Download as Excel File
</a>
</li>
</ul>
</div>
<div class="btn-group dropup" uib-dropdown ng-if="!$ctrl.query.hasParameters()">
<button type="button" class="btn btn-default btn-sm dropdown-toggle" aria-haspopup="true" uib-dropdown-toggle
aria-expanded="false">
Download Dataset <span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right" uib-dropdown-menu>
<li>
<a ng-href="{{$ctrl.queryResult.getLink($ctrl.query.id, 'csv', $ctrl.apiKey)}}" download="{{$ctrl.queryResult.getName($ctrl.query.name, 'csv')}}" target="_self">
<span class="fa fa-file-o"></span> Download as CSV File
</a>
</li>
<li>
<a ng-href="{{$ctrl.queryResult.getLink($ctrl.query.id, 'xlsx', $ctrl.apiKey)}}" download="{{$ctrl.queryResult.getName($ctrl.query.name, 'xlsx')}}" target="_self">
<span class="fa fa-file-excel-o"></span> Download as Excel File
</a>
</li>
</ul>
</div>
</div>
</span>
</div>
</div>
46 changes: 26 additions & 20 deletions redash/query_runner/query_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sqlite3

from redash import models
from redash.permissions import has_access, not_view_only
from redash.permissions import has_access, view_only
from redash.query_runner import BaseQueryRunner, TYPE_STRING, guess_type, register
from redash.utils import json_dumps, json_loads

Expand All @@ -24,7 +24,8 @@ def extract_query_ids(query):


def extract_cached_query_ids(query):
queries = re.findall(r'(?:join|from)\s+cached_query_(\d+)', query, re.IGNORECASE)
queries = re.findall(r'(?:join|from)\s+cached_query_(\d+)', query,
re.IGNORECASE)
return [int(q) for q in queries]


Expand All @@ -34,9 +35,11 @@ def _load_query(user, query_id):
if user.org_id != query.org_id:
raise PermissionError("Query id {} not found.".format(query.id))

if not has_access(query.data_source, user, not_view_only):
raise PermissionError(u"You are not allowed to execute queries on {} data source (used for query id {}).".format(
query.data_source.name, query.id))
# TODO: this duplicates some of the logic we already have in the redash.handlers.query_results.
# We should merge it so it's consistent.
if not has_access(query.data_source, user, view_only):
raise PermissionError(u"You do not have access to query id {}.".format(
query.id))

return query

Expand All @@ -47,16 +50,22 @@ def get_query_results(user, query_id, bring_from_cache):
if query.latest_query_data_id is not None:
results = query.latest_query_data.data
else:
raise Exception("No cached result available for query {}.".format(query.id))
raise Exception("No cached result available for query {}.".format(
query.id))
else:
results, error = query.data_source.query_runner.run_query(query.query_text, user)
results, error = query.data_source.query_runner.run_query(
query.query_text, user)
if error:
raise Exception("Failed loading results for query id {}.".format(query.id))
raise Exception("Failed loading results for query id {}.".format(
query.id))

return json_loads(results)


def create_tables_from_query_ids(user, connection, query_ids, cached_query_ids=[]):
def create_tables_from_query_ids(user,
connection,
query_ids,
cached_query_ids=[]):
for query_id in set(cached_query_ids):
results = get_query_results(user, query_id, True)
table_name = 'cached_query_{query_id}'.format(query_id=query_id)
Expand All @@ -81,8 +90,7 @@ def flatten(value):

def create_table(connection, table_name, query_results):
try:
columns = [column['name']
for column in query_results['columns']]
columns = [column['name'] for column in query_results['columns']]
safe_columns = [fix_column_name(column) for column in columns]

column_list = ", ".join(safe_columns)
Expand All @@ -91,7 +99,8 @@ def create_table(connection, table_name, query_results):
logger.debug("CREATE TABLE query: %s", create_table)
connection.execute(create_table)
except sqlite3.OperationalError as exc:
raise CreateTableError(u"Error creating table {}: {}".format(table_name, exc.message))
raise CreateTableError(u"Error creating table {}: {}".format(
table_name, exc.message))

insert_template = u"insert into {table_name} ({column_list}) values ({place_holders})".format(
table_name=table_name,
Expand All @@ -109,11 +118,7 @@ class Results(BaseQueryRunner):

@classmethod
def configuration_schema(cls):
return {
"type": "object",
"properties": {
}
}
return {"type": "object", "properties": {}}

@classmethod
def name(cls):
Expand All @@ -124,16 +129,17 @@ def run_query(self, query, user):

query_ids = extract_query_ids(query)
cached_query_ids = extract_cached_query_ids(query)
create_tables_from_query_ids(user, connection, query_ids, cached_query_ids)
create_tables_from_query_ids(user, connection, query_ids,
cached_query_ids)

cursor = connection.cursor()

try:
cursor.execute(query)

if cursor.description is not None:
columns = self.fetch_columns(
[(i[0], None) for i in cursor.description])
columns = self.fetch_columns([(i[0], None)
for i in cursor.description])

rows = []
column_names = [c['name'] for c in columns]
Expand Down
Loading

0 comments on commit f82dedb

Please sign in to comment.