diff --git a/indicators.py b/indicators.py index c3329bd..4ad75d3 100644 --- a/indicators.py +++ b/indicators.py @@ -25,7 +25,7 @@ from numpy.fft import fft, ifft import scipy.signal as sig import plotly.express as px 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 flask_caching import Cache from dash.exceptions import PreventUpdate @@ -79,10 +79,21 @@ def get_watchlist(username : str): tuples_list = curs.fetchall() df = pd.DataFrame(tuples_list) - 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: table_name = f"{username + '_watch_list'}" else: # username is None, use default table @@ -674,7 +685,8 @@ app.layout = [ 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( @@ -693,19 +705,32 @@ app.clientside_callback( 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'), Input('input-ticker', component_property='value')) def update_tickers(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: - insert_ticker(auth.username, ticker.upper(), long_name) - return True + insert_into_db(auth.username, ticker_upper, long_name) + return ticker_upper return no_update - # start / stop button callback @callback(Output('interval-component', 'disabled'), Output("start-button", "children"), @@ -726,31 +751,44 @@ def start_cycle(n, value): Output('interval-component', 'n_intervals'), Output('interval-component', 'max_intervals'), Input("reload-button", "n_clicks"), - Input("signaling", "data")) - -def reload_syms(n, s): - if n or s: + Input("signaling", "data"), + Input("del_sig", "data"), + ) +def reload_syms(n, s, d): + if n or s or d: watchlist = get_watchlist(auth.username) symbols = (watchlist.iloc[:, 0] + " - " + watchlist.iloc[:, 1]).tolist() return symbols, 0, 2*len(symbols) + return no_update # interval callback @callback(Output('symbols_dropdown_list', 'value'), + Input("signaling", "data"), Input("interval-component", "n_intervals"), State('symbols_dropdown_list', 'options'), - # State('dropdown-index', 'data'), ) -def cycle_syms(n, syms): - if syms: - return syms[n % len(syms)] - else: +def cycle_syms(tick_input, n, syms): + if not syms: 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 @callback( Output(component_id='controls-and-graph', component_property='figure'), - # Output("dropdown-index", "data"), Input(component_id='symbols_dropdown_list', component_property='value'), # State('symbols_dropdown_list', 'options') # Input(component_id='signal', component_property='data'),