0

I was working on the implementation of buy and trying to buy stocks when all of a sudden I began receiveing incorrect/invalid stock symbol for every stock symbol I tried(as if the stock symbol was not in IEX's data).The /quote route also uses same lookup function and also started returning the same error messages. What is weird is that the stock symbols looked up are valid stock symbols like AAPL (Apple) and AMZN(Amazon), for example. Also, both quote and buy were running fine on the app moments and days leading up to this.I think it might have something to do with the lookup function.At the time I checked the staff's solution and experienced the same problem.Then I thought it may have to do with the API key? The staff's solution quote/sell/buy now works again. However, mine still does not. Anyone have any idea of what might be happening?

Here is code for app.py

    import os
    
    from cs50 import SQL
    from flask import Flask, flash, redirect, render_template, request, session
    from flask_session import Session
    from tempfile import mkdtemp
    from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
    from werkzeug.security import check_password_hash, generate_password_hash
    
    from helpers import apology, login_required, lookup, usd,is_digit
    
    # Configure application
    app = Flask(__name__)
    
    # Ensure templates are auto-reloaded
    app.config["TEMPLATES_AUTO_RELOAD"] = True
    
    
    # Ensure responses aren't cached
    @app.after_request
    def after_request(response):
        response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
        response.headers["Expires"] = 0
        response.headers["Pragma"] = "no-cache"
        return response
    
    
    # Custom filter
    app.jinja_env.filters["usd"] = usd
    
    # Configure session to use filesystem (instead of signed cookies)
    app.config["SESSION_FILE_DIR"] = mkdtemp()
    app.config["SESSION_PERMANENT"] = False
    app.config["SESSION_TYPE"] = "filesystem"
    Session(app)
    
    # Configure CS50 Library to use SQLite database
    db = SQL("sqlite:///finance.db")
    
    # Make sure API key is set
    if not os.environ.get("API_KEY"):
        raise RuntimeError("API_KEY not set")
    
    
    @app.route("/")
    @login_required
    def index():
        """Show portfolio of stocks"""
        return apology("TODO")
    
    
    @app.route("/buy", methods=["GET", "POST"])
    @login_required
    def buy():
        """Buy shares of stock"""
        if request.method == "POST":
            symbol=request.form.get("symbol")
            shares= int(request.form.get("shares"))
            integer_result=is_digit(shares)
            if not integer_result:
                return apology("Enter a postive number")
            quotes=lookup(symbol)
            if not quotes:
                return apology("Stock symbol not valid")
            """Check if user can afford"""
            share_price=quotes['price']
            print(share_price)
            total_due=shares*share_price
            print(total_due)
            #query database for cash amount
           ## cash= db.execute("SELECT cash FROM users WHERE username= ?", )
            ##print(cash)
    
    
    
            return apology("TODO")
        else:
              return render_template("buy.html")
    
    
    
    @app.route("/history")
    @login_required
    def history():
        """Show history of transactions"""
        return apology("TODO")
    
    
    @app.route("/login", methods=["GET", "POST"])
    def login():
        """Log user in"""
    
        # Forget any user_id
        session.clear()
    
        # User reached route via POST (as by submitting a form via POST)
        if request.method == "POST":
    
            # Ensure username was submitted
            if not request.form.get("username"):
                return apology("must provide username", 403)
    
            # Ensure password was submitted
            elif not request.form.get("password"):
                return apology("must provide password", 403)
    
            # Query database for username
            rows = db.execute("SELECT * FROM users WHERE username = ?", request.form.get("username"))
    
            # Ensure username exists and password is correct
            if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
                return apology("invalid username and/or password", 403)
    
            # Remember which user has logged in
            session["user_id"] = rows[0]["id"]
    
            # Redirect user to home page
            return redirect("/")
    
        # User reached route via GET (as by clicking a link or via redirect)
        else:
            return render_template("login.html")
    
    
    @app.route("/logout")
    def logout():
        """Log user out"""
    
        # Forget any user_id
        session.clear()
    
        # Redirect user to login form
        return redirect("/")
    
    
    @app.route("/quote", methods=["GET", "POST"])
    @login_required
    def quote():
        """Get stock quote."""
        if request.method =="POST":
            symbol=request.form.get("symbol")
            quotes=lookup(symbol)
            if not quotes:
                return apology("Symbol not valid")
            return render_template("quoted.html",quotas=quotes)
    
        else:
            return render_template("quote.html")
    
    
    
    @app.route("/register", methods=["GET", "POST"])
    def register():
        """Register user"""
        #User registration:
        if request.method == "POST":
            #checking username and password meet requirements
            name=request.form.get("username")
            if not name:
                return apology("Enter username")
            password=request.form.get("password")
            if not password:
                return apology("Enter password")
            confirmation=request.form.get("confirmation")
            if not confirmation:
                return apology("Confirm password")
            if password != confirmation:
                return apology("Passwords do not match.")
            #User registration
            db.execute("INSERT INTO users (username,hash) VALUES(?,?)", name, generate_password_hash(password) )
            return redirect("/")
        else:
            return render_template("register.html")
    
    
    
    @app.route("/sell", methods=["GET", "POST"])
    @login_required
    def sell():
        """Sell shares of stock"""
        return apology("TODO")
    
    
    def errorhandler(e):
        """Handle error"""
        if not isinstance(e, HTTPException):
            e = InternalServerError()
        return apology(e.name, e.code)
    
    
    # Listen for errors
    for code in default_exceptions:
        app.errorhandler(code)(errorhandler)

and here is helpers.py where lookup is defined:

    import os
    import requests
    import urllib.parse
    
    from flask import redirect, render_template, request, session
    from functools import wraps
    
    
    def apology(message, code=400):
        """Render message as an apology to user."""
        def escape(s):
            """
            Escape special characters.
    
            https://github.com/jacebrowning/memegen#special-characters
            """
            for old, new in [("-", "--"), (" ", "-"), ("_", "__"), ("?", "~q"),
                             ("%", "~p"), ("#", "~h"), ("/", "~s"), ("\"", "''")]:
                s = s.replace(old, new)
            return s
        return render_template("apology.html", top=code, bottom=escape(message)), code
    
    
    def login_required(f):
        """
        Decorate routes to require login.
    
        https://flask.palletsprojects.com/en/1.1.x/patterns/viewdecorators/
        """
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if session.get("user_id") is None:
                return redirect("/login")
            return f(*args, **kwargs)
        return decorated_function
    
    
    def lookup(symbol):
        """Look up quote for symbol."""
    
        # Contact API
        try:
            api_key = os.environ.get("API_KEY")
            url = f"https://cloud-sse.iexapis.com/stable/stock/{urllib.parse.quote_plus(symbol)}/quote?token={api_key}"
            response = requests.get(url)
            response.raise_for_status()
        except requests.RequestException:
            return None
    
        # Parse response
        try:
            quote = response.json()
            return {
                "name": quote["companyName"],
                "price": float(quote["latestPrice"]),
                "symbol": quote["symbol"]
            }
        except (KeyError, TypeError, ValueError):
            return None
    
    
    def usd(value):
        """Format value as USD."""
        return f"${value:,.2f}"
    
    def is_digit(shares):
        if shares.isdigit() and float(shares)>0:
            return True
        return None

And here is last two lines of error code received:

INFO: 192.168.5.175 - - [09/Apr/2021 23:06:47] "POST /quote HTTP/1.0" 400 -
INFO: 192.168.5.175 - - [09/Apr/2021 23:06:47] "GET /static/styles.css HTTP/1.0" 200 -

Any help would be very appreciated! Thanks!

0

The IEX api has changed, which required a change to the lookup function. The updated version of helpers.py can be found here.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .