This commit is contained in:
NY 2025-03-05 11:18:21 +08:00
parent 938aa3ea8d
commit 60bbbd40d0
3 changed files with 59 additions and 43 deletions

View File

@ -113,7 +113,7 @@ def layout_config(app):
dcc.Tabs(id='tabs', value='line', children=[ dcc.Tabs(id='tabs', value='line', children=[
dcc.Tab(label='Line Chart', value='line'), dcc.Tab(label='Line Chart', value='line'),
dcc.Tab(label='Heatmap', value='heatmap'), dcc.Tab(label='Heatmap', value='heatmap'),
dcc.Tab(label='Scatter Plot', value='scatter'), dcc.Tab(label='One Day Heatmap Plot', value='one_day_heatmap'),
]), ]),
html.Div(id='tabs-content'), html.Div(id='tabs-content'),
], style={'marginLeft': '50px'}), ], style={'marginLeft': '50px'}),

View File

@ -2,12 +2,12 @@ from datetime import datetime, timedelta
from dash.dependencies import Input, Output from dash.dependencies import Input, Output
from pkg.dash.app_init import app from pkg.dash.app_init import app
from pkg.config import render_data from pkg.config import render_data
from pkg.tool import aggregate_data,generate_xticks,minutes_to_time from pkg.tool import aggregate_data, generate_xticks, minutes_to_time
from dash import dcc from dash import dcc
import plotly.graph_objs as go import plotly.graph_objs as go
import pandas as pd import pandas as pd
# Callback for updating tabs content (Modified to add Thursday-Friday lines)
@app.callback( @app.callback(
[Output('tabs-content', 'children'), [Output('tabs-content', 'children'),
Output('multi-day-warning', 'children'), Output('multi-day-warning', 'children'),
@ -20,7 +20,7 @@ import pandas as pd
) )
def render_tab_content(tab, selected_dates, interval, time_zones, days_to_display): def render_tab_content(tab, selected_dates, interval, time_zones, days_to_display):
warning = "" warning = ""
if tab != 'heatmap': if tab == 'line':
if len(selected_dates) > 10: if len(selected_dates) > 10:
selected_dates = selected_dates[:10] selected_dates = selected_dates[:10]
warning = "Maximum of 10 days can be selected. Showing first 10 selected days." warning = "Maximum of 10 days can be selected. Showing first 10 selected days."
@ -81,52 +81,66 @@ def render_tab_content(tab, selected_dates, interval, time_zones, days_to_displa
xref="x", xref="x",
yref="paper" yref="paper"
) )
fig.update_layout( fig.update_layout(
title=f'Tweet Heatmap (Interval: {interval} minutes, EST, {len(selected_dates)} days)', title=f'Tweet Heatmap (Interval: {interval} minutes, EST, {len(selected_dates)} days)',
xaxis_title='Time of Day (HH:MM EST)', xaxis_title='Time of Day (HH:MM EST)',
yaxis_title='Date', yaxis_title='Date',
height=max(400, len(selected_dates) * 20), height=max(400, len(selected_dates) * 20),
yaxis=dict(autorange='reversed') yaxis=dict(autorange='reversed')
) )
elif tab == 'scatter': elif tab == 'one_day_heatmap':
fig = go.Figure() one_day_data = agg_data.groupby('interval_group')['tweet_count'].sum().reset_index()
for date in selected_dates:
day_data = multi_data_raw[multi_data_raw['date'] == date]
if not day_data.empty:
hover_times = [t.strftime('%Y-%m-%d %H:%M:%S EST') for t in day_data['datetime_est']]
fig.add_trace(go.Scatter(
x=day_data['minute_of_day'],
y=[str(date)] * len(day_data),
mode='markers',
name=str(date),
customdata=hover_times,
hovertemplate='%{customdata}<extra></extra>',
marker=dict(size=8)
))
if tab in ['line', 'scatter']: hours = list(range(24))
intervals_per_hour = 60 // interval
z_values = [[0] * intervals_per_hour for _ in range(24)]
for _, row in one_day_data.iterrows():
minute = row['interval_group']
hour = int(minute // 60) # Convert to integer
interval_idx = int((minute % 60) // interval) # Convert to integer
if hour < 24:
z_values[hour][interval_idx] = row['tweet_count']
x_labels = [f"{i * interval:02d}" for i in range(intervals_per_hour)]
fig = go.Figure(data=go.Heatmap(
z=z_values,
x=x_labels,
y=[f"{h:02d}:00" for h in hours],
colorscale='Viridis',
hoverongaps=False,
hovertemplate='%{y} %{x} EST<br>Tweets: %{z}<extra></extra>'
))
# Add time zone annotations if selected
if 'PST' in time_zones: if 'PST' in time_zones:
pacific_2am_est = (2 + 3) * 60 pacific_2am_est = (2 + 3) * 60 // interval
pacific_7am_est = (7 + 3) * 60 pacific_7am_est = (7 + 3) * 60 // interval
fig.add_vline(x=pacific_2am_est, line_dash="dash", line_color="blue", annotation_text="CA 2AM PST") fig.add_vline(x=pacific_2am_est, line_dash="dash", line_color="blue", annotation_text="CA 2AM PST")
fig.add_vline(x=pacific_7am_est, line_dash="dash", line_color="blue", annotation_text="CA 7AM PST") fig.add_vline(x=pacific_7am_est, line_dash="dash", line_color="blue", annotation_text="CA 7AM PST")
if 'CST' in time_zones: if 'CST' in time_zones:
central_2am_est = (2 + 1) * 60 central_2am_est = (2 + 1) * 60 // interval
central_7am_est = (7 + 1) * 60 central_7am_est = (7 + 1) * 60 // interval
fig.add_vline(x=central_2am_est, line_dash="dash", line_color="green", annotation_text="TX 2AM CST") fig.add_vline(x=central_2am_est, line_dash="dash", line_color="green", annotation_text="TX 2AM CST")
fig.add_vline(x=central_7am_est, line_dash="dash", line_color="green", annotation_text="TX 7AM CST") fig.add_vline(x=central_7am_est, line_dash="dash", line_color="green", annotation_text="TX 7AM CST")
if tab in ['line', 'scatter']: if tab in ['line', 'one_day_heatmap']:
fig.update_layout( fig.update_layout(
title=f'{"Line" if tab == "line" else "Scatter"} Tweet Frequency (Interval: {interval} minutes, EST)', title=f'{"Line" if tab == "line" else "One-Day Heatmap"} Tweet Frequency (Interval: {interval} minutes, EST, {len(selected_dates)} days)',
xaxis_title='Eastern Time (HH:MM)', xaxis_title='Minutes' if tab == 'one_day_heatmap' else 'Eastern Time (HH:MM)',
yaxis_title='Tweet Count' if tab == 'line' else 'Date', yaxis_title='Hour of Day' if tab == 'one_day_heatmap' else 'Tweet Count',
xaxis=dict(range=[0, 1440], tickvals=xticks, ticktext=xtick_labels, tickangle=45), xaxis=dict(
range=[0, 1440] if tab == 'line' else None,
tickvals=xticks if tab == 'line' else None,
ticktext=xtick_labels if tab == 'line' else None,
tickangle=45 if tab == 'line' else 0
),
height=600, height=600,
showlegend=True, showlegend=(tab == 'line'),
yaxis=dict(autorange='reversed') if tab == 'scatter' else None yaxis=dict(autorange='reversed') if tab == 'one_day_heatmap' else None
) )
summary = f"Total tweets for selected dates: {int(tweet_count_total)}" summary = f"Total tweets for selected dates: {int(tweet_count_total)}"
return dcc.Graph(figure=fig), warning, summary return dcc.Graph(figure=fig), warning, summary

View File

@ -1,12 +1,14 @@
from pkg.dash.app_init import app from pkg.dash.app_init import app
from dash.dependencies import Input, Output from dash.dependencies import Input, Output
@app.callback( @app.callback(
[Output('date-picker-container', 'style'), [Output('date-picker-container', 'style'),
Output('days-display-container', 'style')], Output('days-display-container', 'style'),
Output('time-zone-checklist', 'style')],
[Input('tabs', 'value')] [Input('tabs', 'value')]
) )
def toggle_controls_visibility(tab): def toggle_controls_visibility(tab):
if tab == 'heatmap': if tab == 'heatmap' or tab == 'one_day_heatmap':
return {'display': 'none'}, {'display': 'block'} return {'display': 'none'}, {'display': 'block'}, {'display': 'none'}
return {'display': 'block'}, {'display': 'none'} return {'display': 'block'}, {'display': 'none'}, {'display': 'block'}