def get_data_response(self, command: ChartDataCommand, force_cached: bool = False) -> Response: try: result = command.run(force_cached=force_cached) except ChartDataCacheLoadError as exc: return self.response_422(message=exc.message) except ChartDataQueryFailedError as exc: return self.response_400(message=exc.message) result_format = result["query_context"].result_format if result_format == ChartDataResultFormat.CSV: # Verify user has permission to export CSV file if not security_manager.can_access("can_csv", "Superset"): return self.response_403() # return the first result data = result["queries"][0]["data"] return CsvResponse(data, headers=generate_download_headers("csv")) if result_format == ChartDataResultFormat.JSON: response_data = simplejson.dumps( {"result": result["queries"]}, default=json_int_dttm_ser, ignore_nan=True, ) resp = make_response(response_data, 200) resp.headers["Content-Type"] = "application/json; charset=utf-8" return resp return self.response_400( message=f"Unsupported result_format: {result_format}")
def load_chart_data_into_cache( job_metadata: Dict[str, Any], form_data: Dict[str, Any], ) -> None: from superset.charts.commands.data import ( ChartDataCommand, ) # load here due to circular imports with app.app_context(): # type: ignore try: ensure_user_is_set(job_metadata.get("user_id")) command = ChartDataCommand() command.set_query_context(form_data) result = command.run(cache=True) cache_key = result["cache_key"] result_url = f"/api/v1/chart/data/{cache_key}" async_query_manager.update_job( job_metadata, async_query_manager.STATUS_DONE, result_url=result_url, ) except Exception as exc: # TODO: QueryContext should support SIP-40 style errors error = exc.message if hasattr(exc, "message") else str(exc) # type: ignore # pylint: disable=no-member errors = [{"message": error}] async_query_manager.update_job(job_metadata, async_query_manager.STATUS_ERROR, errors=errors) raise exc return None
def _run_async(self, command: ChartDataCommand) -> Response: """ Execute command as an async query. """ # First, look for the chart query results in the cache. try: result = command.run(force_cached=True) except ChartDataCacheLoadError: result = None # type: ignore already_cached_result = result is not None # If the chart query has already been cached, return it immediately. if already_cached_result: return self.send_chart_response(result) # Otherwise, kick off a background job to run the chart query. # Clients will either poll or be notified of query completion, # at which point they will call the /data/<cache_key> endpoint # to retrieve the results. try: command.validate_async_request(request) except AsyncQueryTokenException: return self.response_401() result = command.run_async(g.user.get_id()) return self.response(202, **result)
def load_chart_data_into_cache( job_metadata: Dict[str, Any], form_data: Dict[str, Any], ) -> None: # pylint: disable=import-outside-toplevel from superset.charts.commands.data import ChartDataCommand try: ensure_user_is_set(job_metadata.get("user_id")) set_form_data(form_data) command = ChartDataCommand() command.set_query_context(form_data) result = command.run(cache=True) cache_key = result["cache_key"] result_url = f"/api/v1/chart/data/{cache_key}" async_query_manager.update_job( job_metadata, async_query_manager.STATUS_DONE, result_url=result_url, ) except SoftTimeLimitExceeded as ex: logger.warning( "A timeout occurred while loading chart data, error: %s", ex) raise ex except Exception as ex: # TODO: QueryContext should support SIP-40 style errors error = ex.message if hasattr(ex, "message") else str(ex) # type: ignore # pylint: disable=no-member errors = [{"message": error}] async_query_manager.update_job(job_metadata, async_query_manager.STATUS_ERROR, errors=errors) raise ex
def get_data_response(self, command: ChartDataCommand, force_cached: bool = False) -> Response: try: result = command.run(force_cached=force_cached) except ChartDataCacheLoadError as exc: return self.response_422(message=exc.message) except ChartDataQueryFailedError as exc: return self.response_400(message=exc.message) return self.send_chart_response(result)
def get_data_response(self, command: ChartDataCommand, force_cached: bool = False) -> Response: try: result = command.run(force_cached=force_cached) except ChartDataCacheLoadError as exc: return self.response_422(message=exc.message) except ChartDataQueryFailedError as exc: return self.response_400(message=exc.message) result_format = result["query_context"].result_format if result_format == ChartDataResultFormat.CSV: # return the first result data = result["queries"][0]["data"] return CsvResponse(data, headers=generate_download_headers("csv")) if result_format == ChartDataResultFormat.JSON: response_data = simplejson.dumps( {"result": result["queries"]}, default=json_int_dttm_ser, ignore_nan=True, ) resp = make_response(response_data, 200) resp.headers["Content-Type"] = "application/json; charset=utf-8" return resp if result_format == ChartDataResultFormat.XLSX: sio = BytesIO() df = pandas.DataFrame(result["queries"][0]["data"]) writer = pandas.ExcelWriter(sio, engine='xlsxwriter') df.to_excel(writer, sheet_name="Лист 1", index=None) writer.save() sio.seek(0) workbook = sio.getvalue() return CsvResponse(workbook, headers=generate_download_headers("xlsx")) return self.response_400( message=f"Unsupported result_format: {result_format}")
def data(self) -> Response: """ Takes a query context constructed in the client and returns payload data response for the given query. --- post: description: >- Takes a query context constructed in the client and returns payload data response for the given query. requestBody: description: >- A query context consists of a datasource from which to fetch data and one or many query objects. required: true content: application/json: schema: $ref: "#/components/schemas/ChartDataQueryContextSchema" responses: 200: description: Query result content: application/json: schema: $ref: "#/components/schemas/ChartDataResponseSchema" 202: description: Async job details content: application/json: schema: $ref: "#/components/schemas/ChartDataAsyncResponseSchema" 400: $ref: '#/components/responses/400' 401: $ref: '#/components/responses/401' 500: $ref: '#/components/responses/500' """ json_body = None if request.is_json: json_body = request.json elif request.form.get("form_data"): # CSV export submits regular form data try: json_body = json.loads(request.form["form_data"]) except (TypeError, json.JSONDecodeError): pass if json_body is None: return self.response_400(message=_("Request is not JSON")) try: command = ChartDataCommand() query_context = command.set_query_context(json_body) command.validate() except QueryObjectValidationError as error: return self.response_400(message=error.message) except ValidationError as error: return self.response_400( message=_("Request is incorrect: %(error)s", error=error.normalized_messages())) # TODO: support CSV, SQL query and other non-JSON types if (is_feature_enabled("GLOBAL_ASYNC_QUERIES") and query_context.result_format == ChartDataResultFormat.JSON and query_context.result_type == ChartDataResultType.FULL): # First, look for the chart query results in the cache. try: result = command.run(force_cached=True) except ChartDataCacheLoadError: result = None # type: ignore already_cached_result = result is not None # If the chart query has already been cached, return it immediately. if already_cached_result: return self.send_chart_response(result) # Otherwise, kick off a background job to run the chart query. # Clients will either poll or be notified of query completion, # at which point they will call the /data/<cache_key> endpoint # to retrieve the results. try: command.validate_async_request(request) except AsyncQueryTokenException: return self.response_401() result = command.run_async(g.user.get_id()) return self.response(202, **result) return self.get_data_response(command)