diff --git a/caravel/assets/images/tutorial/add_db.png b/caravel/assets/images/tutorial/add_db.png deleted file mode 100644 index 728234376b4c..000000000000 Binary files a/caravel/assets/images/tutorial/add_db.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/bar.png b/caravel/assets/images/viz_thumbnails/bar.png deleted file mode 100644 index b41014788a2f..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/bar.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/big_number.png b/caravel/assets/images/viz_thumbnails/big_number.png deleted file mode 100644 index 2420f25e9fbb..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/big_number.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/bubble.png b/caravel/assets/images/viz_thumbnails/bubble.png deleted file mode 100644 index 08de51f4ff6e..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/bubble.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/filter.png b/caravel/assets/images/viz_thumbnails/filter.png deleted file mode 100644 index 209259c699a2..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/filter.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/force_directed.png b/caravel/assets/images/viz_thumbnails/force_directed.png deleted file mode 100644 index 1cc7ce95799a..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/force_directed.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/line.png b/caravel/assets/images/viz_thumbnails/line.png deleted file mode 100644 index 67f8fe887f09..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/line.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/percent_change.png b/caravel/assets/images/viz_thumbnails/percent_change.png deleted file mode 100644 index 00b0a7c515f2..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/percent_change.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/pie.png b/caravel/assets/images/viz_thumbnails/pie.png deleted file mode 100644 index ab76749f74cc..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/pie.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/pivot_table.png b/caravel/assets/images/viz_thumbnails/pivot_table.png deleted file mode 100644 index 37f86af079c2..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/pivot_table.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/sankey.png b/caravel/assets/images/viz_thumbnails/sankey.png deleted file mode 100644 index 981cdba8708f..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/sankey.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/stacked.png b/caravel/assets/images/viz_thumbnails/stacked.png deleted file mode 100644 index 86f1080cda5b..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/stacked.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/sunburst.png b/caravel/assets/images/viz_thumbnails/sunburst.png deleted file mode 100644 index f155363aad48..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/sunburst.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/table.png b/caravel/assets/images/viz_thumbnails/table.png deleted file mode 100644 index a3dd1bee98dd..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/table.png and /dev/null differ diff --git a/caravel/assets/images/viz_thumbnails/word_cloud.png b/caravel/assets/images/viz_thumbnails/word_cloud.png deleted file mode 100644 index 03936e1253d5..000000000000 Binary files a/caravel/assets/images/viz_thumbnails/word_cloud.png and /dev/null differ diff --git a/caravel/assets/javascripts/modules/caravel.js b/caravel/assets/javascripts/modules/caravel.js index 88c11e7967fd..07059b20bd16 100644 --- a/caravel/assets/javascripts/modules/caravel.js +++ b/caravel/assets/javascripts/modules/caravel.js @@ -272,9 +272,13 @@ var px = (function () { $('.query-and-save button').removeAttr('disabled'); always(data); }, - error: function (msg) { + error: function (error) { + var msg = error.responseText; token.find("img.loading").hide(); var err = '
' + msg + '
'; + if (error.getResponseHeader("Caravel-Exception") === "NoResultsException") { + err = '
' + msg + '
'; + } container.html(err); container.show(); $('span.query').removeClass('disabled'); diff --git a/caravel/assets/visualizations/big_number.js b/caravel/assets/visualizations/big_number.js index 676c9b46c225..6d4dc62b3500 100644 --- a/caravel/assets/visualizations/big_number.js +++ b/caravel/assets/visualizations/big_number.js @@ -13,7 +13,7 @@ function bigNumberVis(slice) { d3.json(slice.jsonEndpoint(), function (error, payload) { //Define the percentage bounds that define color from red to green if (error !== null) { - slice.error(error.responseText); + slice.error(error); return ''; } var fd = payload.form_data; diff --git a/caravel/assets/visualizations/directed_force.js b/caravel/assets/visualizations/directed_force.js index a0252067e636..eb1c7703b3ce 100644 --- a/caravel/assets/visualizations/directed_force.js +++ b/caravel/assets/visualizations/directed_force.js @@ -16,7 +16,7 @@ function directedForceVis(slice) { d3.json(slice.jsonEndpoint(), function (error, json) { if (error !== null) { - slice.error(error.responseText); + slice.error(error); return ''; } var links = json.data; diff --git a/caravel/assets/visualizations/heatmap.js b/caravel/assets/visualizations/heatmap.js index 55ec7ef46efd..7209b0b71051 100644 --- a/caravel/assets/visualizations/heatmap.js +++ b/caravel/assets/visualizations/heatmap.js @@ -23,7 +23,7 @@ function heatmapVis(slice) { d3.json(slice.jsonEndpoint(), function (error, payload) { var matrix = {}; if (error) { - slice.error(error.responseText); + slice.error(error); return ''; } var fd = payload.form_data; diff --git a/caravel/assets/visualizations/sankey.js b/caravel/assets/visualizations/sankey.js index 4c85ff1414ba..e1230b595f33 100644 --- a/caravel/assets/visualizations/sankey.js +++ b/caravel/assets/visualizations/sankey.js @@ -36,7 +36,7 @@ function sankeyVis(slice) { d3.json(slice.jsonEndpoint(), function (error, json) { if (error !== null) { - slice.error(error.responseText); + slice.error(error); return ''; } var links = json.data; diff --git a/caravel/assets/visualizations/sunburst.js b/caravel/assets/visualizations/sunburst.js index 25df68aa2d6e..f71b5e983022 100644 --- a/caravel/assets/visualizations/sunburst.js +++ b/caravel/assets/visualizations/sunburst.js @@ -54,7 +54,7 @@ function sunburstVis(slice) { d3.json(slice.jsonEndpoint(), function (error, rawData) { if (error !== null) { - slice.error(error.responseText); + slice.error(error); return ''; } diff --git a/caravel/assets/visualizations/word_cloud.js b/caravel/assets/visualizations/word_cloud.js index 7efd7c03193c..7f72b9a2072f 100644 --- a/caravel/assets/visualizations/word_cloud.js +++ b/caravel/assets/visualizations/word_cloud.js @@ -8,7 +8,7 @@ function wordCloudChart(slice) { function refresh() { d3.json(slice.jsonEndpoint(), function (error, json) { if (error !== null) { - slice.error(error.responseText); + slice.error(error); return ''; } var data = json.data; diff --git a/caravel/assets/visualizations/world_map.js b/caravel/assets/visualizations/world_map.js index 87f09b30be38..b5ee11c3a084 100644 --- a/caravel/assets/visualizations/world_map.js +++ b/caravel/assets/visualizations/world_map.js @@ -17,7 +17,7 @@ function worldMapChart(slice) { var fd = json.form_data; if (error !== null) { - slice.error(error.responseText); + slice.error(error); return ''; } var ext = d3.extent(json.data, function (d) { diff --git a/caravel/models.py b/caravel/models.py index ed95467e50e9..89736a9b9976 100644 --- a/caravel/models.py +++ b/caravel/models.py @@ -576,7 +576,20 @@ def query( # sqla select_exprs += [timestamp_grain] groupby_exprs += [timestamp_grain] - tf = '%Y-%m-%d %H:%M:%S.%f' + # UGLY: I guess correct way is to delegate on SQLAlchemy dialect + # UPDATE: Datetime depends on each dialect and I haven't found an easy way to manage + # Maybe we can allow user to define its custome format at Database definition + def get_dtformat(type): + if type == 'SMALLDATETIME' or type == 'DATETIME': + return '%Y-%m-%d %H:%M:%S' + if type == 'DATE': + return '%Y-%m-%d' + if type == 'TIME': + return '%H:%M:%S' + return '%Y-%m-%d %H:%M:%S.%f' + + tf = get_dtformat(cols[granularity].type or 'DATE') + time_filter = [ timestamp >= from_dttm.strftime(tf), timestamp <= to_dttm.strftime(tf), diff --git a/caravel/utils.py b/caravel/utils.py index 891964e0d523..492b421e8107 100644 --- a/caravel/utils.py +++ b/caravel/utils.py @@ -224,3 +224,7 @@ def readfile(filepath): with open(filepath) as f: content = f.read() return content + + +class NoResultsException(Exception): + pass diff --git a/caravel/views.py b/caravel/views.py index 738f80f9d50d..c9dfba1de8d4 100644 --- a/caravel/views.py +++ b/caravel/views.py @@ -27,6 +27,7 @@ from wtforms.validators import ValidationError from caravel import appbuilder, db, models, viz, utils, app, sm, ascii_art +from caravel.utils import NoResultsException config = app.config log_this = models.Log.log_this @@ -40,12 +41,12 @@ def validate_json(form, field): # noqa raise ValidationError("json isn't valid") -def generate_download_headers(extension): +def generate_download_headers(extension, headers=None): filename = datetime.now().strftime("%Y%m%d_%H%M%S") content_disp = "attachment; filename={}.{}".format(filename, extension) - headers = { - "Content-Disposition": content_disp, - } + if headers is None: + headers = {} + headers["Content-Disposition"] = content_disp return headers @@ -481,18 +482,24 @@ def explore(self, datasource_type, datasource_id): slice=slc) if request.args.get("json") == "true": status = 200 + headers = {} try: payload = obj.get_json() except Exception as e: + t = type(e) + headers["Caravel-Exception"] = t.__name__ logging.exception(e) - if config.get("DEBUG"): - raise e - payload = str(e) + if t is NoResultsException: + payload = "No results" + else: + if config.get("DEBUG"): + raise e + payload = str(e) status = 500 resp = Response( payload, status=status, - headers=generate_download_headers("json"), + headers=generate_download_headers("json", headers), mimetype="application/json") return resp elif request.args.get("csv") == "true": diff --git a/caravel/viz.py b/caravel/viz.py index 9592235e36b4..efb867fe673d 100644 --- a/caravel/viz.py +++ b/caravel/viz.py @@ -26,6 +26,9 @@ from caravel import app, utils, cache from caravel.forms import FormFactory + +from caravel.utils import NoResultsException + config = app.config @@ -139,8 +142,10 @@ def get_df(self, query_obj=None): self.results = self.datasource.query(**query_obj) self.query = self.results.query df = self.results.df - if df is None or df.empty: - raise Exception("No data, review your incantations!") + if df is None: + raise Exception("Error retrieving data") + elif df.empty: + raise NoResultsException() else: if 'timestamp' in df.columns: df.timestamp = pd.to_datetime(df.timestamp, utc=False) diff --git a/docs/gallery.rst b/docs/gallery.rst deleted file mode 100644 index 16d9af24e490..000000000000 --- a/docs/gallery.rst +++ /dev/null @@ -1,45 +0,0 @@ -Gallery -======= - -.. image:: _static/img/viz_thumbnails/line.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/bubble.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/table.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/pie.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/bar.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/sankey.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/word_cloud.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/filter.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/pivot_table.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/force_directed.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/percent_change.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/sunburst.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/stacked.png - :scale: 25 % - -.. image:: _static/img/viz_thumbnails/big_number.png - :scale: 25 % - diff --git a/tests/core_tests.py b/tests/core_tests.py index 430877b7e353..f4258018b187 100644 --- a/tests/core_tests.py +++ b/tests/core_tests.py @@ -3,6 +3,7 @@ import os import unittest +import sys from flask import escape import caravel @@ -58,7 +59,10 @@ def test_slices(self): slc.viz.json_endpoint, ] for url in urls: - self.client.get(url) + try: + self.client.get(url) + except Exception: + raise ValueError(url, None, sys.exc_info()[2]) def test_csv(self): self.client.get('/caravel/explore/table/1/?viz_type=table&granularity=ds&since=100+years&until=now&metrics=count&groupby=name&limit=50&show_brush=y&show_brush=false&show_legend=y&show_brush=false&rich_tooltip=y&show_brush=false&show_brush=false&show_brush=false&show_brush=false&y_axis_format=&x_axis_showminmax=y&show_brush=false&line_interpolation=linear&rolling_type=None&rolling_periods=&time_compare=&num_period_compare=&where=&having=&flt_col_0=gender&flt_op_0=in&flt_eq_0=&flt_col_0=gender&flt_op_0=in&flt_eq_0=&slice_id=14&slice_name=Boys&collapsed_fieldsets=&action=&datasource_name=birth_names&datasource_id=1&datasource_type=table&previous_viz_type=line&csv=true')