Esempio n. 1
0

source1.on_change('selected', clear_message)

device_slc.on_change('value', device_slc_callback)
device_slc.js_on_change(
    'value',
    CustomJS(args=dict(btn=reset_stream_btn),
             code="""
        document.getElementById('modelid_' + btn.id).click();
    """))

device_conn_btn.on_click(device_conn_btn_callback)
device_conn_btn.callback = CustomJS(args=dict(btn=stream_data_btn),
                                    code="""
        if (!btn.active) {
            document.getElementById('modelid_' + btn.id).click();
        }
    """)

conn_status.js_on_change(
    'value',
    CustomJS(args=dict(btn=device_conn_btn),
             code="""
        console.log('This application is connected to ESP device: ' + cb_obj.value);

        if(cb_obj.value) {
            btn.label = "Disconnect from device";
            btn.button_type = "warning";
        } else {
            btn.label = "Connect to device";
            btn.button_type = "success";
Esempio n. 2
0
    columns = [
        TableColumn(field="x", title="X"),
        TableColumn(field="z", title="Text"),
    ]
    data_table = DataTable(source=source2,
                           columns=columns,
                           width=400,
                           height=280)

    button = Toggle(label="Select")
    button.callback = CustomJS(args=dict(source1=source1, source2=source2),
                               code="""
                                       var inds_in_source2 =
                               source2.get('selected')['1d'].indices;
                                       var d = source2.get('data');
                                       var inds = []
                                       
                                       if (inds_in_source2.length == 0) { return; }

                                       for (i = 0; i < inds_in_source2.length; i++)
                               {
                                           inds.push(d['index'][i])
                                       }

                                       source1.get('selected')['1d'].indices = inds
                                       source1.change.emit()
                                   """)

    #                                       source1.trigger('change');
    show(column(fig1, data_table, button))
Esempio n. 3
0
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()">&#9776;</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()">&times;</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)