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, )
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, )
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
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
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, )