Skip to content

Commit

Permalink
[SQL Lab] Adding DB options for SQL LAb (#1054)
Browse files Browse the repository at this point in the history
* [SQL Lab] Adding DB options for SQL LAb

each db can be exposed or not in SQL Lab
CTAS is an option
target_schema placeholder (not hooked yet, but would force the CTAS to
target a specific schema)

* Addressing comments
  • Loading branch information
mistercrunch committed Sep 1, 2016
1 parent 1667d15 commit 4b77710
Show file tree
Hide file tree
Showing 16 changed files with 170 additions and 66 deletions.
4 changes: 3 additions & 1 deletion .codeclimate.yml
@@ -1,14 +1,16 @@
engines:
csslint:
enabled: false
duplication:
enabled: false
eslint:
enabled: true
config:
config: caravel/assets/.eslintrc
pep8:
enabled: true
fixme:
enabled: true
enabled: false
radon:
enabled: true
ratings:
Expand Down
6 changes: 5 additions & 1 deletion caravel/assets/javascripts/SqlLab/actions.js
Expand Up @@ -16,7 +16,7 @@ export const QUERY_EDITOR_SET_SCHEMA = 'QUERY_EDITOR_SET_SCHEMA';
export const QUERY_EDITOR_SET_TITLE = 'QUERY_EDITOR_SET_TITLE';
export const QUERY_EDITOR_SET_AUTORUN = 'QUERY_EDITOR_SET_AUTORUN';
export const QUERY_EDITOR_SET_SQL = 'QUERY_EDITOR_SET_SQL';
export const SET_WORKSPACE_DB = 'SET_WORKSPACE_DB';
export const SET_DATABASES = 'SET_DATABASES';
export const ADD_WORKSPACE_QUERY = 'ADD_WORKSPACE_QUERY';
export const REMOVE_WORKSPACE_QUERY = 'REMOVE_WORKSPACE_QUERY';
export const SET_ACTIVE_QUERY_EDITOR = 'SET_ACTIVE_QUERY_EDITOR';
Expand All @@ -28,6 +28,10 @@ export function resetState() {
return { type: RESET_STATE };
}

export function setDatabases(databases) {
return { type: SET_DATABASES, databases };
}

export function addQueryEditor(queryEditor) {
return { type: ADD_QUERY_EDITOR, queryEditor };
}
Expand Down
Expand Up @@ -15,7 +15,7 @@ class QueryAutoRefresh extends React.Component {
}
startTimer() {
if (!(this.timer)) {
this.timer = setInterval(this.stopwatch.bind(this), 5000);
this.timer = setInterval(this.stopwatch.bind(this), 1000);
}
}
stopTimer() {
Expand Down
22 changes: 12 additions & 10 deletions caravel/assets/javascripts/SqlLab/components/SouthPane.jsx
Expand Up @@ -19,16 +19,18 @@ const SouthPane = function (props) {
results = <Alert bsStyle="info">Run a query to display results here</Alert>;
}
return (
<Tabs bsStyle="tabs">
<Tab title="Results" eventKey={1}>
<div style={{ overflow: 'auto' }}>
{results}
</div>
</Tab>
<Tab title="Query History" eventKey={2}>
<QueryHistory />
</Tab>
</Tabs>
<div className="SouthPane">
<Tabs bsStyle="tabs">
<Tab title="Results" eventKey={1}>
<div style={{ overflow: 'auto' }}>
{results}
</div>
</Tab>
<Tab title="Query History" eventKey={2}>
<QueryHistory />
</Tab>
</Tabs>
</div>
);
};

Expand Down
49 changes: 28 additions & 21 deletions caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
Expand Up @@ -207,31 +207,37 @@ class SqlEditor extends React.Component {
</OverlayTrigger>
);
}
let ctasControls;
if (this.props.database && this.props.database.allow_ctas) {
ctasControls = (
<FormGroup>
<InputGroup>
<FormControl
type="text"
bsSize="small"
className="input-sm"
placeholder="new table name"
onChange={this.ctasChanged.bind(this)}
/>
<InputGroup.Button>
<Button
bsSize="small"
disabled={this.state.ctas.length === 0}
onClick={this.createTableAs.bind(this)}
>
<i className="fa fa-table" /> CTAS
</Button>
</InputGroup.Button>
</InputGroup>
</FormGroup>
);
}
const editorBottomBar = (
<div className="sql-toolbar clearfix">
<div className="pull-left">
<Form inline>
{runButtons}
<FormGroup>
<InputGroup>
<FormControl
type="text"
bsSize="small"
className="input-sm"
placeholder="new table name"
onChange={this.ctasChanged.bind(this)}
/>
<InputGroup.Button>
<Button
bsSize="small"
disabled={this.state.ctas.length === 0}
onClick={this.createTableAs.bind(this)}
>
<i className="fa fa-table" /> CTAS
</Button>
</InputGroup.Button>
</InputGroup>
</FormGroup>
{ctasControls}
</Form>
</div>
<div className="pull-right">
Expand Down Expand Up @@ -276,9 +282,10 @@ class SqlEditor extends React.Component {
}

SqlEditor.propTypes = {
queryEditor: React.PropTypes.object,
actions: React.PropTypes.object,
database: React.PropTypes.object,
latestQuery: React.PropTypes.object,
queryEditor: React.PropTypes.object,
};

SqlEditor.defaultProps = {
Expand Down
21 changes: 17 additions & 4 deletions caravel/assets/javascripts/SqlLab/components/SqlEditorLeft.jsx
Expand Up @@ -6,6 +6,7 @@ import { connect } from 'react-redux';
import * as Actions from '../actions';
import shortid from 'shortid';
import Select from 'react-select';
import { Button } from 'react-bootstrap';
import TableElement from './TableElement';


Expand All @@ -26,6 +27,9 @@ class SqlEditorTopToolbar extends React.Component {
this.fetchSchemas();
this.fetchTables();
}
resetState() {
this.props.actions.resetState();
}
fetchTables(dbId, schema) {
const actualDbId = dbId || this.props.queryEditor.dbId;
if (actualDbId) {
Expand Down Expand Up @@ -73,11 +77,17 @@ class SqlEditorTopToolbar extends React.Component {
}
fetchDatabaseOptions() {
this.setState({ databaseLoading: true });
const url = '/databaseasync/api/read';
const url = '/databaseasync/api/read?_flt_0_expose_in_sqllab=1';
$.get(url, (data) => {
const options = data.result.map((db) => ({ value: db.id, label: db.database_name }));
this.props.actions.setDatabases(data.result);
this.setState({ databaseOptions: options });
this.setState({ databaseLoading: false });

// Auto select if only one option
if (options.length === 1) {
this.changeDb(options[0]);
}
});
}
closePopover(ref) {
Expand Down Expand Up @@ -112,7 +122,7 @@ class SqlEditorTopToolbar extends React.Component {
<div>
<Select
name="select-db"
placeholder="[Database]"
placeholder={`Select a database (${this.state.databaseOptions.length})`}
options={this.state.databaseOptions}
value={this.props.queryEditor.dbId}
isLoading={this.state.databaseLoading}
Expand All @@ -123,7 +133,7 @@ class SqlEditorTopToolbar extends React.Component {
<div className="m-t-5">
<Select
name="select-schema"
placeholder="[Schema]"
placeholder={`Select a schema (${this.state.schemaOptions.length})`}
options={this.state.schemaOptions}
value={this.props.queryEditor.schema}
isLoading={this.state.schemaLoading}
Expand All @@ -136,7 +146,7 @@ class SqlEditorTopToolbar extends React.Component {
name="select-table"
ref="selectTable"
isLoading={this.state.tableLoading}
placeholder="Add a table"
placeholder={`Add a table (${this.state.tableOptions.length})`}
autosize={false}
value={this.state.tableName}
onChange={this.changeTable.bind(this)}
Expand All @@ -149,6 +159,9 @@ class SqlEditorTopToolbar extends React.Component {
<TableElement table={table} queryEditor={this.props.queryEditor} />
))}
</div>
<Button bsSize="small" bsStyle="danger" onClick={this.resetState.bind(this)}>
<i className="fa fa-bomb" /> Reset State
</Button>
</div>
);
}
Expand Down
Expand Up @@ -48,6 +48,8 @@ class QueryEditors extends React.Component {
render() {
const editors = this.props.queryEditors.map((qe, i) => {
let latestQuery = this.props.queries[qe.latestQueryId];
const database = this.props.databases[qe.dbId];

const state = (latestQuery) ? latestQuery.state : '';
const tabTitle = (
<div>
Expand Down Expand Up @@ -76,6 +78,7 @@ class QueryEditors extends React.Component {
<SqlEditor
queryEditor={qe}
latestQuery={latestQuery}
database={database}
/>
</div>
</div>
Expand All @@ -95,6 +98,7 @@ class QueryEditors extends React.Component {
}
QueryEditors.propTypes = {
actions: React.PropTypes.object,
databases: React.PropTypes.object,
queries: React.PropTypes.object,
queryEditors: React.PropTypes.array,
tabHistory: React.PropTypes.array,
Expand All @@ -106,6 +110,7 @@ QueryEditors.defaultProps = {

function mapStateToProps(state) {
return {
databases: state.databases,
queryEditors: state.queryEditors,
queries: state.queries,
tabHistory: state.tabHistory,
Expand Down
Expand Up @@ -49,7 +49,7 @@ class TableElement extends React.Component {
{this.props.table.columns.map((col) => (
<div className="clearfix">
<span className="pull-left m-l-5">{col.name}</span>
<span className="pull-right">{col.type}</span>
<span className="pull-right text-muted">{col.type}</span>
</div>
))}
<hr />
Expand Down
4 changes: 4 additions & 0 deletions caravel/assets/javascripts/SqlLab/main.css
Expand Up @@ -246,3 +246,7 @@ div.tablePopover:hover {
opacity: 1;
transition: visibility 0s, opacity 0.3s linear;
}

.SouthPane .tab-content {
padding-top: 10px;
}
8 changes: 8 additions & 0 deletions caravel/assets/javascripts/SqlLab/reducers.js
Expand Up @@ -15,6 +15,7 @@ const defaultQueryEditor = {
export const initialState = {
alerts: [],
queries: {},
databases: {},
queryEditors: [defaultQueryEditor],
tabHistory: [defaultQueryEditor.id],
tables: [],
Expand Down Expand Up @@ -166,6 +167,13 @@ export const sqlLabReducer = function (state, action) {
[actions.ADD_ALERT]() {
return addToArr(state, 'alerts', action.alert);
},
[actions.SET_DATABASES]() {
const databases = {};
action.databases.forEach((db) => {
databases[db.id] = db;
});
return Object.assign({}, state, { databases });
},
[actions.REMOVE_ALERT]() {
return removeFromArr(state, 'alerts', action.alert);
},
Expand Down
27 changes: 8 additions & 19 deletions caravel/data/__init__.py
Expand Up @@ -14,6 +14,7 @@
import pandas as pd
from sqlalchemy import String, DateTime, Date, Float, BigInteger

import caravel
from caravel import app, db, models, utils

# Shortcuts
Expand All @@ -27,18 +28,6 @@
DATA_FOLDER = os.path.join(config.get("BASE_DIR"), 'data')


def get_or_create_db(session):
print("Creating database reference")
dbobj = session.query(DB).filter_by(database_name='main').first()
if not dbobj:
dbobj = DB(database_name="main")
print(config.get("SQLALCHEMY_DATABASE_URI"))
dbobj.sqlalchemy_uri = config.get("SQLALCHEMY_DATABASE_URI")
session.add(dbobj)
session.commit()
return dbobj


def merge_slice(slc):
o = db.session.query(Slice).filter_by(slice_name=slc.slice_name).first()
if o:
Expand Down Expand Up @@ -76,7 +65,7 @@ def load_energy():
tbl = TBL(table_name=tbl_name)
tbl.description = "Energy consumption"
tbl.is_featured = True
tbl.database = get_or_create_db(db.session)
tbl.database = utils.get_or_create_main_db(caravel)
db.session.merge(tbl)
db.session.commit()
tbl.fetch_metadata()
Expand Down Expand Up @@ -202,7 +191,7 @@ def load_world_bank_health_n_pop():
tbl.description = utils.readfile(os.path.join(DATA_FOLDER, 'countries.md'))
tbl.main_dttm_col = 'year'
tbl.is_featured = True
tbl.database = get_or_create_db(db.session)
tbl.database = utils.get_or_create_main_db(caravel)
db.session.merge(tbl)
db.session.commit()
tbl.fetch_metadata()
Expand Down Expand Up @@ -593,7 +582,7 @@ def load_birth_names():
if not obj:
obj = TBL(table_name='birth_names')
obj.main_dttm_col = 'ds'
obj.database = get_or_create_db(db.session)
obj.database = utils.get_or_create_main_db(caravel)
obj.is_featured = True
db.session.merge(obj)
db.session.commit()
Expand Down Expand Up @@ -841,7 +830,7 @@ def load_unicode_test_data():
if not obj:
obj = TBL(table_name='unicode_test')
obj.main_dttm_col = 'date'
obj.database = get_or_create_db(db.session)
obj.database = utils.get_or_create_main_db(caravel)
obj.is_featured = False
db.session.merge(obj)
db.session.commit()
Expand Down Expand Up @@ -920,7 +909,7 @@ def load_random_time_series_data():
if not obj:
obj = TBL(table_name='random_time_series')
obj.main_dttm_col = 'ds'
obj.database = get_or_create_db(db.session)
obj.database = utils.get_or_create_main_db(caravel)
obj.is_featured = False
db.session.merge(obj)
db.session.commit()
Expand Down Expand Up @@ -988,7 +977,7 @@ def load_long_lat_data():
if not obj:
obj = TBL(table_name='long_lat')
obj.main_dttm_col = 'date'
obj.database = get_or_create_db(db.session)
obj.database = utils.get_or_create_main_db(caravel)
obj.is_featured = False
db.session.merge(obj)
db.session.commit()
Expand Down Expand Up @@ -1052,7 +1041,7 @@ def load_multiformat_time_series_data():
if not obj:
obj = TBL(table_name='multiformat_time_series')
obj.main_dttm_col = 'ds'
obj.database = get_or_create_db(db.session)
obj.database = utils.get_or_create_main_db(caravel)
obj.is_featured = False
dttm_and_expr_dict = {
'ds': [None, None],
Expand Down

0 comments on commit 4b77710

Please sign in to comment.