source.on_change('data', clear_message) hover.callback = CustomJS(args=dict(cb=checkbox), code=""" if(!cb.active.includes(%d)) { document.getElementsByClassName('bk-tooltip')[%d].style.display = 'none'; } """ % (1, 0)) checkbox.callback = CustomJS(args=dict( p0=circle, p1=line_regr, p2=circle_fit, ), code=""" //Toggle glyph visibility based on checkbox status p0.visible = cb_obj.active.includes(0); p1.visible = cb_obj.active.includes(1); p2.visible = cb_obj.active.includes(2); """) click_reset_tool = CustomJS(args=dict(p=plot), code=""" p.toolbar.tools[""" + util.reset_tool_index(g.TOOLS3) + """].trigger('do'); """) device_slc.on_change('value', device_slc_callback) device_slc.js_on_change( 'value',
def templateAcceptedLoanPerRegion(): LABELS = ["Central", "Mid - Atlantic", "NorthEast", "NorthWest", "South", "SouthEast", "SouthWest"] colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#17becf'] data = pd.read_csv(DATA + 'perc_acc_loan_per_region_date_compact.csv') central = data['Central'] / 100 midatl = data['Mid-Atlantic'] / 100 northe = data['Northeast'] / 100 northw = data['Northwest'] / 100 south = data['South'] / 100 southe = data['Southeast'] / 100 southw = data['Southwest'] / 100 date = data['date'] source = ColumnDataSource(data=dict(x=[datetime.strptime(d, '%b-%Y') for d in date.values], Central=central, MidAtlantic=midatl, Northeast=northe, Northwest=northw, South=south, Southeast=southe, Southwest=southw )) props = dict(line_width=4, line_alpha=0.8) p = Figure(x_axis_type="datetime", width=1200, height=380) p0 = p.line('x', 'Central', source=source, legend="Central", line_color=colors[0], **props) p1 = p.line('x', 'MidAtlantic', source=source, legend="Mid - Atlantic", line_color=colors[1], **props) p2 = p.line('x', 'Northeast', source=source, legend="NorthEast", line_color=colors[2], **props) p3 = p.line('x', 'Northwest', source=source, legend="NorthWest", line_color=colors[3], **props) p4 = p.line('x', 'South', source=source, legend="South", line_color=colors[4], **props) p5 = p.line('x', 'Southeast', source=source, legend="SouthEast", line_color=colors[5], **props) p6 = p.line('x', 'Southwest', source=source, legend="SouthWest", line_color=colors[6], **props) p.yaxis.axis_label = 'Percentage of accepted loans' p.yaxis[0].formatter = NumeralTickFormatter(format="0.0%") p.border_fill_color = LIGHT_GREEN p.background_fill_color = LIGHT_GREEN p.legend.background_fill_color = LIGHT_GREEN p.legend.background_fill_alpha = 0.5 checkbox = CheckboxGroup( labels=LABELS, inline=True, active=[0, 1, 2, 3, 4, 5, 6], width=800) code = """ //console.log(cb_obj.active); p0.visible = false; p1.visible = false; p2.visible = false; p3.visible = false; p4.visible = false; p5.visible = false; p6.visible = false; for (i in checkbox.active) { //console.log(cb_obj.active[i]); if (checkbox.active[i] == 0) { p0.visible = true; } else if (checkbox.active[i] == 1) { p1.visible = true; } else if (checkbox.active[i] == 2) { p2.visible = true; } else if (checkbox.active[i] == 3) { p3.visible = true; } else if (checkbox.active[i] == 4) { p4.visible = true; } else if (checkbox.active[i] == 5) { p5.visible = true; } else if (checkbox.active[i] == 6) { p6.visible = true; } } """ checkbox.callback = CustomJS(args=dict(p0=p0, p1=p1, p2=p2, p3=p3, p4=p4, p5=p5, p6=p6, checkbox=checkbox), code=code) boundaries = open(DATA + 'boundaries.json').read() states = json.loads(boundaries) region_state = pd.read_csv(DATA + 'region-state.csv', header=0) region_state = region_state.set_index('state') state_xs = [states[code]["lons"] for code in states] state_ys = [states[code]["lats"] for code in states] name = states.keys() colors_state = [] for i in name: if i != 'AK' and i != 'HI': reg = region_state.loc[i]['region'] if reg == "Central": colors_state.append(colors[0]) elif reg == "Mid-Atlantic": colors_state.append(colors[1]) elif reg == "Northeast": colors_state.append(colors[2]) elif reg == "Northwest": colors_state.append(colors[3]) elif reg == "South": colors_state.append(colors[4]) elif reg == "Southeast": colors_state.append(colors[5]) elif reg == "Southwest": colors_state.append(colors[6]) source = ColumnDataSource(data=dict( x=state_xs, y=state_ys, name=name, colors=colors_state, )) q = figure(title="", toolbar_location=None, plot_width=300, plot_height=160 ) q.xaxis.visible = False q.yaxis.visible = False q.xgrid.grid_line_color = None q.ygrid.grid_line_color = None q.min_border_left = False q.min_border_right = False q.min_border_top = False q.min_border_bottom = False q.border_fill_color = LIGHT_GREEN q.background_fill_color = LIGHT_GREEN q.patches('x', 'y', source=source, fill_color='colors', fill_alpha=0.9, line_color="white", line_width=0.1) layout = VBox(q, checkbox, p) # show(layout) script, div = components(layout) return script, div
checkbox1 = CheckboxGroup(labels=["", "", "", "", "", "", "", ""], active=[0, 1, 2, 3, 4, 5, 6, 7, 8], sizing_mode='scale_both', width=200, height=200) checkbox1.height = 200 checkbox1.callback = CustomJS.from_coffeescript(args=dict(l0=l_mon, l1=l_tue, l2=l_wed, l3=l_thu, l4=l_fri, l5=l_sat, l6=l_sun, l7=l_std, checkbox=checkbox1), code=""" l0.visible = 0 in checkbox.active; l1.visible = 1 in checkbox.active; l2.visible = 2 in checkbox.active; l3.visible = 3 in checkbox.active; l4.visible = 4 in checkbox.active; l5.visible = 5 in checkbox.active; l6.visible = 6 in checkbox.active; l7.visible = 7 in checkbox.active; """) checkbox2 = CheckboxGroup(labels=["", "", ""], active=[0, 1, 2]) checkbox2.callback = CustomJS.from_coffeescript(args=dict(l0=l_14, l1=l_15, l2=l_16, checkbox=checkbox2),
def plot_density_onegame(df, name, feat_plot): bandwidth = 5 range_start = -100 range_end = 180 r = len(df.groupby(name).count().index) l = list(df.groupby(name).count().index) l = sorted(l) def style(p): # Title p.title.align = 'center' p.title.text_font_size = '20pt' p.title.text_font = 'serif' # Axis titles p.xaxis.axis_label_text_font_size = '14pt' p.xaxis.axis_label_text_font_style = 'bold' p.yaxis.axis_label_text_font_size = '14pt' p.yaxis.axis_label_text_font_style = 'bold' # Tick labels p.xaxis.major_label_text_font_size = '12pt' p.yaxis.major_label_text_font_size = '12pt' return p x = np.linspace(range_start, range_end, 100) source_dict = dict(x=x) line_dict = dict() line_colors = Category20_16 line_colors.sort() for i in range(0, r): tmp_df = df[df[name] == l[i]] tmp_kde = gaussian_kde(tmp_df[feat_plot], bw_method=bandwidth) tmp_y = tmp_kde.pdf(x) #y_number = "y%s" % i y_number = l[i] source_dict[y_number] = tmp_y source = ColumnDataSource(data=source_dict) p = figure(plot_width=600, plot_height=400) for i in range(0, r): #y_number = "y%s" % i y_number = l[i] line_number = "line%s" % i line = p.line('x', y_number, source=source, line_width=3, line_alpha=0.6, line_color=line_colors[i]) line_dict[line_number] = line hover = HoverTool(tooltips=[(feat_plot, '$x'), ('Density', '$y')], line_policy='next') p.add_tools(hover) p = style(p) checkbox = CheckboxGroup(labels=l, active=list(range(r))) checkbox.callback = CustomJS(args=line_dict, code=""" //console.log(cb_obj.active); line0.visible = false; line1.visible = false; line2.visible = false; line3.visible = false; line4.visible = false; line5.visible = false; line6.visible = false; line7.visible = false; line8.visible = false; line9.visible = false; line10.visible = false; line11.visible = false; for (i in cb_obj.active) { //console.log(cb_obj.active[i]); if (cb_obj.active[i] == 0) { line0.visible = true; } else if (cb_obj.active[i] == 1) { line1.visible = true; } else if (cb_obj.active[i] == 2) { line2.visible = true; } else if (cb_obj.active[i] == 3) { line3.visible = true; } else if (cb_obj.active[i] == 4) { line4.visible = true; } else if (cb_obj.active[i] == 5) { line5.visible = true; } else if (cb_obj.active[i] == 6) { line6.visible = true; } else if (cb_obj.active[i] == 7) { line7.visible = true; } else if (cb_obj.active[i] == 8) { line8.visible = true; } else if (cb_obj.active[i] == 9) { line9.visible = true; } else if (cb_obj.active[i] == 10) { line10.visible = true; } else if (cb_obj.active[i] == 11) { line11.visible = true; } } """) controls = WidgetBox(checkbox) layout = row(controls, p) tab = Panel(child=layout, title="Density Plot of " + feat_plot) return tab
def make_timeline_html(input_dir_path: str, output_path: str) -> None: # Load Data path = Path(input_dir_path) / 'pep_graph.gpickle' pep_graph = nx.read_gpickle(path) path = Path(input_dir_path) / 'python_release_info.csv' release_df = pd.read_csv(path, encoding='utf-8', parse_dates=['release_date']) release_df = release_df[release_df.micro == 0] release_df['color'] = release_df.major.apply(lambda x: PYTHON_YELLOW_COLOR_CODE if x == 2 else PYTHON_BLUE_COLOR_CODE) # 2, 3以外が出てきたら再考すること node_dict = dict(pep_graph.nodes(data=True)) date_list = [value['Created_dt'] for key, value in node_dict.items()] min_date = datetime.datetime(min(date_list).year, 1, 1) py2_release_label_data_source = tl_compo.generate_release_label_source(release_df, major_version=2, pos_x=min_date) py3_release_label_data_source = tl_compo.generate_release_label_source(release_df, major_version=3, pos_x=min_date) py2_release_line_data_source = tl_compo.generate_release_line_data_source(release_df, major_version=2) py3_release_line_data_source = tl_compo.generate_release_line_data_source(release_df, major_version=3) release_source_dict = {'py2_release_label_source': py2_release_label_data_source, 'py3_release_label_source': py3_release_label_data_source, 'py2_release_line_source': py2_release_line_data_source, 'py3_release_line_source': py3_release_line_data_source } all_pep_data_source = compo.generate_node_data_source(pep_graph) # DataTable用のデータソースを用意する linked_from_table_source = table_compo.generate_data_table_data_source(pep_graph) link_to_table_source = table_compo.generate_data_table_data_source(pep_graph) linked_from_data_table = table_compo.generate_data_table(linked_from_table_source) link_to_data_table = table_compo.generate_data_table(link_to_table_source) linked_from_table_title_div = Div(text='<strong>PEP N is linked from ...</strong>', style={'color': BASE_FONT_COLOR}) link_to_table_title_div = Div(text='<strong>PEP N links to ...</strong>', style={'color': BASE_FONT_COLOR}) # Timeline用のデータソースを用意する timeline_display_circle_source = tl_compo.generate_timeline_data_source(pep_graph) timeline_label_source = tl_compo.generate_timeline_label_data_source(pep_graph) desc_start_date, _ = tl_compo.get_timeline_plot_range(pep_graph) desc_start_date = desc_start_date + datetime.timedelta(days=30) timeline_desc_label_source = tl_compo.generate_timeline_desc_data_source(xs=[desc_start_date, desc_start_date], ys=[1.7, 0.1], font_size=15) # 入力ボックス用のデータソース生成 error_message_div = Div(width=300, height=8, style={'color': 'red'}, text='') title_div = Div(width=700, style={'font-size': 'large', 'line-height': '1.5', 'color': BASE_FONT_COLOR}) title_div.text = """ Let's enter the PEP number in the left text box.<br> Then you can see the following information. <li>Which PEPs do link that PEP?</li> <li>Which PEPs are linked from that PEP?</li> """ checkbox_group = CheckboxGroup(labels=["Show Python 2 release dates", "Show Python 3 release dates"], active=[0, 1]) def callback_input_pep_number(all_pep_data_source=all_pep_data_source, link_to_table_source=link_to_table_source, linked_from_table_source=linked_from_table_source, link_to_table_title_div=link_to_table_title_div, linked_from_table_title_div=linked_from_table_title_div, timeline_display_circle_source=timeline_display_circle_source, timeline_label_source=timeline_label_source, timeline_desc_source=timeline_desc_label_source, title_div=title_div, error_message_div=error_message_div) -> None: """ テキストボックスに文字入力されたときに実行される関数。 PyScriptを使ってJavaScriptに変換される。 参考: https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/callbacks.html#customjs-with-a-python-function * このパラメータの設定の仕方以外にもあるかもしれない :param all_pep_data_source: :param link_to_table_source: :param linked_from_table_source: :param link_to_table_title_div: :param linked_from_table_title_div: :param timeline_display_circle_source: :param timeline_label_source: :param timeline_desc_source: :param title_div: :param error_message_div: :param debug_div: :return: """ inputed_text = cb_obj['value'] # PyScriptで変換するときに自分の外側の関数の呼び方がわからないので、 # 暫定で関数内関数で定義する。 def create_header_text(pep_dict: dict) -> str: if not pep_dict: return 'Not Found.' # .formatを使うとJavaScript変換後に実行時エラーになるので、 # +演算子で連結している description_text = "<span>Created: " \ + pep_dict['Created_str'] \ + " Type: " \ + pep_dict['Type'] \ + "<br>" link_text = "<a href='https://www.python.org/dev/peps/pep-" \ + pep_dict['index'] \ + "/' target='_blank'>PEP " \ + pep_dict['PEP'] \ + "</a>" title_text = "<span style='font-size : xx-large;'>" \ + link_text \ + "<br>" \ + pep_dict['Title'] \ + " (" + pep_dict['Status'] + ")" \ + "</span>" return description_text + title_text def search_index_by_pep_number(text: str) -> int: index = 0 for pep_number in all_pep_data_source['data']['PEP']: if text == str(pep_number): return index index += 1 return -1 # Not Found def get_inputed_pep_dict(text: int) -> dict: selected_index = search_index_by_pep_number(text) if selected_index == -1: return {} pep_dict = {} for key, value in all_pep_data_source.data.items(): pep_dict[key] = value[selected_index] return pep_dict def get_pep_info(list_index: int) -> dict: pep_dict = {} for key, value in all_pep_data_source['data'].items(): pep_dict[key] = value[list_index] return pep_dict def get_neighbor_node_info(pep_dict: dict, neighbor_type: str) -> dict: neighbors = dict() for key, value in all_pep_data_source.data.items(): neighbors[key] = [] for pep_id in pep_dict[neighbor_type]: index = search_index_by_pep_number(int(pep_id)) work_pep_dict = get_pep_info(index) for key in neighbors.keys(): neighbors[key].append(work_pep_dict[key]) return neighbors def generate_timeline_source_dict(pep_dict: dict) -> dict: in_edge_nodes_dict = get_neighbor_node_info(pep_dict, 'in_edge_nodes') out_edge_nodes_dict = get_neighbor_node_info(pep_dict, 'out_edge_nodes') in_edge_nodes_dict['y'] = [1.5] * len(in_edge_nodes_dict['PEP']) out_edge_nodes_dict['y'] = [0.5] * len(out_edge_nodes_dict['PEP']) timeline_source_dict = in_edge_nodes_dict del timeline_source_dict['in_degree'] del timeline_source_dict['in_edge_nodes'] del timeline_source_dict['out_degree'] del timeline_source_dict['out_edge_nodes'] for key, value in timeline_source_dict.items(): timeline_source_dict[key] += out_edge_nodes_dict[key] if key == 'y': timeline_source_dict[key].append(1) else: timeline_source_dict[key].append(pep_dict[key]) return timeline_source_dict def update_data_table(pep_dict: dict) -> None: linked_from_table_title_div.text = '<strong>PEP ' \ + pep_dict['PEP'] \ + ' is linked from ...</strong>' link_to_table_title_div.text = '<strong>PEP ' \ + pep_dict['PEP'] \ + ' links to ...</strong>' in_edge_nodes_dict = get_neighbor_node_info(pep_dict, 'in_edge_nodes') linked_from_table_source.data = in_edge_nodes_dict linked_from_table_source.change.emit() out_edge_nodes_dict = get_neighbor_node_info(pep_dict, 'out_edge_nodes') link_to_table_source.data = out_edge_nodes_dict link_to_table_source.change.emit() def update_timeline(pep_dict: dict) -> None: timeline_circle_dict = generate_timeline_source_dict(pep_dict) timeline_display_circle_source.data = timeline_circle_dict timeline_display_circle_source.change.emit() timeline_label_dict = generate_timeline_source_dict(pep_dict) timeline_label_dict['displayed_text'] = timeline_label_dict['PEP'] timeline_label_source.data = timeline_label_dict timeline_label_source.change.emit() texts = ['PEP ' + pep_dict['PEP'] + ' is linked from...', 'PEP ' + pep_dict['PEP'] + ' links to...', ] timeline_desc_dict = dict(x=timeline_desc_source.data['x'], y=timeline_desc_source.data['y'], text=texts, size=timeline_desc_source.data['size'], ) timeline_desc_source.data = timeline_desc_dict timeline_desc_source.change.emit() # 正規表現でタグを除去したいけど、JS変換後にreを呼べないので、暫定で<と>を外す inputed_text = inputed_text.replace('<', '') inputed_text = inputed_text.replace('>', '') inputed_text = inputed_text.strip() # 入力文字のチェック if not inputed_text: error_message_div.text = 'Please enter the number of PEP' + inputed_text return inputed_text = inputed_text.lstrip('0') # 表示の更新 selected_pep_dict = get_inputed_pep_dict(inputed_text) if selected_pep_dict: title_div.text = create_header_text(selected_pep_dict) update_data_table(selected_pep_dict) update_timeline(selected_pep_dict) error_message_div.text = "" else: error_message_div.text = "Not Found: PEP " + inputed_text def callback_change_checkbox(py2_label_source=py2_release_label_data_source, py2_line_source=py2_release_line_data_source, py3_label_source=py3_release_label_data_source, py3_line_source=py3_release_line_data_source): def switch_show_or_hide(label_source, line_source, major_version): label_data = label_source.data line_data = line_source.data check_box_index_map = {2: 0, 3: 1} if check_box_index_map[major_version] in cb_obj.active: label_data['alpha'] = [1] * len(label_data['alpha']) line_data['alpha'] = [1] * len(line_data['alpha']) else: label_data['alpha'] = [0] * len(label_data['alpha']) line_data['alpha'] = [0] * len(line_data['alpha']) label_source.data = label_data label_source.change.emit() line_source.data = line_data line_source.change.emit() switch_show_or_hide(py2_label_source, py2_line_source, 2) switch_show_or_hide(py3_label_source, py3_line_source, 3) # Header Component pep_textinput = TextInput(title='PEP:', placeholder='Please enter the PEP number.', callback=CustomJS.from_py_func(callback_input_pep_number), width=190) info_div = Div(width=200, height=20, style={'background-color': '#175A89', 'padding': '5px', 'color': '#FFFFFF'}) info_div.text = "<li>" \ "<a href='https://github.com/komo-fr/pep_map_site'><font color='#FFFFFF'>repository</font></a>" \ "</li>" inputbox_component = column(pep_textinput, error_message_div) color_desc_component = compo.generate_color_description_component() header_component = row(column(inputbox_component, color_desc_component), title_div, info_div) # Timeline Component checkbox_group.callback = CustomJS.from_py_func(callback_change_checkbox) timeline_plot = tl_compo.generate_timeline_plot(pep_graph, timeline_display_circle_source, timeline_label_source, timeline_desc_label_source, release_source_dict) as_of_date_div = Div(width=200, height=8, style={'color': 'red'}) # TODO: fetch_start_datetimeを持っていないときの対応について決める fetch_datetime = pep_graph.graph['fetch_start_datetime'].strftime('%Y/%m/%d') \ if 'fetch_start_datetime' in pep_graph.graph else 'Unknown' as_of_date_div.text = '* Data as of {}'.format(fetch_datetime) # データ取得日 timeline_component = column(timeline_plot, as_of_date_div) # Table Component margin_div = Div(width=50) link_to_table_component = column(link_to_table_title_div, link_to_data_table) linked_from_table_component = column(linked_from_table_title_div, linked_from_data_table) table_component = row(linked_from_table_component, margin_div, link_to_table_component) # TODO: ここでshowしなくても出力できる方法はないか? output_file(output_path, 'PEP Map | Timeline') show(column(header_component, checkbox_group, timeline_component, table_component))
def basic_TS_plot(fullDataset): # Init values ticker = "" yLabel = "" descLine = "" plotX = [] plotXLabel = [] plotY = [] legendList = [] # BollingerBands all_top_bollingers = [] all_bot_bollingers = [] all_mid_bollingers = [] all_bollinger_dates = [] # Quartiles firstQuartiles = [] medians = [] thirdQuartiles = [] # SETUP DATA for element in fullDataset: try: elementX = [] elementXLabel = [] elementY = [] # Extract x and y's from the pairs provided in each element of the dataset if element["dataset"]["data"]: for e in range(0, len(element["dataset"]["data"])): elementX.append( datetime.strptime(element["dataset"]["data"][e][0], '%Y-%m-%d').date()) elementXLabel.append(str(element["dataset"]["data"][e][0])) elementY.append(element["dataset"]["data"][e][1]) databaseCode = "[" + element["dataset"]["database_code"] + "] " name = element["dataset"]["name"] if len(elementX) > 0: # Reverse the incoming data if it starts with the closest-to-present data. We want it list to start with the oldest data if len(elementX) > 1: if elementX[0] > elementX[1]: elementX.reverse() elementY.reverse() plotX.append(elementX) plotXLabel.append(elementXLabel) plotY.append(elementY) # Bollinger Band Info prepare date_b, top_b, bot_b, mid_b = bollinger_bands( 2, elementX, elementY, 20) all_top_bollingers.append(top_b) all_mid_bollingers.append(mid_b) all_bot_bollingers.append(bot_b) # all_bollinger_dates.append(date_b) all_bollinger_dates.append(elementX) if element["dataset"]["data"]: if len(name) > 15: legendList.append("Show/Hide: " + name[:30] + "...") else: legendList.append(name) if element["dataset"]["data"]: # Prepare Quartiles numpyY = np.array(elementY) first_q = np.percentile(numpyY, 25) medi = np.percentile(numpyY, 50) third_q = np.percentile(numpyY, 75) firstQuartiles.append(first_q) medians.append(medi) thirdQuartiles.append(third_q) except Exception as e: logger.debug("ERROR: element in fullDataset could not be added") logger.debug(element) logger.debug(e) all_quartiles = [[]] * len(plotX) quartiles_names = [["First Quartile", "Median", "Third Quartile"] ] * len(plotX) for i in range(len(plotX)): all_quartiles[i] = [firstQuartiles[i], medians[i], thirdQuartiles[i]] # If only one item is available from the fullDataset, the report can be tailored to that time-series only if len(fullDataset) == 1: titleLong = fullDataset[0]["dataset"]["name"] if len(titleLong) > 20: titleShort = titleLong[:10] + "..." else: titleShort = titleLong descLong = "Long description placeholder." descLine = '<abbr title="' + descLong + '">' + ticker + '</abbr>' yLabel = fullDataset[0]["dataset"]["column_names"][1] # generic title for multiple data sets else: titleShort = "Time-series for: " + fullDataset[0]["dataset"][ "start_date"] + " to " + fullDataset[0]["dataset"]["end_date"] descLine = ticker yLabel = "Value(s)" # The table below will be used to show the units. Ideally it should also figure in the legend # Prepare column data sources for the graphs. This is also where hover tool and other widgets get their information source = [] # 'color' is created specifically for lasso tool - for the invisible, but selectable circle glyphs for i in range(len(plotX)): color = ["navy"] * len(plotX[i]) source.append( ColumnDataSource(data=dict(x=plotX[i], y=plotY[i], color=color, topx=all_bollinger_dates[i], topy=all_top_bollingers[i], midx=all_bollinger_dates[i], midy=all_mid_bollingers[i], botx=all_bollinger_dates[i], boty=all_bot_bollingers[i]))) PLOT_OPTIONS = dict(plot_width=400, plot_height=170) crosshair = CrosshairTool(dimensions="height") TOOLS = ['pan,wheel_zoom,box_zoom,reset,save,lasso_select'] + [crosshair] plot = figure(title=titleShort, x_axis_type="datetime", tools=TOOLS, toolbar_location="above", sizing_mode='scale_width', **PLOT_OPTIONS) plot.xaxis.formatter = DatetimeTickFormatter( hours=["%d %B %Y"], days=["%d %B %Y"], months=["%d %B %Y"], years=["%d %B %Y"], ) plot.xaxis.axis_label = 'Date(s)' plot.yaxis.axis_label = yLabel # Magma color palette is: black - purple - orange - tan - light tan # Viridis is a color paledtte: dark blue - greenblue - green - yellow # More palettes @ # http://bokeh.pydata.org/en/latest/docs/reference/palettes.html colors_list = Viridis256 shuffle(colors_list) # visible lines on graph creation plot_line_list = [] bollinger_band_list = [] for i in range(len(plotX)): # circles created for lasso average finding tool plot.circle("x", "y", color='color', size=1, source=source[i], alpha=0) # visible graph: plot_line_list.append( plot.line("x", "y", color=colors_list[i], line_width=4, name='standard', source=source[i])) shuffle(colors_list) bollinger_band_list.append( plot.line("topx", "topy", color=colors_list[i], line_width=1, name='top', source=source[i])) bollinger_band_list.append( plot.line("midx", "midy", color=colors_list[i], line_width=1, line_dash="dashed", name='mid', source=source[i])) bollinger_band_list.append( plot.line("botx", "boty", color=colors_list[i], line_width=1, name='bot', source=source[i])) # Bollinger bands are initially off for i in range(len(bollinger_band_list)): bollinger_band_list[i].visible = True # A hover tool for each line (standard, top, mid, lower) hover = HoverTool( names=['standard'], tooltips=[ ("Value", "@y{0,0.0000}"), # FRED REMOVED comma HERE ("Date", "@x{%F}") ], formatters={"x": "datetime"}, mode='vline') hover2 = HoverTool(names=['top'], tooltips=[("Upper Band", "@topy{0,0.0000}"), ("Date", "@x{%F}")], formatters={"x": "datetime"}, mode='vline') hover3 = HoverTool(names=['mid'], tooltips=[("SMA", "@midy{0,0.0000}"), ("Date", "@x{%F}")], formatters={"x": "datetime"}, mode='vline') hover4 = HoverTool(names=['bot'], tooltips=[("Lower Band", "@boty{0,0.0000}"), ("Date", "@x{%F}")], formatters={"x": "datetime"}, mode='vline') plot.add_tools(hover, hover2, hover3, hover4) # Set-up the multi-line representation (not using bokeh's multi-line) and the checkboxes list_active = [] dictArgs = {} button_titles = [] for i in range(0, len(plotX)): button_titles.append("Graph " + str(i + 1) + ": " + legendList[i]) # Generate the JS code for checkbox group - show/hide graphs dynamicJS = '//console.log(cb_obj.active);' for line in range(0, len(plot_line_list)): dynamicJS += '\nline' + str(line * 4) + '.visible = false; ' for i in range(3): dynamicJS += '\nline' + str(line * 4 + i + 1) + '.visible = false; ' dynamicJS += """ for (i in cb_obj.active) { //console.log(cb_obj.active[i]); if (cb_obj.active[i] == 0) { line0.visible = true; if (bollToggle.active == true)\n""" dynamicJS += "{\n" for i in range(3): dynamicJS += 'line' + str(i + 1) + '.visible = true;\n' dynamicJS += '}}' for line in range(1, len(plot_line_list)): dynamicJS += '\nelse if (cb_obj.active[i] == ' + str(line) + ') {\n' dynamicJS += 'line' + str(line * 4) + '.visible = true;\n' dynamicJS += 'if (bollToggle.active == true) {\n' for i in range(3): dynamicJS += 'line' + str(line * 4 + i + 1) + '.visible = true;\n' dynamicJS += '\n}}' dynamicJS += '\nelse if (cb_obj.active[i] == ' + str( len(plot_line_list)) + ') {\n' for t in range(0, len(plot_line_list)): dynamicJS += 'if (bollToggle.active == true) {\n' for i in range(3): dynamicJS += 'line' + str(t * 4 + i + 1) + '.visible = true;\n' dynamicJS += '}\n' dynamicJS += '\n}' dynamicJS += '\n}' logger.debug("DEBUG: DynamicJS") logger.debug(dynamicJS) # list_active means the buttons initially ON at page load for line in range(0, len(plot_line_list)): list_active.append(line) key = "line" + str(line * 4) dictArgs.update({key: plot_line_list[line]}) for i in range(3): key = "line" + str(line * 4 + i + 1) dictArgs.update({key: bollinger_band_list[line * 3 + i]}) bollButton = Toggle(label='Bollinger On/Off', button_type='success') key = "bollToggle" dictArgs.update({key: bollButton}) checkboxColor = '' if len(plotY) > 9: checkboxColor += '#E4E4E4;' else: checkboxColor += 'white;' checkbox = CheckboxGroup(labels=button_titles, active=list_active, width=450, height=210) checkbox.callback = CustomJS(args=dictArgs, code=dynamicJS) # checkbox is 210 pixels high(max), with a vertical scrollbar and grey background color checkbox.css_classes = ["checkbox-scrollbar"] key = "checkbox" dictArgs.update({key: checkbox}) selectAllJS = '' selectAllJS += "checkbox.active = [" for i in range(len(plotY)): if i != len(plotY) - 1: selectAllJS += str(i) + ', ' else: selectAllJS += str(i) selectAllJS += "];\n" for i in range(len(plotY)): selectAllJS += 'line' + str(i * 4) + '.visible = true;\n' selectAllJS += 'if (bollToggle.active == true) {\n' for i in range(len(plotY)): for t in range(3): selectAllJS += 'line' + str(i * 4 + t + 1) + '.visible = true;\n' selectAllJS += '}' selectAllCallback = CustomJS(args=dictArgs, code=selectAllJS) selectAllButton = Button(label='Select All', button_type='warning', callback=selectAllCallback) deselectAllJS = "checkbox.active = [];\n" for i in range(len(plotY) * 4): deselectAllJS += 'line' + str(i) + '.visible = false;\n' deselectAllCallback = CustomJS(args=dictArgs, code=deselectAllJS) deselectAllButton = Button(label='Deselect All', button_type='warning', callback=deselectAllCallback) plotScript, plotDiv = components(plot) # printPlotJS = """ # var div = plot; # console.log(plot); # var data=divID.innerHTML; # var myWindow = window.open('', 'my div', 'height=400,width=600'); # myWindow.document.write(div); # myWindow.document.close(); // necessary for IE >= 10 # # myWindow.onload=function(){ // necessary if the div contain images # # myWindow.focus(); // necessary for IE >= 10 # myWindow.print(); # myWindow.close(); # }; # """ # logger.debug("DEBUG: DynamicJS") # logger.debug(dynamicJS) sliderJS = """ var window = cb_obj.value var sources = [""" for i in range(len(source)): if i != len(source) - 1: sliderJS += 'source' + str(i) + ', ' else: sliderJS += 'source' + str(i) sliderJS += '];' sliderJS += """ for (var i = 0, len=sources.length; i < len; i++) { var data = sources[i].data; var y = data['y'] topy = data['topy'] midy = data['midy'] boty = data['boty'] function StandardDeviation(numbersArr) { //--CALCULATE AVERAGE-- var total = 0; for(var key in numbersArr) total += numbersArr[key]; var meanVal = total / numbersArr.length; //--CALCULATE AVERAGE-- //--CALCULATE STANDARD DEVIATION-- var SDprep = 0; for(var key in numbersArr) SDprep += Math.pow((parseFloat(numbersArr[key]) - meanVal),2); var SDresult = Math.sqrt(SDprep/numbersArr.length); //--CALCULATE STANDARD DEVIATION-- return SDresult; } function standardDev(window, y) { var sd = []; var x = window; while (x <= y.length) { var a2c = y.slice(x - window, x); var standev = StandardDeviation(a2c); sd.push(standev); x += 1; } return sd; } //moving average calc function movingAvg(array, count, qualifier){ // calculate average for subarray var avg = function(array, qualifier){ var sum = 0, count = 0, val; for (var i in array){ val = array[i]; if (!qualifier || qualifier(val)){ sum += val; count++; } } return sum / count; }; var result = [], val; // pad beginning of result with null values for (var i=0; i < count-1; i++) result.push(NaN); // calculate average for each subarray and add to result for (var i=0, len=array.length - count; i <= len; i++){ val = avg(array.slice(i, i + count), qualifier); if (isNaN(val)) result.push(NaN); else result.push(val); } return result; } //end moving avg var top_band = []; var mid_band = []; var bot_band = []; var x = window; while (x < y.length) { var sma_array = movingAvg(y.slice(x - window, x), window); var sma_last = sma_array[sma_array.length - 1]; var curSD = standardDev(window, y.slice(0, window)); var curSD_last = curSD[curSD.length - 1]; var tb = sma_last + curSD_last * 2; var bb = sma_last - curSD_last * 2; top_band.push(tb); mid_band.push(sma_last); bot_band.push(bb); x+=1; } if (top_band < topy) { shift_amt = topy.length - top_band.length; for (var q=0; q < shift_amt; q++) { top_band.unshift(NaN); mid_band.unshift(NaN); bot_band.unshift(NaN); } } //for (var t=0; t < y.length; t++) { // if (isNaN(top_band[0]) == true) { // topy[t] = top_band[t + 1]; // boty[t] = bot_band[t + 1]; // midy[t] = mid_band[t + 1]; // } // else { // topy[t] = top_band[t]; // boty[t] = bot_band[t]; // midy[t] = mid_band[t]; // } //} for (var t=0; t < y.length; t++) { topy[t] = top_band[t]; boty[t] = bot_band[t]; midy[t] = mid_band[t]; } console.log(sources[i]); sources[i].change.emit(); } """ sourceArgs = {} for i in range(0, len(plot_line_list)): key = "source" + str(i) sourceArgs.update({key: source[i]}) print(sourceArgs) SMAcallback = CustomJS(args=sourceArgs, code=sliderJS) SMAwindow_slider = Slider(start=1, end=100, value=20, step=1, title="SMA Window(period)", callback=SMAcallback) # for space between checkboxes and buttons spacer = Spacer(height=100, width=50) # for temporary space when slider is invisible no_slider = Spacer(height=75, width=100) SMAwindow_slider.height = 65 bollColumn = Column() bollColumn.width = 175 bollColumn.children = [ bollButton, no_slider, selectAllButton, deselectAllButton ] bollRow = Row(checkbox, spacer, bollColumn) # -------------------------------------------------------------- # Allow bollinger button to turn on/off the slider # dynamicJSboll = '\ncolumn.children = [no_slider];' # # dynamicJSboll += """ # if (cb_obj.active == true) # { # column.children = [boll_button, slider, select_all, deselect_all] # } # else if (cb_obj.active == false) # { # column.children = [boll_button, no_slider, select_all, deselect_all]; # }""" key = "column" dictArgs.update({key: bollColumn}) key = "boll_button" dictArgs.update({key: bollButton}) key = "slider" dictArgs.update({key: SMAwindow_slider}) key = "no_slider" dictArgs.update({key: no_slider}) key = "select_all" dictArgs.update({key: selectAllButton}) key = "deselect_all" dictArgs.update({key: deselectAllButton}) # javascript for bollinger toggle button dynamicJSboll = """ //console.log(cb_obj.active); if (cb_obj.active == true) {\n""" # slider is made visible dynamicJSboll += 'column.children = [boll_button, slider, select_all, deselect_all];\n' for t in range(0, len(plot_line_list)): dynamicJSboll += 'if (line' + str(t * 4) + '.visible == true) {\n' for i in range(3): dynamicJSboll += 'line' + str(t * 4 + i + 1) + '.visible = true;\n' dynamicJSboll += "}\n" dynamicJSboll += "}\n" dynamicJSboll += "else if (cb_obj.active == false) {\n" # slider is turned invisible dynamicJSboll += 'column.children = [boll_button, no_slider, select_all, deselect_all];\n' for t in range(0, len(plot_line_list)): for i in range(3): dynamicJSboll += 'line' + str(t * 4 + i + 1) + '.visible = false;\n' dynamicJSboll += "}\n" bollButton.callback = CustomJS(args=dictArgs, code=dynamicJSboll) # -------------------------------------------------------------- ''' # display data tables only if there are 10 or less data sets if len(plotY) < 11: doc_layout = layout([ [Column((plot), sizing_mode='scale_width')], [bollRow], [Row(children=data_table, sizing_mode='scale_width')], ], sizing_mode='scale_width') ''' doc_layout = layout([[Column(plot)], [bollRow]], sizing_mode='scale_width') plots = [plot, [bollRow]] scripts, div = components(plot) cssCode = """ <style type="text/css"> svg { font-family: 'Helvetica Neue', Helvetica; } .line { fill: none; stroke: #000; stroke-width: 2px; } body { height: 100%; overflow: hidden; margin: 0px; display: flex; box-sizing: border-box; } h1 { font-family: 'Helvetica Neue', Helvetica; color: #ffc800; text-align: center; } h2 { font-family: 'Helvetica Neue', Helvetica; color: grey; text-align: center; } p { font-family: 'Helvetica Neue', Helvetica; font-size: 14px; color: dark-grey; text-align: justify; } metric { font-family: 'Helvetica Neue', Helvetica; font-size: 20px; color: dark-grey; text-decoration: underline; text-align: center; } cop { font-family: 'Helvetica Neue', Helvetica; font-size: 10px; color: black; text-align: center; } #main { transition: margin-left .5s; padding: 16px; margin-top: 70px; flex-grow: 1; display: flex; overflow-y: auto; /*adds scroll to this container*/ max-width: 100%; } .navbar { overflow: hidden; background-color: #ffc800; position: fixed; top: 0; width: 100%; } .navbar a { float: left; display: block; color: black; text-align: center; padding: 14px 16px; text-decoration: none; font-size: 15px; } .sidenav { max-height: 82%; width: 0; position: fixed; z-index: 1; top: 0; left: 0; background-color: #DCDCDC; overflow-x: hidden; overflow-y: auto; transition: 0.5s; padding-top: 30px; margin-top: 68px; } .sidenav a { padding: 8px 8px 8px 32px; text-decoration: none; font-size: 25px; color: white; display: block; transition: 0.3s; } .sidenav a:hover, .offcanvas a:focus{ color: #ffc800; } .sidenav .closebtn { color: black; position: absolute; top: 0; right: 25px; color: black; font-size: 36px; margin-left: 50px; } .table-custom { border:1px; border-style: solid; border-color:#ffc800; padding: 3em; border-radius: 6px; text-align:center; margin: auto; max-width: 100%; max-height: 100%; display: block; } .checkbox-scrollbar { overflow: auto; background-color: """ + str(checkboxColor) + """ } @media screen and (max-height: 450px) { .sidenav {padding-top: 15px;} .sidenav a {font-size: 18px;} </style> """ metrics_shown = "" reportId = fullDataset[0]["reportId"] counter = 1 for element in fullDataset: if counter == 1: metrics_shown += "<b>Graph " + str( counter) + "</b><br>" + element["dataset"]["name"] + "<br>" else: metrics_shown += "<b><br>Graph " + str( counter) + "</b><br>" + element["dataset"]["name"] + "<br>" counter += 1 redirect_head_script = """ <script type="text/javascript"> <!-- if (screen.width <= 1200) {""" redirect_head_script += 'window.location = "https://s3.amazonaws.com/reports.tools.neonrisk.com/' + reportId + '/mobile_bokeh_report.html";' redirect_head_script += """} //--> </script> """ toolbar_scripts = """ <script> window.onload = function(){ var pr = document.getElementById('print'); pr.onclick = printReport; function printReport() { window.print(); return false; }}; </script> <script> window.onload = function(){ var el = document.getElementById('email'); el.onclick = createEmail; function createEmail() { var subject = "See the enclosed report" var link = "http://www.neonrisk.com" var body = "Please click here to access the report: " + link window.open('mailto:?subject=' + subject + '&body=' + body); return false; }}; </script> <script> window.onload = function(){ var sa = document.getElementById('saveAs'); sa.onclick = saveAs; function saveAs() { alert("Please use the SaveAs function of your browser to save the document locally. Note however, that viewing the document always requires a connection.") return false; }}; </script> <script> jQuery(function($) { $('#bookmark-this').click(function(e) { var bookmarkURL = window.location.href; var bookmarkTitle = document.title; if ('addToHomescreen' in window && addToHomescreen.isCompatible) { // Mobile browsers addToHomescreen({ autostart: false, startDelay: 0 }).show(true); } else if (window.sidebar && window.sidebar.addPanel) { // Firefox <=22 window.sidebar.addPanel(bookmarkTitle, bookmarkURL, ''); } else if ((window.sidebar && /Firefox/i.test(navigator.userAgent)) || (window.opera && window.print)) { // Firefox 23+ and Opera <=14 $(this).attr({ href: bookmarkURL, title: bookmarkTitle, rel: 'sidebar' }).off(e); return true; } else if (window.external && ('AddFavorite' in window.external)) { // IE Favorites window.external.AddFavorite(bookmarkURL, bookmarkTitle); } else { // Other browsers (mainly WebKit & Blink - Safari, Chrome, Opera 15+) alert('Press ' + (/Mac/i.test(navigator.userAgent) ? 'Cmd' : 'Ctrl') + '+D to bookmark this page.'); } return false; }); }); </script> """ infoCol = """ </head> <body> <div class="navbar"> <a style="font-size:30px;cursor:pointer" onclick="openNav()">☰</a> <a id="bookmark-this" href="#"><img src="https://s3.amazonaws.com/www.neonrisk.com/icons/bookmark.png" alt="Bookmark" width=30 height=30></a> <a href="#" title="Save this report as .pdf" id="saveAs"> <img src="https://s3.amazonaws.com/www.neonrisk.com/icons/save.png" alt="Save this report as pdf" width=30 height=30></a> <a href="#" title="Print this report" id="print"> <img src="https://s3.amazonaws.com/www.neonrisk.com/icons/print.png" alt="Print" width=30 height=30></a>""" linkToReport = 'https://s3.amazonaws.com/reports.tools.neonrisk.com/' + reportId + '/bokeh_report.html' infoCol += '<a href="mailto:?subject=Please%20see%20the%20enclosed%20report&body=Link%20to%20the%20report' + linkToReport + '">' infoCol += """<img src="https://s3.amazonaws.com/www.neonrisk.com/icons/email.png" alt="Email a link" width=30 height=30></a> <a href="http://www.neonrisk.com"><img src="https://s3.amazonaws.com/www.neonrisk.com/icons/help.png" alt="Open Online Help" width=30 height=30></a> <a href="https://www.linkedin.com/company/neon-risk"><img src="https://s3.amazonaws.com/www.neonrisk.com/icons/linkedin.png" alt="Find us on LinkedIn" width=30 height=30></a> <a href="https://twitter.com/NeonRisk"><img src="https://s3.amazonaws.com/www.neonrisk.com/icons/twitter.png" alt="Find us on Twitter" width=30 height=30></a> </div> <div id="mySidenav" class="sidenav"> <a href="javascript:void(0)" class="closebtn" onclick="closeNav()">×</a> <p><img src="http://www.neonrisk.com/uploads/6/9/5/2/69527361/hi-res-fpe-phat-flat-x4.png" alt="logo" align="left"></p> <metric>Metrics shown</metric>""" infoCol += '<p>' + metrics_shown + '</p><br>' infoCol += '<p>Report # ' + reportId + '</p>' infoCol += """<p>Thank you for using Neon Risk Tools. For support, please contact <a href="mailto:[email protected]">[email protected]</a></p> <cop>copyright Neon Risk, Inc. 2017</cop> </div> <div id="main"> """ closeTableHTML = """ </div> <script> function openNav() { document.getElementById("mySidenav").style.width = "250px"; document.getElementById("main").style.marginLeft = "250px"; document.getElementById("main").style.maxWidth = "87%"; } function closeNav() { document.getElementById("mySidenav").style.width = "0"; document.getElementById("main").style.marginLeft= "0"; document.getElementById("main").style.maxWidth = "100%"; } </script> </body> </html> """ bokehCode = '<link href="https://cdnjs.cloudflare.com/ajax/libs/bokeh/1.0.2/bokeh.min.css" rel="stylesheet" type="text/css">' bokehCode += '<link href="https://cdnjs.cloudflare.com/ajax/libs/bokeh/1.0.2/bokeh-widgets.min.css" rel="stylesheet" type="text/css">' bokehCode += '<script src="https://cdnjs.cloudflare.com/ajax/libs/bokeh/1.0.2/bokeh.min.js"></script>' bokehCode += '<script src="https://cdnjs.cloudflare.com/ajax/libs/bokeh/1.0.2/bokeh-widgets.min.js"></script>' # -- NOW generate page reportBodyDataHTML_bokeh = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' reportBodyDataHTML_bokeh += '<html xmlns="http://www.w3.org/1999/xhtml" style="height:100%;width:100%;font-size:100%">' reportBodyDataHTML_bokeh += '<head><meta content="text/html; charset=US-ASCII" http-equiv="Content-Type">' reportBodyDataHTML_bokeh += cssCode reportBodyDataHTML_bokeh += redirect_head_script + bokehCode + scripts + toolbar_scripts reportBodyDataHTML_bokeh += infoCol reportBodyDataHTML_bokeh += div reportBodyDataHTML_bokeh += closeTableHTML html_file = open("testing.html", "w") html_file.write(reportBodyDataHTML_bokeh) html_file.close() print(reportBodyDataHTML_bokeh)