def _list(client): """ Lists all of the apps that the user has rated If no apps have been rated, then an empty formatted list is printed Args: client (two1.server.rest_client.TwentyOneRestClient) an object for sending authenticated requests to the TwentyOne backend. Raises: ServerRequestError: If server error occurs other than a 404 """ logger.info(uxstring.UxString.rating_list) try: ratings = client.get_ratings() headers = ["id", "App title", "Creator", "Rating", "Rating Date"] ratings = ratings.json()["ratings"] rows = [] for rating in ratings: rating_date = util.format_date(rating["rating_date"]) rating_score = "{}/5".format(rating["rating"]) rows.append([rating["app_id"], rating["app_title"], rating["app_creator"], rating_score, rating_date]) logger.info(tabulate(rows, headers, tablefmt="simple")) except exceptions.ServerRequestError as e: if e.status_code == 404: logger.info(uxstring.UxString.no_ratings) return else: raise e
def buy_bitcoin(client, amount): if click.confirm(uxstring.UxString.buybitcoin_confirmation_prompt): logger.info(uxstring.UxString.coinbase_purchase_in_progress) try: resp = client.buy_bitcoin_from_exchange(amount, "satoshis", commit=True) except exceptions.ServerRequestError as e: if e.status_code == 403 and e.data.get("error") == "TO703": logger.error(uxstring.UxString.coinbase_max_buy_reached) return elif e.status_code == 422: logger.error(e.data.get("message")) buy_result = resp.json() if buy_result["status"] == "canceled": logger.info(uxstring.UxString.buybitcoin_error.format( click.style("Buy was canceled.", bold=True, fg="red"))) return buy_result amount_bought = int(float(buy_result["amount"]["amount"]) * 1e8) btc_bought = "{} {}".format(amount_bought, 'satoshis') dollars_paid = "{} {}".format(buy_result["total"]["amount"], buy_result["total"]["currency"]) logger.info(uxstring.UxString.buybitcoin_success.format(btc_bought, dollars_paid)) if "instant" in buy_result and buy_result["instant"]: logger.info(uxstring.UxString.buybitcoin_success_instant) elif "payout_at" in buy_result: payout_time = util.format_date(buy_result["payout_at"]) logger.info(uxstring.UxString.buybitcoin_success_payout_time.format(payout_time)) else: logger.info("\nPurchase canceled", fg="magenta")
def buy_bitcoin(client, amount): if click.confirm(uxstring.UxString.buybitcoin_confirmation_prompt): logger.info(uxstring.UxString.coinbase_purchase_in_progress) try: resp = client.buy_bitcoin_from_exchange(amount, "satoshis", commit=True) except exceptions.ServerRequestError as e: PHOTO_ID_ERROR = "Before you will be able to complete this buy, "\ "you must provide additional information at "\ "https://www.coinbase.com/photo-id" USERNAME_ERROR = "To process payments we require a valid user name. "\ "Please go to settings to update your information." if e.status_code == 403 and e.data.get("error") == "TO703": logger.error(uxstring.UxString.coinbase_max_buy_reached) return elif e.status_code == 500 and e.data.get( "error") == PHOTO_ID_ERROR: logger.error(uxstring.UxString.coinbase_needs_photo_id) return elif e.status_code == 500 and e.data.get( "error") == USERNAME_ERROR: logger.error(uxstring.UxString.coinbase_needs_username) return buy_result = resp.json() if buy_result["status"] == "canceled": logger.info( uxstring.UxString.buybitcoin_error.format( click.style("Buy was canceled.", bold=True, fg="red"))) return buy_result amount_bought = int(float(buy_result["amount"]["amount"]) * 1e8) btc_bought = "{} {}".format(amount_bought, 'satoshis') dollars_paid = "{} {}".format(buy_result["total"]["amount"], buy_result["total"]["currency"]) logger.info( uxstring.UxString.buybitcoin_success.format( btc_bought, dollars_paid)) if "instant" in buy_result and buy_result["instant"]: logger.info(uxstring.UxString.buybitcoin_success_instant) elif "payout_at" in buy_result: payout_time = util.format_date(buy_result["payout_at"]) logger.info( uxstring.UxString.buybitcoin_success_payout_time.format( payout_time)) else: logger.info("\nPurchase canceled", fg="magenta")
def buybitcoin_history(config, client, exchange): resp = client.get_coinbase_status() if not resp.ok: raise exceptions.Two1Error("Failed to get exchange status") coinbase = resp.json()["coinbase"] if not coinbase: # Not linked, prompt user to info return buybitcoin_config(config, client, exchange) else: resp = client.get_coinbase_history() history = resp.json()["history"] lines = [uxstring.UxString.coinbase_history_title] for entry in history: amount = entry["amount"] deposit_status = entry["deposit_status"] payout_time = util.format_date(entry["payout_time"]) payout_address = entry["payout_address"] description = "N/A" if deposit_status == "COMPLETED": description = uxstring.UxString.coinbase_wallet_completed.format( payout_time) else: description = uxstring.UxString.coinbase_wallet_pending.format( payout_time) created = util.format_date(entry["created"]) lines.append( uxstring.UxString.coinbase_history.format( created, amount, payout_address, description)) if len(history) == 0: lines.append(uxstring.UxString.coinbase_no_bitcoins_purchased) prints = "\n\n".join(lines) logger.info(prints, pager=True)
def history(ctx, n, reverse, json_output, account): """ Print the wallet's history """ w = ctx.obj['wallet'] history = w.transaction_history(accounts=list(account)) if reverse: h = list(reversed(history)) else: h = history if n > 0: h = h[:n] if json_output: click.echo(json.dumps(h)) return for i, th in enumerate(h): dt = util.format_date(int(th['time'])) click.echo("%s (%s)" % (th['txid'], dt)) click.echo("%s" % ('-' * 86)) click.echo("Type: %s" % (th['classification'])) if th['classification'] == "deposit": for d in th['deposits']: click.echo("Received %d satoshis into %s (Account: %s)" % ( d['value'], d['address'], d['acct'])) elif th['classification'] in ["spend", "internal_transfer"]: for i in range(max(len(th['spends']), len(th['deposits'])) + 1): msg = "" if i < len(th['spends']): s = th['spends'][i] msg = "%12d satoshis from %35s" % (s['value'], s['address']) else: msg = "%s" % (" " * 62) if i < len(th['deposits']): d = th['deposits'][i] msg += "%s%12d satoshis to %35s (%s)" % ( " " * 5, d['value'], d['address'], d['addr_type']) if i == len(th['deposits']): msg += "%s%12d satoshis to %35s (fees)" % ( " " * 5, th['fees'], "miner") click.echo(msg) click.echo()
def get_search_results(config, client, page): """ Queries the marketplace for published apps Args: config (Config): config object used for getting .two1 information client (TwentyOneRestClient): rest client used for communication with the backend api. page (int): the page number used in querying the paginated marketplace api. Returns: int: the total number of pages returned by the server """ resp = client.get_published_apps(config.username, page) resp_json = resp.json() search_results = resp_json["results"] if search_results is None or len(search_results) == 0: logger.info(click.style( "You haven't published any apps to the marketplace yet. Use ", fg="blue") + click.style("21 publish submit {PATH_TO_MANIFEST_FILE}", bold=True, fg="blue") + click.style(" to publish your apps to the marketplace.", fg="blue"), fg="blue") return 0 total_pages = resp_json["total_pages"] logger.info("\nPage {}/{}".format(page + 1, total_pages), fg="green") headers = [ "id", "Title", "Url", "Rating", "Is up", "Is healthy", "Average Uptime", "Last Update" ] rows = [] for r in search_results: rating = "Not yet Rated" if r["rating_count"] > 0: rating = "{:.1f} ({} rating".format(r["average_rating"], int(r["rating_count"])) if r["rating_count"] > 1: rating += "s" rating += ")" rows.append([ r["id"], r["title"], r["app_url"], rating, str(r["is_up"]), str(r["is_healthy"]), "{:.2f}%".format(r["average_uptime"] * 100), util.format_date(r["last_update"]) ]) logger.info(tabulate(rows, headers, tablefmt="simple")) return total_pages
def _get_headline(entry): # headline local_date = util.format_date(entry["date"]) if entry["amount"] > 0: headline = uxstring.UxString.debit_message.format(local_date, entry["amount"]) elif entry["reason"] == "flush_payout" or entry["reason"] == "earning_payout": headline = uxstring.UxString.blockchain_credit_message.format(local_date, entry["amount"], -entry["amount"]) else: headline = uxstring.UxString.credit_message.format(local_date, entry["amount"]) headline = click.style(headline, fg="cyan") return headline
def buybitcoin_history(config, client, exchange): resp = client.get_coinbase_status() if not resp.ok: raise exceptions.Two1Error("Failed to get exchange status") coinbase = resp.json()["coinbase"] if not coinbase: # Not linked, prompt user to info return buybitcoin_config(config, client, exchange) else: resp = client.get_coinbase_history() history = resp.json()["history"] lines = [uxstring.UxString.coinbase_history_title] for entry in history: amount = entry["amount"] deposit_status = entry["deposit_status"] payout_time = util.format_date(entry["payout_time"]) payout_address = entry["payout_address"] description = "N/A" if deposit_status == "COMPLETED": description = uxstring.UxString.coinbase_wallet_completed.format(payout_time) else: description = uxstring.UxString.coinbase_wallet_pending.format(payout_time) created = util.format_date(entry["created"]) lines.append(uxstring.UxString.coinbase_history.format( created, amount, payout_address, description)) if len(history) == 0: lines.append(uxstring.UxString.coinbase_no_bitcoins_purchased) prints = "\n\n".join(lines) logger.info(prints, pager=True)
def create_notification_line(msg): """Create a formatted notification line from a message dict. Args: msg (dict): a raw inbox notification in dict format Returns: str: a formatted notification message """ local_time = util.format_date(msg["time"]) message_line = click.style("{} : {} from {}\n".format(local_time, msg["type"], msg["from"]), fg="cyan") message_line += "{}\n".format(msg["content"]) return message_line
def history(ctx, n, reverse, json_output, account): """ Print the wallet's history """ w = ctx.obj['wallet'] history = w.transaction_history(accounts=list(account)) if reverse: h = list(reversed(history)) else: h = history if n > 0: h = h[:n] if json_output: click.echo(json.dumps(h)) return for i, th in enumerate(h): dt = util.format_date(int(th['time'])) click.echo("%s (%s)" % (th['txid'], dt)) click.echo("%s" % ('-' * 86)) click.echo("Type: %s" % (th['classification'])) if th['classification'] == "deposit": for d in th['deposits']: click.echo("Received %d satoshis into %s (Account: %s)" % (d['value'], d['address'], d['acct'])) elif th['classification'] in ["spend", "internal_transfer"]: for i in range(max(len(th['spends']), len(th['deposits'])) + 1): msg = "" if i < len(th['spends']): s = th['spends'][i] msg = "%12d satoshis from %35s" % (s['value'], s['address']) else: msg = "%s" % (" " * 62) if i < len(th['deposits']): d = th['deposits'][i] msg += "%s%12d satoshis to %35s (%s)" % ( " " * 5, d['value'], d['address'], d['addr_type']) if i == len(th['deposits']): msg += "%s%12d satoshis to %35s (fees)" % ( " " * 5, th['fees'], "miner") click.echo(msg) click.echo()
def get_search_results(config, client, page): """ Queries the marketplace for published apps Args: config (Config): config object used for getting .two1 information client (TwentyOneRestClient): rest client used for communication with the backend api. page (int): the page number used in querying the paginated marketplace api. Returns: int: the total number of pages returned by the server """ resp = client.get_published_apps(config.username, page) resp_json = resp.json() search_results = resp_json["results"] if search_results is None or len(search_results) == 0: logger.info( click.style("You haven't published any apps to the marketplace yet. Use ", fg="blue") + click.style("21 publish submit {PATH_TO_MANIFEST_FILE}", bold=True, fg="blue") + click.style(" to publish your apps to the marketplace.", fg="blue"), fg="blue") return 0 total_pages = resp_json["total_pages"] logger.info("\nPage {}/{}".format(page + 1, total_pages), fg="green") headers = ["id", "Title", "Url", "Rating", "Is up", "Is healthy", "Average Uptime", "Last Update"] rows = [] for r in search_results: rating = "Not yet Rated" if r["rating_count"] > 0: rating = "{:.1f} ({} rating".format(r["average_rating"], int(r["rating_count"])) if r["rating_count"] > 1: rating += "s" rating += ")" rows.append([r["id"], r["title"], r["app_url"], rating, str(r["is_up"]), str(r["is_healthy"]), "{:.2f}%".format(r["average_uptime"] * 100), util.format_date(r["last_update"])]) logger.info(tabulate(rows, headers, tablefmt="simple")) return total_pages
def buy_bitcoin(client, amount): if click.confirm(uxstring.UxString.buybitcoin_confirmation_prompt): logger.info(uxstring.UxString.coinbase_purchase_in_progress) try: resp = client.buy_bitcoin_from_exchange(amount, "satoshis", commit=True) except exceptions.ServerRequestError as e: PHOTO_ID_ERROR = "Before you will be able to complete this buy, "\ "you must provide additional information at "\ "https://www.coinbase.com/photo-id" USERNAME_ERROR = "To process payments we require a valid user name. "\ "Please go to settings to update your information." if e.status_code == 403 and e.data.get("error") == "TO703": logger.error(uxstring.UxString.coinbase_max_buy_reached) return elif e.status_code == 500 and e.data.get("error") == PHOTO_ID_ERROR: logger.error(uxstring.UxString.coinbase_needs_photo_id) return elif e.status_code == 500 and e.data.get("error") == USERNAME_ERROR: logger.error(uxstring.UxString.coinbase_needs_username) return buy_result = resp.json() if buy_result["status"] == "canceled": logger.info(uxstring.UxString.buybitcoin_error.format( click.style("Buy was canceled.", bold=True, fg="red"))) return buy_result amount_bought = int(float(buy_result["amount"]["amount"]) * 1e8) btc_bought = "{} {}".format(amount_bought, 'satoshis') dollars_paid = "{} {}".format(buy_result["total"]["amount"], buy_result["total"]["currency"]) logger.info(uxstring.UxString.buybitcoin_success.format(btc_bought, dollars_paid)) if "instant" in buy_result and buy_result["instant"]: logger.info(uxstring.UxString.buybitcoin_success_instant) elif "payout_at" in buy_result: payout_time = util.format_date(buy_result["payout_at"]) logger.info(uxstring.UxString.buybitcoin_success_payout_time.format(payout_time)) else: logger.info("\nPurchase canceled", fg="magenta")
def display_search_info(client, listing_id): """ Given a listing id, format and print detailed information to the command line Args: client (TwentyOneRestClient): rest client used for communication with the backend api listing_id (str): unique marketplace listing id Raises: ServerRequestError: If server returns an error code other than a 404 """ try: resp = client.get_listing_info(listing_id) except exceptions.ServerRequestError as e: if e.status_code == 404: logger.info(uxstring.UxString.app_does_not_exist.format(listing_id)) return else: raise e result_json = resp.json() title = click.style("App Name : ", fg="blue") + click.style( "{}".format(result_json["title"])) created_by = click.style("Created By : ", fg="blue") + click.style( "{}".format(result_json["username"])) desc = click.style("Description : ", fg="blue") + click.style( "{}".format(result_json["description"])) price = click.style("Price Range : ", fg="blue") + click.style( "{} - {} Satoshis").format(result_json["min_price"], result_json["max_price"]) if result_json["rating_count"] == 0: rating_str = "Not yet rated" else: rating_str = "{:.1f} ({} rating".format(result_json["average_rating"], int(result_json["rating_count"])) if result_json["rating_count"] > 1: rating_str += "s" rating_str += ")" rating = click.style("Rating : ", fg="blue") + click.style("{}".format(rating_str)) doc_url = click.style("Docs URL : ", fg="blue") + click.style( "{}".format(result_json["website_url"])) app_url = click.style("App URL : ", fg="blue") + click.style( "{}".format(result_json["app_url"])) category = click.style("Category : ", fg="blue") + click.style( "{}".format(result_json["category"])) version = click.style("Version : ", fg="blue") + click.style( "{}".format(result_json["version"])) last_updated_str = util.format_date(result_json["updated"]) last_update = click.style("Last Update : ", fg="blue") + click.style( "{}".format(last_updated_str)) quick_start = click.style("Quick Start\n\n", fg="blue") + click.style( result_json["quick_buy"]) is_active = click.style("Status : ", fg="blue") if result_json["is_active"] and result_json["is_up"] and result_json["is_healthy"]: is_active += click.style("Active") else: is_active += click.style("Inactive") availability = click.style("Availability : ", fg="blue") + click.style( "{:.2f}%".format(result_json["average_uptime"] * 100)) usage_docs = None if "usage_docs" in result_json: usage_docs = click.style("Detailed usage\n\n", fg="blue") + click.style( result_json["usage_docs"]) pager_components = [title, desc, created_by, price, rating, "\n", is_active, availability, "\n", doc_url, app_url, "\n", category, version, last_update, "\n", quick_start, "\n"] if usage_docs: pager_components.append(usage_docs + "\n") final_str = "\n".join(pager_components) logger.info(final_str, pager=True)
def display_app_info(config, client, app_id): """ Displays info about the application selected Args: config (Config): config object used for getting .two1 information client (TwentyOneRestClient): rest client used for communication with the backend api Raises: ServerRequestError: if server returns an error code other than 404 """ try: resp = client.get_app_full_info(config.username, app_id) result = resp.json() app_info = result["app_info"] title = click.style("App Name : ", fg="blue") + click.style( "{}".format(app_info["title"])) if app_info["rating_count"] == 0: rating = "Not yet rated" else: rating = "{:.1f} ({} rating".format(app_info["average_rating"], int(app_info["rating_count"])) if app_info["rating_count"] > 1: rating += "s" rating += ")" rating_row = click.style("Rating : ", fg="blue") + click.style("{}".format(rating)) up_status = click.style("Status : ", fg="blue") if app_info["is_up"]: up_status += click.style("Up") else: up_status += click.style("Down") last_crawl_str = "Not yet crawled" if "last_crawl" in app_info: last_crawl_str = util.format_date(app_info["last_crawl"]) last_crawl = click.style("Last Crawl Time : ", fg="blue") + click.style( "{}".format(last_crawl_str)) version = click.style("Version : ", fg="blue") + click.style( "{}".format(app_info["version"])) last_updated_str = util.format_date(app_info["updated"]) last_update = click.style("Last Update : ", fg="blue") + click.style( "{}".format(last_updated_str)) availability = click.style("Availability : ", fg="blue") + click.style( "{:.2f}%".format(app_info["average_uptime"] * 100)) app_url = click.style("App URL : ", fg="blue") + click.style( "{}".format(app_info["app_url"])) category = click.style("Category : ", fg="blue") + click.style( "{}".format(app_info["category"])) desc = click.style("Description : ", fg="blue") + click.style( "{}".format(app_info["description"])) price = click.style("Price Range : ", fg="blue") + click.style( "{} - {} Satoshis").format( app_info["min_price"], app_info["max_price"]) doc_url = click.style("Docs URL : ", fg="blue") + click.style( "{}".format(app_info["docs_url"])) manifest_url = click.style("Manifest URL : ", fg="blue") + click.style( "{}".format(app_info["manifest_url"])) quick_start = click.style("Quick Start\n\n", fg="blue") + click.style( app_info["quick_buy"]) usage_docs = None if "usage_docs" in app_info: usage_docs = click.style("Detailed usage\n\n", fg="blue") + click.style( app_info["usage_docs"]) page_components = [title, "\n", rating_row, up_status, availability, last_crawl, last_update, version, "\n", desc, app_url, doc_url, manifest_url, "\n", category, price, "\n", quick_start, "\n"] if usage_docs: page_components.append(usage_docs + "\n") final_str = "\n".join(page_components) logger.info(final_str, pager=True) except ServerRequestError as e: if e.status_code == 404: logger.info(uxstring.UxString.app_does_not_exist.format(app_id)) else: raise e
def display_app_info(config, client, app_id): """ Displays info about the application selected Args: config (Config): config object used for getting .two1 information client (TwentyOneRestClient): rest client used for communication with the backend api Raises: ServerRequestError: if server returns an error code other than 404 """ try: resp = client.get_app_full_info(config.username, app_id) result = resp.json() app_info = result["app_info"] title = click.style("App Name : ", fg="blue") + click.style( "{}".format(app_info["title"])) if app_info["rating_count"] == 0: rating = "Not yet rated" else: rating = "{:.1f} ({} rating".format(app_info["average_rating"], int(app_info["rating_count"])) if app_info["rating_count"] > 1: rating += "s" rating += ")" rating_row = click.style("Rating : ", fg="blue") + click.style("{}".format(rating)) up_status = click.style("Status : ", fg="blue") if app_info["is_up"]: up_status += click.style("Up") else: up_status += click.style("Down") last_crawl_str = "Not yet crawled" if "last_crawl" in app_info: last_crawl_str = util.format_date(app_info["last_crawl"]) last_crawl = click.style("Last Crawl Time : ", fg="blue") + click.style( "{}".format(last_crawl_str)) version = click.style("Version : ", fg="blue") + click.style( "{}".format(app_info["version"])) last_updated_str = util.format_date(app_info["updated"]) last_update = click.style("Last Update : ", fg="blue") + click.style( "{}".format(last_updated_str)) availability = click.style("Availability : ", fg="blue") + click.style("{:.2f}%".format( app_info["average_uptime"] * 100)) app_url = click.style("Public App URL : ", fg="blue") + click.style( "{}".format(app_info["app_url"])) original_url = click.style("Private App URL : ", fg="blue") + click.style("{}".format( app_info["original_url"])) category = click.style("Category : ", fg="blue") + click.style( "{}".format(app_info["category"])) desc = click.style("Description : ", fg="blue") + click.style( "{}".format(app_info["description"])) price = click.style( "Price Range : ", fg="blue") + click.style("{} - {} Satoshis").format( app_info["min_price"], app_info["max_price"]) doc_url = click.style("Docs URL : ", fg="blue") + click.style( "{}".format(app_info["docs_url"])) manifest_url = click.style("Manifest URL : ", fg="blue") + click.style("{}".format( app_info["manifest_url"])) quick_start = click.style("Quick Start\n\n", fg="blue") + click.style( app_info["quick_buy"]) usage_docs = None if "usage_docs" in app_info: usage_docs = click.style("Detailed usage\n\n", fg="blue") + click.style( app_info["usage_docs"]) page_components = [ title, "\n", rating_row, up_status, availability, last_crawl, last_update, version, "\n", desc, app_url, original_url, doc_url, manifest_url, "\n", category, price, "\n", quick_start, "\n" ] if usage_docs: page_components.append(usage_docs + "\n") final_str = "\n".join(page_components) logger.info(final_str, pager=True) except ServerRequestError as e: if e.status_code == 404: logger.info( "The specified id for the app ({}) does not match any apps in the " "marketplace.".format(app_id)) else: raise e
def display_search_info(client, listing_id): """ Given a listing id, format and print detailed information to the command line Args: client (TwentyOneRestClient): rest client used for communication with the backend api listing_id (str): unique marketplace listing id Raises: ServerRequestError: If server returns an error code other than a 404 """ try: resp = client.get_listing_info(listing_id) except exceptions.ServerRequestError as e: if e.status_code == 404: logger.info( uxstring.UxString.app_does_not_exist.format(listing_id)) return else: raise e result_json = resp.json() title = click.style("App Name : ", fg="blue") + click.style( "{}".format(result_json["title"])) created_by = click.style("Created By : ", fg="blue") + click.style( "{}".format(result_json["username"])) desc = click.style("Description : ", fg="blue") + click.style("{}".format( result_json["description"])) price = click.style("Price Range : ", fg="blue") + click.style("{} - {} Satoshis").format( result_json["min_price"], result_json["max_price"]) if result_json["rating_count"] == 0: rating_str = "Not yet rated" else: rating_str = "{:.1f} ({} rating".format( result_json["average_rating"], int(result_json["rating_count"])) if result_json["rating_count"] > 1: rating_str += "s" rating_str += ")" rating = click.style("Rating : ", fg="blue") + click.style( "{}".format(rating_str)) doc_url = click.style("Docs URL : ", fg="blue") + click.style( "{}".format(result_json["website_url"])) app_url = click.style("App URL : ", fg="blue") + click.style( "{}".format(result_json["app_url"])) category = click.style("Category : ", fg="blue") + click.style( "{}".format(result_json["category"])) version = click.style("Version : ", fg="blue") + click.style( "{}".format(result_json["version"])) last_updated_str = util.format_date(result_json["updated"]) last_update = click.style("Last Update : ", fg="blue") + click.style( "{}".format(last_updated_str)) quick_start = click.style("Quick Start\n\n", fg="blue") + click.style( result_json["quick_buy"]) is_active = click.style("Status : ", fg="blue") if result_json["is_active"] and result_json["is_up"] and result_json[ "is_healthy"]: is_active += click.style("Active") else: is_active += click.style("Inactive") availability = click.style("Availability : ", fg="blue") + click.style( "{:.2f}%".format(result_json["average_uptime"] * 100)) usage_docs = None if "usage_docs" in result_json: usage_docs = click.style("Detailed usage\n\n", fg="blue") + click.style( result_json["usage_docs"]) pager_components = [ title, desc, created_by, price, rating, "\n", is_active, availability, "\n", doc_url, app_url, "\n", category, version, last_update, "\n", quick_start, "\n" ] if usage_docs: pager_components.append(usage_docs + "\n") final_str = "\n".join(pager_components) logger.info(final_str, pager=True)