Skip to content

Commit

Permalink
[sqllab] add support for Jinja templating (apache#1426)
Browse files Browse the repository at this point in the history
* [sqllab] add support for Jinja templating

* Adressing comments

* Presto macros

* Progress

* Addressing coments
  • Loading branch information
mistercrunch authored Oct 26, 2016
1 parent 8c5e495 commit 5944643
Show file tree
Hide file tree
Showing 20 changed files with 444 additions and 65 deletions.
1 change: 1 addition & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ exclude_paths:
- "caravel/assets/node_modules/"
- "caravel/assets/javascripts/dist/"
- "caravel/migrations"
- "docs/"
109 changes: 79 additions & 30 deletions caravel/assets/javascripts/SqlLab/components/HighlightedSql.jsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,93 @@
import React from 'react';
import { Well } from 'react-bootstrap';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { github } from 'react-syntax-highlighter/dist/styles';
import ModalTrigger from '../../components/ModalTrigger';

const HighlightedSql = (props) => {
const sql = props.sql || '';
let lines = sql.split('\n');
if (lines.length >= props.maxLines) {
lines = lines.slice(0, props.maxLines);
lines.push('{...}');
const defaultProps = {
maxWidth: 50,
maxLines: 5,
shrink: false,
};

const propTypes = {
sql: React.PropTypes.string.isRequired,
rawSql: React.PropTypes.string,
maxWidth: React.PropTypes.number,
maxLines: React.PropTypes.number,
shrink: React.PropTypes.bool,
};

class HighlightedSql extends React.Component {
constructor(props) {
super(props);
this.state = {
modalBody: null,
};
}
let shownSql = sql;
if (props.shrink) {
shownSql = lines.map((line) => {
shrinkSql() {
const props = this.props;
const sql = props.sql || '';
let lines = sql.split('\n');
if (lines.length >= props.maxLines) {
lines = lines.slice(0, props.maxLines);
lines.push('{...}');
}
return lines.map((line) => {
if (line.length > props.maxWidth) {
return line.slice(0, props.maxWidth) + '{...}';
}
return line;
})
.join('\n');
}
return (
<div>
<SyntaxHighlighter language="sql" style={github}>
{shownSql}
</SyntaxHighlighter>
</div>
);
};

HighlightedSql.defaultProps = {
maxWidth: 60,
maxLines: 6,
shrink: false,
};

HighlightedSql.propTypes = {
sql: React.PropTypes.string,
maxWidth: React.PropTypes.number,
maxLines: React.PropTypes.number,
shrink: React.PropTypes.bool,
};
triggerNode() {
const props = this.props;
let shownSql = props.shrink ? this.shrinkSql(props.sql) : props.sql;
return (
<Well>
<SyntaxHighlighter language="sql" style={github}>
{shownSql}
</SyntaxHighlighter>
</Well>);
}
generateModal() {
const props = this.props;
let rawSql;
if (props.rawSql && props.rawSql !== this.props.sql) {
rawSql = (
<div>
<h4>Raw SQL</h4>
<SyntaxHighlighter language="sql" style={github}>
{props.rawSql}
</SyntaxHighlighter>
</div>
);
}
this.setState({
modalBody: (
<div>
<h4>Source SQL</h4>
<SyntaxHighlighter language="sql" style={github}>
{this.props.sql}
</SyntaxHighlighter>
{rawSql}
</div>
),
});
}
render() {
return (
<ModalTrigger
modalTitle="SQL"
triggerNode={this.triggerNode()}
modalBody={this.state.modalBody}
beforeOpen={this.generateModal.bind(this)}
/>
);
}
}
HighlightedSql.propTypes = propTypes;
HighlightedSql.defaultProps = defaultProps;

export default HighlightedSql;
5 changes: 2 additions & 3 deletions caravel/assets/javascripts/SqlLab/components/QueryTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,8 @@ class QueryTable extends React.Component {
</button>
);
q.started = moment(q.startDttm).format('HH:mm:ss');
const source = (q.ctas) ? q.executedSql : q.sql;
q.sql = (
<HighlightedSql sql={source} shrink maxWidth={100} />
<HighlightedSql sql={q.sql} rawSql={q.executedSql} shrink maxWidth={60} />
);
if (q.resultsKey) {
q.output = (
Expand Down Expand Up @@ -169,7 +168,7 @@ class QueryTable extends React.Component {
q.querylink = (
<div style={{ width: '100px' }}>
<a
href={this.getQueryLink(q.dbId, source)}
href={this.getQueryLink(q.dbId, q.sql)}
className="btn btn-primary btn-xs"
>
<i className="fa fa-external-link" />Open in SQL Editor
Expand Down
1 change: 1 addition & 0 deletions caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class SqlEditor extends React.Component {
sql: this.props.queryEditor.sql,
sqlEditorId: this.props.queryEditor.id,
tab: this.props.queryEditor.title,
schema: this.props.queryEditor.schema,
tempTableName: this.state.ctas,
runAsync,
ctas,
Expand Down
4 changes: 4 additions & 0 deletions caravel/assets/javascripts/SqlLab/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,7 @@ div.tablePopover:hover {
a.Link {
cursor: pointer;
}
.QueryTable .well {
padding: 3px 5px;
margin: 3px 5px;
}
8 changes: 4 additions & 4 deletions caravel/assets/javascripts/components/ModalTrigger.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import cx from 'classnames';
const propTypes = {
triggerNode: PropTypes.node.isRequired,
modalTitle: PropTypes.node.isRequired,
modalBody: PropTypes.node.isRequired,
modalBody: PropTypes.node, // not required because it can be generated by beforeOpen
beforeOpen: PropTypes.func,
onExit: PropTypes.func,
isButton: PropTypes.bool,
Expand Down Expand Up @@ -46,8 +46,8 @@ export default class ModalTrigger extends React.Component {
'btn btn-default btn-sm': this.props.isButton,
});
return (
<a href="#" className={classNames} onClick={this.open}>
{this.props.triggerNode}
<span className={classNames} onClick={this.open} style={{ cursor: 'pointer' }}>
{this.props.triggerNode}
<Modal
show={this.state.showModal}
onHide={this.close}
Expand All @@ -62,7 +62,7 @@ export default class ModalTrigger extends React.Component {
{this.props.modalBody}
</Modal.Body>
</Modal>
</a>
</span>
);
}
}
Expand Down
23 changes: 15 additions & 8 deletions caravel/assets/spec/javascripts/sqllab/HighlightedSql_spec.jsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
import React from 'react';
import HighlightedSql from '../../../javascripts/SqlLab/components/HighlightedSql';
import ModalTrigger from '../../../javascripts/components/ModalTrigger';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { shallow } from 'enzyme';
import { mount, shallow } from 'enzyme';
import { describe, it } from 'mocha';
import { expect } from 'chai';


describe('HighlightedSql', () => {
const sql = "SELECT * FROM test WHERE something='fkldasjfklajdslfkjadlskfjkldasjfkladsjfkdjsa'";
it('renders', () => {
expect(React.isValidElement(<HighlightedSql />)).to.equal(true);
});
it('renders with props', () => {
expect(React.isValidElement(<HighlightedSql sql={sql} />))
.to.equal(true);
});
it('renders a SyntaxHighlighter', () => {
it('renders a ModalTrigger', () => {
const wrapper = shallow(<HighlightedSql sql={sql} />);
expect(wrapper.find(SyntaxHighlighter)).to.have.length(1);
expect(wrapper.find(ModalTrigger)).to.have.length(1);
});
it('renders a SyntaxHighlighter while using shrink', () => {
it('renders a ModalTrigger while using shrink', () => {
const wrapper = shallow(<HighlightedSql sql={sql} shrink maxWidth={20} />);
expect(wrapper.find(SyntaxHighlighter)).to.have.length(1);
expect(wrapper.find(ModalTrigger)).to.have.length(1);
});
it('renders two SyntaxHighlighter in modal', () => {
const wrapper = mount(
<HighlightedSql sql={sql} rawSql="SELECT * FORM foo" shrink maxWidth={5} />);
const well = wrapper.find('.well');
expect(well).to.have.length(1);
well.simulate('click');
const modalBody = mount(wrapper.state().modalBody);
expect(modalBody.find(SyntaxHighlighter)).to.have.length(2);
});
});
6 changes: 6 additions & 0 deletions caravel/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,12 @@ class CeleryConfig(object):
# in SQL Lab by using the "Run Async" button/feature
RESULTS_BACKEND = None

# A dictionary of items that gets merged into the Jinja context for
# SQL Lab. The existing context gets updated with this dictionary,
# meaning values for existing keys get overwritten by the content of this
# dictionary.
JINJA_CONTEXT_ADDONS = {}

try:
from caravel_config import * # noqa
except ImportError:
Expand Down
7 changes: 3 additions & 4 deletions caravel/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
INFER_COL_TYPES_SAMPLE_SIZE = 100


# http://pandas.pydata.org/pandas-docs/stable/internals.html#
# subclassing-pandas-data-structures
class CaravelDataFrame(object):
def __init__(self, df):
self.__df = df.where((pd.notnull(df)), None)
Expand Down Expand Up @@ -91,13 +89,14 @@ def datetime_conversion_rate(data_series):


def is_date(dtype):
return dtype.name.startswith('datetime')
if dtype.name:
return dtype.name.startswith('datetime')


def is_dimension(dtype, column_name):
if is_id(column_name):
return False
return dtype == np.object or dtype == np.bool
return dtype.name in ('object', 'bool')


def is_id(column_name):
Expand Down
Loading

0 comments on commit 5944643

Please sign in to comment.