def pe_time_series_selection_to_sunburst_and_transaction_table( figure, selectedData, time_resolution, time_span, data_store, param_store): """Selecting specific points from the time series chart updates the account burst and the detail labels. Reminder to self: When you think selectedData input is broken, remember that unaltered default action in the graph is to zoom, not to select. Note: all of the necessary information is in figure but that doesn't seem to trigger reliably. Adding selectedData as a second Input causes reliable triggering. """ params: Params() = Params.from_json(param_store) preventupdate_if_empty(data_store) trans, atree, eras, dstore = Datastore.get_parts(data_store, params.pe_roots) if not time_resolution: time_resolution = params.init_time_res if not time_span: time_span = params.init_time_span if len(trans) == 0: app.logger.error( "Tried to make burst figure from transactions, but no transactions provided." ) raise PreventUpdate() unit = params.unit ts_label = CONST["time_span_lookup"].get(time_span)["label"] min_period_start: np.datetime64 = None max_period_end: np.datetime64 = None selected_accounts = [] selected_trans = pd.DataFrame() desc_account_count = 0 colormap = {} # Get the names and colors of all accounts in the Input figure. # If anything is clicked, set the selection dates, accounts, and transactions. if figure: for trace in figure.get("data"): account = trace.get("name") points = trace.get("selectedpoints") colormap[account] = trace.get("marker").get("color") if not points: continue selected_accounts.append(account) for point in points: point_x = trace["x"][point] period_start, period_end = period_to_date_range( time_resolution, point_x, eras) if min_period_start is None: min_period_start = period_start else: min_period_start = min(min_period_start, period_start) if max_period_end is None: max_period_end = period_end else: max_period_end = max(max_period_end, period_end) desc_accounts = atree.get_descendent_ids(account) desc_account_count = desc_account_count + len(desc_accounts) subtree_accounts = [account] + desc_accounts new_trans = (trans.loc[trans["account"].isin( subtree_accounts)].loc[trans["date"] >= period_start].loc[ trans["date"] <= period_end]) new_trans = Ledger.positize( new_trans) # each top-level account should net positive if len(selected_trans) > 0: selected_trans = selected_trans.append(new_trans) else: selected_trans = new_trans selected_count = len(selected_trans) if selected_count > 0 and len(selected_accounts) > 0: # If there are selected data, describe the contents of the sunburst # TODO: desc_account_count is still wrong. description = Burst.pretty_account_label( selected_accounts, desc_account_count, selected_count, ) else: # If no trans are selected, show everything. Note that we # could logically get here even if valid accounts are # selected, in which case it would be confusing to get back # all trans instead of none, but this should never happen haha # because any clickable bar must have $$, and so, trans description = f"Click a bar in the graph to filter from {len(trans):,d} records" selected_trans = trans min_period_start = trans["date"].min() max_period_end = trans["date"].max() title = f"{ts_label} {unit} from {pretty_date(min_period_start)} to {pretty_date(max_period_end)}" pe_selection_store = { "start": min_period_start, "end": max_period_end, "count": len(selected_trans), "accounts": selected_accounts, } duration = round( pd.to_timedelta((max_period_end - min_period_start), unit="ms") / np.timedelta64(1, "M")) factor = Ledger.prorate_factor(time_span, duration=duration) try: sun_fig = Burst.from_trans(atree, selected_trans, time_span, unit, factor, colormap, title) except LError as E: text = f"Failed to generate sunburst. Error: {E}" app.logger.warning(text) description = text return (sun_fig, title, description, pe_selection_store)