Compare commits

...

6 Commits

Author SHA1 Message Date
NY
ff0a646c81 fix 2025-03-11 09:47:44 +08:00
NY
ab64ec4536 fix 2025-03-07 14:14:08 +08:00
NY
c4534abe52 fix 2025-03-07 13:52:35 +08:00
NY
82f69596b8 fix 2025-03-06 14:53:17 +08:00
NY
a0f436c98f fix 2025-03-06 13:59:37 +08:00
NY
205b1ed311 1.增加info,目前支持计算Xtracker的Pace。
2.增加Total Tweets(本周多少Tweets)
2025-03-06 13:59:21 +08:00
6 changed files with 223 additions and 11 deletions

View File

@ -63,8 +63,36 @@ def layout_config(app):
'whiteSpace': 'nowrap'
}
),
# 新增按钮和提示框
html.Div(
id='info-button',
children='',
style={
'fontSize': '24px',
'cursor': 'pointer',
'padding': '5px',
'marginTop': '10px'
}
),
html.Div(
id='info-tooltip',
children='这是一个信息按钮示例', # 默认显示的信息
style={
'position': 'absolute',
'left': '35px',
'top': '80px', # 调整位置,避免与其他 tooltip 重叠
'backgroundColor': 'rgba(0, 0, 0, 0.8)',
'color': 'white',
'padding': '10px',
'borderRadius': '5px',
'fontSize': '14px',
'display': 'none',
'whiteSpace': 'nowrap'
}
),
html.A(
href='https://x.com/elonmusk',
target='_blank',
children=[
html.Img(
src='https://pbs.twimg.com/profile_images/1893803697185910784/Na5lOWi5_400x400.jpg',

View File

@ -21,4 +21,3 @@ for sub_dir in sub_dirs:

119
pkg/dash/func/info.py Normal file
View File

@ -0,0 +1,119 @@
import pytz
from pkg.tool import get_tweets_since_last_friday, format_time_str, get_time_since_last_tweet
from pkg.dash.app_init import app
from dash.dependencies import Input, Output
from datetime import timedelta
from datetime import datetime
from dash import html
@app.callback(
[Output('info-tooltip', 'children')],
[Input('clock-interval', 'n_intervals')]
)
def update_info(n):
# 获取所有指标
pace = calculate_tweet_pace()
decline_rates = calculate_pace_decline_rate()
pace_increases = calculate_pace_increase_in_hour()
_, days_to_next_friday = get_pace_params()
table_style_border = {'textAlign': 'center', 'border': '1px solid white'}
table_style_c = {'textAlign': 'center'}
table_style_l = {'textAlign': 'left'}
# First table for Pace
pace_table_rows = [
html.Tr([
html.Th('Pace', style=table_style_border),
html.Th('DCL', style=table_style_border),
html.Th('0(1h)', style=table_style_border),
html.Th('1(1h)', style=table_style_border),
html.Th('5(1h)', style=table_style_border),
html.Th('10(1h)', style=table_style_border),
html.Th('20(1h)', style=table_style_border),
html.Th('30(1h)', style=table_style_border)
]),
html.Tr([
html.Td(f"{pace:.2f}", style=table_style_border),
html.Td(decline_rates, style=table_style_border),
html.Td(f"{pace_increases['increase_0']:.2f}", style=table_style_border),
html.Td(f"{pace_increases['increase_1']:.2f}", style=table_style_border),
html.Td(f"{pace_increases['increase_5']:.2f}", style=table_style_border),
html.Td(f"{pace_increases['increase_10']:.2f}", style=table_style_border),
html.Td(f"{pace_increases['increase_20']:.2f}", style=table_style_border),
html.Td(f"{pace_increases['increase_30']:.2f}", style=table_style_border)
]),
html.Tr([
html.Td(f"Remain",
colSpan=4,
style=table_style_c),
html.Td(format_time_str(days_to_next_friday),
colSpan=4,
style=table_style_l)
]),
html.Tr([
html.Td(f"Last Tweet",
colSpan=4,
style=table_style_c),
html.Td(format_time_str(get_time_since_last_tweet()),
colSpan=4,
style=table_style_l)
]),
html.Tr([
html.Td(f"Warning:",
colSpan=1,
style=table_style_c),
html.Td("1.Check Reply",
colSpan=7,
style=table_style_l)
])
]
pace_table = html.Table(pace_table_rows, style={
'width': '100%',
'textAlign': 'left',
'borderCollapse': 'collapse'
})
return [pace_table]
def get_pace_params():
est = pytz.timezone('US/Eastern')
now = datetime.now(est)
today = now.date()
days_to_next_friday = (4 - today.weekday()) % 7
next_friday = (now.replace(hour=12, minute=0, second=0, microsecond=0) +
timedelta(days=days_to_next_friday))
if now > next_friday:
next_friday += timedelta(days=7)
days_to_next_friday = (next_friday - now).total_seconds() / (24 * 60 * 60)
tweet_count = get_tweets_since_last_friday()
return tweet_count, days_to_next_friday
def calculate_tweet_pace():
tweet_count, days_to_next_friday = get_pace_params()
pace = (tweet_count / (7 - days_to_next_friday)) * days_to_next_friday + tweet_count
return round(pace, 6) if pace > 0 else float(tweet_count)
def calculate_pace_decline_rate():
tweet_count, days_to_next_friday = get_pace_params()
T = 7
decline_per_day = -(tweet_count * T) / ((T - days_to_next_friday) ** 2)
decline_per_hour = decline_per_day / 24
return round(decline_per_hour, 2)
def calculate_pace_increase_in_hour():
tweet_count, days_to_next_friday = get_pace_params()
current_pace = (tweet_count / (7 - days_to_next_friday)) * days_to_next_friday + tweet_count
future_days = days_to_next_friday - (1 / 24)
increments = [0, 1, 5, 10, 20, 30]
pace_increases = {}
for increment in increments:
new_tweet_count = tweet_count + increment
new_pace = (new_tweet_count / (7 - future_days)) * future_days + new_tweet_count
pace_increase = new_pace - current_pace
# Add current pace to the increase value
pace_increases[f'increase_{increment}'] = round(current_pace + pace_increase, 2)
return pace_increases

View File

@ -2,7 +2,7 @@ from datetime import datetime, timedelta
from dash.dependencies import Input, Output
from pkg.dash.app_init import app
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, get_tweets_since_last_friday
from dash import dcc
import plotly.graph_objs as go
import pandas as pd
@ -137,5 +137,5 @@ def render_tab_content(tab, selected_dates, interval, time_zones, days_to_displa
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)}Total tweets: {get_tweets_since_last_friday()}"
return dcc.Graph(figure=fig), warning, summary

View File

@ -1,18 +1,35 @@
from dash.dependencies import Input, Output, State
from dash import clientside_callback
def setting_callback():
clientside_callback(
"""
function(n_intervals) {
const button = document.getElementById('clock-button');
const tooltip = document.getElementById('clock-tooltip');
if (button && tooltip) {
button.addEventListener('mouseover', () => {
tooltip.style.display = 'block';
const clockButton = document.getElementById('clock-button');
const clockTooltip = document.getElementById('clock-tooltip');
if (clockButton && clockTooltip) {
clockButton.addEventListener('mouseover', () => {
clockTooltip.style.display = 'block';
});
button.addEventListener('mouseout', () => {
tooltip.style.display = 'none';
clockButton.addEventListener('mouseout', () => {
clockTooltip.style.display = 'none';
});
}
const infoButton = document.getElementById('info-button');
const infoTooltip = document.getElementById('info-tooltip');
if (infoButton && infoTooltip) {
infoButton.addEventListener('mouseover', () => {
infoTooltip.style.display = 'block';
});
infoButton.addEventListener('mouseout', () => {
infoTooltip.style.display = 'none';
});
infoTooltip.addEventListener('mouseover', () => {
infoTooltip.style.display = 'block';
});
infoTooltip.addEventListener('mouseout', () => {
infoTooltip.style.display = 'none';
});
}
return window.dash_clientside.no_update;

View File

@ -1,5 +1,7 @@
from datetime import datetime
from datetime import datetime, timedelta
import pandas as pd
from pkg.config import render_data
import pytz
def aggregate_data(data, interval):
all_minutes = pd.DataFrame({'interval_group': range(0, 1440, interval)})
@ -46,3 +48,50 @@ def minutes_to_time(minutes):
hours = minutes // 60
mins = minutes % 60
return f"{hours:02d}:{mins:02d}"
def get_tweets_since_last_friday():
est = pytz.timezone('US/Eastern')
now_est = datetime.now(est)
today = now_est.date()
days_since_friday = (today.weekday() - 4) % 7
this_friday = today - timedelta(days=days_since_friday)
this_friday_datetime = est.localize(datetime.combine(this_friday, datetime.strptime("12:00", "%H:%M").time()))
last_friday = this_friday - timedelta(days=7)
last_friday_datetime = est.localize(datetime.combine(last_friday, datetime.strptime("12:00", "%H:%M").time()))
if now_est < this_friday_datetime:
start_datetime = last_friday_datetime
else:
start_datetime = this_friday_datetime
if hasattr(render_data, 'global_df') and not render_data.global_df.empty:
df = render_data.global_df.copy()
mask = df['datetime_est'] >= start_datetime
filtered_df = df[mask]
tweet_count = len(filtered_df)
return int(tweet_count)
return 0
def get_time_since_last_tweet():
est = pytz.timezone('US/Eastern')
now_est = datetime.now(est)
if (not hasattr(render_data, 'global_df') or
render_data.global_df is None or
render_data.global_df.empty):
return 0.0
df = render_data.global_df
if 'datetime_est' not in df.columns:
return 0.0
latest_tweet_time = df['datetime_est'].max()
time_diff = now_est - latest_tweet_time
days_diff = time_diff.total_seconds() / (24 * 60 * 60) # 转换为天数
return days_diff
def format_time_str(days_to_next_friday):
total_seconds = days_to_next_friday * 24 * 60 * 60
days = int(total_seconds // (24 * 60 * 60))
hours = int((total_seconds % (24 * 60 * 60)) // (60 * 60))
minutes = int((total_seconds % (60 * 60)) // 60)
seconds = int(total_seconds % 60)
total_hours = round(days_to_next_friday * 24, 2)
return f"{days}d {hours:02d}h {minutes:02d}m {seconds:02d}s ({total_hours}h)"