{"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"}) #app.css.append_css({'external_url': 'https://cdn.rawgit.com/plotly/dash-app-stylesheets/2d266c578d2a6e8850ebce48fdb52759b2aef506/stylesheet-oil-and-gas.css'}) # noqa: E501 app.layout = html.Div(children=[ html.Div([html.Button(id='button', n_clicks=0, children='STARTING...')], style={'color': 'white'}), html.Div([dcc.Graph(id='graph_one', figure={})], style={'display': 'inline-block'}), dcc.Interval(id='live-update', interval=1000), ], style={'background-color': "#F0F0F0"}) @app.callback(dep.Output('graph_one', 'figure'), [], [dep.State('graph_one', 'figure')], [dep.Event('live-update', 'interval')]) def do_plot(figure): df = pd.read_csv('/tmp/mon.csv') plt_w = uj2w(df.time, df.cpu_uj) sys_w = uj2w(df.time, df.sys_uj) * 2 t = df.time - df.time[0] y_data = ( df.v0 / 1e6, df.v1 / 1e6, df.i0 / 1e6, df.i1 / 1e6, df.i0 * df.v0 / 1e12, df.i1 * df.v1 / 1e12,
def init_dash(self): """Load the inital Dataset and show it in initial layout.""" app = dash.Dash() # The following config were neccessary, as the CDN serving the files # seems to be unstable. app.css.config.serve_locally = True app.scripts.config.serve_locally = True # Values for Checkboxes topics_options = [{'label': i, 'value': i} for i in self.topics] # Layout of Dashboard app.layout = html.Div([ html.Link( rel='stylesheet', href='/static/style.css' ), html.Link( rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css' ), # Header html.Div([ html.H1('Crypto Crawler\'s Dashboard'), html.Img( src='/static/logo.png'), ], className='banner'), # Live Tweets html.Div([ html.Div([ html.H3( 'Tweets per {} sec.' \ .format(self.update_interval)) ], className='title'), html.Div([ # Dropdown to select time range for Live Tweet Chart dcc.Dropdown( id='tweets-live-dropdown', options=[ {'label': 'Stop', 'value': 0}, {'label': 'Last minute', 'value': 1}, {'label': 'Last 5 min', 'value': 5}, {'label': 'Last 15 min', 'value': 15}, {'label': 'Last 30 min', 'value': 30} ], value=5 ), # Chart for Live Tweets dcc.Graph( id='tweets-live-plot', style={'width': '878px', 'height': '200px'}, figure=self.plot_live_tweets( self.topics_default, 5), config={ 'displayModeBar': False }) ], className='content') ], className='live-box'), # Hidden element to store data # See: https://plot.ly/dash/sharing-data-between-callbacks html.Div(id='hidden-tweet-data', style={'display': 'none'}), html.Div(id='hidden-stock-data', style={'display': 'none'}), html.Div(id='hidden-layout-data', style={'display': 'none'}), # Topic Selection html.Div([ html.Div([ html.H3("Topic Selection") ], className='title'), html.Div([ dcc.Checklist( id='global-topic-checklist', options=topics_options, values=self.topics_default ), ], className='content') ], className='box'), # Overall Tweets per Hour html.Div([ html.Div([ html.H3([ html.Span('∑', className='icon'), 'Tweets per Hour']) ], className='title'), html.Div([ dcc.Checklist( id='tweet-anoms-toggle', options=[ {'label': 'Show Anomalies', 'value': 'anoms'} ], className='anoms-toggle', values=[] ), ], className='content'), html.Div([ # Chart for All Tweets dcc.Graph( style={'width': '878px', 'height': '250px'}, id='tweets-plot') ], className='content') ], className='box'), # Sentiment per Hour html.Div([ html.Div([ html.H3([ html.Span('☺', className='icon'), 'Avg. Sentiment per Hour']) ], className='title'), html.Div([ dcc.Checklist( id='senti-anoms-toggle', options=[ {'label': 'Show Anomalies', 'value': 'anoms'} ], className='anoms-toggle', values=[] ), ], className='content'), html.Div([ dcc.Graph( style={'width': '878px', 'height': '250px'}, id='senti-plot') ], className='content') ], className='box'), # Prices per Hour html.Div([ html.Div([ html.H3([ html.Span('€', className='icon'), 'Avg. Stock Prices per Hour']) ], className='title'), html.Div([ dcc.Checklist( id='stock-anoms-toggle', options=[ {'label': 'Show Anomalies', 'value': 'anoms'} ], values=[], className='anoms-toggle' ), ], className='content'), html.Div([ dcc.Graph( style={'width': '878px', 'height': '250px'}, id='stock-plot') ], className='content') ], className='box'), # Random Tweets html.Div([ html.Div([ html.H3([ html.Span(className='fa fa-twitter icon'), 'Random tweets for the selected topic']), html.Button(className='fa fa-refresh refresh', id='refresh-tweets-button') ], className='title'), html.Div([ ], id='tweetbox', className='content') ], className='box'), # Topic Models html.Div([ html.Div([ html.H3([ html.Span(className='fa fa-newspaper-o icon'), 'Identify Topics']) ], className='title'), html.Div([ html.Div([ dcc.Dropdown( options=[{'label': i, 'value': i} for i in self.topics], value='bitcoin', id='topic-collection-dropdown' ), dcc.DatePickerRange( id='topic-date-picker', start_date=datetime.datetime(2018, 1, 1, 0, 0, 0), end_date=datetime.datetime(2018, 2, 1, 0, 0, 0), end_date_placeholder_text='Select a date!' ), dcc.Input( placeholder='No. of Topics...', type='number', value=5, min=2, max=10, inputmode='numeric', id='topic-number-input' ), html.Button(className='fa fa-search', id='topic-button') ], className='settings-bar'), html.Div([ html.Div([html.Span(className='fa fa-arrow-circle-o-up'), 'Make your selection and wait some Minutes!'], className='topic-placeholder') ], id='topic-results') ], id='topicbox', className='content') ], className='box'), # Footer html.Div( 'Build in 01/2018 by kevhen & dynobo with ❤ and Plotly Dash', id='bottom-line'), dcc.Interval(id='live-update', interval=1000 * self.update_interval), ], className='container') @app.server.route('/static/<path:path>') def static_file(path): static_folder = os.path.join(os.getcwd(), 'static') return send_from_directory(static_folder, path) @app.callback(dash.dependencies.Output('tweetbox', 'children'), [ ddp.Input(component_id='global-topic-checklist', component_property='values'), ddp.Input(component_id='refresh-tweets-button', component_property='n_clicks'), ddp.Input('tweets-plot', 'relayoutData'), ddp.Input('senti-plot', 'relayoutData'), ddp.Input('stock-plot', 'relayoutData') ], [], []) def returnUpdatedTweetbox(topic_values, n_clicks, rd_tweets, rd_senti, rd_stock): timeframe = {} if rd_tweets is not None: timeframe = rd_tweets if rd_senti is not None: timeframe = rd_senti if rd_stock is not None: timeframe = rd_stock if 'xaxis.range[0]' in timeframe: fromTs = convertDate(timeframe['xaxis.range[0]']) else: fromTs = 0 if 'xaxis.range[1]' in timeframe: toTs = convertDate(timeframe['xaxis.range[1]']) else: toTs = 999999999999 payload = { "topics": ','.join(topic_values), "amount": 5, "from": fromTs, "to": toTs } tweets = [] response = requests.get('http://*****:*****@app.callback(ddp.Output('tweets-live-plot', 'figure'), [ ddp.Input(component_id='global-topic-checklist', component_property='values'), ddp.Input(component_id='tweets-live-dropdown', component_property='value') ], [], [ddp.Event('live-update', 'interval')]) def update_live_timeseries(topic_values, live_range): # Do nothing, if Live Chart is set to "off" (value = 0) if (live_range == 0) or (live_range is None): return return self.plot_live_tweets(topic_values, live_range) @app.callback(ddp.Output('hidden-tweet-data', 'children'), [ ddp.Input(component_id='global-topic-checklist', component_property='values') ]) def clean_tweet_data(topic_values): # Query for Data df = self.get_agg_data(topic_values, 'score') if (df is None) or (len(df) < 1 or ('timestamp_ms' not in df.columns.values)): df_empty = pd.DataFrame() data = (df_empty.to_json(date_format='iso', orient='split') + "\n") * 3 return data # Group and aggregate df = df.groupby(['timestamp_ms', 'collection']) df_score = df['score'].mean().unstack('collection').fillna(0) df_count = df['count'].sum().unstack('collection').fillna(0) # Get Anomalies for count df_anoms_count = pd.DataFrame() for col in df_count.columns.values: df_temp = self.get_anomalies(df_count[col]) df_anoms_count = pd.concat([df_anoms_count, df_temp], axis=1) # Get Anomalies for score df_anoms_score = pd.DataFrame() for col in df_score.columns.values: df_temp = self.get_anomalies(df_score[col]) df_anoms_score = pd.concat([df_anoms_score, df_temp], axis=1) # Jsonfy & concat DFs, then store string in hidden element data = df_count.to_json(date_format='iso', orient='split') \ + "\n" + \ df_score.to_json(date_format='iso', orient='split') \ + "\n" + \ df_anoms_count.to_json(date_format='iso', orient='split') \ + "\n" + \ df_anoms_score.to_json(date_format='iso', orient='split') \ return data @app.callback(ddp.Output('hidden-stock-data', 'children'), [ ddp.Input(component_id='global-topic-checklist', component_property='values') ]) def clean_stock_data(topic_values): # Get Data currencies = { 'BTC': 'bitcoin', 'IOT': 'iota', 'ETH': 'ethereum', } currency_codes = [] for key, val in currencies.items(): if val in topic_values: currency_codes.append(key) if len(currency_codes) > 0: df = self.get_agg_data(currency_codes, 'EUR') # Replace codes with names df['collection'] = df['collection'].replace(currencies) # Group and aggregate df = df.groupby(['timestamp_ms', 'collection']) df = df['EUR'].mean().unstack('collection').fillna(0) else: df = pd.DataFrame() # Get Anomalies for score df_anoms = pd.DataFrame() for col in df.columns.values: df_anoms[col] = self.get_anomalies(df[col]) # Jsonfy & concat DFs, then store string in hidden element data = df.to_json(date_format='iso', orient='split') \ + "\n" + \ df_anoms.to_json(date_format='iso', orient='split') return data axises = ['', '', ''] # Used to store axis scales of the three charts @app.callback(ddp.Output('hidden-layout-data', 'children'), [ ddp.Input('tweets-plot', 'relayoutData'), ddp.Input('senti-plot', 'relayoutData'), ddp.Input('stock-plot', 'relayoutData') ]) def set_layout_data(rd_tweet, rd_senti, rd_stock): new_axises = [rd_tweet, rd_senti, rd_stock] for idx, val in enumerate(new_axises): if val != axises[idx]: axises[idx] = val return json.dumps(val) return json.dumps({}) @app.callback(ddp.Output('tweets-plot', 'figure'), [ ddp.Input('hidden-tweet-data', 'children'), ddp.Input('hidden-layout-data', 'children'), ddp.Input('tweet-anoms-toggle', 'values') ]) def update_timeseries(jsonified_data, rd_data, toggle): data = jsonified_data.split('\n')[0] df = pd.read_json(data, orient='split') if 'anoms' in toggle: anoms = jsonified_data.split('\n')[2] df_anoms = pd.read_json(anoms, orient='split') else: df_anoms = None x_axis = self.get_x(json.loads(rd_data)) return self.plot_timeseries('Tweets', df, df_anoms, x_axis) @app.callback(ddp.Output('senti-plot', 'figure'), [ ddp.Input('hidden-tweet-data', 'children'), ddp.Input('hidden-layout-data', 'children'), ddp.Input('senti-anoms-toggle', 'values') ]) def update_senti(jsonified_data, rd_data, toggle): data = jsonified_data.split('\n')[1] df = pd.read_json(data, orient='split') if 'anoms' in toggle: anoms = jsonified_data.split('\n')[3] df_anoms = pd.read_json(anoms, orient='split') else: df_anoms = None x_axis = self.get_x(json.loads(rd_data)) return self.plot_timeseries('Sentiment Score', df, df_anoms, x_axis) @app.callback(ddp.Output('stock-plot', 'figure'), [ ddp.Input('hidden-stock-data', 'children'), ddp.Input('hidden-layout-data', 'children'), ddp.Input('stock-anoms-toggle', 'values') ]) def update_plot(jsonified_data, rd_data, toggle): data = jsonified_data.split('\n')[0] df = pd.read_json(data, orient='split') if 'anoms' in toggle: anoms = jsonified_data.split('\n')[1] df_anoms = pd.read_json(anoms, orient='split') else: df_anoms = None x_axis = self.get_x(json.loads(rd_data)) return self.plot_timeseries('€ Stock Price', df, df_anoms, x_axis) self.app = app @app.callback(ddp.Output('topic-results', 'children'), [ddp.Input('topic-button', 'n_clicks')], [ ddp.State('topic-collection-dropdown', 'value'), ddp.State('topic-date-picker', 'start_date'), ddp.State('topic-date-picker', 'end_date'), ddp.State('topic-number-input', 'value'), ]) def update_topics(btn, coll, start_date, end_date, number): print(btn) placeholder = html.Div([ html.Span(className='fa fa-arrow-circle-o-up'), 'Make your selection and wait some Minutes!' ], className='topic-placeholder') if not start_date or not end_date \ or not number or not coll or not btn: logger.warn('Parameter is missing.') return placeholder if self.topic_btn_clicks == btn: return placeholder else: self.topic_btn_clicks = btn start_ms = int( time.mktime(time.strptime(start_date[:10], '%Y-%m-%d'))) * 1000 end_ms = int(time.mktime(time.strptime(end_date[:10], '%Y-%m-%d'))) * 1000 data = self.get_topics(coll, start_ms, end_ms, number) if (not data) or ('topics' not in data): logger.warn('No data received.') return placeholder all_topics = [ html.Div('Topics from {} Tweets'.format(data['tweet_count']), className='topic-header') ] for topic in data['topics']: topic_html = html.Div([ html.Div([ t, html.Span('{:.3f}'.format(c)[1:], className='topic-value') ], className='topic-token') for t, c in topic[:15] ], className='topic-row') all_topics.append(topic_html) return html.Div(all_topics) self.app = app
df=df.astype('str') row = df.iloc[[0]] fromcity, tocity, fromtuple, totuple = geoloc.updateLatLong(row, master, geolocator) fig = geoloc.plotGeoPlot(m, fromcity, tocity, fromtuple, totuple) return fig def serve_layout(): return html.Div(children=[ html.Div([ html.H1("Plotly test with Live update", style={"font-family": "Helvetica", "border-bottom": "1px #000000 solid"}), ], className='banner'), html.Div([dcc.Graph(id='plot', figure=gen_plot())],), dcc.Interval(id='live-update', interval=interval), ],) app = dash.Dash() app.layout = serve_layout app.callback( ddp.Output('plot', 'figure'), [], [], [ddp.Event('live-update', 'interval')])(gen_plot) if __name__ == '__main__': app.run_server(debug=True)
class VisualizationView: _model = None # type: VisualizationModel # https://github.com/plotly/dash/issues/214 _flask = Flask(__name__) _dash = Dash(__name__, server=_flask) _axis_styles = dict() _plot_styles = dict() _dist_axes = None length = 0 _dash.layout = get_layout() _iterations = 0 @staticmethod @_flask.route("/init_model", methods=["POST"]) def init_model(): data = request.data Logger.log(f"initializing {str(data):s}") d = json.loads(data) axes = d["axes"] VisualizationView._dist_axes = {_axis_name for _axis_name, _, _is_dist in axes if _is_dist} VisualizationView._axis_styles.clear() VisualizationView._plot_styles.clear() VisualizationView.length = d.get("length", 0) axes_model = tuple((_axis_name, _width) for _axis_name, _width, _ in axes) VisualizationView._model = VisualizationModel(axes_model, length=VisualizationView.length) VisualizationView._dash.layout = get_layout() _iterations = 0 return jsonify(f"initialized {str(axes):s}, length {VisualizationView.length:d}") @staticmethod @_flask.route("/style", methods=["POST"]) def style(): data = request.data Logger.log(f"styling {str(data):s}") d = json.loads(data) VisualizationView._axis_styles.clear() VisualizationView._axis_styles.update(d["axes"]) VisualizationView._plot_styles.clear() VisualizationView._plot_styles.update(d["plots"]) return jsonify("styling done") @staticmethod @_flask.route("/data", methods=["POST"]) def add_data(): if VisualizationView._model is None: raise ValueError("visualization model not initialized") data = request.data Logger.log(f"adding batch") d = json.loads(data) batch = d["batch"] iteration = d["iteration"] VisualizationView._model.add_batch(iteration, batch) return jsonify(f"added batch of size {len(batch):d}") @staticmethod def _get_concentration(_axis_name: str, this_plot_style: Dict[str, Any]) -> Sequence[BasePlotlyType]: axis_data = [] for _j, _plot_name in enumerate(VisualizationView._model.get_plot_names(_axis_name)): series = VisualizationView._model.get_plot(_axis_name, _plot_name) no_series = len(series) half_plus_one = no_series // 2 + 1 no_bands = no_series - half_plus_one + 1 alpha = 1. / no_bands color = hsv_to_rgb((distribute_circular(_j), .7, .7)) color_str = ", ".join([f"{int(_x * 255.):d}" for _x in color]) fillcolor = f"rgba({color_str:s}, {alpha:.2f})" plot_properties = this_plot_style.get(_plot_name, {"fillcolor": fillcolor}) for _i in range(no_bands): series_a = series[_i] series_b = series[_i + half_plus_one - 1] outline = series_a + series_b[::-1] range_a = VisualizationView._model.x_range each_range = range_a + range_a[::-1] data = graph_objs.Scatter( **plot_properties, showlegend=_i == 0, x=each_range, y=outline, name=_plot_name, fill="tozerox", mode="lines", line={ "color": f"rgba({color_str:s}, 1)", "width": 0, "shape": "hv", #"shape": "spline", }, # line={"color": "rgba(255, 255, 255, 0)"}, ) axis_data.append(data) return axis_data @staticmethod def _get_lines(_axis_name: str, this_plot_style: Dict[str, Any]) -> Sequence[BasePlotlyType]: axis_data = [] for _j, _plot_name in enumerate(VisualizationView._model.get_plot_names(_axis_name)): color = hsv_to_rgb((distribute_circular(_j), .7, .7)) color_str = ", ".join([f"{int(_x * 255.):d}" for _x in color]) plot_properties = this_plot_style.get(_plot_name, dict()) series = VisualizationView._model.get_plot(_axis_name, _plot_name) for each_series in series: data = graph_objs.Scatter( **plot_properties, showlegend=True, x=VisualizationView._model.x_range, y=each_series, name=_plot_name, mode="lines", line={ "color": f"rgba({color_str:s}, 1)", "width": 1, "shape": "hv", #"shape": "spline", }, ) axis_data.append(data) return axis_data @staticmethod @_dash.callback(dependencies.Output("graphs", "children"), events=[dependencies.Event("graph-update", "interval")]) def __update_graph(): graphs = [] if VisualizationView._model is None or len(VisualizationView._model.x_range) < 1: return graphs if VisualizationView.length < 0: x_min = max(0, VisualizationView._model.x_range[-1] + VisualizationView.length) x_max = x_min - VisualizationView.length elif 0 < VisualizationView.length: x_min = 0 x_max = VisualizationView.length else: x_min = 0 x_max = VisualizationView._model.x_range[-1] for _axis_name in VisualizationView._model.axes: this_plot_style = VisualizationView._plot_styles.get(_axis_name, dict()) if _axis_name not in VisualizationView._dist_axes: axis_data = VisualizationView._get_lines(_axis_name, this_plot_style) else: axis_data = VisualizationView._get_concentration(_axis_name, this_plot_style) axis_properties = VisualizationView._axis_styles.get(_axis_name, dict()) # todo: get axis, update with default values # todo: also set y axis in constructor # todo: try axis property "autorange=True" layout = graph_objs.Layout( **axis_properties, xaxis={ "range": [x_min, x_max], "title": "iterations", }, yaxis={ "title": _axis_name, #"type": "log", "autorange": True # only works if Graph animate=False, see https://community.plot.ly/t/how-to-enable-automatic-autoscale/5276/9 }, legend={ "x": 1, "y": 1 } ) graphs.append(dash_html_components.Div(children=[ dash_core_components.Graph( id=_axis_name, animate=False, animation_options={ "mode": "immediate", #"frame": { # "duration": 200, # "redraw": False, #}, #"transition": { # "duration": 0, #} }, figure={ "data": axis_data, "layout": layout} ) ])) return graphs