def get_package(entity_name, channel_request, fields): # Get entity info from API package = app.store_api.get_item_details(entity_name, channel=channel_request, fields=fields) # If the package is not published, return a 404 if not package["default-release"]: abort(404) if COMMANDS_OVERWRITE.get(entity_name): package["command"] = COMMANDS_OVERWRITE[entity_name] else: package["command"] = entity_name package = logic.add_store_front_data(package, True) if package["name"] not in CS: package["cs"] = True for channel in package["channel-map"]: channel["channel"]["released-at"] = logic.convert_date( channel["channel"]["released-at"]) return package
def details_library(entity_name, library_name): channel_request = request.args.get("channel", default=None, type=str) package = get_package(entity_name, channel_request, FIELDS) libraries = logic.process_libraries( publisher_api.get_charm_libraries(entity_name)) library_id = logic.get_library(library_name, libraries) if not library_id: abort(404) library = publisher_api.get_charm_library(entity_name, library_id) docstrings = logic.process_python_docs(library, module_name=library_name) if "source-code" in request.path[1:]: template = "details/libraries/source-code.html" else: template = "details/libraries/docstring.html" return render_template( template, entity_name=entity_name, package=package, libraries=libraries, library=library, docstrings=docstrings, channel_requested=channel_request, library_name=library_name, creation_date=logic.convert_date(library["created-at"]), )
def details_overview(entity_name): # Get entity info from API package = app.store_api.get_item_details(entity_name, fields=FIELDS) package = logic.add_store_front_data(package) for channel in package["channel-map"]: channel["channel"]["released-at"] = logic.convert_date( channel["channel"]["released-at"]) readme = package["default-release"]["revision"]["readme-md"] # Remove Markdown comments readme = re.sub("(<!--.*-->)", "", readme, flags=re.DOTALL) readme = parser(readme) soup = BeautifulSoup(readme, features="html.parser") # Change all the headers (value + 2, eg h1 => h3) for h in soup.find_all(re.compile("^h[1-6]$")): level = int(h.name[1:]) + 2 if level > 6: level = 6 h.name = f"h{str(level)}" return render_template( "details/overview.html", package=package, readme=soup, package_type=package["type"], )
def index(): query = request.args.get("q", default=None, type=str) sort = request.args.get("sort", default="sort-asc", type=str) fields = [ "categories", "summary", "media", "name", "publisher", "revision", "channel", ] if query: results = app.store_api.find(query=query, fields=fields).get("results") else: results = app.store_api.find(fields=fields).get("results", []) charms = [] categories = [] for i, item in enumerate(results): results[i]["store_front"] = {} results[i]["store_front"]["icons"] = logic.get_icons(results[i]) results[i]["store_front"]["last_release"] = logic.convert_date( results[i]["default-release"]["channel"]["released-at"]) if results[i]["result"]["categories"]: results[i]["store_front"]["categories"] = logic.get_categories( results[i]["result"]["categories"]) else: results[i]["store_front"]["categories"] = [{ "name": "Other", "slug": "other" }] if (results[i]["type"] == "charm" and results[i]["result"]["publisher"]["display-name"]): for category in results[i]["store_front"]["categories"]: if category not in categories: categories.append(category) charms.append(results[i]) sorted_categories = sorted(categories, key=lambda k: k["name"]) sort_order = True if sort == "name-desc" else False charms = sorted(charms, key=lambda c: c["name"], reverse=sort_order) context = { "categories": sorted_categories, "sort": sort, "q": query, "results": charms, } return render_template("store.html", **context)
def get_package(entity_name, channel_request, fields): # Get entity info from API package = app.store_api.get_item_details(entity_name, channel=channel_request, fields=fields) # If the package is not published, return a 404 if not package["default-release"]: abort(404) package = logic.add_store_front_data(package, True) for channel in package["channel-map"]: channel["channel"]["released-at"] = logic.convert_date( channel["channel"]["released-at"]) return package
def entity_embedded_card(entity_name): channel_request = request.args.get("channel", default=None, type=str) package = get_package(entity_name, channel_request, FIELDS) package["default-release"]["channel"]["released-at"] = logic.convert_date( package["default-release"]["channel"]["released-at"]) button = request.args.get("button") if button and button not in ["black", "white"]: button = None context = { "button": button, "package": package, "show_channels": request.args.get("channels"), "show_summary": request.args.get("summary"), "show_base": request.args.get("base"), } return render_template( "embeddable-card.html", **context, )
def details_overview(entity_name): # Get entity info from API package = app.store_api.get_item_details(entity_name, fields=FIELDS) package = logic.add_store_front_data(package) for channel in package["channel-map"]: channel["channel"]["released-at"] = logic.convert_date( channel["channel"]["released-at"]) readme = package["default-release"]["revision"]["readme-md"] # Remove Markdown comments readme = re.sub("(<!--.*-->)", "", readme, flags=re.DOTALL) readme = md_parser(readme) readme = increase_headers(readme) return render_template( "details/overview.html", package=package, readme=readme, package_type=package["type"], )
def test_convert_date_more_today(self): date_test = datetime.datetime.now().strftime("%Y-%m-%d") result = logic.convert_date(date_test) self.assertEqual(result, "Today")
def test_convert_date_more_than_yesterday(self): date_test = "2019-01-12T16:48:41.821037+00:00" result = logic.convert_date(date_test) self.assertEqual(result, "12 January 2019")
def _get_context_snap_details(snap_name): try: details = api.get_item_details(snap_name, api_version=2) except StoreApiTimeoutError as api_timeout_error: flask.abort(504, str(api_timeout_error)) except StoreApiResponseDecodeError as api_response_decode_error: flask.abort(502, str(api_response_decode_error)) except StoreApiResponseErrorList 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 StoreApiResponseError as api_response_error: flask.abort(502, str(api_response_error)) except StoreApiCircuitBreaker: flask.abort(503) except (StoreApiError, ApiError) as api_error: flask.abort(502, str(api_error)) # When removing all the channel maps of an existing 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)) clean_description = bleach.clean(details["snap"]["description"], tags=[]) formatted_description = parse_markdown_description(clean_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"] # filter out banner and banner-icon images from screenshots screenshots = logic.filter_screenshots(details["snap"]["media"]) icons = logic.get_icon(details["snap"]["media"]) publisher_info = helpers.get_yaml( "{}{}.yaml".format( flask.current_app.config["CONTENT_DIRECTORY"] ["PUBLISHER_PAGES"], details["snap"]["publisher"]["username"], ), typ="safe", ) publisher_snaps = helpers.get_yaml( "{}{}-snaps.yaml".format( flask.current_app.config["CONTENT_DIRECTORY"] ["PUBLISHER_PAGES"], details["snap"]["publisher"]["username"], ), typ="safe", ) publisher_featured_snaps = None if publisher_info: publisher_featured_snaps = publisher_info.get("featured_snaps") publisher_snaps = logic.get_n_random_snaps( publisher_snaps["snaps"], 4) videos = logic.get_videos(details["snap"]["media"]) # until default tracks are supported by the API we special case node # to use 10, rather then latest default_track = helpers.get_default_track(details["name"]) if not default_track: default_track = (details.get("default-track") if details.get("default-track") 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) last_version = logic.get_version(channel_maps_list, default_track, lowest_risk_available) is_users_snap = False if authentication.is_authenticated(flask.session): if (flask.session.get("openid").get("nickname") == details["snap"]["publisher"]["username"] ) or ("user_shared_snaps" in flask.session and snap_name in flask.session.get("user_shared_snaps")): is_users_snap = True # build list of categories of a snap categories = logic.get_snap_categories(details["snap"]["categories"]) developer = logic.get_snap_developer(details["name"]) context = { "snap-id": details.get("snap-id"), # Data direct from details API "snap_title": details["snap"]["title"], "package_name": details["name"], "categories": categories, "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, "videos": videos, "publisher_snaps": publisher_snaps, "publisher_featured_snaps": publisher_featured_snaps, "has_publisher_page": publisher_info is not None, "prices": details["snap"]["prices"], "contact": details["snap"].get("contact"), "website": details["snap"].get("website"), "summary": details["snap"]["summary"], "description": formatted_description, "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, "trending": details["snap"]["trending"], # Transformed API data "filesize": humanize.naturalsize(binary_filesize), "last_updated": logic.convert_date(last_updated), "last_updated_raw": last_updated, "is_users_snap": is_users_snap, "unlisted": details["snap"]["unlisted"], "developer": developer, # TODO: This is horrible and hacky "appliances": { "adguard-home": "adguard", "mosquitto": "mosquitto", "nextcloud": "nextcloud", "plexmediaserver": "plex", "openhab": "openhab", }, } return context
def _get_context_snap_details(snap_name): 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)) clean_description = bleach.clean(details["snap"]["description"]) formatted_description = parse_markdown_description(clean_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"] # filter out banner and banner-icon images from screenshots screenshots = logic.filter_screenshots(details["snap"]["media"]) icons = logic.get_icon(details["snap"]["media"]) videos = logic.get_videos(details["snap"]["media"]) # until default tracks are supported by the API we special case node # to use 10, rather then latest default_track = helpers.get_default_track(details["name"]) 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 ) last_version = logic.get_version( channel_maps_list, default_track, lowest_risk_available ) is_users_snap = False if flask.session and "openid" in flask.session: if ( flask.session.get("openid").get("nickname") == details["snap"]["publisher"]["username"] ): is_users_snap = True # build list of categories of a snap categories = logic.get_snap_categories(details["snap"]["categories"]) context = { "snap-id": details.get("snap-id"), # Data direct from details API "snap_title": details["snap"]["title"], "package_name": details["name"], "categories": categories, "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, "videos": videos, "prices": details["snap"]["prices"], "contact": details["snap"].get("contact"), "website": details["snap"].get("website"), "summary": details["snap"]["summary"], "description": formatted_description, "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": logic.convert_date(last_updated), "last_updated_raw": last_updated, "is_users_snap": is_users_snap, } return context
def test_convert_date_more_today(self): date_test = datetime.datetime.now().strftime("%Y-%m-%d") result = logic.convert_date(date_test) self.assertEqual(result, "Today")
def test_convert_date_more_than_yesterday(self): date_test = "2019-01-12T16:48:41.821037+00:00" result = logic.convert_date(date_test) self.assertEqual(result, "12 January 2019")
def _get_context_snap_details(snap_name): 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)) clean_description = bleach.clean(details["snap"]["description"], tags=[]) formatted_description = parse_markdown_description(clean_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"] # filter out banner and banner-icon images from screenshots screenshots = logic.filter_screenshots(details["snap"]["media"]) icons = logic.get_icon(details["snap"]["media"]) videos = logic.get_videos(details["snap"]["media"]) # until default tracks are supported by the API we special case node # to use 10, rather then latest default_track = helpers.get_default_track(details["name"]) 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) last_version = logic.get_version(channel_maps_list, default_track, lowest_risk_available) is_users_snap = False if flask.session and "openid" in flask.session: if (flask.session.get("openid").get("nickname") == details["snap"] ["publisher"]["username"]): is_users_snap = True # build list of categories of a snap categories = logic.get_snap_categories(details["snap"]["categories"]) context = { "snap-id": details.get("snap-id"), # Data direct from details API "snap_title": details["snap"]["title"], "package_name": details["name"], "categories": categories, "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, "videos": videos, "prices": details["snap"]["prices"], "contact": details["snap"].get("contact"), "website": details["snap"].get("website"), "summary": details["snap"]["summary"], "description": formatted_description, "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": logic.convert_date(last_updated), "last_updated_raw": last_updated, "is_users_snap": is_users_snap, } return context