def snap_details(snap_name):
        """
        A view to display the snap details page for specific snaps.

        This queries the snapcraft API (api.snapcraft.io) and passes
        some of the data through to the snap-details.html template,
        with appropriate sanitation.
        """

        error_info = {}
        status_code = 200

        context = _get_context_snap_details(snap_name)

        webapp_config = flask.current_app.config.get("WEBAPP_CONFIG")

        if "STORE_QUERY" not in webapp_config:
            country_metric_name = "weekly_installed_base_by_country_percent"
            os_metric_name = (
                "weekly_installed_base_by_operating_system_normalized")

            end = metrics_helper.get_last_metrics_processed_date()

            metrics_query_json = [
                metrics_helper.get_filter(
                    metric_name=country_metric_name,
                    snap_id=context["snap-id"],
                    start=end,
                    end=end,
                ),
                metrics_helper.get_filter(
                    metric_name=os_metric_name,
                    snap_id=context["snap-id"],
                    start=end,
                    end=end,
                ),
            ]

            try:
                metrics_response = api.get_public_metrics(metrics_query_json)
            except (StoreApiError, ApiError) as api_error:
                status_code, error_info = handle_errors(api_error)
                metrics_response = None

            os_metrics = None
            country_devices = None
            if metrics_response:
                oses = metrics_helper.find_metric(metrics_response,
                                                  os_metric_name)
                os_metrics = metrics.OsMetric(
                    name=oses["metric_name"],
                    series=oses["series"],
                    buckets=oses["buckets"],
                    status=oses["status"],
                )

                territories = metrics_helper.find_metric(
                    metrics_response, country_metric_name)
                country_devices = metrics.CountryDevices(
                    name=territories["metric_name"],
                    series=territories["series"],
                    buckets=territories["buckets"],
                    status=territories["status"],
                    private=False,
                )
        else:
            os_metrics = None
            country_devices = None

        context.update({
            "countries":
            (country_devices.country_data if country_devices else None),
            "normalized_os":
            os_metrics.os if os_metrics else None,
            # Context info
            "is_linux":
            ("Linux" in flask.request.headers.get("User-Agent", "")
             and "Android" not in flask.request.headers.get("User-Agent", "")),
            "error_info":
            error_info,
        })

        return (
            flask.render_template("store/snap-details.html", **context),
            status_code,
        )
    def snap_details(snap_name):
        """
        A view to display the snap details page for specific snaps.

        This queries the snapcraft API (api.snapcraft.io) and passes
        some of the data through to the snap-details.html template,
        with appropriate sanitation.
        """

        error_info = {}
        status_code = 200

        context = _get_context_snap_details(snap_name)

        webapp_config = flask.current_app.config.get("WEBAPP_CONFIG")

        if "STORE_QUERY" not in webapp_config:
            country_metric_name = "weekly_installed_base_by_country_percent"
            os_metric_name = (
                "weekly_installed_base_by_operating_system_normalized"
            )

            end = metrics_helper.get_last_metrics_processed_date()

            metrics_query_json = [
                metrics_helper.get_filter(
                    metric_name=country_metric_name,
                    snap_id=context["snap-id"],
                    start=end,
                    end=end,
                ),
                metrics_helper.get_filter(
                    metric_name=os_metric_name,
                    snap_id=context["snap-id"],
                    start=end,
                    end=end,
                ),
            ]

            try:
                metrics_response = api.get_public_metrics(
                    snap_name, metrics_query_json
                )
            except ApiError as api_error:
                status_code, error_info = handle_errors(api_error)
                metrics_response = None

            os_metrics = None
            country_devices = None
            if metrics_response:
                oses = metrics_helper.find_metric(
                    metrics_response, os_metric_name
                )
                os_metrics = metrics.OsMetric(
                    name=oses["metric_name"],
                    series=oses["series"],
                    buckets=oses["buckets"],
                    status=oses["status"],
                )

                territories = metrics_helper.find_metric(
                    metrics_response, country_metric_name
                )
                country_devices = metrics.CountryDevices(
                    name=territories["metric_name"],
                    series=territories["series"],
                    buckets=territories["buckets"],
                    status=territories["status"],
                    private=False,
                )
        else:
            os_metrics = None
            country_devices = None

        context.update(
            {
                "countries": (
                    country_devices.country_data if country_devices else None
                ),
                "normalized_os": os_metrics.os if os_metrics else None,
                # Context info
                "is_linux": (
                    "Linux" in flask.request.headers.get("User-Agent", "")
                    and "Android"
                    not in flask.request.headers.get("User-Agent", "")
                ),
                "error_info": error_info,
            }
        )

        return (
            flask.render_template("store/snap-details.html", **context),
            status_code,
        )
Beispiel #3
0
    def snap_details(snap_name):
        """
        A view to display the snap details page for specific snaps.

        This queries the snapcraft API (api.snapcraft.io) and passes
        some of the data through to the snap-details.html template,
        with appropriate sanitation.
        """

        error_info = {}
        status_code = 200

        try:
            details = api.get_snap_details(snap_name)
        except ApiTimeoutError as api_timeout_error:
            flask.abort(504, str(api_timeout_error))
        except ApiResponseDecodeError as api_response_decode_error:
            flask.abort(502, str(api_response_decode_error))
        except ApiResponseErrorList as api_response_error_list:
            if api_response_error_list.status_code == 404:
                flask.abort(404, "No snap named {}".format(snap_name))
            else:
                if api_response_error_list.errors:
                    error_messages = ", ".join(
                        api_response_error_list.errors.key())
                else:
                    error_messages = "An error occurred."
                flask.abort(502, error_messages)
        except ApiResponseError as api_response_error:
            flask.abort(502, str(api_response_error))
        except ApiCircuitBreaker:
            flask.abort(503)
        except ApiError as api_error:
            flask.abort(502, str(api_error))

        # When removing all the channel maps of an exsting snap the API,
        # responds that the snaps still exists with data.
        # Return a 404 if not channel maps, to avoid having a error.
        # For example: mir-kiosk-browser
        if not details.get("channel-map"):
            flask.abort(404, "No snap named {}".format(snap_name))

        formatted_paragraphs = logic.split_description_into_paragraphs(
            details["snap"]["description"])

        channel_maps_list = logic.convert_channel_maps(
            details.get("channel-map"))

        latest_channel = logic.get_last_updated_version(
            details.get("channel-map"))

        last_updated = latest_channel["created-at"]
        last_version = latest_channel["version"]
        binary_filesize = latest_channel["download"]["size"]

        country_metric_name = "weekly_installed_base_by_country_percent"
        os_metric_name = "weekly_installed_base_by_operating_system_normalized"

        webapp_config = flask.current_app.config.get("WEBAPP_CONFIG")

        if "STORE_QUERY" not in webapp_config:
            end = metrics_helper.get_last_metrics_processed_date()

            metrics_query_json = [
                metrics_helper.get_filter(
                    metric_name=country_metric_name,
                    snap_id=details["snap-id"],
                    start=end,
                    end=end,
                ),
                metrics_helper.get_filter(
                    metric_name=os_metric_name,
                    snap_id=details["snap-id"],
                    start=end,
                    end=end,
                ),
            ]

            try:
                metrics_response = api.get_public_metrics(
                    snap_name, metrics_query_json)
            except ApiError as api_error:
                status_code, error_info = _handle_errors(api_error)
                metrics_response = None

            os_metrics = None
            country_devices = None
            if metrics_response:
                oses = metrics_helper.find_metric(metrics_response,
                                                  os_metric_name)
                os_metrics = metrics.OsMetric(
                    name=oses["metric_name"],
                    series=oses["series"],
                    buckets=oses["buckets"],
                    status=oses["status"],
                )

                territories = metrics_helper.find_metric(
                    metrics_response, country_metric_name)
                country_devices = metrics.CountryDevices(
                    name=territories["metric_name"],
                    series=territories["series"],
                    buckets=territories["buckets"],
                    status=territories["status"],
                    private=False,
                )
        else:
            os_metrics = None
            country_devices = None

        # filter out banner and banner-icon images from screenshots
        screenshots = [
            m["url"] for m in details["snap"]["media"]
            if m["type"] == "screenshot" and "banner" not in m["url"]
        ]
        icons = [
            m["url"] for m in details["snap"]["media"] if m["type"] == "icon"
        ]

        # until default tracks are supported by the API we special case node
        # to use 10, rather then latest
        default_track = "10" if details["name"] == "node" else "latest"

        lowest_risk_available = logic.get_lowest_available_risk(
            channel_maps_list, default_track)

        confinement = logic.get_confinement(channel_maps_list, default_track,
                                            lowest_risk_available)

        context = {
            # Data direct from details API
            "snap_title":
            details["snap"]["title"],
            "package_name":
            details["name"],
            "icon_url":
            icons[0] if icons else None,
            "version":
            last_version,
            "license":
            details["snap"]["license"],
            "publisher":
            details["snap"]["publisher"]["display-name"],
            "username":
            details["snap"]["publisher"]["username"],
            "screenshots":
            screenshots,
            "prices":
            details["snap"]["prices"],
            "contact":
            details["snap"].get("contact"),
            "website":
            details["snap"].get("website"),
            "summary":
            details["snap"]["summary"],
            "description_paragraphs":
            formatted_paragraphs,
            "channel_map":
            channel_maps_list,
            "has_stable":
            logic.has_stable(channel_maps_list),
            "developer_validation":
            details["snap"]["publisher"]["validation"],
            "default_track":
            default_track,
            "lowest_risk_available":
            lowest_risk_available,
            "confinement":
            confinement,
            # Transformed API data
            "filesize":
            humanize.naturalsize(binary_filesize),
            "last_updated": (humanize.naturaldate(parser.parse(last_updated))),
            "last_updated_raw":
            last_updated,
            # Data from metrics API
            "countries":
            (country_devices.country_data if country_devices else None),
            "normalized_os":
            os_metrics.os if os_metrics else None,
            # Context info
            "is_linux":
            ("Linux" in flask.request.headers.get("User-Agent", "")
             and "Android" not in flask.request.headers.get("User-Agent", "")),
            "error_info":
            error_info,
        }

        return (
            flask.render_template("store/snap-details.html", **context),
            status_code,
        )
Beispiel #4
0
def snap_details(snap_name):
    """
    A view to display the snap details page for specific snaps.

    This queries the snapcraft API (api.snapcraft.io) and passes
    some of the data through to the snap-details.html template,
    with appropriate sanitation.
    """

    error_info = {}
    default_channel = logic.get_default_channel(snap_name)

    try:
        details = api.get_snap_details(
                snap_name, default_channel)
    except ApiTimeoutError as api_timeout_error:
        flask.abort(504, str(api_timeout_error))
    except ApiResponseDecodeError as api_response_decode_error:
        flask.abort(502, str(api_response_decode_error))
    except ApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            flask.abort(404, 'No snap named {}'.format(snap_name))
        else:
            error_messages = ', '.join(api_response_error_list.errors.key())
            flask.abort(502, error_messages)
    except ApiResponseError as api_response_error:
        flask.abort(502, str(api_response_error))
    except ApiError as api_error:
        flask.abort(502, str(api_error))

    formatted_paragraphs = logic.split_description_into_paragraphs(
        details['description'])

    channel_maps_list = logic.convert_channel_maps(
        details.get('channel_maps_list'))

    end = metrics_helper.get_last_metrics_processed_date()
    country_metric_name = 'weekly_installed_base_by_country_percent'
    os_metric_name = 'weekly_installed_base_by_operating_system_normalized'

    metrics_query_json = [
        metrics_helper.get_filter(
            metric_name=country_metric_name,
            snap_id=details['snap_id'],
            start=end,
            end=end),
        metrics_helper.get_filter(
            metric_name=os_metric_name,
            snap_id=details['snap_id'],
            start=end,
            end=end)]

    status_code = 200
    try:
        metrics_response = api.get_public_metrics(
            snap_name,
            metrics_query_json)
    except ApiError as api_error:
        status_code, error_info = _handle_errors(api_error)

    oses = metrics_helper.find_metric(metrics_response, os_metric_name)
    os_metrics = metrics.OsMetric(
        name=oses['metric_name'],
        series=oses['series'],
        buckets=oses['buckets'],
        status=oses['status'])

    territories = metrics_helper.find_metric(
        metrics_response, country_metric_name)
    country_devices = metrics.CountryDevices(
        name=territories['metric_name'],
        series=territories['series'],
        buckets=territories['buckets'],
        status=territories['status'],
        private=False)

    # filter out banner and banner-icon images from screenshots
    screenshots = [
        m['url'] for m in details['media']
        if m['type'] == "screenshot" and "banner" not in m['url']
    ]
    icons = [m['url'] for m in details['media'] if m['type'] == "icon"]

    context = {
        # Data direct from details API
        'snap_title': details['title'],
        'package_name': details['package_name'],
        'icon_url': icons[0] if icons else None,
        'version': details['version'],
        'revision': details['revision'],
        'license': details['license'],
        'publisher': details['publisher'],
        'screenshots': screenshots,
        'prices': details['prices'],
        'contact': details.get('contact'),
        'website': details.get('website'),
        'summary': details['summary'],
        'description_paragraphs': formatted_paragraphs,
        'channel_map': channel_maps_list,
        'default_channel': default_channel,

        # Transformed API data
        'filesize': humanize.naturalsize(details['binary_filesize']),
        'last_updated': (
            humanize.naturaldate(
                parser.parse(details.get('last_updated'))
            )
        ),
        'last_updated_raw': details.get('last_updated'),

        # Data from metrics API
        'countries': country_devices.country_data,
        'normalized_os': os_metrics.os,

        # Context info
        'is_linux': (
            'Linux' in flask.request.headers.get('User-Agent', '') and
            'Android' not in flask.request.headers.get('User-Agent', '')
        ),

        'error_info': error_info
    }

    return flask.render_template(
        'snap-details.html',
        **context
    ), status_code
Beispiel #5
0
    def snap_details(snap_name):
        """
        A view to display the snap details page for specific snaps.

        This queries the snapcraft API (api.snapcraft.io) and passes
        some of the data through to the snap-details.html template,
        with appropriate sanitation.
        """

        error_info = {}

        try:
            details = api.get_snap_details(snap_name)
        except ApiTimeoutError as api_timeout_error:
            flask.abort(504, str(api_timeout_error))
        except ApiResponseDecodeError as api_response_decode_error:
            flask.abort(502, str(api_response_decode_error))
        except ApiResponseErrorList as api_response_error_list:
            if api_response_error_list.status_code == 404:
                flask.abort(404, 'No snap named {}'.format(snap_name))
            else:
                error_messages = ', '.join(
                    api_response_error_list.errors.key())
                flask.abort(502, error_messages)
        except ApiResponseError as api_response_error:
            flask.abort(502, str(api_response_error))
        except ApiError as api_error:
            flask.abort(502, str(api_error))

        # When removing all the channel maps of an exsting snap the API,
        # responds that the snaps still exists with data.
        # Return a 404 if not channel maps, to avoid having a error.
        # For example: mir-kiosk-browser
        if not details.get('channel-map'):
            flask.abort(404, 'No snap named {}'.format(snap_name))

        formatted_paragraphs = logic.split_description_into_paragraphs(
            details['snap']['description'])

        channel_maps_list = logic.convert_channel_maps(
            details.get('channel-map'))

        latest_channel = logic.get_last_updated_version(
            details.get('channel-map'))

        last_updated = latest_channel['created-at']
        last_version = latest_channel['version']
        binary_filesize = latest_channel['download']['size']

        end = metrics_helper.get_last_metrics_processed_date()
        country_metric_name = 'weekly_installed_base_by_country_percent'
        os_metric_name = 'weekly_installed_base_by_operating_system_normalized'

        metrics_query_json = [
            metrics_helper.get_filter(metric_name=country_metric_name,
                                      snap_id=details['snap-id'],
                                      start=end,
                                      end=end),
            metrics_helper.get_filter(metric_name=os_metric_name,
                                      snap_id=details['snap-id'],
                                      start=end,
                                      end=end)
        ]

        status_code = 200
        try:
            metrics_response = api.get_public_metrics(snap_name,
                                                      metrics_query_json)
        except ApiError as api_error:
            status_code, error_info = _handle_errors(api_error)
            metrics_response = None

        os_metrics = None
        country_devices = None
        if metrics_response:
            oses = metrics_helper.find_metric(metrics_response, os_metric_name)
            os_metrics = metrics.OsMetric(name=oses['metric_name'],
                                          series=oses['series'],
                                          buckets=oses['buckets'],
                                          status=oses['status'])

            territories = metrics_helper.find_metric(metrics_response,
                                                     country_metric_name)
            country_devices = metrics.CountryDevices(
                name=territories['metric_name'],
                series=territories['series'],
                buckets=territories['buckets'],
                status=territories['status'],
                private=False)

        # filter out banner and banner-icon images from screenshots
        screenshots = [
            m['url'] for m in details['snap']['media']
            if m['type'] == "screenshot" and "banner" not in m['url']
        ]
        icons = [
            m['url'] for m in details['snap']['media'] if m['type'] == "icon"
        ]

        # until default tracks are supported by the API we special case node
        # to use 10, rather then latest
        default_track = '10' if details['name'] == 'node' else 'latest'

        lowest_risk_available = logic.get_lowest_available_risk(
            channel_maps_list, default_track)

        confinement = logic.get_confinement(channel_maps_list, default_track,
                                            lowest_risk_available)

        context = {
            # Data direct from details API
            'snap_title':
            details['snap']['title'],
            'package_name':
            details['name'],
            'icon_url':
            icons[0] if icons else None,
            'version':
            last_version,
            'license':
            details['snap']['license'],
            'publisher':
            details['snap']['publisher']['display-name'],
            'screenshots':
            screenshots,
            'prices':
            details['snap']['prices'],
            'contact':
            details['snap'].get('contact'),
            'website':
            details['snap'].get('website'),
            'summary':
            details['snap']['summary'],
            'description_paragraphs':
            formatted_paragraphs,
            'channel_map':
            channel_maps_list,
            'has_stable':
            logic.has_stable(channel_maps_list),
            'developer_validation':
            details['snap']['publisher']['validation'],
            'default_track':
            default_track,
            'lowest_risk_available':
            lowest_risk_available,
            'confinement':
            confinement,

            # Transformed API data
            'filesize':
            humanize.naturalsize(binary_filesize),
            'last_updated': (humanize.naturaldate(parser.parse(last_updated))),
            'last_updated_raw':
            last_updated,

            # Data from metrics API
            'countries':
            (country_devices.country_data if country_devices else None),
            'normalized_os':
            os_metrics.os if os_metrics else None,

            # Context info
            'is_linux':
            ('Linux' in flask.request.headers.get('User-Agent', '')
             and 'Android' not in flask.request.headers.get('User-Agent', '')),
            'error_info':
            error_info
        }

        return flask.render_template('store/snap-details.html',
                                     **context), status_code
Beispiel #6
0
    def snap_details(snap_name):
        """
        A view to display the snap details page for specific snaps.

        This queries the snapcraft API (api.snapcraft.io) and passes
        some of the data through to the snap-details.html template,
        with appropriate sanitation.
        """

        error_info = {}
        status_code = 200

        try:
            details = api.get_snap_details(snap_name)
        except ApiTimeoutError as api_timeout_error:
            flask.abort(504, str(api_timeout_error))
        except ApiResponseDecodeError as api_response_decode_error:
            flask.abort(502, str(api_response_decode_error))
        except ApiResponseErrorList as api_response_error_list:
            if api_response_error_list.status_code == 404:
                flask.abort(404, "No snap named {}".format(snap_name))
            else:
                error_messages = ", ".join(
                    api_response_error_list.errors.key()
                )
                flask.abort(502, error_messages)
        except ApiResponseError as api_response_error:
            flask.abort(502, str(api_response_error))
        except ApiError as api_error:
            flask.abort(502, str(api_error))

        # When removing all the channel maps of an exsting snap the API,
        # responds that the snaps still exists with data.
        # Return a 404 if not channel maps, to avoid having a error.
        # For example: mir-kiosk-browser
        if not details.get("channel-map"):
            flask.abort(404, "No snap named {}".format(snap_name))

        formatted_paragraphs = logic.split_description_into_paragraphs(
            details["snap"]["description"]
        )

        channel_maps_list = logic.convert_channel_maps(
            details.get("channel-map")
        )

        latest_channel = logic.get_last_updated_version(
            details.get("channel-map")
        )

        last_updated = latest_channel["created-at"]
        last_version = latest_channel["version"]
        binary_filesize = latest_channel["download"]["size"]

        country_metric_name = "weekly_installed_base_by_country_percent"
        os_metric_name = "weekly_installed_base_by_operating_system_normalized"

        webapp_config = flask.current_app.config.get("WEBAPP_CONFIG")

        if "STORE_QUERY" not in webapp_config:
            end = metrics_helper.get_last_metrics_processed_date()

            metrics_query_json = [
                metrics_helper.get_filter(
                    metric_name=country_metric_name,
                    snap_id=details["snap-id"],
                    start=end,
                    end=end,
                ),
                metrics_helper.get_filter(
                    metric_name=os_metric_name,
                    snap_id=details["snap-id"],
                    start=end,
                    end=end,
                ),
            ]

            try:
                metrics_response = api.get_public_metrics(
                    snap_name, metrics_query_json
                )
            except ApiError as api_error:
                status_code, error_info = _handle_errors(api_error)
                metrics_response = None

            os_metrics = None
            country_devices = None
            if metrics_response:
                oses = metrics_helper.find_metric(
                    metrics_response, os_metric_name
                )
                os_metrics = metrics.OsMetric(
                    name=oses["metric_name"],
                    series=oses["series"],
                    buckets=oses["buckets"],
                    status=oses["status"],
                )

                territories = metrics_helper.find_metric(
                    metrics_response, country_metric_name
                )
                country_devices = metrics.CountryDevices(
                    name=territories["metric_name"],
                    series=territories["series"],
                    buckets=territories["buckets"],
                    status=territories["status"],
                    private=False,
                )
        else:
            os_metrics = None
            country_devices = None

        # filter out banner and banner-icon images from screenshots
        screenshots = [
            m["url"]
            for m in details["snap"]["media"]
            if m["type"] == "screenshot" and "banner" not in m["url"]
        ]
        icons = [
            m["url"] for m in details["snap"]["media"] if m["type"] == "icon"
        ]

        # until default tracks are supported by the API we special case node
        # to use 10, rather then latest
        default_track = "10" if details["name"] == "node" else "latest"

        lowest_risk_available = logic.get_lowest_available_risk(
            channel_maps_list, default_track
        )

        confinement = logic.get_confinement(
            channel_maps_list, default_track, lowest_risk_available
        )

        context = {
            # Data direct from details API
            "snap_title": details["snap"]["title"],
            "package_name": details["name"],
            "icon_url": icons[0] if icons else None,
            "version": last_version,
            "license": details["snap"]["license"],
            "publisher": details["snap"]["publisher"]["display-name"],
            "username": details["snap"]["publisher"]["username"],
            "screenshots": screenshots,
            "prices": details["snap"]["prices"],
            "contact": details["snap"].get("contact"),
            "website": details["snap"].get("website"),
            "summary": details["snap"]["summary"],
            "description_paragraphs": formatted_paragraphs,
            "channel_map": channel_maps_list,
            "has_stable": logic.has_stable(channel_maps_list),
            "developer_validation": details["snap"]["publisher"]["validation"],
            "default_track": default_track,
            "lowest_risk_available": lowest_risk_available,
            "confinement": confinement,
            # Transformed API data
            "filesize": humanize.naturalsize(binary_filesize),
            "last_updated": (humanize.naturaldate(parser.parse(last_updated))),
            "last_updated_raw": last_updated,
            # Data from metrics API
            "countries": (
                country_devices.country_data if country_devices else None
            ),
            "normalized_os": os_metrics.os if os_metrics else None,
            # Context info
            "is_linux": (
                "Linux" in flask.request.headers.get("User-Agent", "")
                and "Android"
                not in flask.request.headers.get("User-Agent", "")
            ),
            "error_info": error_info,
        }

        return (
            flask.render_template("store/snap-details.html", **context),
            status_code,
        )