-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
103 lines (76 loc) · 3.08 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import os, json
import psutil
from cachetools import TTLCache
from finviz import get_stock, get_insider, get_news, get_analyst_price_targets
import requests
from flask import Flask
import markdown
import markdown.extensions.fenced_code
import markdown.extensions.codehilite
from pygments.formatters import HtmlFormatter
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
get_stock_cache = TTLCache(maxsize=100, ttl=60*15)
get_insider_cache = TTLCache(maxsize=100, ttl=60*15)
get_news_cache = TTLCache(maxsize=100, ttl=60*15)
get_analyst_price_targets_cache = TTLCache(maxsize=100, ttl=60*15)
app = Flask(__name__)
# https://www.section.io/engineering-education/implementing-rate-limiting-in-flask/
limiter = Limiter(app, key_func=get_remote_address)
def get_from_cache(key, cache, get_func):
'''
Try to fetch value of key from cache
If not value found, execute get_func to find value that should be cached
Parameters:
key (str): keyname that should exist/will exist in cache
cache (cachetools.TTLCache): instance of TTLCache that should be queryed
get_func (callable func): function that should be executed if key&val not in cache
Returns:
contents of cache at key
'''
try:
return cache[key]
except KeyError:
pass
try:
cache[key] = json.dumps(get_func(key))
return cache[key]
except requests.exceptions.HTTPError: # 404 error
return json.dumps({'response':'not found'}), 404
except Exception as e:
return json.dumps({'response': str(e)}), 500
@app.route("/get_analyst_price_targets/<string:ticker>", methods=['GET'])
@limiter.limit("5/hour")
def route_get_analyst_price_targets(ticker):
return get_from_cache(ticker, get_analyst_price_targets_cache, get_analyst_price_targets)
@app.route("/get_news/<string:ticker>", methods=['GET'])
@limiter.limit("5/hour")
def route_get_news(ticker):
return get_from_cache(ticker, get_news_cache, get_news)
@limiter.limit("10/hour") # maximum of 10 requests per minute
@app.route("/get_insider/<string:ticker>", methods=['GET'])
def route_get_insider(ticker):
return get_from_cache(ticker, get_insider_cache, get_insider)
@app.route("/get_stock/<string:ticker>", methods=['GET'])
@limiter.limit("5/hour")
def route_get_stock(ticker):
return get_from_cache(ticker, get_stock_cache, get_stock)
@app.route("/util", methods=['GET'])
@limiter.limit("5/hour")
def route_util():
return json.dumps(psutil.virtual_memory())
@app.errorhandler(404)
def route_missing(err):
# https://rudra.dev/posts/rendering-markdown-from-flask/
md_template_string = 'For routes and demo, please see https://github.com/frank-besson/finviz-api'
with open('README.md','r') as f:
md_template_string = markdown.markdown(
f.read(), extensions=["fenced_code", "codehilite"]
)
formatter = HtmlFormatter(style="emacs",full=True,cssclass="codehilite")
css_string = formatter.get_style_defs()
md_css_string = "<style>" + css_string + "</style>"
md_template = md_css_string + md_template_string
return md_template
if __name__ == "__main__":
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 33507)), debug=False)