This commit is contained in:
SengokuCola
2025-06-23 00:39:23 +08:00
2 changed files with 213 additions and 174 deletions

View File

@@ -29,10 +29,6 @@ class HFCPerformanceLogger:
)
self.current_session_data = []
def record_cycle(self, cycle_data: Dict[str, Any]):
"""记录单次循环数据"""
try:
@@ -80,7 +76,7 @@ class HFCPerformanceLogger:
"version": self.version,
"session_file": str(self.session_file),
"record_count": len(self.current_session_data),
"start_time": self.session_start_time.isoformat()
"start_time": self.session_start_time.isoformat(),
}
def finalize_session(self):
@@ -111,11 +107,7 @@ class HFCPerformanceLogger:
for log_file in log_dir.glob("*.json"):
try:
file_stat = log_file.stat()
log_files.append({
'path': log_file,
'size': file_stat.st_size,
'mtime': file_stat.st_mtime
})
log_files.append({"path": log_file, "size": file_stat.st_size, "mtime": file_stat.st_mtime})
total_size += file_stat.st_size
except Exception as e:
logger.warning(f"无法获取文件信息 {log_file}: {e}")
@@ -134,7 +126,7 @@ class HFCPerformanceLogger:
return
# 按修改时间排序(最早的在前面)
log_files.sort(key=lambda x: x['mtime'])
log_files.sort(key=lambda x: x["mtime"])
deleted_count = 0
deleted_size = 0
@@ -144,8 +136,8 @@ class HFCPerformanceLogger:
break
try:
file_size = file_info['size']
file_path = file_info['path']
file_size = file_info["size"]
file_path = file_info["path"]
file_path.unlink()
total_size -= file_size

View File

@@ -512,7 +512,7 @@ class StatisticOutputTask(AsyncTask):
try:
# 从文件名解析时间戳 (格式: hash_version_date_time.json)
filename = os.path.basename(json_file)
name_parts = filename.replace('.json', '').split('_')
name_parts = filename.replace(".json", "").split("_")
if len(name_parts) >= 4:
date_str = name_parts[-2] # YYYYMMDD
time_str = name_parts[-1] # HHMMSS
@@ -521,7 +521,7 @@ class StatisticOutputTask(AsyncTask):
# 如果文件时间在查询范围内,则处理该文件
if file_time >= query_start_time:
with open(json_file, 'r', encoding='utf-8') as f:
with open(json_file, "r", encoding="utf-8") as f:
cycles_data = json.load(f)
self._process_focus_file_data(cycles_data, stats, collect_period, file_time)
except Exception as e:
@@ -532,8 +532,13 @@ class StatisticOutputTask(AsyncTask):
self._calculate_focus_averages(stats)
return stats
def _process_focus_file_data(self, cycles_data: List[Dict], stats: Dict[str, Any],
collect_period: List[Tuple[str, datetime]], file_time: datetime):
def _process_focus_file_data(
self,
cycles_data: List[Dict],
stats: Dict[str, Any],
collect_period: List[Tuple[str, datetime]],
file_time: datetime,
):
"""
处理单个focus文件的数据
"""
@@ -542,7 +547,7 @@ class StatisticOutputTask(AsyncTask):
# 解析时间戳
timestamp_str = cycle_data.get("timestamp", "")
if timestamp_str:
cycle_time = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
cycle_time = datetime.fromisoformat(timestamp_str.replace("Z", "+00:00"))
else:
cycle_time = file_time # 使用文件时间作为后备
@@ -646,8 +651,6 @@ class StatisticOutputTask(AsyncTask):
else:
stat["focus_exec_times_by_version_action"][version][action_type] = 0.0
def _collect_all_statistics(self, now: datetime) -> Dict[str, Dict[str, Any]]:
"""
收集各时间段的统计数据
@@ -804,11 +807,7 @@ class StatisticOutputTask(AsyncTask):
if stats[FOCUS_TOTAL_CYCLES] <= 0:
return ""
output = [
"Focus系统统计:",
f"总循环数: {stats[FOCUS_TOTAL_CYCLES]}",
""
]
output = ["Focus系统统计:", f"总循环数: {stats[FOCUS_TOTAL_CYCLES]}", ""]
# 全局阶段平均时间
if stats[FOCUS_AVG_TIMES_BY_STAGE]:
@@ -842,23 +841,24 @@ class StatisticOutputTask(AsyncTask):
try:
# 首先尝试从chat_stream获取真实群组名称
from src.chat.message_receive.chat_stream import get_chat_manager
chat_manager = get_chat_manager()
if chat_id in chat_manager.streams:
stream = chat_manager.streams[chat_id]
if stream.group_info and hasattr(stream.group_info, 'group_name'):
if stream.group_info and hasattr(stream.group_info, "group_name"):
group_name = stream.group_info.group_name
if group_name and group_name.strip():
return group_name.strip()
elif stream.user_info and hasattr(stream.user_info, 'user_nickname'):
elif stream.user_info and hasattr(stream.user_info, "user_nickname"):
user_name = stream.user_info.user_nickname
if user_name and user_name.strip():
return user_name.strip()
# 如果从chat_stream获取失败尝试解析chat_id格式
if chat_id.startswith('g'):
if chat_id.startswith("g"):
return f"群聊{chat_id[1:]}"
elif chat_id.startswith('u'):
elif chat_id.startswith("u"):
return f"用户{chat_id[1:]}"
else:
return chat_id
@@ -952,22 +952,30 @@ class StatisticOutputTask(AsyncTask):
if stat_data.get(FOCUS_TOTAL_CYCLES, 0) > 0:
# Action类型统计
total_actions = sum(stat_data[FOCUS_ACTION_RATIOS].values()) if stat_data[FOCUS_ACTION_RATIOS] else 0
_focus_action_rows = "\n".join([
f"<tr><td>{action_type}</td><td>{count}</td><td>{(count/total_actions*100):.1f}%</td></tr>"
_focus_action_rows = "\n".join(
[
f"<tr><td>{action_type}</td><td>{count}</td><td>{(count / total_actions * 100):.1f}%</td></tr>"
for action_type, count in sorted(stat_data[FOCUS_ACTION_RATIOS].items())
])
]
)
# 按聊天流统计
_focus_chat_rows = "\n".join([
_focus_chat_rows = "\n".join(
[
f"<tr><td>{self.name_mapping.get(chat_id, (chat_id, 0))[0]}</td><td>{count}</td><td>{stat_data[FOCUS_TOTAL_TIME_BY_CHAT].get(chat_id, 0):.2f}秒</td></tr>"
for chat_id, count in sorted(stat_data[FOCUS_CYCLE_CNT_BY_CHAT].items(), key=lambda x: x[1], reverse=True)
])
for chat_id, count in sorted(
stat_data[FOCUS_CYCLE_CNT_BY_CHAT].items(), key=lambda x: x[1], reverse=True
)
]
)
# 全局阶段时间统计
_focus_stage_rows = "\n".join([
_focus_stage_rows = "\n".join(
[
f"<tr><td>{stage}</td><td>{avg_time:.3f}秒</td></tr>"
for stage, avg_time in sorted(stat_data[FOCUS_AVG_TIMES_BY_STAGE].items())
])
]
)
# 按Action类型的阶段时间统计
focus_action_stage_items = []
@@ -975,10 +983,12 @@ class StatisticOutputTask(AsyncTask):
for stage, avg_time in stage_times.items():
focus_action_stage_items.append((action_type, stage, avg_time))
_focus_action_stage_rows = "\n".join([
_focus_action_stage_rows = "\n".join(
[
f"<tr><td>{action_type}</td><td>{stage}</td><td>{avg_time:.3f}秒</td></tr>"
for action_type, stage, avg_time in sorted(focus_action_stage_items)
])
]
)
# 生成HTML
return f"""
<div id=\"{div_id}\" class=\"tab-content\">
@@ -1222,10 +1232,12 @@ class StatisticOutputTask(AsyncTask):
# Action类型统计
total_actions = sum(stat_data[FOCUS_ACTION_RATIOS].values()) if stat_data[FOCUS_ACTION_RATIOS] else 0
if total_actions > 0:
focus_action_rows = "\n".join([
f"<tr><td>{action_type}</td><td>{count}</td><td>{(count/total_actions*100):.1f}%</td></tr>"
focus_action_rows = "\n".join(
[
f"<tr><td>{action_type}</td><td>{count}</td><td>{(count / total_actions * 100):.1f}%</td></tr>"
for action_type, count in sorted(stat_data[FOCUS_ACTION_RATIOS].items())
])
]
)
# 按聊天流统计横向表格显示各阶段时间差异和不同action的平均时间
focus_chat_rows = ""
@@ -1244,13 +1256,17 @@ class StatisticOutputTask(AsyncTask):
existing_basic_stages.append(stage)
# 获取所有action类型按出现频率排序
all_action_types = sorted(stat_data[FOCUS_ACTION_RATIOS].keys(),
key=lambda x: stat_data[FOCUS_ACTION_RATIOS][x], reverse=True)
all_action_types = sorted(
stat_data[FOCUS_ACTION_RATIOS].keys(), key=lambda x: stat_data[FOCUS_ACTION_RATIOS][x], reverse=True
)
# 为每个聊天流生成一行
chat_rows = []
for chat_id in sorted(stat_data[FOCUS_CYCLE_CNT_BY_CHAT].keys(),
key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_CHAT][x], reverse=True):
for chat_id in sorted(
stat_data[FOCUS_CYCLE_CNT_BY_CHAT].keys(),
key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_CHAT][x],
reverse=True,
):
chat_name = self.name_mapping.get(chat_id, (chat_id, 0))[0]
cycle_count = stat_data[FOCUS_CYCLE_CNT_BY_CHAT][chat_id]
@@ -1280,28 +1296,36 @@ class StatisticOutputTask(AsyncTask):
# 生成表头
stage_headers = "".join([f"<th>{stage}</th>" for stage in existing_basic_stages])
action_headers = "".join([f"<th>{action_type}<br><small>(执行)</small></th>" for action_type in all_action_types])
action_headers = "".join(
[f"<th>{action_type}<br><small>(执行)</small></th>" for action_type in all_action_types]
)
focus_chat_table_header = f"<tr><th>聊天流</th>{stage_headers}{action_headers}</tr>"
focus_chat_rows = focus_chat_table_header + "\n" + "\n".join(chat_rows)
# 全局阶段时间统计
focus_stage_rows = "\n".join([
focus_stage_rows = "\n".join(
[
f"<tr><td>{stage}</td><td>{avg_time:.3f}秒</td></tr>"
for stage, avg_time in sorted(stat_data[FOCUS_AVG_TIMES_BY_STAGE].items())
])
]
)
# 聊天流Action选择比例对比表横向表格
focus_chat_action_ratios_rows = ""
if stat_data.get("focus_action_ratios_by_chat"):
# 获取所有action类型按全局频率排序
all_action_types_for_ratio = sorted(stat_data[FOCUS_ACTION_RATIOS].keys(),
key=lambda x: stat_data[FOCUS_ACTION_RATIOS][x], reverse=True)
all_action_types_for_ratio = sorted(
stat_data[FOCUS_ACTION_RATIOS].keys(), key=lambda x: stat_data[FOCUS_ACTION_RATIOS][x], reverse=True
)
if all_action_types_for_ratio:
# 为每个聊天流生成数据行(按循环数排序)
chat_ratio_rows = []
for chat_id in sorted(stat_data[FOCUS_CYCLE_CNT_BY_CHAT].keys(),
key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_CHAT][x], reverse=True):
for chat_id in sorted(
stat_data[FOCUS_CYCLE_CNT_BY_CHAT].keys(),
key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_CHAT][x],
reverse=True,
):
chat_name = self.name_mapping.get(chat_id, (chat_id, 0))[0]
total_cycles = stat_data[FOCUS_CYCLE_CNT_BY_CHAT][chat_id]
chat_action_counts = stat_data["focus_action_ratios_by_chat"].get(chat_id, {})
@@ -1354,11 +1378,16 @@ class StatisticOutputTask(AsyncTask):
# 计算时间范围
if period_name == "all_time":
from src.manager.local_store_manager import local_storage
start_time = datetime.fromtimestamp(local_storage["deploy_time"])
time_range = f"{start_time.strftime('%Y-%m-%d %H:%M:%S')} ~ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
time_range = (
f"{start_time.strftime('%Y-%m-%d %H:%M:%S')} ~ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
)
else:
start_time = datetime.now() - period_delta
time_range = f"{start_time.strftime('%Y-%m-%d %H:%M:%S')} ~ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
time_range = (
f"{start_time.strftime('%Y-%m-%d %H:%M:%S')} ~ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
)
# 生成该时间段的Focus统计HTML
section_html = f"""
@@ -1431,7 +1460,7 @@ class StatisticOutputTask(AsyncTask):
<strong>统计内容:</strong> 各时间段的Focus循环性能分析
</p>
{''.join(focus_sections)}
{"".join(focus_sections)}
<style>
.focus-period-section {{
@@ -1548,8 +1577,11 @@ class StatisticOutputTask(AsyncTask):
continue
# 获取所有版本(按循环数排序)
all_versions = sorted(stat_data[FOCUS_CYCLE_CNT_BY_VERSION].keys(),
key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_VERSION][x], reverse=True)
all_versions = sorted(
stat_data[FOCUS_CYCLE_CNT_BY_VERSION].keys(),
key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_VERSION][x],
reverse=True,
)
# 生成版本Action分布表
focus_version_action_rows = ""
@@ -1576,7 +1608,9 @@ class StatisticOutputTask(AsyncTask):
version_action_rows.append(f"<tr>{''.join(row_cells)}</tr>")
# 生成表头
action_headers = "".join([f"<th>{action_type}</th>" for action_type in all_action_types_for_version])
action_headers = "".join(
[f"<th>{action_type}</th>" for action_type in all_action_types_for_version]
)
version_action_table_header = f"<tr><th>版本</th>{action_headers}</tr>"
focus_version_action_rows = version_action_table_header + "\n" + "\n".join(version_action_rows)
@@ -1634,18 +1668,28 @@ class StatisticOutputTask(AsyncTask):
# 生成表头
basic_headers = "".join([f"<th>{stage}</th>" for stage in existing_basic_stages])
action_headers = "".join([f"<th>执行时间<br><small>[{action_type}]</small></th>" for action_type in all_action_types_for_exec])
action_headers = "".join(
[
f"<th>执行时间<br><small>[{action_type}]</small></th>"
for action_type in all_action_types_for_exec
]
)
version_stage_table_header = f"<tr><th>版本</th>{basic_headers}{action_headers}</tr>"
focus_version_stage_rows = version_stage_table_header + "\n" + "\n".join(version_stage_rows)
# 计算时间范围
if period_name == "all_time":
from src.manager.local_store_manager import local_storage
start_time = datetime.fromtimestamp(local_storage["deploy_time"])
time_range = f"{start_time.strftime('%Y-%m-%d %H:%M:%S')} ~ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
time_range = (
f"{start_time.strftime('%Y-%m-%d %H:%M:%S')} ~ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
)
else:
start_time = datetime.now() - period_delta
time_range = f"{start_time.strftime('%Y-%m-%d %H:%M:%S')} ~ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
time_range = (
f"{start_time.strftime('%Y-%m-%d %H:%M:%S')} ~ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
)
# 生成该时间段的版本对比HTML
section_html = f"""
@@ -1694,7 +1738,7 @@ class StatisticOutputTask(AsyncTask):
<strong>数据来源:</strong> log/hfc_loop/ 目录下JSON文件中的version字段
</p>
{''.join(version_sections)}
{"".join(version_sections)}
<style>
.version-period-section {{
@@ -1884,7 +1928,7 @@ class StatisticOutputTask(AsyncTask):
try:
# 解析文件时间
filename = os.path.basename(json_file)
name_parts = filename.replace('.json', '').split('_')
name_parts = filename.replace(".json", "").split("_")
if len(name_parts) >= 4:
date_str = name_parts[-2]
time_str = name_parts[-1]
@@ -1892,14 +1936,14 @@ class StatisticOutputTask(AsyncTask):
file_time = datetime.strptime(file_time_str, "%Y%m%d_%H%M%S")
if file_time >= start_time:
with open(json_file, 'r', encoding='utf-8') as f:
with open(json_file, "r", encoding="utf-8") as f:
cycles_data = json.load(f)
for cycle in cycles_data:
try:
timestamp_str = cycle.get("timestamp", "")
if timestamp_str:
cycle_time = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
cycle_time = datetime.fromisoformat(timestamp_str.replace("Z", "+00:00"))
else:
cycle_time = file_time
@@ -1937,8 +1981,6 @@ class StatisticOutputTask(AsyncTask):
"focus_time_by_stage": focus_time_by_stage,
}
def _generate_chart_tab(self, chart_data: dict) -> str:
"""生成图表选项卡HTML内容"""
@@ -2298,8 +2340,13 @@ class AsyncStatisticOutputTask(AsyncTask):
def _collect_focus_statistics_for_period(self, collect_period: List[Tuple[str, datetime]]) -> Dict[str, Any]:
return StatisticOutputTask._collect_focus_statistics_for_period(self, collect_period)
def _process_focus_file_data(self, cycles_data: List[Dict], stats: Dict[str, Any],
collect_period: List[Tuple[str, datetime]], file_time: datetime):
def _process_focus_file_data(
self,
cycles_data: List[Dict],
stats: Dict[str, Any],
collect_period: List[Tuple[str, datetime]],
file_time: datetime,
):
return StatisticOutputTask._process_focus_file_data(self, cycles_data, stats, collect_period, file_time)
def _calculate_focus_averages(self, stats: Dict[str, Any]):