release candidate

This commit is contained in:
George 2024-10-05 22:53:39 -07:00
parent 1b8612e504
commit eceafeee2f

View File

@ -25,7 +25,7 @@ from numpy.fft import fft, ifft
import scipy.signal as sig import scipy.signal as sig
import plotly.express as px import plotly.express as px
from plotly.subplots import make_subplots from plotly.subplots import make_subplots
from dash import Dash, html, dcc, callback, Output, Input, State, no_update from dash import Dash, html, dcc, callback, Output, Input, State, no_update, ctx
from waitress import serve from waitress import serve
from flask_caching import Cache from flask_caching import Cache
from dash.exceptions import PreventUpdate from dash.exceptions import PreventUpdate
@ -79,10 +79,21 @@ def get_watchlist(username : str):
tuples_list = curs.fetchall() tuples_list = curs.fetchall()
df = pd.DataFrame(tuples_list) df = pd.DataFrame(tuples_list)
return df return df
def insert_ticker(username : str, tick : str, name : str): def remove_from_db(username, tick):
if username:
table_name = f"{username + '_watch_list'}"
else: # username is None, use default table
table_name = "stock_watch_list"
QUERY = f"DELETE FROM {table_name} WHERE tick = '{tick}';"
with connect_db() as conn:
with conn.cursor() as curs:
curs.execute(QUERY)
def insert_into_db(username : str, tick : str, name : str):
if username: if username:
table_name = f"{username + '_watch_list'}" table_name = f"{username + '_watch_list'}"
else: # username is None, use default table else: # username is None, use default table
@ -674,7 +685,8 @@ app.layout = [
disabled=True, disabled=True,
), ),
dcc.Store(id="signaling"), dcc.Store(id="signaling"), # to pass the input ticker and signal reload
dcc.Store(id="del_sig") # to signal reload only after delete a ticker
] ]
app.clientside_callback( app.clientside_callback(
@ -693,19 +705,32 @@ app.clientside_callback(
Input("start-button", "id") Input("start-button", "id")
) )
# tiker input callback # delete ticker button callback
@callback(Output('del_sig', 'data'),
Input("remove-button", "n_clicks"),
State("symbols_dropdown_list", "value")
)
def remove_ticker(n, sym_raw):
if n and len(sym_raw) > 0:
sym = sym_raw.split()[0]
remove_from_db(auth.username, sym.upper())
return n
return no_update
# ticker input callback
@callback(Output('signaling', component_property='data'), @callback(Output('signaling', component_property='data'),
Input('input-ticker', component_property='value')) Input('input-ticker', component_property='value'))
def update_tickers(ticker): def update_tickers(ticker):
if ticker: if ticker:
long_name = StockMapper().ticker_to_company_name.get(ticker) ticker_upper = ticker.upper()
long_name = StockMapper().ticker_to_company_name.get(ticker_upper)
if long_name: if long_name:
insert_ticker(auth.username, ticker.upper(), long_name) insert_into_db(auth.username, ticker_upper, long_name)
return True return ticker_upper
return no_update return no_update
# start / stop button callback # start / stop button callback
@callback(Output('interval-component', 'disabled'), @callback(Output('interval-component', 'disabled'),
Output("start-button", "children"), Output("start-button", "children"),
@ -726,31 +751,44 @@ def start_cycle(n, value):
Output('interval-component', 'n_intervals'), Output('interval-component', 'n_intervals'),
Output('interval-component', 'max_intervals'), Output('interval-component', 'max_intervals'),
Input("reload-button", "n_clicks"), Input("reload-button", "n_clicks"),
Input("signaling", "data")) Input("signaling", "data"),
Input("del_sig", "data"),
def reload_syms(n, s): )
if n or s: def reload_syms(n, s, d):
if n or s or d:
watchlist = get_watchlist(auth.username) watchlist = get_watchlist(auth.username)
symbols = (watchlist.iloc[:, 0] + " - " + watchlist.iloc[:, 1]).tolist() symbols = (watchlist.iloc[:, 0] + " - " + watchlist.iloc[:, 1]).tolist()
return symbols, 0, 2*len(symbols) return symbols, 0, 2*len(symbols)
return no_update return no_update
# interval callback # interval callback
@callback(Output('symbols_dropdown_list', 'value'), @callback(Output('symbols_dropdown_list', 'value'),
Input("signaling", "data"),
Input("interval-component", "n_intervals"), Input("interval-component", "n_intervals"),
State('symbols_dropdown_list', 'options'), State('symbols_dropdown_list', 'options'),
# State('dropdown-index', 'data'),
) )
def cycle_syms(n, syms): def cycle_syms(tick_input, n, syms):
if syms: if not syms:
return syms[n % len(syms)]
else:
return no_update return no_update
triggered_id = ctx.triggered_id
if triggered_id == "interval-component":
return syms[n % len(syms)]
elif triggered_id == "signaling":
row_num = None
tick_len = len(tick_input) + 2
tick_match = tick_input + ' -'
for i in range(0, len(syms)):
if syms[i][0:tick_len] == tick_match:
row_num = i
break
if row_num is not None:
return syms[row_num]
return no_update
# dropdown callback # dropdown callback
@callback( @callback(
Output(component_id='controls-and-graph', component_property='figure'), Output(component_id='controls-and-graph', component_property='figure'),
# Output("dropdown-index", "data"),
Input(component_id='symbols_dropdown_list', component_property='value'), Input(component_id='symbols_dropdown_list', component_property='value'),
# State('symbols_dropdown_list', 'options') # State('symbols_dropdown_list', 'options')
# Input(component_id='signal', component_property='data'), # Input(component_id='signal', component_property='data'),