diff --git a/helpers0.py b/helpers0.py new file mode 100644 index 0000000..f163498 --- /dev/null +++ b/helpers0.py @@ -0,0 +1,61 @@ +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. + + http://flask.pocoo.org/docs/1.0/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: + response = requests.get(f"https://api.iextrading.com/1.0/stock/{urllib.parse.quote_plus(symbol)}/quote") + 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}" diff --git a/requirements.txt b/requirements.txt index 191f5e5..8917ccb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,6 +28,7 @@ Flask-WTF==0.14.2 gast==0.2.2 google-pasta==0.1.7 grpcio==1.24.1 +gunicorn==20.0.4 h5py==2.10.0 idna==2.8 importlib-metadata==0.23