Merge branch 'dev' of https://github.com/MaiM-with-u/MaiBot into dev
This commit is contained in:
@@ -29,10 +29,6 @@ class HFCPerformanceLogger:
|
|||||||
)
|
)
|
||||||
self.current_session_data = []
|
self.current_session_data = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def record_cycle(self, cycle_data: Dict[str, Any]):
|
def record_cycle(self, cycle_data: Dict[str, Any]):
|
||||||
"""记录单次循环数据"""
|
"""记录单次循环数据"""
|
||||||
try:
|
try:
|
||||||
@@ -80,7 +76,7 @@ class HFCPerformanceLogger:
|
|||||||
"version": self.version,
|
"version": self.version,
|
||||||
"session_file": str(self.session_file),
|
"session_file": str(self.session_file),
|
||||||
"record_count": len(self.current_session_data),
|
"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):
|
def finalize_session(self):
|
||||||
@@ -95,7 +91,7 @@ class HFCPerformanceLogger:
|
|||||||
def cleanup_old_logs(cls, max_size_mb: float = 50.0):
|
def cleanup_old_logs(cls, max_size_mb: float = 50.0):
|
||||||
"""
|
"""
|
||||||
清理旧的HFC日志文件,保持目录大小在指定限制内
|
清理旧的HFC日志文件,保持目录大小在指定限制内
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
max_size_mb: 最大目录大小限制(MB)
|
max_size_mb: 最大目录大小限制(MB)
|
||||||
"""
|
"""
|
||||||
@@ -103,62 +99,58 @@ class HFCPerformanceLogger:
|
|||||||
if not log_dir.exists():
|
if not log_dir.exists():
|
||||||
logger.info("HFC日志目录不存在,跳过日志清理")
|
logger.info("HFC日志目录不存在,跳过日志清理")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 获取所有日志文件及其信息
|
# 获取所有日志文件及其信息
|
||||||
log_files = []
|
log_files = []
|
||||||
total_size = 0
|
total_size = 0
|
||||||
|
|
||||||
for log_file in log_dir.glob("*.json"):
|
for log_file in log_dir.glob("*.json"):
|
||||||
try:
|
try:
|
||||||
file_stat = log_file.stat()
|
file_stat = log_file.stat()
|
||||||
log_files.append({
|
log_files.append({"path": log_file, "size": file_stat.st_size, "mtime": file_stat.st_mtime})
|
||||||
'path': log_file,
|
|
||||||
'size': file_stat.st_size,
|
|
||||||
'mtime': file_stat.st_mtime
|
|
||||||
})
|
|
||||||
total_size += file_stat.st_size
|
total_size += file_stat.st_size
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"无法获取文件信息 {log_file}: {e}")
|
logger.warning(f"无法获取文件信息 {log_file}: {e}")
|
||||||
|
|
||||||
if not log_files:
|
if not log_files:
|
||||||
logger.info("没有找到HFC日志文件")
|
logger.info("没有找到HFC日志文件")
|
||||||
return
|
return
|
||||||
|
|
||||||
max_size_bytes = max_size_mb * 1024 * 1024
|
max_size_bytes = max_size_mb * 1024 * 1024
|
||||||
current_size_mb = total_size / (1024 * 1024)
|
current_size_mb = total_size / (1024 * 1024)
|
||||||
|
|
||||||
logger.info(f"HFC日志目录当前大小: {current_size_mb:.2f}MB,限制: {max_size_mb}MB")
|
logger.info(f"HFC日志目录当前大小: {current_size_mb:.2f}MB,限制: {max_size_mb}MB")
|
||||||
|
|
||||||
if total_size <= max_size_bytes:
|
if total_size <= max_size_bytes:
|
||||||
logger.info("HFC日志目录大小在限制范围内,无需清理")
|
logger.info("HFC日志目录大小在限制范围内,无需清理")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 按修改时间排序(最早的在前面)
|
# 按修改时间排序(最早的在前面)
|
||||||
log_files.sort(key=lambda x: x['mtime'])
|
log_files.sort(key=lambda x: x["mtime"])
|
||||||
|
|
||||||
deleted_count = 0
|
deleted_count = 0
|
||||||
deleted_size = 0
|
deleted_size = 0
|
||||||
|
|
||||||
for file_info in log_files:
|
for file_info in log_files:
|
||||||
if total_size <= max_size_bytes:
|
if total_size <= max_size_bytes:
|
||||||
break
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
file_size = file_info['size']
|
file_size = file_info["size"]
|
||||||
file_path = file_info['path']
|
file_path = file_info["path"]
|
||||||
|
|
||||||
file_path.unlink()
|
file_path.unlink()
|
||||||
total_size -= file_size
|
total_size -= file_size
|
||||||
deleted_size += file_size
|
deleted_size += file_size
|
||||||
deleted_count += 1
|
deleted_count += 1
|
||||||
|
|
||||||
logger.info(f"删除旧日志文件: {file_path.name} ({file_size / 1024:.1f}KB)")
|
logger.info(f"删除旧日志文件: {file_path.name} ({file_size / 1024:.1f}KB)")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"删除日志文件失败 {file_info['path']}: {e}")
|
logger.error(f"删除日志文件失败 {file_info['path']}: {e}")
|
||||||
|
|
||||||
final_size_mb = total_size / (1024 * 1024)
|
final_size_mb = total_size / (1024 * 1024)
|
||||||
deleted_size_mb = deleted_size / (1024 * 1024)
|
deleted_size_mb = deleted_size / (1024 * 1024)
|
||||||
|
|
||||||
logger.info(f"HFC日志清理完成: 删除了{deleted_count}个文件,释放{deleted_size_mb:.2f}MB空间")
|
logger.info(f"HFC日志清理完成: 删除了{deleted_count}个文件,释放{deleted_size_mb:.2f}MB空间")
|
||||||
logger.info(f"清理后目录大小: {final_size_mb:.2f}MB")
|
logger.info(f"清理后目录大小: {final_size_mb:.2f}MB")
|
||||||
|
|||||||
@@ -512,16 +512,16 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
try:
|
try:
|
||||||
# 从文件名解析时间戳 (格式: hash_version_date_time.json)
|
# 从文件名解析时间戳 (格式: hash_version_date_time.json)
|
||||||
filename = os.path.basename(json_file)
|
filename = os.path.basename(json_file)
|
||||||
name_parts = filename.replace('.json', '').split('_')
|
name_parts = filename.replace(".json", "").split("_")
|
||||||
if len(name_parts) >= 4:
|
if len(name_parts) >= 4:
|
||||||
date_str = name_parts[-2] # YYYYMMDD
|
date_str = name_parts[-2] # YYYYMMDD
|
||||||
time_str = name_parts[-1] # HHMMSS
|
time_str = name_parts[-1] # HHMMSS
|
||||||
file_time_str = f"{date_str}_{time_str}"
|
file_time_str = f"{date_str}_{time_str}"
|
||||||
file_time = datetime.strptime(file_time_str, "%Y%m%d_%H%M%S")
|
file_time = datetime.strptime(file_time_str, "%Y%m%d_%H%M%S")
|
||||||
|
|
||||||
# 如果文件时间在查询范围内,则处理该文件
|
# 如果文件时间在查询范围内,则处理该文件
|
||||||
if file_time >= query_start_time:
|
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)
|
cycles_data = json.load(f)
|
||||||
self._process_focus_file_data(cycles_data, stats, collect_period, file_time)
|
self._process_focus_file_data(cycles_data, stats, collect_period, file_time)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -532,8 +532,13 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
self._calculate_focus_averages(stats)
|
self._calculate_focus_averages(stats)
|
||||||
return stats
|
return stats
|
||||||
|
|
||||||
def _process_focus_file_data(self, cycles_data: List[Dict], stats: Dict[str, Any],
|
def _process_focus_file_data(
|
||||||
collect_period: List[Tuple[str, datetime]], file_time: datetime):
|
self,
|
||||||
|
cycles_data: List[Dict],
|
||||||
|
stats: Dict[str, Any],
|
||||||
|
collect_period: List[Tuple[str, datetime]],
|
||||||
|
file_time: datetime,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
处理单个focus文件的数据
|
处理单个focus文件的数据
|
||||||
"""
|
"""
|
||||||
@@ -542,7 +547,7 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
# 解析时间戳
|
# 解析时间戳
|
||||||
timestamp_str = cycle_data.get("timestamp", "")
|
timestamp_str = cycle_data.get("timestamp", "")
|
||||||
if timestamp_str:
|
if timestamp_str:
|
||||||
cycle_time = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
|
cycle_time = datetime.fromisoformat(timestamp_str.replace("Z", "+00:00"))
|
||||||
else:
|
else:
|
||||||
cycle_time = file_time # 使用文件时间作为后备
|
cycle_time = file_time # 使用文件时间作为后备
|
||||||
|
|
||||||
@@ -563,7 +568,7 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
if cycle_time >= period_start:
|
if cycle_time >= period_start:
|
||||||
for period_key, _ in collect_period[idx:]:
|
for period_key, _ in collect_period[idx:]:
|
||||||
stat = stats[period_key]
|
stat = stats[period_key]
|
||||||
|
|
||||||
# 基础统计
|
# 基础统计
|
||||||
stat[FOCUS_TOTAL_CYCLES] += 1
|
stat[FOCUS_TOTAL_CYCLES] += 1
|
||||||
stat[FOCUS_ACTION_RATIOS][action_type] += 1
|
stat[FOCUS_ACTION_RATIOS][action_type] += 1
|
||||||
@@ -572,7 +577,7 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
stat["focus_action_ratios_by_chat"][chat_id][action_type] += 1
|
stat["focus_action_ratios_by_chat"][chat_id][action_type] += 1
|
||||||
stat[FOCUS_TOTAL_TIME_BY_CHAT][chat_id] += total_time
|
stat[FOCUS_TOTAL_TIME_BY_CHAT][chat_id] += total_time
|
||||||
stat[FOCUS_TOTAL_TIME_BY_ACTION][action_type] += total_time
|
stat[FOCUS_TOTAL_TIME_BY_ACTION][action_type] += total_time
|
||||||
|
|
||||||
# 版本统计
|
# 版本统计
|
||||||
stat[FOCUS_CYCLE_CNT_BY_VERSION][version] += 1
|
stat[FOCUS_CYCLE_CNT_BY_VERSION][version] += 1
|
||||||
stat[FOCUS_ACTION_RATIOS_BY_VERSION][version][action_type] += 1
|
stat[FOCUS_ACTION_RATIOS_BY_VERSION][version][action_type] += 1
|
||||||
@@ -583,7 +588,7 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
stat[FOCUS_AVG_TIMES_BY_CHAT_ACTION][chat_id][stage].append(time_val)
|
stat[FOCUS_AVG_TIMES_BY_CHAT_ACTION][chat_id][stage].append(time_val)
|
||||||
stat[FOCUS_AVG_TIMES_BY_ACTION][action_type][stage].append(time_val)
|
stat[FOCUS_AVG_TIMES_BY_ACTION][action_type][stage].append(time_val)
|
||||||
stat[FOCUS_AVG_TIMES_BY_VERSION][version][stage].append(time_val)
|
stat[FOCUS_AVG_TIMES_BY_VERSION][version][stage].append(time_val)
|
||||||
|
|
||||||
# 专门收集执行动作阶段的时间,按聊天流和action类型分组
|
# 专门收集执行动作阶段的时间,按聊天流和action类型分组
|
||||||
if stage == "执行动作":
|
if stage == "执行动作":
|
||||||
stat["focus_exec_times_by_chat_action"][chat_id][action_type].append(time_val)
|
stat["focus_exec_times_by_chat_action"][chat_id][action_type].append(time_val)
|
||||||
@@ -646,8 +651,6 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
else:
|
else:
|
||||||
stat["focus_exec_times_by_version_action"][version][action_type] = 0.0
|
stat["focus_exec_times_by_version_action"][version][action_type] = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _collect_all_statistics(self, now: datetime) -> Dict[str, Dict[str, Any]]:
|
def _collect_all_statistics(self, now: datetime) -> Dict[str, Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
收集各时间段的统计数据
|
收集各时间段的统计数据
|
||||||
@@ -803,12 +806,8 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
"""
|
"""
|
||||||
if stats[FOCUS_TOTAL_CYCLES] <= 0:
|
if stats[FOCUS_TOTAL_CYCLES] <= 0:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
output = [
|
output = ["Focus系统统计:", f"总循环数: {stats[FOCUS_TOTAL_CYCLES]}", ""]
|
||||||
"Focus系统统计:",
|
|
||||||
f"总循环数: {stats[FOCUS_TOTAL_CYCLES]}",
|
|
||||||
""
|
|
||||||
]
|
|
||||||
|
|
||||||
# 全局阶段平均时间
|
# 全局阶段平均时间
|
||||||
if stats[FOCUS_AVG_TIMES_BY_STAGE]:
|
if stats[FOCUS_AVG_TIMES_BY_STAGE]:
|
||||||
@@ -842,23 +841,24 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
try:
|
try:
|
||||||
# 首先尝试从chat_stream获取真实群组名称
|
# 首先尝试从chat_stream获取真实群组名称
|
||||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||||
|
|
||||||
chat_manager = get_chat_manager()
|
chat_manager = get_chat_manager()
|
||||||
|
|
||||||
if chat_id in chat_manager.streams:
|
if chat_id in chat_manager.streams:
|
||||||
stream = chat_manager.streams[chat_id]
|
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
|
group_name = stream.group_info.group_name
|
||||||
if group_name and group_name.strip():
|
if group_name and group_name.strip():
|
||||||
return 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
|
user_name = stream.user_info.user_nickname
|
||||||
if user_name and user_name.strip():
|
if user_name and user_name.strip():
|
||||||
return user_name.strip()
|
return user_name.strip()
|
||||||
|
|
||||||
# 如果从chat_stream获取失败,尝试解析chat_id格式
|
# 如果从chat_stream获取失败,尝试解析chat_id格式
|
||||||
if chat_id.startswith('g'):
|
if chat_id.startswith("g"):
|
||||||
return f"群聊{chat_id[1:]}"
|
return f"群聊{chat_id[1:]}"
|
||||||
elif chat_id.startswith('u'):
|
elif chat_id.startswith("u"):
|
||||||
return f"用户{chat_id[1:]}"
|
return f"用户{chat_id[1:]}"
|
||||||
else:
|
else:
|
||||||
return chat_id
|
return chat_id
|
||||||
@@ -942,43 +942,53 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
for chat_id, count in sorted(stat_data[MSG_CNT_BY_CHAT].items())
|
for chat_id, count in sorted(stat_data[MSG_CNT_BY_CHAT].items())
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Focus统计数据
|
# Focus统计数据
|
||||||
# focus_action_rows = ""
|
# focus_action_rows = ""
|
||||||
# focus_chat_rows = ""
|
# focus_chat_rows = ""
|
||||||
# focus_stage_rows = ""
|
# focus_stage_rows = ""
|
||||||
# focus_action_stage_rows = ""
|
# focus_action_stage_rows = ""
|
||||||
|
|
||||||
if stat_data.get(FOCUS_TOTAL_CYCLES, 0) > 0:
|
if stat_data.get(FOCUS_TOTAL_CYCLES, 0) > 0:
|
||||||
# Action类型统计
|
# Action类型统计
|
||||||
total_actions = sum(stat_data[FOCUS_ACTION_RATIOS].values()) if stat_data[FOCUS_ACTION_RATIOS] else 0
|
total_actions = sum(stat_data[FOCUS_ACTION_RATIOS].values()) if stat_data[FOCUS_ACTION_RATIOS] else 0
|
||||||
_focus_action_rows = "\n".join([
|
_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())
|
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)
|
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
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# 全局阶段时间统计
|
# 全局阶段时间统计
|
||||||
_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())
|
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类型的阶段时间统计
|
# 按Action类型的阶段时间统计
|
||||||
focus_action_stage_items = []
|
focus_action_stage_items = []
|
||||||
for action_type, stage_times in stat_data[FOCUS_AVG_TIMES_BY_ACTION].items():
|
for action_type, stage_times in stat_data[FOCUS_AVG_TIMES_BY_ACTION].items():
|
||||||
for stage, avg_time in stage_times.items():
|
for stage, avg_time in stage_times.items():
|
||||||
focus_action_stage_items.append((action_type, stage, avg_time))
|
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)
|
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
|
# 生成HTML
|
||||||
return f"""
|
return f"""
|
||||||
<div id=\"{div_id}\" class=\"tab-content\">
|
<div id=\"{div_id}\" class=\"tab-content\">
|
||||||
@@ -1046,11 +1056,11 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
# 添加Focus统计内容
|
# 添加Focus统计内容
|
||||||
focus_tab = self._generate_focus_tab(stat)
|
focus_tab = self._generate_focus_tab(stat)
|
||||||
tab_content_list.append(focus_tab)
|
tab_content_list.append(focus_tab)
|
||||||
|
|
||||||
# 添加版本对比内容
|
# 添加版本对比内容
|
||||||
versions_tab = self._generate_versions_tab(stat)
|
versions_tab = self._generate_versions_tab(stat)
|
||||||
tab_content_list.append(versions_tab)
|
tab_content_list.append(versions_tab)
|
||||||
|
|
||||||
# 添加图表内容
|
# 添加图表内容
|
||||||
chart_data = self._generate_chart_data(stat)
|
chart_data = self._generate_chart_data(stat)
|
||||||
tab_content_list.append(self._generate_chart_tab(chart_data))
|
tab_content_list.append(self._generate_chart_tab(chart_data))
|
||||||
@@ -1203,30 +1213,32 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
|
|
||||||
def _generate_focus_tab(self, stat: dict[str, Any]) -> str:
|
def _generate_focus_tab(self, stat: dict[str, Any]) -> str:
|
||||||
"""生成Focus统计独立分页的HTML内容"""
|
"""生成Focus统计独立分页的HTML内容"""
|
||||||
|
|
||||||
# 为每个时间段准备Focus数据
|
# 为每个时间段准备Focus数据
|
||||||
focus_sections = []
|
focus_sections = []
|
||||||
|
|
||||||
for period_name, period_delta, period_desc in self.stat_period:
|
for period_name, period_delta, period_desc in self.stat_period:
|
||||||
stat_data = stat.get(period_name, {})
|
stat_data = stat.get(period_name, {})
|
||||||
|
|
||||||
if stat_data.get(FOCUS_TOTAL_CYCLES, 0) <= 0:
|
if stat_data.get(FOCUS_TOTAL_CYCLES, 0) <= 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 生成Focus统计数据行
|
# 生成Focus统计数据行
|
||||||
focus_action_rows = ""
|
focus_action_rows = ""
|
||||||
focus_chat_rows = ""
|
focus_chat_rows = ""
|
||||||
focus_stage_rows = ""
|
focus_stage_rows = ""
|
||||||
focus_action_stage_rows = ""
|
focus_action_stage_rows = ""
|
||||||
|
|
||||||
# Action类型统计
|
# Action类型统计
|
||||||
total_actions = sum(stat_data[FOCUS_ACTION_RATIOS].values()) if stat_data[FOCUS_ACTION_RATIOS] else 0
|
total_actions = sum(stat_data[FOCUS_ACTION_RATIOS].values()) if stat_data[FOCUS_ACTION_RATIOS] else 0
|
||||||
if total_actions > 0:
|
if total_actions > 0:
|
||||||
focus_action_rows = "\n".join([
|
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())
|
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的平均时间)
|
# 按聊天流统计(横向表格,显示各阶段时间差异和不同action的平均时间)
|
||||||
focus_chat_rows = ""
|
focus_chat_rows = ""
|
||||||
if stat_data[FOCUS_AVG_TIMES_BY_CHAT_ACTION]:
|
if stat_data[FOCUS_AVG_TIMES_BY_CHAT_ACTION]:
|
||||||
@@ -1242,72 +1254,84 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
break
|
break
|
||||||
if stage_exists:
|
if stage_exists:
|
||||||
existing_basic_stages.append(stage)
|
existing_basic_stages.append(stage)
|
||||||
|
|
||||||
# 获取所有action类型(按出现频率排序)
|
# 获取所有action类型(按出现频率排序)
|
||||||
all_action_types = sorted(stat_data[FOCUS_ACTION_RATIOS].keys(),
|
all_action_types = sorted(
|
||||||
key=lambda x: stat_data[FOCUS_ACTION_RATIOS][x], reverse=True)
|
stat_data[FOCUS_ACTION_RATIOS].keys(), key=lambda x: stat_data[FOCUS_ACTION_RATIOS][x], reverse=True
|
||||||
|
)
|
||||||
|
|
||||||
# 为每个聊天流生成一行
|
# 为每个聊天流生成一行
|
||||||
chat_rows = []
|
chat_rows = []
|
||||||
for chat_id in sorted(stat_data[FOCUS_CYCLE_CNT_BY_CHAT].keys(),
|
for chat_id in sorted(
|
||||||
key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_CHAT][x], reverse=True):
|
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]
|
chat_name = self.name_mapping.get(chat_id, (chat_id, 0))[0]
|
||||||
cycle_count = stat_data[FOCUS_CYCLE_CNT_BY_CHAT][chat_id]
|
cycle_count = stat_data[FOCUS_CYCLE_CNT_BY_CHAT][chat_id]
|
||||||
|
|
||||||
# 获取该聊天流的各阶段平均时间
|
# 获取该聊天流的各阶段平均时间
|
||||||
stage_times = stat_data[FOCUS_AVG_TIMES_BY_CHAT_ACTION].get(chat_id, {})
|
stage_times = stat_data[FOCUS_AVG_TIMES_BY_CHAT_ACTION].get(chat_id, {})
|
||||||
|
|
||||||
row_cells = [f"<td><strong>{chat_name}</strong><br><small>({cycle_count}次循环)</small></td>"]
|
row_cells = [f"<td><strong>{chat_name}</strong><br><small>({cycle_count}次循环)</small></td>"]
|
||||||
|
|
||||||
# 添加基础阶段时间
|
# 添加基础阶段时间
|
||||||
for stage in existing_basic_stages:
|
for stage in existing_basic_stages:
|
||||||
time_val = stage_times.get(stage, 0.0)
|
time_val = stage_times.get(stage, 0.0)
|
||||||
row_cells.append(f"<td>{time_val:.3f}秒</td>")
|
row_cells.append(f"<td>{time_val:.3f}秒</td>")
|
||||||
|
|
||||||
# 添加每个action类型的平均执行时间
|
# 添加每个action类型的平均执行时间
|
||||||
for action_type in all_action_types:
|
for action_type in all_action_types:
|
||||||
# 使用真实的按聊天流+action类型分组的执行时间数据
|
# 使用真实的按聊天流+action类型分组的执行时间数据
|
||||||
exec_times_by_chat_action = stat_data.get("focus_exec_times_by_chat_action", {})
|
exec_times_by_chat_action = stat_data.get("focus_exec_times_by_chat_action", {})
|
||||||
chat_action_times = exec_times_by_chat_action.get(chat_id, {})
|
chat_action_times = exec_times_by_chat_action.get(chat_id, {})
|
||||||
avg_exec_time = chat_action_times.get(action_type, 0.0)
|
avg_exec_time = chat_action_times.get(action_type, 0.0)
|
||||||
|
|
||||||
if avg_exec_time > 0:
|
if avg_exec_time > 0:
|
||||||
row_cells.append(f"<td>{avg_exec_time:.3f}秒</td>")
|
row_cells.append(f"<td>{avg_exec_time:.3f}秒</td>")
|
||||||
else:
|
else:
|
||||||
row_cells.append("<td>-</td>")
|
row_cells.append("<td>-</td>")
|
||||||
|
|
||||||
chat_rows.append(f"<tr>{''.join(row_cells)}</tr>")
|
chat_rows.append(f"<tr>{''.join(row_cells)}</tr>")
|
||||||
|
|
||||||
# 生成表头
|
# 生成表头
|
||||||
stage_headers = "".join([f"<th>{stage}</th>" for stage in existing_basic_stages])
|
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_table_header = f"<tr><th>聊天流</th>{stage_headers}{action_headers}</tr>"
|
||||||
focus_chat_rows = focus_chat_table_header + "\n" + "\n".join(chat_rows)
|
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())
|
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选择比例对比表(横向表格)
|
# 聊天流Action选择比例对比表(横向表格)
|
||||||
focus_chat_action_ratios_rows = ""
|
focus_chat_action_ratios_rows = ""
|
||||||
if stat_data.get("focus_action_ratios_by_chat"):
|
if stat_data.get("focus_action_ratios_by_chat"):
|
||||||
# 获取所有action类型(按全局频率排序)
|
# 获取所有action类型(按全局频率排序)
|
||||||
all_action_types_for_ratio = sorted(stat_data[FOCUS_ACTION_RATIOS].keys(),
|
all_action_types_for_ratio = sorted(
|
||||||
key=lambda x: stat_data[FOCUS_ACTION_RATIOS][x], reverse=True)
|
stat_data[FOCUS_ACTION_RATIOS].keys(), key=lambda x: stat_data[FOCUS_ACTION_RATIOS][x], reverse=True
|
||||||
|
)
|
||||||
|
|
||||||
if all_action_types_for_ratio:
|
if all_action_types_for_ratio:
|
||||||
# 为每个聊天流生成数据行(按循环数排序)
|
# 为每个聊天流生成数据行(按循环数排序)
|
||||||
chat_ratio_rows = []
|
chat_ratio_rows = []
|
||||||
for chat_id in sorted(stat_data[FOCUS_CYCLE_CNT_BY_CHAT].keys(),
|
for chat_id in sorted(
|
||||||
key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_CHAT][x], reverse=True):
|
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]
|
chat_name = self.name_mapping.get(chat_id, (chat_id, 0))[0]
|
||||||
total_cycles = stat_data[FOCUS_CYCLE_CNT_BY_CHAT][chat_id]
|
total_cycles = stat_data[FOCUS_CYCLE_CNT_BY_CHAT][chat_id]
|
||||||
chat_action_counts = stat_data["focus_action_ratios_by_chat"].get(chat_id, {})
|
chat_action_counts = stat_data["focus_action_ratios_by_chat"].get(chat_id, {})
|
||||||
|
|
||||||
row_cells = [f"<td><strong>{chat_name}</strong><br><small>({total_cycles}次循环)</small></td>"]
|
row_cells = [f"<td><strong>{chat_name}</strong><br><small>({total_cycles}次循环)</small></td>"]
|
||||||
|
|
||||||
# 添加每个action类型的数量和百分比
|
# 添加每个action类型的数量和百分比
|
||||||
for action_type in all_action_types_for_ratio:
|
for action_type in all_action_types_for_ratio:
|
||||||
count = chat_action_counts.get(action_type, 0)
|
count = chat_action_counts.get(action_type, 0)
|
||||||
@@ -1316,9 +1340,9 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
row_cells.append(f"<td>{count}<br><small>({ratio:.1f}%)</small></td>")
|
row_cells.append(f"<td>{count}<br><small>({ratio:.1f}%)</small></td>")
|
||||||
else:
|
else:
|
||||||
row_cells.append("<td>-<br><small>(0%)</small></td>")
|
row_cells.append("<td>-<br><small>(0%)</small></td>")
|
||||||
|
|
||||||
chat_ratio_rows.append(f"<tr>{''.join(row_cells)}</tr>")
|
chat_ratio_rows.append(f"<tr>{''.join(row_cells)}</tr>")
|
||||||
|
|
||||||
# 生成表头
|
# 生成表头
|
||||||
action_headers = "".join([f"<th>{action_type}</th>" for action_type in all_action_types_for_ratio])
|
action_headers = "".join([f"<th>{action_type}</th>" for action_type in all_action_types_for_ratio])
|
||||||
chat_action_ratio_table_header = f"<tr><th>聊天流</th>{action_headers}</tr>"
|
chat_action_ratio_table_header = f"<tr><th>聊天流</th>{action_headers}</tr>"
|
||||||
@@ -1333,33 +1357,38 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
for stage in stage_order:
|
for stage in stage_order:
|
||||||
if any(stage in stage_times for stage_times in stat_data[FOCUS_AVG_TIMES_BY_ACTION].values()):
|
if any(stage in stage_times for stage_times in stat_data[FOCUS_AVG_TIMES_BY_ACTION].values()):
|
||||||
all_stages.append(stage)
|
all_stages.append(stage)
|
||||||
|
|
||||||
# 为每个Action类型生成一行
|
# 为每个Action类型生成一行
|
||||||
action_rows = []
|
action_rows = []
|
||||||
for action_type in sorted(stat_data[FOCUS_AVG_TIMES_BY_ACTION].keys()):
|
for action_type in sorted(stat_data[FOCUS_AVG_TIMES_BY_ACTION].keys()):
|
||||||
stage_times = stat_data[FOCUS_AVG_TIMES_BY_ACTION][action_type]
|
stage_times = stat_data[FOCUS_AVG_TIMES_BY_ACTION][action_type]
|
||||||
row_cells = [f"<td><strong>{action_type}</strong></td>"]
|
row_cells = [f"<td><strong>{action_type}</strong></td>"]
|
||||||
|
|
||||||
for stage in all_stages:
|
for stage in all_stages:
|
||||||
time_val = stage_times.get(stage, 0.0)
|
time_val = stage_times.get(stage, 0.0)
|
||||||
row_cells.append(f"<td>{time_val:.3f}秒</td>")
|
row_cells.append(f"<td>{time_val:.3f}秒</td>")
|
||||||
|
|
||||||
action_rows.append(f"<tr>{''.join(row_cells)}</tr>")
|
action_rows.append(f"<tr>{''.join(row_cells)}</tr>")
|
||||||
|
|
||||||
# 生成表头
|
# 生成表头
|
||||||
stage_headers = "".join([f"<th>{stage}</th>" for stage in all_stages])
|
stage_headers = "".join([f"<th>{stage}</th>" for stage in all_stages])
|
||||||
focus_action_stage_table_header = f"<tr><th>Action类型</th>{stage_headers}</tr>"
|
focus_action_stage_table_header = f"<tr><th>Action类型</th>{stage_headers}</tr>"
|
||||||
focus_action_stage_rows = focus_action_stage_table_header + "\n" + "\n".join(action_rows)
|
focus_action_stage_rows = focus_action_stage_table_header + "\n" + "\n".join(action_rows)
|
||||||
|
|
||||||
# 计算时间范围
|
# 计算时间范围
|
||||||
if period_name == "all_time":
|
if period_name == "all_time":
|
||||||
from src.manager.local_store_manager import local_storage
|
from src.manager.local_store_manager import local_storage
|
||||||
|
|
||||||
start_time = datetime.fromtimestamp(local_storage["deploy_time"])
|
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:
|
else:
|
||||||
start_time = datetime.now() - period_delta
|
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
|
# 生成该时间段的Focus统计HTML
|
||||||
section_html = f"""
|
section_html = f"""
|
||||||
<div class="focus-period-section">
|
<div class="focus-period-section">
|
||||||
@@ -1410,9 +1439,9 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
focus_sections.append(section_html)
|
focus_sections.append(section_html)
|
||||||
|
|
||||||
# 如果没有任何Focus数据
|
# 如果没有任何Focus数据
|
||||||
if not focus_sections:
|
if not focus_sections:
|
||||||
focus_sections.append("""
|
focus_sections.append("""
|
||||||
@@ -1422,7 +1451,7 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
<p class="info-item">请确保 <code>log/hfc_loop/</code> 目录下存在相应的JSON文件。</p>
|
<p class="info-item">请确保 <code>log/hfc_loop/</code> 目录下存在相应的JSON文件。</p>
|
||||||
</div>
|
</div>
|
||||||
""")
|
""")
|
||||||
|
|
||||||
return f"""
|
return f"""
|
||||||
<div id="focus" class="tab-content">
|
<div id="focus" class="tab-content">
|
||||||
<h1>Focus系统详细统计</h1>
|
<h1>Focus系统详细统计</h1>
|
||||||
@@ -1431,7 +1460,7 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
<strong>统计内容:</strong> 各时间段的Focus循环性能分析
|
<strong>统计内容:</strong> 各时间段的Focus循环性能分析
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{''.join(focus_sections)}
|
{"".join(focus_sections)}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.focus-period-section {{
|
.focus-period-section {{
|
||||||
@@ -1537,20 +1566,23 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
|
|
||||||
def _generate_versions_tab(self, stat: dict[str, Any]) -> str:
|
def _generate_versions_tab(self, stat: dict[str, Any]) -> str:
|
||||||
"""生成版本对比独立分页的HTML内容"""
|
"""生成版本对比独立分页的HTML内容"""
|
||||||
|
|
||||||
# 为每个时间段准备版本对比数据
|
# 为每个时间段准备版本对比数据
|
||||||
version_sections = []
|
version_sections = []
|
||||||
|
|
||||||
for period_name, period_delta, period_desc in self.stat_period:
|
for period_name, period_delta, period_desc in self.stat_period:
|
||||||
stat_data = stat.get(period_name, {})
|
stat_data = stat.get(period_name, {})
|
||||||
|
|
||||||
if not stat_data.get(FOCUS_CYCLE_CNT_BY_VERSION):
|
if not stat_data.get(FOCUS_CYCLE_CNT_BY_VERSION):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 获取所有版本(按循环数排序)
|
# 获取所有版本(按循环数排序)
|
||||||
all_versions = sorted(stat_data[FOCUS_CYCLE_CNT_BY_VERSION].keys(),
|
all_versions = sorted(
|
||||||
key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_VERSION][x], reverse=True)
|
stat_data[FOCUS_CYCLE_CNT_BY_VERSION].keys(),
|
||||||
|
key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_VERSION][x],
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
|
||||||
# 生成版本Action分布表
|
# 生成版本Action分布表
|
||||||
focus_version_action_rows = ""
|
focus_version_action_rows = ""
|
||||||
if stat_data[FOCUS_ACTION_RATIOS_BY_VERSION]:
|
if stat_data[FOCUS_ACTION_RATIOS_BY_VERSION]:
|
||||||
@@ -1559,40 +1591,42 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
for version_actions in stat_data[FOCUS_ACTION_RATIOS_BY_VERSION].values():
|
for version_actions in stat_data[FOCUS_ACTION_RATIOS_BY_VERSION].values():
|
||||||
all_action_types_for_version.update(version_actions.keys())
|
all_action_types_for_version.update(version_actions.keys())
|
||||||
all_action_types_for_version = sorted(all_action_types_for_version)
|
all_action_types_for_version = sorted(all_action_types_for_version)
|
||||||
|
|
||||||
if all_action_types_for_version:
|
if all_action_types_for_version:
|
||||||
version_action_rows = []
|
version_action_rows = []
|
||||||
for version in all_versions:
|
for version in all_versions:
|
||||||
version_actions = stat_data[FOCUS_ACTION_RATIOS_BY_VERSION].get(version, {})
|
version_actions = stat_data[FOCUS_ACTION_RATIOS_BY_VERSION].get(version, {})
|
||||||
total_cycles = stat_data[FOCUS_CYCLE_CNT_BY_VERSION][version]
|
total_cycles = stat_data[FOCUS_CYCLE_CNT_BY_VERSION][version]
|
||||||
|
|
||||||
row_cells = [f"<td><strong>{version}</strong><br><small>({total_cycles}次循环)</small></td>"]
|
row_cells = [f"<td><strong>{version}</strong><br><small>({total_cycles}次循环)</small></td>"]
|
||||||
|
|
||||||
for action_type in all_action_types_for_version:
|
for action_type in all_action_types_for_version:
|
||||||
count = version_actions.get(action_type, 0)
|
count = version_actions.get(action_type, 0)
|
||||||
ratio = (count / total_cycles * 100) if total_cycles > 0 else 0
|
ratio = (count / total_cycles * 100) if total_cycles > 0 else 0
|
||||||
row_cells.append(f"<td>{count}<br><small>({ratio:.1f}%)</small></td>")
|
row_cells.append(f"<td>{count}<br><small>({ratio:.1f}%)</small></td>")
|
||||||
|
|
||||||
version_action_rows.append(f"<tr>{''.join(row_cells)}</tr>")
|
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>"
|
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)
|
focus_version_action_rows = version_action_table_header + "\n" + "\n".join(version_action_rows)
|
||||||
|
|
||||||
# 生成版本阶段时间表(按action类型分解执行时间)
|
# 生成版本阶段时间表(按action类型分解执行时间)
|
||||||
focus_version_stage_rows = ""
|
focus_version_stage_rows = ""
|
||||||
if stat_data[FOCUS_AVG_TIMES_BY_VERSION]:
|
if stat_data[FOCUS_AVG_TIMES_BY_VERSION]:
|
||||||
# 基础三个阶段
|
# 基础三个阶段
|
||||||
basic_stages = ["观察", "并行调整动作、处理", "规划器"]
|
basic_stages = ["观察", "并行调整动作、处理", "规划器"]
|
||||||
|
|
||||||
# 获取所有action类型用于执行时间列
|
# 获取所有action类型用于执行时间列
|
||||||
all_action_types_for_exec = set()
|
all_action_types_for_exec = set()
|
||||||
if stat_data.get("focus_exec_times_by_version_action"):
|
if stat_data.get("focus_exec_times_by_version_action"):
|
||||||
for version_actions in stat_data["focus_exec_times_by_version_action"].values():
|
for version_actions in stat_data["focus_exec_times_by_version_action"].values():
|
||||||
all_action_types_for_exec.update(version_actions.keys())
|
all_action_types_for_exec.update(version_actions.keys())
|
||||||
all_action_types_for_exec = sorted(all_action_types_for_exec)
|
all_action_types_for_exec = sorted(all_action_types_for_exec)
|
||||||
|
|
||||||
# 检查哪些基础阶段存在数据
|
# 检查哪些基础阶段存在数据
|
||||||
existing_basic_stages = []
|
existing_basic_stages = []
|
||||||
for stage in basic_stages:
|
for stage in basic_stages:
|
||||||
@@ -1603,23 +1637,23 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
break
|
break
|
||||||
if stage_exists:
|
if stage_exists:
|
||||||
existing_basic_stages.append(stage)
|
existing_basic_stages.append(stage)
|
||||||
|
|
||||||
# 构建表格
|
# 构建表格
|
||||||
if existing_basic_stages or all_action_types_for_exec:
|
if existing_basic_stages or all_action_types_for_exec:
|
||||||
version_stage_rows = []
|
version_stage_rows = []
|
||||||
|
|
||||||
# 为每个版本生成数据行
|
# 为每个版本生成数据行
|
||||||
for version in all_versions:
|
for version in all_versions:
|
||||||
version_stages = stat_data[FOCUS_AVG_TIMES_BY_VERSION].get(version, {})
|
version_stages = stat_data[FOCUS_AVG_TIMES_BY_VERSION].get(version, {})
|
||||||
total_cycles = stat_data[FOCUS_CYCLE_CNT_BY_VERSION][version]
|
total_cycles = stat_data[FOCUS_CYCLE_CNT_BY_VERSION][version]
|
||||||
|
|
||||||
row_cells = [f"<td><strong>{version}</strong><br><small>({total_cycles}次循环)</small></td>"]
|
row_cells = [f"<td><strong>{version}</strong><br><small>({total_cycles}次循环)</small></td>"]
|
||||||
|
|
||||||
# 添加基础阶段时间
|
# 添加基础阶段时间
|
||||||
for stage in existing_basic_stages:
|
for stage in existing_basic_stages:
|
||||||
time_val = version_stages.get(stage, 0.0)
|
time_val = version_stages.get(stage, 0.0)
|
||||||
row_cells.append(f"<td>{time_val:.3f}秒</td>")
|
row_cells.append(f"<td>{time_val:.3f}秒</td>")
|
||||||
|
|
||||||
# 添加不同action类型的执行时间
|
# 添加不同action类型的执行时间
|
||||||
for action_type in all_action_types_for_exec:
|
for action_type in all_action_types_for_exec:
|
||||||
# 获取该版本该action类型的平均执行时间
|
# 获取该版本该action类型的平均执行时间
|
||||||
@@ -1629,24 +1663,34 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
row_cells.append(f"<td>{exec_time:.3f}秒</td>")
|
row_cells.append(f"<td>{exec_time:.3f}秒</td>")
|
||||||
else:
|
else:
|
||||||
row_cells.append("<td>-</td>")
|
row_cells.append("<td>-</td>")
|
||||||
|
|
||||||
version_stage_rows.append(f"<tr>{''.join(row_cells)}</tr>")
|
version_stage_rows.append(f"<tr>{''.join(row_cells)}</tr>")
|
||||||
|
|
||||||
# 生成表头
|
# 生成表头
|
||||||
basic_headers = "".join([f"<th>{stage}</th>" for stage in existing_basic_stages])
|
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>"
|
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)
|
focus_version_stage_rows = version_stage_table_header + "\n" + "\n".join(version_stage_rows)
|
||||||
|
|
||||||
# 计算时间范围
|
# 计算时间范围
|
||||||
if period_name == "all_time":
|
if period_name == "all_time":
|
||||||
from src.manager.local_store_manager import local_storage
|
from src.manager.local_store_manager import local_storage
|
||||||
|
|
||||||
start_time = datetime.fromtimestamp(local_storage["deploy_time"])
|
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:
|
else:
|
||||||
start_time = datetime.now() - period_delta
|
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
|
# 生成该时间段的版本对比HTML
|
||||||
section_html = f"""
|
section_html = f"""
|
||||||
<div class="version-period-section">
|
<div class="version-period-section">
|
||||||
@@ -1673,9 +1717,9 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
version_sections.append(section_html)
|
version_sections.append(section_html)
|
||||||
|
|
||||||
# 如果没有任何版本数据
|
# 如果没有任何版本数据
|
||||||
if not version_sections:
|
if not version_sections:
|
||||||
version_sections.append("""
|
version_sections.append("""
|
||||||
@@ -1685,7 +1729,7 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
<p class="info-item">请确保 <code>log/hfc_loop/</code> 目录下的JSON文件包含版本信息。</p>
|
<p class="info-item">请确保 <code>log/hfc_loop/</code> 目录下的JSON文件包含版本信息。</p>
|
||||||
</div>
|
</div>
|
||||||
""")
|
""")
|
||||||
|
|
||||||
return f"""
|
return f"""
|
||||||
<div id="versions" class="tab-content">
|
<div id="versions" class="tab-content">
|
||||||
<h1>Focus HFC版本对比分析</h1>
|
<h1>Focus HFC版本对比分析</h1>
|
||||||
@@ -1694,7 +1738,7 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
<strong>数据来源:</strong> log/hfc_loop/ 目录下JSON文件中的version字段
|
<strong>数据来源:</strong> log/hfc_loop/ 目录下JSON文件中的version字段
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{''.join(version_sections)}
|
{"".join(version_sections)}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.version-period-section {{
|
.version-period-section {{
|
||||||
@@ -1876,7 +1920,7 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
# 查询Focus循环记录
|
# 查询Focus循环记录
|
||||||
focus_cycles_by_action = {}
|
focus_cycles_by_action = {}
|
||||||
focus_time_by_stage = {}
|
focus_time_by_stage = {}
|
||||||
|
|
||||||
log_dir = "log/hfc_loop"
|
log_dir = "log/hfc_loop"
|
||||||
if os.path.exists(log_dir):
|
if os.path.exists(log_dir):
|
||||||
json_files = glob.glob(os.path.join(log_dir, "*.json"))
|
json_files = glob.glob(os.path.join(log_dir, "*.json"))
|
||||||
@@ -1884,39 +1928,39 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
try:
|
try:
|
||||||
# 解析文件时间
|
# 解析文件时间
|
||||||
filename = os.path.basename(json_file)
|
filename = os.path.basename(json_file)
|
||||||
name_parts = filename.replace('.json', '').split('_')
|
name_parts = filename.replace(".json", "").split("_")
|
||||||
if len(name_parts) >= 4:
|
if len(name_parts) >= 4:
|
||||||
date_str = name_parts[-2]
|
date_str = name_parts[-2]
|
||||||
time_str = name_parts[-1]
|
time_str = name_parts[-1]
|
||||||
file_time_str = f"{date_str}_{time_str}"
|
file_time_str = f"{date_str}_{time_str}"
|
||||||
file_time = datetime.strptime(file_time_str, "%Y%m%d_%H%M%S")
|
file_time = datetime.strptime(file_time_str, "%Y%m%d_%H%M%S")
|
||||||
|
|
||||||
if file_time >= start_time:
|
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)
|
cycles_data = json.load(f)
|
||||||
|
|
||||||
for cycle in cycles_data:
|
for cycle in cycles_data:
|
||||||
try:
|
try:
|
||||||
timestamp_str = cycle.get("timestamp", "")
|
timestamp_str = cycle.get("timestamp", "")
|
||||||
if timestamp_str:
|
if timestamp_str:
|
||||||
cycle_time = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
|
cycle_time = datetime.fromisoformat(timestamp_str.replace("Z", "+00:00"))
|
||||||
else:
|
else:
|
||||||
cycle_time = file_time
|
cycle_time = file_time
|
||||||
|
|
||||||
if cycle_time >= start_time:
|
if cycle_time >= start_time:
|
||||||
# 计算时间间隔索引
|
# 计算时间间隔索引
|
||||||
time_diff = (cycle_time - start_time).total_seconds()
|
time_diff = (cycle_time - start_time).total_seconds()
|
||||||
interval_index = int(time_diff // interval_seconds)
|
interval_index = int(time_diff // interval_seconds)
|
||||||
|
|
||||||
if 0 <= interval_index < len(time_points):
|
if 0 <= interval_index < len(time_points):
|
||||||
action_type = cycle.get("action_type", "unknown")
|
action_type = cycle.get("action_type", "unknown")
|
||||||
step_times = cycle.get("step_times", {})
|
step_times = cycle.get("step_times", {})
|
||||||
|
|
||||||
# 累计action类型数据
|
# 累计action类型数据
|
||||||
if action_type not in focus_cycles_by_action:
|
if action_type not in focus_cycles_by_action:
|
||||||
focus_cycles_by_action[action_type] = [0] * len(time_points)
|
focus_cycles_by_action[action_type] = [0] * len(time_points)
|
||||||
focus_cycles_by_action[action_type][interval_index] += 1
|
focus_cycles_by_action[action_type][interval_index] += 1
|
||||||
|
|
||||||
# 累计阶段时间数据
|
# 累计阶段时间数据
|
||||||
for stage, time_val in step_times.items():
|
for stage, time_val in step_times.items():
|
||||||
if stage not in focus_time_by_stage:
|
if stage not in focus_time_by_stage:
|
||||||
@@ -1937,8 +1981,6 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
"focus_time_by_stage": focus_time_by_stage,
|
"focus_time_by_stage": focus_time_by_stage,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _generate_chart_tab(self, chart_data: dict) -> str:
|
def _generate_chart_tab(self, chart_data: dict) -> str:
|
||||||
"""生成图表选项卡HTML内容"""
|
"""生成图表选项卡HTML内容"""
|
||||||
|
|
||||||
@@ -2298,8 +2340,13 @@ class AsyncStatisticOutputTask(AsyncTask):
|
|||||||
def _collect_focus_statistics_for_period(self, collect_period: List[Tuple[str, datetime]]) -> Dict[str, Any]:
|
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)
|
return StatisticOutputTask._collect_focus_statistics_for_period(self, collect_period)
|
||||||
|
|
||||||
def _process_focus_file_data(self, cycles_data: List[Dict], stats: Dict[str, Any],
|
def _process_focus_file_data(
|
||||||
collect_period: List[Tuple[str, datetime]], file_time: datetime):
|
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)
|
return StatisticOutputTask._process_focus_file_data(self, cycles_data, stats, collect_period, file_time)
|
||||||
|
|
||||||
def _calculate_focus_averages(self, stats: Dict[str, Any]):
|
def _calculate_focus_averages(self, stats: Dict[str, Any]):
|
||||||
@@ -2327,7 +2374,7 @@ class AsyncStatisticOutputTask(AsyncTask):
|
|||||||
|
|
||||||
def _generate_chart_tab(self, chart_data: dict) -> str:
|
def _generate_chart_tab(self, chart_data: dict) -> str:
|
||||||
return StatisticOutputTask._generate_chart_tab(self, chart_data)
|
return StatisticOutputTask._generate_chart_tab(self, chart_data)
|
||||||
|
|
||||||
def _get_chat_display_name_from_id(self, chat_id: str) -> str:
|
def _get_chat_display_name_from_id(self, chat_id: str) -> str:
|
||||||
return StatisticOutputTask._get_chat_display_name_from_id(self, chat_id)
|
return StatisticOutputTask._get_chat_display_name_from_id(self, chat_id)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user