Linode服务器上Flask中的Bokeh

我正在努力使使用bokeh的flask应用程序在linode服务器上运行。我已经成功地实现了在本地工作的bokeh,但是在Linode服务器上使其与gunicorn一起运行时遇到了麻烦。据我了解,您需要在server_document()函数中指定正在运行的url bokeh。对我来说,我相信它正在'/ bkapp'上运行,但是我的错误却相反。这一切都发生在我的本地计算机上。我尚未在linode上尝试过它,因为我什至无法使其首先在本地运行。

这是其中包含散景图的脚本。

dashboard.py

from flask import render_template,request,flash,redirect,url_for
from flask_login import current_user,login_user,logout_user,login_required
from werkzeug.urls import url_parse
from app.models import User,Dashboard
from app import db
from app.backend.company import Company
from app.backend.summary.compliance import getcompliance

from bokeh.application import Application
from bokeh.application.handlers import FunctionHandler
from bokeh.plotting import figure
from bokeh.embed import server_document
from bokeh.models import ColumnDataSource,Select,CheckboxGroup,CustomJS,Range1d,LinearAxis,Span,Label
from bokeh.models.axes import LogAxis
from bokeh.layouts import layout
from bokeh.server.server import BaseServer
from bokeh.server.tornado import BokehTornado
from bokeh.server.util import bind_sockets
from threading import Thread
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
import asyncio

import pandas as pd
import numpy as np  
import os

# global df being used for the graph (changes with the dropdowns)
df = None
sentiment_df = None
current_feature = None
current_period = None
companyObject = None

def dashboard(companyName):

    # setup
    print('getting bulk data...')
    print('Getting ticker symbol...')
    global companyObject
    companyObject = Company(companyName)

    if not companyObject.found:
        print('Company not found!')
        return render_template('companyNotFound.html',title='Company Not Found',companyName=companyName)


    def create_stocks_graph(doc):
        global df,sentiment_df,current_feature,current_period,companyObject
        feature_names = ['Open','High','Low','Close']
        periods = ['1d','5d','1mo','3mo','6mo','1y','2y','5y','10y','ytd','max']
        default_period = '3mo'
        default_feature = 'Close'
        current_feature = default_feature
        current_period = default_period
        base = os.getcwd()
        sentiment_path = base + "\\app\\static\\sentiment_csv\\" + companyObject.ticker + ".csv"
        sentiment_df = pd.read_csv(sentiment_path)
        df = companyObject.getHistoricalPricesDf(period=default_period)
        source = ColumnDataSource(data={

        'x' : df.index,'y' : df['Close']
        })

        p = figure(title=f"Stocks at {default_feature} for past {default_period}",x_axis_label='Date',y_axis_label=default_feature,tools="pan,wheel_zoom,reset,box_zoom",plot_width=920,plot_height=600,x_axis_type="datetime") 

        # x ticks cleaning
        df.index = pd.to_datetime(df.index)
        df.index.name = 'Date'
        df.sort_index(inplace=True)

        sentiment_df['Time'] = pd.to_datetime(sentiment_df['Time'])

        # shift sentiment graph
        earliest_sentiment = sentiment_df['Time'].iloc[0]
        df_subset = df[df.index >= earliest_sentiment]
        avg_df_subset = df_subset['Close'].mean()
        sentiment_df['Sentiment'] *= 10
        sentiment_df['Sentiment'] += avg_df_subset

        # make line graphs
        stock_line = p.line(x='x',y='y',legend_label = "Stock",line_width=2,line_alpha=0.6,line_color='blue',source=source)
        sentiment_line = p.line(x=sentiment_df['Time'],y=sentiment_df['Sentiment'],legend_label = "Sentiment",line_color='red')

        # sentiment reference lines
        neutralline = Span(location=avg_df_subset + 5,dimension='width',line_color='black',line_dash='dashed',line_width=1,line_alpha=0.5)
        neutralLabel = Label(x=70,y=avg_df_subset + 5.2,x_units='screen',text='Neutral Sentiment',render_mode='css',border_line_alpha=0,background_fill_color='white',background_fill_alpha=1.0)
        p.renderers.extend([neutralline,neutralLabel])

        # figure styling
        p.outline_line_color = 'black'
        p.legend.visible = True
        p.legend.click_policy="hide"

        checkbox = CheckboxGroup(labels=["Stock","Sentiment"],active=[0,1])
        callback = CustomJS(code="""
                            stock_line.visible = false;
                            sentiment_line.visible = false;
                            neutralline.visible = false;
                            neutralLabel.visible = false;
                            // p.xaxis.visible = false;

                            // cb_obj injected in by the callback
                            if (cb_obj.active.includes(0)){
                                stock_line.visible = true; 
                                // p.xaxis.visible = true;
                                } // 0 index box is stock_line

                            if (cb_obj.active.includes(1))
                            {sentiment_line.visible = true; 
                            neutralline.visible = true; 
                            neutralLabel.visible = true;
                            }
                            """,args={'stock_line': stock_line,'sentiment_line': sentiment_line,'neutralline': neutralline,'p':p,'neutralLabel':neutralLabel})

        checkbox.js_on_change('active',callback)

        def update_plot(attr,old,new):
            global df,current_period
            if new in feature_names:
                # change feature
                print('updating feature')
                print('old:',old)
                print('new:',new)
                current_feature = new
                p.yaxis.axis_label = current_feature # update y-axis
                # p.title = f"Stocks at {current_feature} for past {current_period}"
                source.data = {
                    'x' : df.index,'y' : df[current_feature] 
                }
            else:
                # change period
                print('updating period')
                print('old:',new)
                current_period = new
                df = companyObject.getHistoricalPricesDf(period=new)
                df.index = pd.to_datetime(df.index)
                df.index.name = 'Date'
                df.sort_index(inplace=True)
                x_ticks = [d.strftime("%m/%d/%Y)")[:-1] for d in df.index.date]
                # p.title = f"Stocks at {current_feature} for past {current_period}"
                source.data = {
                    'x' : df.index,'y' : df[current_feature]
                }


        # add selects
        period_select = Select(title="Period",value=default_period,options=periods)
        feature_select = Select(title="Feature",value=default_feature,options=feature_names)
        period_select.on_change("value",update_plot) # update period
        feature_select.on_change("value",update_plot) # update feature

        doc.add_root(layout(
            [feature_select,period_select],[checkbox],[p]))

    # can't use shortcuts here,since we are passing to low level BokehTornado
    bkapp = Application(FunctionHandler(create_stocks_graph))

    # This is so that if this app is run using something like "gunicorn -w 4" then
    # each process will listen on its own port
    sockets,port = bind_sockets("localhost",0)

    # initiate bokeh server
    def bk_worker():
        # https://github.com/bokeh/bokeh/blob/1.1.0/examples/howto/server_embed/flask_embed.py
        asyncio.set_event_loop(asyncio.new_event_loop())

        bokeh_tornado = BokehTornado({'/bkapp': bkapp}),extra_websocket_origins=["localhost:8000"])
        bokeh_http = HTTPServer(bokeh_tornado)
        bokeh_http.add_sockets(sockets)

        server = BaseServer(IOLoop.current(),bokeh_tornado,bokeh_http)
        server.start()
        server.io_loop.start()

    Thread(target=bk_worker).start()

    ############### stocks ########################
    keys = ['bid','ask','weeklyHigh','weeklyLow','dailyMvmt']
    stockInfo = dict.fromkeys(keys,0)
    print('Getting stock price...')
    stockInfo['bid'],stockInfo['ask']  = companyObject.getStockPrice()
    print('Getting historical data...')
    infoDF = companyObject.getHistoricalPricesDf(period='5d')
    stockInfo['weeklyHigh'] = round(max(infoDF['High']),2)
    stockInfo['weeklyLow'] = round(min(infoDF['Low']),2)
    print('Getting Stock Price Change...')
    stockInfo['dailyMvmt'] = companyObject.getStockPriceChange()


    # stocks graph script
    script = server_document(url=r'/bkapp',relative_urls=True)

    ############################ summaries ##################### 

    print('getting summaries...')
    summaries = getcompliance(companyObject,multiplier=3) # update later when we add preferences

    print('Dashboard Ready!')

    # add finished dashboard to database
    dash = Dashboard(company_name=companyObject.displayName,author=current_user)
    db.session.add(dash)
    db.session.commit()

    return render_template('dashboard.html',ticker=companyObject.ticker,stockInfo=stockInfo,script=script,summaries=summaries,displayName=companyObject.displayName)

这是我的错误日志


...

Dashboard Ready!
127.0.0.1 - - [16/Jun/2020 19:53:25] "POST /dashboard HTTP/1.1" 200 -
INFO:werkzeug:127.0.0.1 - - [16/Jun/2020 19:53:25] "POST /dashboard HTTP/1.1" 200 -
127.0.0.1 - - [16/Jun/2020 19:53:26] "GET /bkapp/autoload.js?bokeh-autoload-element=1001&bokeh-app
-path=/bkapp HTTP/1.1" 404 -
INFO:werkzeug:127.0.0.1 - - [16/Jun/2020 19:53:26] "GET /bkapp/autoload.js?bokeh-autoload-element=
1001&bokeh-app-path=/bkapp HTTP/1.1" 404 -

是像更改url端点一样简单,还是我错过了很多?

谢谢

iCMS 回答:Linode服务器上Flask中的Bokeh

暂时没有好的解决方案,如果你有好的解决方案,请发邮件至:iooj@foxmail.com
本文链接:https://www.f2er.com/2118113.html

大家都在问