Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI fixes for https://github.com/mozilla/redash/pull/293 #1

Open
wants to merge 54 commits into
base: toggle-autocomplete-282
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
658e4de
In docker-entrypoint ensure tables exist
Apr 12, 2017
8a0a4e9
upgrade node and npm in dockerfile
alison985 Aug 11, 2017
6bb9cf7
Update circle.yml for our workflow
robotblake Apr 19, 2017
81e6427
add pyup config document
alison985 Jul 8, 2017
dd52ba3
Pin PyAthena dependency to 1.2.0.
jezdez Oct 25, 2017
b0c33a0
Switch to PyMySQL for MySQL 5.7 support
Sep 14, 2017
2e141f5
Documentation links for data sources (re #6)
Nov 8, 2016
93fee0d
Don't execute query when changing data sources (fixes #29)
Feb 10, 2017
2dbe0ad
Retry fetching query result on failure (fixes #36)
Feb 16, 2017
631f93a
Use saved values for parameters in scheduled queries (re #43)
Feb 24, 2017
a4f9899
delay schema filtering to improve responsiveness (fixes #45)
Mar 8, 2017
ce66dea
Add `schedule_until` field to queries, to allow expiry (re #15)
Mar 2, 2017
186dfe9
add compare query version support (re #7)
spasovski Feb 6, 2017
58db6cd
accommodate '/' in dashboard tags (re #48)
Mar 24, 2017
fa018ab
upgrade flask-oauthlib (re #63)
Apr 19, 2017
6d499c2
reject non-list dashboard layouts (re #25)
Apr 26, 2017
f50acc2
Reject empty names for dashboards (re #76)
May 5, 2017
52fbfcb
allow unpublished queries to be used as alerts (re #97)
alison985 Jul 4, 2017
45ca0c3
dashboard textbox edit close fix (re #111)
alison985 Jun 25, 2017
7777169
works for filtering _v literally (re #31)
alison985 Jul 4, 2017
a542636
Expose data source version info in UI (re #89)
alison985 Jun 24, 2017
1bf0f22
give warning/error msg on inaccurate graph config (re #57)
alison985 Jun 22, 2017
c1277b5
adding activedata query_runner (re #83)
alison985 Jul 4, 2017
84be34b
Display amount of data scanned from various query runners (re #69)
alison985 Jul 4, 2017
795dcc5
Re-prompt for login if an unauthorized Google account is used (fixes …
Jul 21, 2017
996220d
Report bytes scanned (re #69)
alison985 Jul 19, 2017
25a4ff1
add toggle_table_string (re #130)
alison985 Jul 24, 2017
259ba8a
add column type info to query runners (re #152, #23)
alison985 Jul 28, 2017
3121ae1
make the enter button submit the form (re #172)
alison985 Jul 28, 2017
8d992b7
add hideParameters param (re #163)
alison985 Jul 28, 2017
6b67387
add ability to add query to dashboard from query page (re #154)
alison985 Aug 8, 2017
42f6569
Add last_active_at column to users page (re #155)
alison985 Aug 12, 2017
7037603
add partition key marker to Athena and Presto columns (re #185)
Dec 9, 2017
6c3ef66
make autocomplete for large schemas faster (re #232)
Dec 9, 2017
35a883a
hide query more menu if empty (re #208)
spasovski Aug 17, 2017
121b2b9
only show query versions when more than one exists (re #194)
spasovski Aug 16, 2017
1166a9e
sort query version select options (re #198)
spasovski Aug 16, 2017
5cf1a4f
run the query if it doesn't have latest_query_data (re #166)
alison985 Aug 14, 2017
8eca965
change pie chart colors (re #240)
alison985 Aug 18, 2017
716991e
adds redash db size metrics to status page (re #247)
alison985 Sep 6, 2017
a30f347
Move events server side (re #245)
alison985 Sep 1, 2017
546a7b5
Run queries with no cached result in public dashboards (re #220)
Sep 6, 2017
15104f8
fix pie chart multiple colors changed (re #242)
alison985 Sep 11, 2017
58573d8
allow x-axis label truncation (re #249)
alison985 Sep 11, 2017
7b44358
secure cookies, add X-Content-Type-Options header (bug 1371613)
Sep 27, 2017
5f48ebc
Add full text search for queries based on the Postgres tsvector type.…
jezdez Oct 17, 2017
4ac5d97
Merge mozilla schema updates with schema from master
Dec 12, 2017
2f08a71
fixup! allow x-axis label truncation (re #249)
Dec 15, 2017
2f9fcc7
fixup! In docker-entrypoint ensure tables exist
Dec 15, 2017
748f834
fixup! fix pie chart multiple colors changed (re #242)
Dec 15, 2017
ad094e9
Toggle for query editor autocomplete (re #282)
Dec 19, 2017
bb91772
Add loader-utils to fix an error.
jezdez Jan 3, 2018
1ca69e4
Position autocomplete and format button next to each other and add la…
jezdez Jan 3, 2018
cb0158f
Use dropdown for editor specific actions to save UI space.
jezdez Jan 3, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .pyup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
schedule: "every day"
search: False
update: insecure
requirements:
- requirements.txt:
update: insecure
- requirements-newrelic.txt:
update: security
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ COPY requirements.txt requirements_dev.txt requirements_all_ds.txt ./
RUN pip install -r requirements.txt -r requirements_dev.txt -r requirements_all_ds.txt

COPY . ./

# Upgrade node to LTS 6.11.2
RUN cd ~
RUN wget https://nodejs.org/download/release/v6.11.2/node-v6.11.2-linux-x64.tar.gz
RUN sudo tar --strip-components 1 -xzvf node-v* -C /usr/local

# Upgrade npm
RUN npm upgrade npm

RUN npm install && npm run build && rm -rf node_modules
RUN chown -R redash /app
USER redash
Expand Down
19 changes: 19 additions & 0 deletions bin/deploy
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

set -eo pipefail

[ ! -z $DOCKERHUB_REPO ] && [ $# -eq 1 ]

VERSION="$1"

printf '{"commit":"%s","version":"%s","source":"https://github.com/%s/%s","build":"%s"}\n' \
"$CIRCLE_SHA1" \
"$VERSION" \
"$CIRCLE_PROJECT_USERNAME" \
"$CIRCLE_PROJECT_REPONAME" \
"$CIRCLE_BUILD_URL" \
> version.json

docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
docker build -t $DOCKERHUB_REPO:$VERSION .
docker push $DOCKERHUB_REPO:$VERSION
3 changes: 3 additions & 0 deletions bin/docker-entrypoint
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
set -e

worker() {
/app/manage.py db upgrade
WORKERS_COUNT=${WORKERS_COUNT:-2}
QUEUES=${QUEUES:-queries,scheduled_queries,celery}

Expand All @@ -10,6 +11,7 @@ worker() {
}

scheduler() {
/app/manage.py db upgrade
WORKERS_COUNT=${WORKERS_COUNT:-1}
QUEUES=${QUEUES:-celery}

Expand All @@ -19,6 +21,7 @@ scheduler() {
}

server() {
/app/manage.py db upgrade
exec /usr/local/bin/gunicorn -b 0.0.0.0:5000 --name redash -w${REDASH_WEB_WORKERS:-4} redash.wsgi:app
}

Expand Down
22 changes: 9 additions & 13 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,16 @@ test:
override:
- pytest --junitxml=$CIRCLE_TEST_REPORTS/junit.xml tests/
deployment:
github_and_docker:
branch: [master, /release.*/]
latest:
branch: master
owner: mozilla
commands:
- make pack
# Skipping uploads for now, until master is stable.
# - make upload
#- echo "client/app" >> .dockerignore
#- docker pull redash/redash:latest
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
- docker build -t redash/redash:$(./manage.py version | sed -e "s/\+/./") .
- docker push redash/redash:$(./manage.py version | sed -e "s/\+/./")
notify:
webhooks:
- url: https://webhooks.gitter.im/e/895d09c3165a0913ac2f
- ./bin/deploy "latest"
hub_releases:
tag: /^m[0-9]+(\.[0-9]+)?$/
owner: mozilla
commands:
- ./bin/deploy "$CIRCLE_TAG"
general:
branches:
ignore:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ <h4 class="modal-title">Edit: {{$ctrl.dashboard.name}}</h4>
</div>
<div class="modal-body">
<p>
<input type="text" class="form-control" placeholder="Dashboard Name" ng-model="$ctrl.dashboard.name" autofocus>
<input type="text" class="form-control" placeholder="Dashboard Name" ng-model="$ctrl.dashboard.name" ng-keyup="$ctrl.saveDashboardOnEnter($event)" autofocus required>
</p>

<p ng-if="$ctrl.dashboard.id">
Expand Down
11 changes: 10 additions & 1 deletion client/app/components/dashboards/edit-dashboard-dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ const EditDashboardDialog = {
'Please copy/backup your changes and reload this page.', { autoDismiss: false });
}
});
Events.record('edit', 'dashboard', this.dashboard.id);
} else {
$http.post('api/dashboards', {
name: this.dashboard.name,
Expand All @@ -96,6 +95,16 @@ const EditDashboardDialog = {
Events.record('create', 'dashboard');
}
};
this.saveDashboardOnEnter = ($event) => {
// keyCode 13 is the Enter key
if ($event.keyCode === 13) {
this.saveDashboard();
}
};
this.closeWithoutSave = () => {
this.dashboard.name = this.dashboard.existing_name;
this.dismiss();
};
},
};

Expand Down
4 changes: 2 additions & 2 deletions client/app/components/dashboards/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ function DashboardWidgetCtrl($location, $uibModal, $window, Events, currentUser)
resolve: {
widget: this.widget,
},
backdrop: 'static',
keyboard: false,
});
};

Expand All @@ -57,8 +59,6 @@ function DashboardWidgetCtrl($location, $uibModal, $window, Events, currentUser)
return;
}

Events.record('delete', 'widget', this.widget.id);

this.widget.$delete((response) => {
this.dashboard.widgets =
this.dashboard.widgets.map(row => row.filter(widget => widget.id !== undefined));
Expand Down
1 change: 1 addition & 0 deletions client/app/components/dynamic-form.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<label ng-if="field.property.type !== 'checkbox'">{{field.property.title || field.name | capitalize}}</label>
<input name="input" type="{{field.property.type}}" class="form-control" ng-model="target.options[field.name]" ng-required="field.property.required"
ng-if="field.property.type !== 'file' && field.property.type !== 'checkbox'" accesskey="tab" placeholder="{{field.property.default}}">
<div class="mute-text"> {{ field.property.info }} </div>

<label ng-if="field.property.type=='checkbox'">
<input name="input" type="{{field.property.type}}" ng-model="target.options[field.name]" ng-required="field.property.required"
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/parameters.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="form-inline bg-white p-5"
ng-if="parameters | notEmpty"
ng-if="hideParameters != 'true' && parameters | notEmpty"
ui-sortable="{ 'ui-floating': true, 'disabled': !editable }"
ng-model="parameters">
<div class="form-group m-l-10 m-r-10"
Expand Down
1 change: 1 addition & 0 deletions client/app/components/parameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ function ParametersDirective($location, $uibModal) {
},
});
};
scope.hideParameters = $location.search().hideParameters;
},
};
}
Expand Down
90 changes: 53 additions & 37 deletions client/app/components/queries/query-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,49 @@ function queryEditor(QuerySnippet) {
pre($scope) {
$scope.syntax = $scope.syntax || 'sql';

const schemaCompleter = {
getCompletions(state, session, pos, prefix, callback) {
// make a variable for the auto completion in the query editor
$scope.autoCompleteSchema = $scope.schema; // removeExtraSchemaInfo(

if (prefix.length === 0 || !$scope.autoCompleteSchema) {
callback(null, []);
return;
}

if (!$scope.autoCompleteSchema.keywords) {
const keywords = {};

$scope.autoCompleteSchema.forEach((table) => {
keywords[table.name] = 'Table';

table.columns.forEach((c) => { // autoCompleteColumns
if (c.charAt(c.length - 1) === ')') {
let parensStartAt = c.indexOf('(') - 1;
c = c.substring(0, parensStartAt);
parensStartAt = 1; // linter complains without this line
}
// remove '[P] ' for partition keys
if (c.charAt(0) === '[') {
c = c.substring(4, c.length);
}
// keywords[c] = 'Column'; // dups columns
keywords[`${table.name}.${c}`] = 'Column';
});
});

$scope.autoCompleteSchema.keywords = map(keywords, (v, k) =>
({
name: k,
value: k,
score: 0,
meta: v,
}));
}
callback(null, $scope.autoCompleteSchema.keywords);
},
};

$scope.editorOptions = {
mode: 'json',
// require: ['ace/ext/language_tools'],
Expand Down Expand Up @@ -70,61 +113,34 @@ function queryEditor(QuerySnippet) {
editor.getSession().setMode(newMode);
});

$scope.$watch('schema', (newSchema, oldSchema) => {
$scope.$watch('autoCompleteSchema', (newSchema, oldSchema) => {
if (newSchema !== oldSchema) {
const tokensCount =
newSchema.reduce((totalLength, table) => totalLength + table.columns.length, 0);
// If there are too many tokens we disable live autocomplete,
// as it makes typing slower.
if (tokensCount > 5000) {
// If there are too many tokens or if it's requested via the UI
// we disable live autocomplete, as it makes typing slower.
if (tokensCount > 5000 || !$scope.$parent.autocompleteQuery) {
editor.setOption('enableLiveAutocompletion', false);
editor.setOption('enableBasicAutocompletion', false);
} else {
editor.setOption('enableLiveAutocompletion', true);
editor.setOption('enableBasicAutocompletion', true);
}
}
});

$scope.$parent.$on('angular-resizable.resizing', () => {
editor.resize();
});
$scope.$parent.$watch('autocompleteQuery', () => {
editor.setOption('enableLiveAutocompletion', $scope.$parent.autocompleteQuery);
editor.setOption('enableBasicAutocompletion', $scope.$parent.autocompleteQuery);
});

editor.focus();
},
};


const schemaCompleter = {
getCompletions(state, session, pos, prefix, callback) {
if (prefix.length === 0 || !$scope.schema) {
callback(null, []);
return;
}

if (!$scope.schema.keywords) {
const keywords = {};

$scope.schema.forEach((table) => {
keywords[table.name] = 'Table';

table.columns.forEach((c) => {
keywords[c] = 'Column';
keywords[`${table.name}.${c}`] = 'Column';
});
});

$scope.schema.keywords = map(keywords, (v, k) =>
({
name: k,
value: k,
score: 0,
meta: v,
}));
}
callback(null, $scope.schema.keywords);
},
};


window.ace.acequire(['ace/ext/language_tools'], (langTools) => {
langTools.addCompleter(schemaCompleter);
});
Expand Down
4 changes: 4 additions & 0 deletions client/app/components/queries/schedule-dialog.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ <h4 class="modal-title">Refresh Schedule</h4>
<query-time-picker refresh-type="$ctrl.refreshType" query="$ctrl.query" save-query="$ctrl.saveQuery"></query-time-picker>
</label>
</div>
<label>
Stop scheduling at date/time (format yyyy-MM-ddTHH:mm:ss, like 2016-12-28T14:57:00):
<schedule-until query="$ctrl.query" save-query="$ctrl.saveQuery"></schedule-until>
</label>
</div>
12 changes: 12 additions & 0 deletions client/app/components/queries/schedule-dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,17 @@ function queryRefreshSelect() {
};
}

function scheduleUntil() {
return {
restrict: 'E',
scope: {
query: '=',
saveQuery: '=',
},
template: '<input type="datetime-local" step="1" class="form-control" ng-model="query.scheduleUntil" ng-change="saveQuery()">',
};
}

const ScheduleForm = {
controller() {
this.query = this.resolve.query;
Expand All @@ -146,5 +157,6 @@ const ScheduleForm = {
export default function init(ngModule) {
ngModule.directive('queryTimePicker', queryTimePicker);
ngModule.directive('queryRefreshSelect', queryRefreshSelect);
ngModule.directive('scheduleUntil', scheduleUntil);
ngModule.component('scheduleDialog', ScheduleForm);
}
14 changes: 12 additions & 2 deletions client/app/components/queries/schema-browser.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
<div class="schema-container">
<div class="schema-control">
<input type="text" placeholder="Search schema..." class="form-control" ng-model="$ctrl.schemaFilter">
<input type="text" placeholder="Search schema..." class="form-control" ng-model="$ctrl.schemaFilter" ng-model-options="{ debounce: 500 }">
<button class="btn btn-default"
title="Refresh Schema"
ng-click="$ctrl.onRefresh()">
<span class="zmdi zmdi-refresh"></span>
</button>

<button class="btn btn-default"
title="Toggle Versioned Tables"
ng-click="$ctrl.flipToggleVersionedTables($ctrl.versionToggle, $ctrl.tabletogglestring)"
ng-if="$ctrl.tabletogglestring && $ctrl.tabletogglestring != ''"
>
<span class="fa " ng-class="{'fa-toggle-on': $ctrl.versionToggle == true, 'fa-toggle-off': !$ctrl.versionToggle}">
<input type="checkbox" id="versioned-tables-toggle" ng-model="$ctrl.versionToggle" hidden/>
</span>
</button>
</div>

<div class="schema-browser" vs-repeat vs-size="$ctrl.getSize(table)">
<div ng-repeat="table in $ctrl.schema | filter:$ctrl.schemaFilter track by table.name">
<div ng-repeat="table in $ctrl.schema | filter:$ctrl.schemaFilter | filter:'!'+$ctrl.versionFilter">
<div class="table-name" ng-click="$ctrl.showTable(table)">
<i class="fa fa-table"></i>
<strong>
Expand Down
15 changes: 15 additions & 0 deletions client/app/components/queries/schema-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import template from './schema-browser.html';
function SchemaBrowserCtrl($scope) {
'ngInject';

this.versionToggle = false;
this.versionFilter = 'abcdefghijklmnop';

this.showTable = (table) => {
table.collapsed = !table.collapsed;
$scope.$broadcast('vsRepeatTrigger');
Expand All @@ -17,12 +20,24 @@ function SchemaBrowserCtrl($scope) {

return size;
};

this.flipToggleVersionedTables = (versionToggle, toggleString) => {
if (versionToggle === false) {
this.versionToggle = true;
this.versionFilter = toggleString;
} else {
this.versionToggle = false;
this.versionFilter = 'abcdefghijklmnop';
}
};
}

const SchemaBrowser = {
bindings: {
schema: '<',
tabletogglestring: '<',
onRefresh: '&',
flipToggleVersionedTables: '&',
},
controller: SchemaBrowserCtrl,
template,
Expand Down
Loading