superset-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From maximebeauche...@apache.org
Subject [incubator-superset] branch master updated: [explore] add datasource metadata (#4104)
Date Tue, 02 Jan 2018 16:41:34 GMT
This is an automated email from the ASF dual-hosted git repository.

maximebeauchemin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 0a62082  [explore] add datasource metadata (#4104)
0a62082 is described below

commit 0a6208296e83f2b70116071e56cd6104b31a1e6e
Author: Maxime Beauchemin <maximebeauchemin@gmail.com>
AuthorDate: Tue Jan 2 08:41:27 2018 -0800

    [explore] add datasource metadata (#4104)
---
 .../assets/javascripts/components/ColumnOption.jsx |  10 +-
 .../components/controls/DatasourceControl.jsx      | 151 +++++++++++++++------
 .../explore/components/DatasourceControl_spec.jsx  |   6 +
 superset/assets/stylesheets/superset.less          |   3 +
 superset/connectors/base/models.py                 |   3 +-
 superset/connectors/druid/models.py                |   7 +
 superset/models/core.py                            |   7 +
 7 files changed, 140 insertions(+), 47 deletions(-)

diff --git a/superset/assets/javascripts/components/ColumnOption.jsx b/superset/assets/javascripts/components/ColumnOption.jsx
index c150937..f579126 100644
--- a/superset/assets/javascripts/components/ColumnOption.jsx
+++ b/superset/assets/javascripts/components/ColumnOption.jsx
@@ -5,9 +5,13 @@ import InfoTooltipWithTrigger from './InfoTooltipWithTrigger';
 
 const propTypes = {
   column: PropTypes.object.isRequired,
+  showType: PropTypes.bool,
+};
+const defaultProps = {
+  showType: false,
 };
 
-export default function ColumnOption({ column }) {
+export default function ColumnOption({ column, showType }) {
   return (
     <span>
       <span className="m-r-5 option-label">
@@ -29,6 +33,10 @@ export default function ColumnOption({ column }) {
           label={`expr-${column.column_name}`}
         />
       }
+      {showType &&
+        <span className="text-muted">{column.type}</span>
+      }
     </span>);
 }
 ColumnOption.propTypes = propTypes;
+ColumnOption.defaultProps = defaultProps;
diff --git a/superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx
b/superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx
index eb7a633..e63c807 100644
--- a/superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx
+++ b/superset/assets/javascripts/explore/components/controls/DatasourceControl.jsx
@@ -2,10 +2,15 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { Table } from 'reactable';
-import { Label, FormControl, Modal, OverlayTrigger, Tooltip } from 'react-bootstrap';
+import {
+  Row, Col, Collapse, Label, FormControl, Modal,
+  OverlayTrigger, Tooltip, Well,
+} from 'react-bootstrap';
 
 import ControlHeader from '../ControlHeader';
 import { t } from '../../../locales';
+import ColumnOption from '../../../components/ColumnOption';
+import MetricOption from '../../../components/MetricOption';
 
 const propTypes = {
   description: PropTypes.string,
@@ -27,11 +32,14 @@ export default class DatasourceControl extends React.PureComponent {
       showModal: false,
       filter: '',
       loading: true,
+      showDatasource: false,
     };
+    this.toggleShowDatasource = this.toggleShowDatasource.bind(this);
+    this.onChange = this.onChange.bind(this);
+    this.onEnterModal = this.onEnterModal.bind(this);
     this.toggleModal = this.toggleModal.bind(this);
     this.changeSearch = this.changeSearch.bind(this);
-    this.setSearchRef = this.setSearchRef.bind(this);
-    this.onEnterModal = this.onEnterModal.bind(this);
+    this.selectDatasource = this.selectDatasource.bind(this);
   }
   onChange(vizType) {
     this.props.onChange(vizType);
@@ -75,6 +83,9 @@ export default class DatasourceControl extends React.PureComponent {
   setSearchRef(searchRef) {
     this.searchRef = searchRef;
   }
+  toggleShowDatasource() {
+    this.setState({ showDatasource: !this.state.showDatasource });
+  }
   toggleModal() {
     this.setState({ showModal: !this.state.showModal });
   }
@@ -85,6 +96,79 @@ export default class DatasourceControl extends React.PureComponent {
     this.setState({ showModal: false });
     this.props.onChange(datasourceId);
   }
+  renderModal() {
+    return (
+      <Modal
+        show={this.state.showModal}
+        onHide={this.toggleModal}
+        onEnter={this.onEnterModal}
+        onExit={this.setSearchRef}
+        bsSize="lg"
+      >
+        <Modal.Header closeButton>
+          <Modal.Title>{t('Select a datasource')}</Modal.Title>
+        </Modal.Header>
+        <Modal.Body>
+          <div>
+            <FormControl
+              id="formControlsText"
+              inputRef={(ref) => { this.setSearchRef(ref); }}
+              type="text"
+              bsSize="sm"
+              value={this.state.filter}
+              placeholder={t('Search / Filter')}
+              onChange={this.changeSearch}
+            />
+          </div>
+          {this.state.loading &&
+            <img
+              className="loading"
+              alt="Loading..."
+              src="/static/assets/images/loading.gif"
+            />
+          }
+          {this.state.datasources &&
+            <Table
+              columns={['name', 'type', 'schema', 'connection', 'creator']}
+              className="table table-condensed"
+              data={this.state.datasources}
+              itemsPerPage={20}
+              filterable={['rawName', 'type', 'connection', 'schema', 'creator']}
+              filterBy={this.state.filter}
+              hideFilterInput
+            />
+          }
+        </Modal.Body>
+      </Modal>);
+  }
+  renderDatasource() {
+    const datasource = this.props.datasource;
+    return (
+      <div className="m-t-10">
+        <Well className="m-t-0">
+          <div className="m-b-10">
+            <Label>
+              <i className="fa fa-database" /> {datasource.database.backend}
+            </Label>
+            {` ${datasource.database.name} `}
+          </div>
+          <Row>
+            <Col md={6}>
+              <strong>Columns</strong>
+              {datasource.columns.map(col => (
+                <div key={col.column_name}><ColumnOption showType column={col} /></div>
+              ))}
+            </Col>
+            <Col md={6}>
+              <strong>Metrics</strong>
+              {datasource.metrics.map(m => (
+                <div key={m.metric_name}><MetricOption metric={m} /></div>
+              ))}
+            </Col>
+          </Row>
+        </Well>
+      </div>);
+  }
   render() {
     return (
       <div>
@@ -108,51 +192,28 @@ export default class DatasourceControl extends React.PureComponent {
           }
         >
           <a href={this.props.datasource.edit_url}>
-            <i className="fa fa-edit" />
+            <i className="fa fa-edit m-r-5" />
           </a>
         </OverlayTrigger>
-        <Modal
-          show={this.state.showModal}
-          onHide={this.toggleModal}
-          onEnter={this.onEnterModal}
-          onExit={this.setSearchRef}
-          bsSize="lg"
+        <OverlayTrigger
+          placement="right"
+          overlay={
+            <Tooltip id={'toggle-datasource-tooltip'}>
+              {t('Show datasource configuration')}
+            </Tooltip>
+          }
         >
-          <Modal.Header closeButton>
-            <Modal.Title>{t('Select a datasource')}</Modal.Title>
-          </Modal.Header>
-          <Modal.Body>
-            <div>
-              <FormControl
-                id="formControlsText"
-                inputRef={(ref) => { this.setSearchRef(ref); }}
-                type="text"
-                bsSize="sm"
-                value={this.state.filter}
-                placeholder={t('Search / Filter')}
-                onChange={this.changeSearch}
-              />
-            </div>
-            {this.state.loading &&
-              <img
-                className="loading"
-                alt="Loading..."
-                src="/static/assets/images/loading.gif"
-              />
-            }
-            {this.state.datasources &&
-              <Table
-                columns={['name', 'type', 'schema', 'connection', 'creator']}
-                className="table table-condensed"
-                data={this.state.datasources}
-                itemsPerPage={20}
-                filterable={['rawName', 'type', 'connection', 'schema', 'creator']}
-                filterBy={this.state.filter}
-                hideFilterInput
-              />
-            }
-          </Modal.Body>
-        </Modal>
+          <a href="#">
+            <i
+              className={`fa fa-${this.state.showDatasource ? 'minus' : 'plus'}-square m-r-5`}
+              onClick={this.toggleShowDatasource}
+            />
+          </a>
+        </OverlayTrigger>
+        <Collapse in={this.state.showDatasource}>
+          {this.renderDatasource()}
+        </Collapse>
+        {this.renderModal()}
       </div>);
   }
 }
diff --git a/superset/assets/spec/javascripts/explore/components/DatasourceControl_spec.jsx
b/superset/assets/spec/javascripts/explore/components/DatasourceControl_spec.jsx
index c46ded0..d206829 100644
--- a/superset/assets/spec/javascripts/explore/components/DatasourceControl_spec.jsx
+++ b/superset/assets/spec/javascripts/explore/components/DatasourceControl_spec.jsx
@@ -15,6 +15,12 @@ const defaultProps = {
     type: 'table',
     uid: '1__table',
     id: 1,
+    columns: [],
+    metrics: [],
+    database: {
+      backend: 'mysql',
+      name: 'main',
+    },
   },
   onChange: sinon.spy(),
 };
diff --git a/superset/assets/stylesheets/superset.less b/superset/assets/stylesheets/superset.less
index ae0be2c..14c7519 100644
--- a/superset/assets/stylesheets/superset.less
+++ b/superset/assets/stylesheets/superset.less
@@ -252,6 +252,9 @@ table.table-no-hover tr:hover {
 .m-t-10 {
     margin-top: 10px;
 }
+.m-b-10 {
+    margin-bottom: 10px;
+}
 .m-l-5 {
     margin-left: 5px;
 }
diff --git a/superset/connectors/base/models.py b/superset/connectors/base/models.py
index 9bead74..2057ea8 100644
--- a/superset/connectors/base/models.py
+++ b/superset/connectors/base/models.py
@@ -157,6 +157,7 @@ class BaseDatasource(AuditMixinNullable, ImportMixin):
         return {
             'all_cols': utils.choicify(self.column_names),
             'column_formats': self.column_formats,
+            'database': self.database.data,  # pylint: disable=no-member
             'edit_url': self.url,
             'filter_select': self.filter_select_enabled,
             'filterable_cols': utils.choicify(self.filterable_column_names),
@@ -256,7 +257,7 @@ class BaseColumn(AuditMixinNullable, ImportMixin):
     def data(self):
         attrs = (
             'column_name', 'verbose_name', 'description', 'expression',
-            'filterable', 'groupby', 'is_dttm')
+            'filterable', 'groupby', 'is_dttm', 'type')
         return {s: getattr(self, s) for s in attrs}
 
 
diff --git a/superset/connectors/druid/models.py b/superset/connectors/druid/models.py
index a1d9ec0..45d5d25 100644
--- a/superset/connectors/druid/models.py
+++ b/superset/connectors/druid/models.py
@@ -91,6 +91,13 @@ class DruidCluster(Model, AuditMixinNullable, ImportMixin):
     def __repr__(self):
         return self.verbose_name if self.verbose_name else self.cluster_name
 
+    @property
+    def data(self):
+        return {
+            'name': self.cluster_name,
+            'backend': 'druid',
+        }
+
     def get_pydruid_client(self):
         cli = PyDruid(
             'http://{0}:{1}/'.format(self.broker_host, self.broker_port),
diff --git a/superset/models/core.py b/superset/models/core.py
index 396db2d..f2ca42b 100644
--- a/superset/models/core.py
+++ b/superset/models/core.py
@@ -582,6 +582,13 @@ class Database(Model, AuditMixinNullable, ImportMixin):
         return self.verbose_name if self.verbose_name else self.database_name
 
     @property
+    def data(self):
+        return {
+            'name': self.database_name,
+            'backend': self.backend,
+        }
+
+    @property
     def unique_name(self):
         return self.database_name
 

-- 
To stop receiving notification emails like this one, please contact
['"commits@superset.apache.org" <commits@superset.apache.org>'].

Mime
View raw message