def handle_add_to_watchlist(request): """ Generate response to intent type AddStockToWatchlistIntent based on the stage of the adding stock to portfolio process. :type request AlexaRequest :return: JSON response including appropriate response based on the stage in the adding process. """ if request.dialog_state() == "STARTED": return _handle_dialog_add_started(request) elif request.dialog_state() == "IN_PROGRESS": return _handle_dialog_add_in_progress(request) elif request.dialog_state() == "COMPLETED": # TODO return ResponseBuilder.create_response( request, "Add to watchlist dialog completed! ") elif request.dialog_state() == "": # TODO print("LOG-d: dialogState not included") return ResponseBuilder.create_response( request, "Add to watchlist dialog state empty! ") else: print("LOG-d: dialogState else") # TODO print("LOG-d: dialogState not included") return ResponseBuilder.create_response( request, "Add to watchlist dialog state ELSE! ")
def _handle_dialog_remove_started(request): """ Check if the provided ticker is valid and stock is in watchlist, if yes, ask for confirmation. Otherwise, inform about the state. :type request AlexaRequest """ logger.debug("dialogState STARTED") user_id = request.user_id() # Check if ticker is provided try: ticker = _check_valid_ticker_provided(request) except AttributeError as e: logger.exception("No valid ticker provided") message = strings.INTENT_REMOVE_FROM_WATCHLIST_FAIL return ResponseBuilder.create_response(request, message=message) \ .with_reprompt(strings.INTENT_GENERAL_REPROMPT) # Check if stock is in users Watchlist is_in_watchlist = Watchlist.ticker_in_watchlist_exists(user_id, ticker) # Inform that stock not in watchlist, or ask user to confirm ticker remove if is_in_watchlist: logger.debug( f"Ask confirmation: remove stock {ticker} from user:{user_id} watchlist" ) message = strings.INTENT_REMOVE_FROM_WATCHLIST_ASK_CONFIRMATION \ .format(ticker) return ResponseBuilder.create_response(request, message) \ .with_dialog_confirm_intent() else: logger.debug( f"Trying to remove stock {ticker}, which is not in wathclist") message = strings.INTENT_REMOVE_FROM_WATCHLIST_NOT_THERE.format(ticker) return ResponseBuilder.create_response(request, message)
def _handle_dialog_add_started(request): """ Check if the provided ticker is supported or is not already in watchlist, if not, ask for confirmation. :type request AlexaRequest """ print("LOG-d: dialogState STARTED") # Check if ticker is provided try: ticker = _check_valid_ticker_provided(request) except AttributeError as e: logger.exception("No valid ticker provided") message = strings.INTENT_ADDED_TO_WATCHLIST_FAIL return ResponseBuilder.create_response(request, message=message) \ .with_reprompt(strings.INTENT_GENERAL_REPROMPT) # Ask user to confirm ticker add message = strings.INTENT_ADD_TO_WATCHLIST_ASK_CONFIRMATION.format(ticker) # Check if ticker not already in Watchlist user_id = request.get_user_id() watchlist_tickers = Watchlist.get_users_tickers(user_id) for ticker_in_watchlist in watchlist_tickers: if ticker == ticker_in_watchlist: message = strings.INTENT_ADDED_TO_WATCHLIST_EXISTS.format(ticker) return ResponseBuilder.create_response(request, message) \ .with_dialog_confirm_intent()
def handle_market_cap(request): """ Generate response to intent type MarketCapIntent with the current market cap of the ticker. :type request AlexaRequest :return: JSON response including market cap of the ticker """ ticker = request.get_slot_value(slot_name="stockTicker").upper() # Query DB for stock data company_stats = Stock.get_stats(ticker) market_cap = company_stats.get('marketcap', None) company_name = company_stats.get('companyName', ticker) if type(market_cap) is NoneType: logger.error(f"There was an error getting market capitalization for {company_name}") message = strings.INTENT_MARKET_CAP_MSG_FAIL.format(ticker) response = ResponseBuilder.create_response(request, message=message) else: message = strings.INTENT_MARKET_CAP_MSG.format(company_name, market_cap) response = ResponseBuilder.create_response(request, message=message) \ .set_session('stockTicker', ticker) reprompt_message = strings.INTENT_GENERAL_REPROMPT return response.with_reprompt(reprompt_message)
def handle_recommendation(request): """ Generate response to intent type RecommendationIntent with the analytics recommendation for a particular stock. :type request AlexaRequest :return: JSON response including a recommendation what the analytics think about that stock """ ticker = request.get_slot_value(slot_name="stockTicker").upper() recommendation = Analyst(ticker).recommendation() logger.info( f"recommendationMean for {ticker} is {recommendation}") # pick right response based on recommendation mean rating if recommendation is None: message = strings.INTENT_RCMD_NO_RCMD elif _in_interval(recommendation, 1, 1.8): message = strings.INTENT_RCMD_STRONG_BUY elif _in_interval(recommendation, 1.8, 2.2): message = strings.INTENT_RCMD_BUY elif _in_interval(recommendation, 2.2, 2.8): message = strings.INTENT_RCMD_OPT_HOLD elif _in_interval(recommendation, 2.8, 3.2): message = strings.INTENT_RCMD_HOLD elif _in_interval(recommendation, 3.2, 3.8): message = strings.INTENT_RCMD_PES_HOLD elif _in_interval(recommendation, 3.8, 4.2): message = strings.INTENT_RCMD_SELL elif _in_interval(recommendation, 4.2, 5): message = strings.INTENT_RCMD_STRONG_SELL response = ResponseBuilder.create_response(request, message=message) reprompt_message = strings.INTENT_GENERAL_REPROMPT return response.with_reprompt(reprompt_message)
def handle_not_authenticated(request): """ Build a response for case where user has not linked the account in Alexa App. """ message = strings.ERROR_NOT_AUTHENTICATED reprompt_message = strings.ERROR_NOT_AUTHENTICATED_REPROMPT return ResponseBuilder.create_response(request, message=message) \ .with_reprompt(message=reprompt_message) \ .with_card("", card_type="LinkAccount")
def handle_launch(request): """ Generate response for a launch request like 'Open OttoBot'. :param request: incoming parsed Alexa request :return: Generated JSON welcome response answer """ message = strings.REQUEST_LAUNCH_MSG reprompt_message = strings.REQUEST_LAUNCH_REPROMPT return ResponseBuilder.create_response(request, message=message) \ .with_reprompt(message=reprompt_message)
def _handle_dialog_add_in_progress(request): """ Check if the stock adding request was confirmed by the user. If it was, add it to portfolio, otherwise do not add. :type request AlexaRequest """ logger.debug("dialogState IN_PROGRESS") if request.get_intent_confirmation_status() == "CONFIRMED": return _add_ticker_to_watchlist(request) else: message = strings.INTENT_ADD_TO_WATCHLIST_DENIED return ResponseBuilder.create_response(request, message=message)
def handle_help_intent(request): """ Generate response to intent type HelpIntent which presents the available futures to the confused user. :type request AlexaRequest :return: JSON response including introduced capabilities of the skill """ logger.info("Help requested by the user") message = strings.INTENT_HELP reprompt_message = strings.INTENT_GENERAL_REPROMPT return ResponseBuilder.create_response(request, message=message)\ .with_reprompt(reprompt_message)
def _handle_dialog_remove_in_progress(request): """ Check if the stock removing request was confirmed by the user. If it was, remove it from portfolio, otherwise do not. :type request AlexaRequest """ logger.debug("dialogState IN_PROGRESS") if request.get_intent_confirmation_status() == "CONFIRMED": return _remove_ticker_from_watchlist(request) else: logger.debug(f"Deny deleting of stock from watchlist") message = strings.INTENT_REMOVE_FROM_WATCHLIST_DENIED return ResponseBuilder.create_response(request, message=message)
def handle_get_stock_price_intent(request): """ Generate response to intent type WhatsTheStockPriceIntent with the current price of the added ticker. :type request AlexaRequest :return: JSON response including stock price """ ticker = request.get_slot_value(slot_name="stockTicker").upper() # Query DB for stock data stock = Stock.get_last(ticker) if type(stock) is NoneType: logger.error(f"There was an error getting data for {ticker}") message = strings.INTENT_STOCK_PRICE_MSG_FAIL.format(ticker) response = ResponseBuilder.create_response(request, message=message) else: message = strings.INTENT_STOCK_PRICE_MSG.format(stock['ticker'], stock['price']) response = ResponseBuilder.create_response(request, message=message) \ .set_session('stockTicker', stock['ticker']) reprompt_message = strings.INTENT_GENERAL_REPROMPT return response.with_reprompt(reprompt_message)
def _handle_dialog_send_article(request): """Create a response with card containing requested article.""" reprompt = strings.INTENT_GENERAL_REPROMPT ticker = request.get_slot_value('stockTicker') company = Ticker2Name.ticker_to_name(ticker) article_no = int(request.get_slot_value('articleNo')) - 1 # last news if article_no == -1: article_no = news_count - 1 feed_reader = FeedReader(ticker) article_title = feed_reader.get_articles_titles()[article_no] article_body = feed_reader.get_article_body(article_no) if article_body is None: # Article not found message = strings.INTENT_NEWS_ABOUT_COMPANY_FAIL_ARTICLE_NOT_FOUND return ResponseBuilder.create_response(request, message=message) \ .with_reprompt(reprompt) # Build response message = strings.INTENT_NEWS_ABOUT_COMPANY_ARTICLE_SENT card_title = strings.INTENT_NEWS_ABOUT_COMPANY_ARTICLE_CARD_TITLE \ .format(company) card_content = strings.INTENT_NEWS_ABOUT_COMPANY_ARTICLE_CARD_CONTENT \ .format(article_body) send_as_card = True if send_as_card: return ResponseBuilder.create_response(request, message=message) \ .with_reprompt(reprompt) \ .with_card(card_title, content=card_content, subtitle=article_title) else: # Present article as speech return ResponseBuilder.create_response(request, message=card_content) \ .with_reprompt(reprompt)
def handle_investing_strategy(request): """ Generate response to intent type InvestingStrategyIntent with explanation of a random investing strategy. :type request AlexaRequest :return: JSON response including investing strategy name and explanation """ strategies = strings_edu.STRATEGIES['data'] pick = random.randint(0, len(strategies) - 1) strategy_name = strategies[pick]['name'] strategy_content = strategies[pick]['content'] message = strings.INTENT_INVEST_STRAT_MSG.format(strategy_name, strategy_content) reprompt_message = strings.INTENT_GENERAL_REPROMPT return ResponseBuilder.create_response(request, message=message) \ .with_reprompt(reprompt_message)
def handle_news(request): """ Generate response to intent type NewsAboutCompanyIntent based on the stage of the dialog. :type request AlexaRequest :return: JSON response including appropriate response based on the stage of the dialog. """ if request.dialog_state() == "STARTED": return _handle_dialog_read_titles(request) if request.dialog_state() == "IN_PROGRESS": if request.get_slot_value('articleNo') is None: if request.get_intent_confirmation_status() == "CONFIRMED": # Ask user which article he wants details about return _handle_dialog_which_one(request) else: # User doesn't want more info message = strings.INTENT_GENERAL_OK return ResponseBuilder.create_response(request, message=message) else: # Send user full article in a card return _handle_dialog_send_article(request)
def _add_ticker_to_watchlist(request): """ Add ticker to users Watchlist and build response. :type request AlexaRequest """ user_id = request.get_user_id() ticker = request.get_slot_value('stockTicker') if ticker is None: ticker = request.get_session_attribute('stockTicker') message = strings.INTENT_ADD_TO_WATCHLIST_CONFIRMED.format(ticker) reprompt_message = strings.INTENT_GENERAL_REPROMPT # Save stock to watchlist try: user_id = request.get_user_id() Watchlist(ticker, user_id).save() except EntryExistsError as e: message = strings.ERROR_CANT_ADD_TO_WATCHLIST.format(ticker) return ResponseBuilder.create_response(request, message=message) \ .with_reprompt(reprompt_message)
def handle_report_stock_watchlist(request): """ Generate response to intent type ReportStockWatchlistIntent reporting the current portfolio performance. :type request AlexaRequest :return: JSON response including the performance report. """ user_id = request.get_user_id() # Query DB for watchlist data ticker_list = Watchlist.get_users_tickers(user_id) changes = _get_stocks_24h_change(ticker_list) if len(ticker_list) > 2: max_idx = _find_biggest_value(changes) gainer_ticker = ticker_list.pop(max_idx) gainer_value = changes.pop(max_idx) min_idx = _find_smallest_value(changes) loser_ticker = ticker_list.pop(min_idx) loser_value = changes.pop(min_idx) gainer_message = strings.INTENT_WATCHLIST_REPORT_TOP_STOCK.format( Ticker2Name.ticker_to_name(gainer_ticker), _get_movement_direction(gainer_value), abs(gainer_value)) loser_message = strings.INTENT_WATCHLIST_REPORT_WORST_STOCK.format( Ticker2Name.ticker_to_name(loser_ticker), _get_movement_direction(loser_value), abs(loser_value)) message = gainer_message + loser_message + _build_report_msg(ticker_list, changes) elif len(ticker_list) == 2 or len(ticker_list) == 1: message = _build_report_msg(ticker_list, changes) else: message = strings.INTENT_WATCHLIST_EMPTY_MSG reprompt_message = strings.INTENT_GENERAL_REPROMPT return ResponseBuilder.create_response(request, message=message) \ .with_reprompt(reprompt_message)
def _remove_ticker_from_watchlist(request): """ Remove ticker from users Watchlist if there and build response. :type request AlexaRequest """ user_id = request.get_user_id() ticker = request.get_slot_value('stockTicker') if ticker == "NONE": ticker = request.get_session_attribute('stockTicker') message = strings.INTENT_REMOVE_FROM_WATCHLIST_CONFIRMED.format(ticker) reprompt_message = strings.INTENT_GENERAL_REPROMPT # Delete stock from watchlist try: Watchlist(ticker, user_id).delete() except EntryExistsError as e: logger.exception( f"Error while deleting ticker {ticker} from Watchlist") logger.debug(f"Deleted stock {ticker} from user:{user_id} watchlist") return ResponseBuilder.create_response(request, message=message) \ .with_reprompt(reprompt_message)
def _handle_dialog_which_one(request): """Create a response asking which article user wants to know about to get articleNo.""" message = strings.INTENT_NEWS_ABOUT_COMPANY_ASK_ARTICLE_NO return ResponseBuilder.create_response(request, message=message) \ .with_dialog_elicit_slot()