Beispiel #1
0
    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)
Beispiel #2
0
    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
Beispiel #3
0
def cache_chart_thumbnail(
    url: str,
    digest: str,
    force: bool = False,
    window_size: Optional[WindowSize] = None,
    thumb_size: Optional[WindowSize] = None,
) -> None:
    with app.app_context():  # type: ignore
        if not thumbnail_cache:
            logger.warning("No cache set, refusing to compute")
            return None
        logger.info("Caching chart: %s", url)
        screenshot = ChartScreenshot(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,
            window_size=window_size,
            thumb_size=thumb_size,
        )
        return None
Beispiel #4
0
def cache_chart_thumbnail(chart_id: int, force: bool = False) -> None:
    with app.app_context():  # type: ignore
        if not thumbnail_cache:
            logger.warning("No cache set, refusing to compute")
            return None
        logging.info(f"Caching chart {chart_id}")
        screenshot = ChartScreenshot(model_id=chart_id)
        user = security_manager.find_user(current_app.config["THUMBNAIL_SELENIUM_USER"])
        screenshot.compute_and_cache(user=user, cache=thumbnail_cache, force=force)
def deliver_alert(alert_id: int, recipients: Optional[str] = None) -> None:
    alert = db.session.query(Alert).get(alert_id)

    logging.info("Triggering alert: %s", alert)
    img_data = None
    images = {}
    recipients = recipients or alert.recipients

    if alert.slice:

        chart_url = get_url_path("Superset.slice",
                                 slice_id=alert.slice.id,
                                 standalone="true")
        screenshot = ChartScreenshot(chart_url, alert.slice.digest)
        image_url = _get_url_path(
            "Superset.slice",
            user_friendly=True,
            slice_id=alert.slice.id,
            standalone="true",
        )
        standalone_index = image_url.find("/?standalone=true")
        if standalone_index != -1:
            image_url = image_url[:standalone_index]

        user = security_manager.find_user(
            current_app.config["THUMBNAIL_SELENIUM_USER"])
        img_data = screenshot.compute_and_cache(
            user=user,
            cache=thumbnail_cache,
            force=True,
        )
    else:
        # TODO: dashboard delivery!
        image_url = "https://media.giphy.com/media/dzaUX7CAG0Ihi/giphy.gif"

    # generate the email
    # TODO add sql query results to email
    subject = f"[Superset] Triggered alert: {alert.label}"
    deliver_as_group = False
    data = None
    if img_data:
        images = {"screenshot": img_data}
    body = render_template(
        "email/alert.txt",
        alert_url=_get_url_path("AlertModelView.show",
                                user_friendly=True,
                                pk=alert.id),
        label=alert.label,
        sql=alert.sql,
        image_url=image_url,
    )

    _deliver_email(recipients, deliver_as_group, subject, body, data, images)
Beispiel #6
0
 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 _get_slice_screenshot(slice_id: int, session: Session) -> ScreenshotData:
    slice_obj = session.query(Slice).get(slice_id)

    chart_url = get_url_path("Superset.slice", slice_id=slice_obj.id, standalone="true")
    screenshot = ChartScreenshot(chart_url, slice_obj.digest)
    image_url = _get_url_path(
        "Superset.slice", user_friendly=True, slice_id=slice_obj.id,
    )

    user = security_manager.find_user(current_app.config["THUMBNAIL_SELENIUM_USER"])
    image_data = screenshot.compute_and_cache(
        user=user, cache=thumbnail_cache, force=True,
    )

    session.commit()
    return ScreenshotData(image_url, image_data)
def deliver_alert(alert: Alert, recipients: Optional[str] = None) -> None:
    logging.info("Triggering alert: %s", alert)
    img_data = None
    images = {}
    recipients = recipients or alert.recipients

    if alert.slice:

        chart_url = get_url_path("Superset.slice",
                                 slice_id=alert.slice.id,
                                 standalone="true")
        screenshot = ChartScreenshot(chart_url, alert.slice.digest)
        cache_key = screenshot.cache_key()
        image_url = get_url_path("ChartRestApi.screenshot",
                                 pk=alert.slice.id,
                                 digest=cache_key)

        user = security_manager.find_user(
            current_app.config["THUMBNAIL_SELENIUM_USER"])
        img_data = screenshot.compute_and_cache(
            user=user,
            cache=thumbnail_cache,
            force=True,
        )
    else:
        # TODO: dashboard delivery!
        image_url = "https://media.giphy.com/media/dzaUX7CAG0Ihi/giphy.gif"

    # generate the email
    subject = f"[Superset] Triggered alert: {alert.label}"
    deliver_as_group = False
    data = None
    if img_data:
        images = {"screenshot": img_data}
    body = __(
        textwrap.dedent("""\
            <h2>Alert: %(label)s</h2>
            <img src="cid:screenshot" alt="%(label)s" />
        """),
        label=alert.label,
        image_url=image_url,
    )

    _deliver_email(recipients, deliver_as_group, subject, body, data, images)
Beispiel #9
0
 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)
Beispiel #10
0
 def thumbnail(self, pk: int, digest: str,
               **kwargs: Dict[str, bool]) -> WerkzeugResponse:
     """Get Chart thumbnail
     ---
     get:
       description: Compute or get already computed chart thumbnail from cache.
       parameters:
       - in: path
         schema:
           type: integer
         name: pk
       - in: path
         schema:
           type: string
         name: sha
       responses:
         200:
           description: Chart thumbnail image
           content:
            image/*:
              schema:
                type: string
                format: binary
         302:
           description: Redirects to the current digest
         400:
           $ref: '#/components/responses/400'
         401:
           $ref: '#/components/responses/401'
         404:
           $ref: '#/components/responses/404'
         500:
           $ref: '#/components/responses/500'
     """
     chart = self.datamodel.get(pk, self._base_filters)
     if not chart:
         return self.response_404()
     if kwargs["rison"].get("force", False):
         cache_chart_thumbnail.delay(chart.id, force=True)
         return self.response(202, message="OK Async")
     # fetch the chart screenshot using the current user and cache if set
     screenshot = ChartScreenshot(pk).get_from_cache(cache=thumbnail_cache)
     # If not screenshot then send request to compute thumb to celery
     if not screenshot:
         cache_chart_thumbnail.delay(chart.id, force=True)
         return self.response(202, message="OK Async")
     # If digests
     if chart.digest != digest:
         return redirect(
             url_for(f"{self.__class__.__name__}.thumbnail",
                     pk=pk,
                     digest=chart.digest))
     return Response(FileWrapper(screenshot),
                     mimetype="image/png",
                     direct_passthrough=True)
Beispiel #11
0
 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_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
Beispiel #13
0
 def test_get_cached_chart_screenshot(self):
     """
         Thumbnails: Simple get cached chart screenshot
     """
     chart = db.session.query(Slice).all()[0]
     # Cache a test "image"
     screenshot = ChartScreenshot(model_id=chart.id)
     thumbnail_cache.set(screenshot.cache_key, self.mock_image)
     self.login(username="******")
     uri = f"api/v1/chart/{chart.id}/thumbnail/{chart.digest}/"
     rv = self.client.get(uri)
     self.assertEqual(rv.status_code, 200)
     self.assertEqual(rv.data, self.mock_image)
Beispiel #14
0
    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
Beispiel #15
0
 def test_get_cached_chart_wrong_digest(self):
     """
         Thumbnails: Simple get chart with wrong digest
     """
     chart = db.session.query(Slice).all()[0]
     chart_url = get_url_path("Superset.slice", slice_id=chart.id, standalone="true")
     # Cache a test "image"
     screenshot = ChartScreenshot(chart_url, chart.digest)
     thumbnail_cache.set(screenshot.cache_key, self.mock_image)
     self.login(username="******")
     uri = f"api/v1/chart/{chart.id}/thumbnail/1234/"
     rv = self.client.get(uri)
     self.assertEqual(rv.status_code, 302)
     self.assertRedirects(rv, f"api/v1/chart/{chart.id}/thumbnail/{chart.digest}/")
def cache_chart_thumbnail(
    url: str,
    digest: str,
    force: bool = False,
    window_size: Optional[WindowSize] = None,
    thumb_size: Optional[WindowSize] = None,
) -> None:
    with app.app_context():  # type: ignore
        if not thumbnail_cache:
            logger.warning("No cache set, refusing to compute")
            return None
        logging.info("Caching chart at {url}")
        screenshot = ChartScreenshot(url, digest)
        user = security_manager.find_user(
            current_app.config["THUMBNAIL_SELENIUM_USER"])
        screenshot.compute_and_cache(
            user=user,
            cache=thumbnail_cache,
            force=force,
            window_size=window_size,
            thumb_size=thumb_size,
        )
        return None
Beispiel #17
0
    def screenshot(self, pk: int, digest: str) -> WerkzeugResponse:
        """Get Chart screenshot
        ---
        get:
          description: Get a computed screenshot from cache.
          parameters:
          - in: path
            schema:
              type: integer
            name: pk
          - in: path
            schema:
              type: string
            name: digest
          responses:
            200:
              description: Chart thumbnail image
              content:
               image/*:
                 schema:
                   type: string
                   format: binary
            302:
              description: Redirects to the current digest
            400:
              $ref: '#/components/responses/400'
            401:
              $ref: '#/components/responses/401'
            404:
              $ref: '#/components/responses/404'
            500:
              $ref: '#/components/responses/500'
        """
        chart = self.datamodel.get(pk, self._base_filters)

        # Making sure the chart still exists
        if not chart:
            return self.response_404()

        # TODO make sure the user has access to the chart

        # fetch the chart screenshot using the current user and cache if set
        img = ChartScreenshot.get_from_cache_key(thumbnail_cache, digest)
        if img:
            return Response(FileWrapper(img),
                            mimetype="image/png",
                            direct_passthrough=True)
        # TODO: return an empty image
        return self.response_404()
Beispiel #18
0
    def cache_screenshot(self, pk: int,
                         **kwargs: Dict[str, bool]) -> WerkzeugResponse:
        """
        ---
        get:
          description: Compute and cache a screenshot.
          parameters:
          - in: path
            schema:
              type: integer
            name: pk
          - in: query
            name: q
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/screenshot_query_schema'
          responses:
            200:
              description: Chart async result
              content:
                application/json:
                  schema:
                    $ref: "#/components/schemas/ChartCacheScreenshotResponseSchema"
            302:
              description: Redirects to the current digest
            400:
              $ref: '#/components/responses/400'
            401:
              $ref: '#/components/responses/401'
            404:
              $ref: '#/components/responses/404'
            500:
              $ref: '#/components/responses/500'
        """
        rison_dict = kwargs["rison"]
        window_size = rison_dict.get("window_size") or (800, 600)

        # Don't shrink the image if thumb_size is not specified
        thumb_size = rison_dict.get("thumb_size") or window_size

        chart = self.datamodel.get(pk, self._base_filters)
        if not chart:
            return self.response_404()

        chart_url = get_url_path("Superset.slice",
                                 slice_id=chart.id,
                                 standalone="true")
        screenshot_obj = ChartScreenshot(chart_url, chart.digest)
        cache_key = screenshot_obj.cache_key(window_size, thumb_size)
        image_url = get_url_path("ChartRestApi.screenshot",
                                 pk=chart.id,
                                 digest=cache_key)

        def trigger_celery() -> WerkzeugResponse:
            logger.info("Triggering screenshot ASYNC")
            kwargs = {
                "url": chart_url,
                "digest": chart.digest,
                "force": True,
                "window_size": window_size,
                "thumb_size": thumb_size,
            }
            cache_chart_thumbnail.delay(**kwargs)
            return self.response(
                202,
                cache_key=cache_key,
                chart_url=chart_url,
                image_url=image_url,
            )

        return trigger_celery()