def _get_screenshots(self) -> List[bytes]: """ Get chart or dashboard screenshots :raises: ReportScheduleScreenshotFailedError """ url = self._get_url() user = self._get_user() if self._report_schedule.chart: screenshot: Union[ ChartScreenshot, DashboardScreenshot] = ChartScreenshot( url, self._report_schedule.chart.digest, window_size=app.config["WEBDRIVER_WINDOW"]["slice"], thumb_size=app.config["WEBDRIVER_WINDOW"]["slice"], ) else: screenshot = DashboardScreenshot( url, self._report_schedule.dashboard.digest, window_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], thumb_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], ) try: image = screenshot.get_screenshot(user=user) except SoftTimeLimitExceeded as ex: logger.warning("A timeout occurred while taking a screenshot.") raise ReportScheduleScreenshotTimeout() from ex except Exception as ex: raise ReportScheduleScreenshotFailedError( f"Failed taking a screenshot {str(ex)}") from ex if not image: raise ReportScheduleScreenshotFailedError() return [image]
def cache_dashboard_thumbnail(dashboard_id: int, force: bool = False) -> None: with app.app_context(): # type: ignore if not thumbnail_cache: logging.warning("No cache set, refusing to compute") return None logger.info(f"Caching dashboard {dashboard_id}") screenshot = DashboardScreenshot(model_id=dashboard_id) user = security_manager.find_user(current_app.config["THUMBNAIL_SELENIUM_USER"]) screenshot.compute_and_cache(user=user, cache=thumbnail_cache, force=force)
def cache_dashboard_thumbnail( url: str, digest: str, force: bool = False, thumb_size: Optional[WindowSize] = None ) -> None: with app.app_context(): # type: ignore if not thumbnail_cache: logging.warning("No cache set, refusing to compute") return logger.info("Caching dashboard: %s", url) screenshot = DashboardScreenshot(url, digest) user = security_manager.find_user(current_app.config["THUMBNAIL_SELENIUM_USER"]) screenshot.compute_and_cache( user=user, cache=thumbnail_cache, force=force, thumb_size=thumb_size, )
def _get_screenshot(self) -> ScreenshotData: """ Get a chart or dashboard screenshot :raises: ReportScheduleScreenshotFailedError """ screenshot: Optional[BaseScreenshot] = None if self._report_schedule.chart: url = self._get_url(standalone="true") screenshot = ChartScreenshot( url, self._report_schedule.chart.digest, window_size=app.config["WEBDRIVER_WINDOW"]["slice"], thumb_size=app.config["WEBDRIVER_WINDOW"]["slice"], ) else: url = self._get_url() screenshot = DashboardScreenshot( url, self._report_schedule.dashboard.digest, window_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], thumb_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], ) image_url = self._get_url(user_friendly=True) user = self._get_screenshot_user() image_data = screenshot.compute_and_cache( user=user, cache=thumbnail_cache, force=True, ) if not image_data: raise ReportScheduleScreenshotFailedError() return ScreenshotData(url=image_url, image=image_data)
def _get_screenshot(self) -> bytes: """ Get a chart or dashboard screenshot :raises: ReportScheduleScreenshotFailedError """ screenshot: Optional[BaseScreenshot] = None if self._report_schedule.chart: url = self._get_url(standalone="true") screenshot = ChartScreenshot( url, self._report_schedule.chart.digest, window_size=app.config["WEBDRIVER_WINDOW"]["slice"], thumb_size=app.config["WEBDRIVER_WINDOW"]["slice"], ) else: url = self._get_url() screenshot = DashboardScreenshot( url, self._report_schedule.dashboard.digest, window_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], thumb_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], ) user = self._get_screenshot_user() try: image_data = screenshot.get_screenshot(user=user) except SoftTimeLimitExceeded: raise ReportScheduleScreenshotTimeout() except Exception as ex: raise ReportScheduleScreenshotFailedError( f"Failed taking a screenshot {str(ex)}") if not image_data: raise ReportScheduleScreenshotFailedError() return image_data
def cache_dashboard_thumbnail(url: str, digest: str, force: bool = False, thumb_size: Optional[WindowSize] = None) -> None: if not thumbnail_cache: logging.warning("No cache set, refusing to compute") return logger.info("Caching dashboard: %s", url) screenshot = DashboardScreenshot(url, digest) with session_scope(nullpool=True) as session: user = security_manager.get_user_by_username( current_app.config["THUMBNAIL_SELENIUM_USER"], session=session) screenshot.compute_and_cache( user=user, cache=thumbnail_cache, force=force, thumb_size=thumb_size, )
def _get_screenshots(self) -> List[bytes]: """ Get chart or dashboard screenshots :raises: ReportScheduleScreenshotFailedError """ image_data = [] screenshots: List[BaseScreenshot] = [] if self._report_schedule.chart: url = self._get_url() logger.info("Screenshotting chart at %s", url) screenshots = [ ChartScreenshot( url, self._report_schedule.chart.digest, window_size=app.config["WEBDRIVER_WINDOW"]["slice"], thumb_size=app.config["WEBDRIVER_WINDOW"]["slice"], ) ] else: tabs: Optional[List[str]] = json.loads(self._report_schedule.extra).get( "dashboard_tab_ids", None ) dashboard_base_url = self._get_url() if tabs is None: urls = [dashboard_base_url] else: urls = [f"{dashboard_base_url}#{tab_id}" for tab_id in tabs] screenshots = [ DashboardScreenshot( url, self._report_schedule.dashboard.digest, window_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], thumb_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], ) for url in urls ] user = self._get_user() for screenshot in screenshots: try: image = screenshot.get_screenshot(user=user) except SoftTimeLimitExceeded as ex: logger.warning("A timeout occurred while taking a screenshot.") raise ReportScheduleScreenshotTimeout() from ex except Exception as ex: raise ReportScheduleScreenshotFailedError( f"Failed taking a screenshot {str(ex)}" ) from ex if image is not None: image_data.append(image) if not image_data: raise ReportScheduleScreenshotFailedError() return image_data
def test_get_cached_dashboard_screenshot(self): """ Thumbnails: Simple get cached dashboard screenshot """ dashboard = db.session.query(Dashboard).all()[0] # Cache a test "image" screenshot = DashboardScreenshot(model_id=dashboard.id) thumbnail_cache.set(screenshot.cache_key, self.mock_image) self.login(username="******") uri = f"api/v1/dashboard/{dashboard.id}/thumbnail/{dashboard.digest}/" rv = self.client.get(uri) self.assertEqual(rv.status_code, 200) self.assertEqual(rv.data, self.mock_image)
def test_get_cached_dashboard_wrong_digest(self): """ Thumbnails: Simple get dashboard with wrong digest """ dashboard = db.session.query(Dashboard).all()[0] dashboard_url = get_url_path("Superset.dashboard", dashboard_id=dashboard.id) # Cache a test "image" screenshot = DashboardScreenshot(dashboard_url, dashboard.digest) thumbnail_cache.set(screenshot.cache_key, self.mock_image) self.login(username="******") uri = f"api/v1/dashboard/{dashboard.id}/thumbnail/1234/" rv = self.client.get(uri) self.assertEqual(rv.status_code, 302) self.assertRedirects( rv, f"api/v1/dashboard/{dashboard.id}/thumbnail/{dashboard.digest}/" )
def _get_screenshot(self, report_schedule: ReportSchedule) -> ScreenshotData: """ Get a chart or dashboard screenshot :raises: ReportScheduleScreenshotFailedError """ url = self._get_url(report_schedule) screenshot: Optional[BaseScreenshot] = None if report_schedule.chart: screenshot = ChartScreenshot(url, report_schedule.chart.digest) else: screenshot = DashboardScreenshot(url, report_schedule.dashboard.digest) image_url = self._get_url(report_schedule, user_friendly=True) user = security_manager.find_user(app.config["THUMBNAIL_SELENIUM_USER"]) image_data = screenshot.compute_and_cache( user=user, cache=thumbnail_cache, force=True, ) if not image_data: raise ReportScheduleScreenshotFailedError() return ScreenshotData(url=image_url, image=image_data)
def _get_screenshot(self) -> ScreenshotData: """ Get a chart or dashboard screenshot :raises: ReportScheduleScreenshotFailedError """ url = self._get_url() screenshot: Optional[BaseScreenshot] = None if self._report_schedule.chart: screenshot = ChartScreenshot(url, self._report_schedule.chart.digest) else: screenshot = DashboardScreenshot( url, self._report_schedule.dashboard.digest ) image_url = self._get_url(user_friendly=True) user = self._get_screenshot_user() image_data = screenshot.compute_and_cache( user=user, cache=thumbnail_cache, force=True, ) if not image_data: raise ReportScheduleScreenshotFailedError() return ScreenshotData(url=image_url, image=image_data)
def _get_screenshot(self) -> bytes: """ Get a chart or dashboard screenshot :raises: ReportScheduleScreenshotFailedError """ screenshot: Optional[BaseScreenshot] = None if self._report_schedule.chart: url = self._get_url() logger.info("Screenshotting chart at %s", url) screenshot = ChartScreenshot( url, self._report_schedule.chart.digest, window_size=app.config["WEBDRIVER_WINDOW"]["slice"], thumb_size=app.config["WEBDRIVER_WINDOW"]["slice"], ) else: url = self._get_url() logger.info("Screenshotting dashboard at %s", url) screenshot = DashboardScreenshot( url, self._report_schedule.dashboard.digest, window_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], thumb_size=app.config["WEBDRIVER_WINDOW"]["dashboard"], ) user = self._get_user() try: image_data = screenshot.get_screenshot(user=user) except SoftTimeLimitExceeded as ex: logger.warning("A timeout occurred while taking a screenshot.") raise ReportScheduleScreenshotTimeout() from ex except Exception as ex: raise ReportScheduleScreenshotFailedError( f"Failed taking a screenshot {str(ex)}" ) from ex if not image_data: raise ReportScheduleScreenshotFailedError() return image_data
def thumbnail(self, pk: int, digest: str, **kwargs: Any) -> WerkzeugResponse: """Get Dashboard thumbnail --- get: description: >- Compute async or get already computed dashboard thumbnail from cache. parameters: - in: path schema: type: integer name: pk - in: path name: digest description: A hex digest that makes this dashboard unique schema: type: string - in: query name: q content: application/json: schema: $ref: '#/components/schemas/thumbnail_query_schema' responses: 200: description: Dashboard thumbnail image content: image/*: schema: type: string format: binary 202: description: Thumbnail does not exist on cache, fired async to compute content: application/json: schema: type: object properties: message: type: string 302: description: Redirects to the current digest 401: $ref: '#/components/responses/401' 404: $ref: '#/components/responses/404' 422: $ref: '#/components/responses/422' 500: $ref: '#/components/responses/500' """ dashboard = self.datamodel.get(pk, self._base_filters) if not dashboard: return self.response_404() dashboard_url = get_url_path("Superset.dashboard", dashboard_id_or_slug=dashboard.id) # If force, request a screenshot from the workers if kwargs["rison"].get("force", False): cache_dashboard_thumbnail.delay(dashboard_url, dashboard.digest, force=True) return self.response(202, message="OK Async") # fetch the dashboard screenshot using the current user and cache if set screenshot = DashboardScreenshot( dashboard_url, dashboard.digest).get_from_cache(cache=thumbnail_cache) # If the screenshot does not exist, request one from the workers if not screenshot: self.incr_stats("async", self.thumbnail.__name__) cache_dashboard_thumbnail.delay(dashboard_url, dashboard.digest, force=True) return self.response(202, message="OK Async") # If digests if dashboard.digest != digest: self.incr_stats("redirect", self.thumbnail.__name__) return redirect( url_for( f"{self.__class__.__name__}.thumbnail", pk=pk, digest=dashboard.digest, )) self.incr_stats("from_cache", self.thumbnail.__name__) return Response(FileWrapper(screenshot), mimetype="image/png", direct_passthrough=True)