update
This commit is contained in:
parent
cf9e902480
commit
db3df13462
@ -415,13 +415,10 @@ def render_today_plan() -> None:
|
|||||||
LOGGER.exception("加载 agent_utils 失败", extra=LOG_EXTRA)
|
LOGGER.exception("加载 agent_utils 失败", extra=LOG_EXTRA)
|
||||||
st.warning("暂未写入部门/代理决策,请先运行回测或策略评估流程。")
|
st.warning("暂未写入部门/代理决策,请先运行回测或策略评估流程。")
|
||||||
return
|
return
|
||||||
|
|
||||||
trade_dates = [row["trade_date"] for row in date_rows]
|
trade_dates = [row["trade_date"] for row in date_rows]
|
||||||
if not trade_dates:
|
if not trade_dates:
|
||||||
st.info("暂无决策记录,完成一次回测后即可在此查看部门意见与投票结果。")
|
st.info("暂无决策记录,完成一次回测后即可在此查看部门意见与投票结果。")
|
||||||
return
|
return
|
||||||
|
|
||||||
# ADD: read default selection from URL
|
|
||||||
q = _get_query_params()
|
q = _get_query_params()
|
||||||
default_trade_date = q.get("date", [trade_dates[0]])[0]
|
default_trade_date = q.get("date", [trade_dates[0]])[0]
|
||||||
try:
|
try:
|
||||||
@ -429,7 +426,6 @@ def render_today_plan() -> None:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
default_idx = 0
|
default_idx = 0
|
||||||
trade_date = st.selectbox("交易日", trade_dates, index=default_idx)
|
trade_date = st.selectbox("交易日", trade_dates, index=default_idx)
|
||||||
|
|
||||||
with db_session(read_only=True) as conn:
|
with db_session(read_only=True) as conn:
|
||||||
code_rows = conn.execute(
|
code_rows = conn.execute(
|
||||||
"""
|
"""
|
||||||
@ -441,22 +437,9 @@ def render_today_plan() -> None:
|
|||||||
(trade_date,),
|
(trade_date,),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
symbols = [row["ts_code"] for row in code_rows]
|
symbols = [row["ts_code"] for row in code_rows]
|
||||||
if not symbols:
|
detail_tab, assistant_tab = st.tabs(["标的详情", "投资助理模式"])
|
||||||
st.info("所选交易日暂无 agent_utils 记录。")
|
with assistant_tab:
|
||||||
return
|
|
||||||
|
|
||||||
# ADD: 投资助理模式开关(不指定具体标的)
|
|
||||||
assistant_mode = st.checkbox(
|
|
||||||
"投资助理(不指定标的)",
|
|
||||||
value=False,
|
|
||||||
help="开启后不显示具体标的,展示基于候选投资池的组合级建议与汇总信息。",
|
|
||||||
)
|
|
||||||
|
|
||||||
if assistant_mode:
|
|
||||||
ts_code = None
|
|
||||||
batch_symbols = []
|
|
||||||
st.info("已开启投资助理模式:以下内容为组合级(去标的)建议,不包含任何具体标的代码。")
|
st.info("已开启投资助理模式:以下内容为组合级(去标的)建议,不包含任何具体标的代码。")
|
||||||
# 展示候选池聚合信息(不暴露具体代码)
|
|
||||||
try:
|
try:
|
||||||
candidates = list_investment_pool(trade_date=trade_date)
|
candidates = list_investment_pool(trade_date=trade_date)
|
||||||
if candidates:
|
if candidates:
|
||||||
@ -525,6 +508,9 @@ def render_today_plan() -> None:
|
|||||||
except Exception:
|
except Exception:
|
||||||
LOGGER.exception("加载候选池聚合信息失败", extra=LOG_EXTRA)
|
LOGGER.exception("加载候选池聚合信息失败", extra=LOG_EXTRA)
|
||||||
st.error("加载候选池数据时发生错误。")
|
st.error("加载候选池数据时发生错误。")
|
||||||
|
with detail_tab:
|
||||||
|
if not symbols:
|
||||||
|
st.info("所选交易日暂无 agent_utils 记录。")
|
||||||
else:
|
else:
|
||||||
default_ts = q.get("code", [symbols[0]])[0]
|
default_ts = q.get("code", [symbols[0]])[0]
|
||||||
try:
|
try:
|
||||||
@ -532,10 +518,8 @@ def render_today_plan() -> None:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
default_ts_idx = 0
|
default_ts_idx = 0
|
||||||
ts_code = st.selectbox("标的", symbols, index=default_ts_idx)
|
ts_code = st.selectbox("标的", symbols, index=default_ts_idx)
|
||||||
# ADD: batch selection for re-evaluation
|
|
||||||
batch_symbols = st.multiselect("批量重评估(可多选)", symbols, default=[])
|
batch_symbols = st.multiselect("批量重评估(可多选)", symbols, default=[])
|
||||||
|
|
||||||
# 一键重评估所有标的按钮
|
|
||||||
if st.button("一键重评估所有标的", type="primary", width='stretch'):
|
if st.button("一键重评估所有标的", type="primary", width='stretch'):
|
||||||
with st.spinner("正在对所有标的进行重评估,请稍候..."):
|
with st.spinner("正在对所有标的进行重评估,请稍候..."):
|
||||||
try:
|
try:
|
||||||
@ -616,10 +600,7 @@ def render_today_plan() -> None:
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
LOGGER.exception("一键重评估失败", extra=LOG_EXTRA)
|
LOGGER.exception("一键重评估失败", extra=LOG_EXTRA)
|
||||||
st.error(f"一键重评估执行过程中发生错误:{exc}")
|
st.error(f"一键重评估执行过程中发生错误:{exc}")
|
||||||
|
|
||||||
# sync URL params
|
|
||||||
_set_query_params(date=str(trade_date), code=str(ts_code))
|
_set_query_params(date=str(trade_date), code=str(ts_code))
|
||||||
|
|
||||||
with db_session(read_only=True) as conn:
|
with db_session(read_only=True) as conn:
|
||||||
rows = conn.execute(
|
rows = conn.execute(
|
||||||
"""
|
"""
|
||||||
@ -630,21 +611,17 @@ def render_today_plan() -> None:
|
|||||||
""",
|
""",
|
||||||
(trade_date, ts_code),
|
(trade_date, ts_code),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
|
|
||||||
if not rows:
|
if not rows:
|
||||||
st.info("未查询到详细决策记录,稍后再试。")
|
st.info("未查询到详细决策记录,稍后再试。")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
feasible_actions = json.loads(rows[0]["feasible"] or "[]")
|
feasible_actions = json.loads(rows[0]["feasible"] or "[]")
|
||||||
except (KeyError, TypeError, json.JSONDecodeError):
|
except (KeyError, TypeError, json.JSONDecodeError):
|
||||||
feasible_actions = []
|
feasible_actions = []
|
||||||
|
|
||||||
global_info = None
|
global_info = None
|
||||||
dept_records: List[Dict[str, object]] = []
|
dept_records: List[Dict[str, object]] = []
|
||||||
dept_details: Dict[str, Dict[str, object]] = {}
|
dept_details: Dict[str, Dict[str, object]] = {}
|
||||||
agent_records: List[Dict[str, object]] = []
|
agent_records: List[Dict[str, object]] = []
|
||||||
|
|
||||||
for item in rows:
|
for item in rows:
|
||||||
agent_name = item["agent"]
|
agent_name = item["agent"]
|
||||||
action = item["action"]
|
action = item["action"]
|
||||||
@ -715,10 +692,8 @@ def render_today_plan() -> None:
|
|||||||
"BUY_L": score_map.get("BUY_L", 0.0),
|
"BUY_L": score_map.get("BUY_L", 0.0),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if feasible_actions:
|
if feasible_actions:
|
||||||
st.caption(f"可行操作集合:{', '.join(feasible_actions)}")
|
st.caption(f"可行操作集合:{', '.join(feasible_actions)}")
|
||||||
|
|
||||||
st.subheader("全局策略")
|
st.subheader("全局策略")
|
||||||
if global_info:
|
if global_info:
|
||||||
col1, col2, col3 = st.columns(3)
|
col1, col2, col3 = st.columns(3)
|
||||||
@ -782,7 +757,6 @@ def render_today_plan() -> None:
|
|||||||
st.json(dept_telemetry)
|
st.json(dept_telemetry)
|
||||||
else:
|
else:
|
||||||
st.info("暂未写入全局策略摘要。")
|
st.info("暂未写入全局策略摘要。")
|
||||||
|
|
||||||
st.subheader("部门意见")
|
st.subheader("部门意见")
|
||||||
if dept_records:
|
if dept_records:
|
||||||
# ADD: keyword filter for department summaries
|
# ADD: keyword filter for department summaries
|
||||||
@ -832,7 +806,6 @@ def render_today_plan() -> None:
|
|||||||
st.json(telemetry)
|
st.json(telemetry)
|
||||||
else:
|
else:
|
||||||
st.info("暂无部门记录。")
|
st.info("暂无部门记录。")
|
||||||
|
|
||||||
st.subheader("代理评分")
|
st.subheader("代理评分")
|
||||||
if agent_records:
|
if agent_records:
|
||||||
# ADD: sorting and CSV export for agents
|
# ADD: sorting and CSV export for agents
|
||||||
@ -857,11 +830,8 @@ def render_today_plan() -> None:
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
st.info("暂无基础代理评分。")
|
st.info("暂无基础代理评分。")
|
||||||
|
|
||||||
# 添加相关新闻展示部分
|
|
||||||
st.divider()
|
st.divider()
|
||||||
st.subheader("相关新闻")
|
st.subheader("相关新闻")
|
||||||
# 获取与当前标的相关的最新新闻
|
|
||||||
try:
|
try:
|
||||||
with db_session(read_only=True) as conn:
|
with db_session(read_only=True) as conn:
|
||||||
# 解析当前trade_date为datetime对象
|
# 解析当前trade_date为datetime对象
|
||||||
@ -959,11 +929,8 @@ def render_today_plan() -> None:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOGGER.exception("获取新闻数据失败", extra=LOG_EXTRA)
|
LOGGER.exception("获取新闻数据失败", extra=LOG_EXTRA)
|
||||||
st.error(f"获取新闻数据时发生错误:{e}")
|
st.error(f"获取新闻数据时发生错误:{e}")
|
||||||
|
|
||||||
st.divider()
|
st.divider()
|
||||||
# 提示用户跳转到单独的“投资池与仓位概览”页
|
|
||||||
st.info("投资池与仓位概览已移至单独页面。请在侧边或页面导航中选择“投资池/仓位”以查看详细信息。")
|
st.info("投资池与仓位概览已移至单独页面。请在侧边或页面导航中选择“投资池/仓位”以查看详细信息。")
|
||||||
|
|
||||||
st.divider()
|
st.divider()
|
||||||
st.subheader("策略重评估")
|
st.subheader("策略重评估")
|
||||||
st.caption("对当前选中的交易日与标的,立即触发一次策略评估并回写 agent_utils。")
|
st.caption("对当前选中的交易日与标的,立即触发一次策略评估并回写 agent_utils。")
|
||||||
@ -1081,129 +1048,6 @@ def render_today_plan() -> None:
|
|||||||
st.error(f"批量重评估失败:{exc}")
|
st.error(f"批量重评估失败:{exc}")
|
||||||
|
|
||||||
|
|
||||||
def render_log_viewer() -> None:
|
|
||||||
"""渲染日志钻取与历史对比视图页面。"""
|
|
||||||
LOGGER.info("渲染日志视图页面", extra=LOG_EXTRA)
|
|
||||||
st.header("日志钻取与历史对比")
|
|
||||||
st.write("查看系统运行日志,支持时间范围筛选、关键词搜索和历史对比功能。")
|
|
||||||
|
|
||||||
# 日志时间范围选择
|
|
||||||
col1, col2 = st.columns(2)
|
|
||||||
with col1:
|
|
||||||
start_date = st.date_input("开始日期", value=date.today() - timedelta(days=7))
|
|
||||||
with col2:
|
|
||||||
end_date = st.date_input("结束日期", value=date.today())
|
|
||||||
|
|
||||||
# 日志级别筛选
|
|
||||||
log_levels = ["ALL", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
||||||
selected_level = st.selectbox("日志级别", log_levels, index=1)
|
|
||||||
|
|
||||||
# 关键词搜索
|
|
||||||
search_query = st.text_input("搜索关键词")
|
|
||||||
|
|
||||||
# 阶段筛选
|
|
||||||
with db_session(read_only=True) as conn:
|
|
||||||
stages = [row["stage"] for row in conn.execute("SELECT DISTINCT stage FROM run_log").fetchall()]
|
|
||||||
stages = [s for s in stages if s] # 过滤空值
|
|
||||||
stages.insert(0, "ALL")
|
|
||||||
selected_stage = st.selectbox("执行阶段", stages)
|
|
||||||
|
|
||||||
# 查询日志
|
|
||||||
with st.spinner("加载日志数据中..."):
|
|
||||||
try:
|
|
||||||
with db_session(read_only=True) as conn:
|
|
||||||
query_parts = ["SELECT ts, stage, level, msg FROM run_log WHERE 1=1"]
|
|
||||||
params = []
|
|
||||||
|
|
||||||
# 添加日期过滤
|
|
||||||
start_ts = f"{start_date.isoformat()}T00:00:00Z"
|
|
||||||
end_ts = f"{end_date.isoformat()}T23:59:59Z"
|
|
||||||
query_parts.append("AND ts BETWEEN ? AND ?")
|
|
||||||
params.extend([start_ts, end_ts])
|
|
||||||
|
|
||||||
# 添加级别过滤
|
|
||||||
if selected_level != "ALL":
|
|
||||||
query_parts.append("AND level = ?")
|
|
||||||
params.append(selected_level)
|
|
||||||
|
|
||||||
# 添加关键词过滤
|
|
||||||
if search_query:
|
|
||||||
query_parts.append("AND msg LIKE ?")
|
|
||||||
params.append(f"%{search_query}%")
|
|
||||||
|
|
||||||
# 添加阶段过滤
|
|
||||||
if selected_stage != "ALL":
|
|
||||||
query_parts.append("AND stage = ?")
|
|
||||||
params.append(selected_stage)
|
|
||||||
|
|
||||||
# 添加排序
|
|
||||||
query_parts.append("ORDER BY ts DESC")
|
|
||||||
|
|
||||||
# 执行查询
|
|
||||||
query = " ".join(query_parts)
|
|
||||||
rows = conn.execute(query, params).fetchall()
|
|
||||||
|
|
||||||
# 转换为DataFrame
|
|
||||||
if rows:
|
|
||||||
# 将sqlite3.Row对象转换为字典列表
|
|
||||||
rows_dict = [{key: row[key] for key in row.keys()} for row in rows]
|
|
||||||
log_df = pd.DataFrame(rows_dict)
|
|
||||||
# 格式化时间戳并确保数据类型一致
|
|
||||||
log_df["ts"] = pd.to_datetime(log_df["ts"]).dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
# 确保所有列都是字符串类型,避免PyArrow序列化错误
|
|
||||||
for col in log_df.columns:
|
|
||||||
log_df[col] = log_df[col].astype(str)
|
|
||||||
else:
|
|
||||||
log_df = pd.DataFrame(columns=["ts", "stage", "level", "msg"])
|
|
||||||
|
|
||||||
# 显示日志表格
|
|
||||||
st.dataframe(
|
|
||||||
log_df,
|
|
||||||
hide_index=True,
|
|
||||||
width="stretch",
|
|
||||||
column_config={
|
|
||||||
"ts": st.column_config.TextColumn("时间"),
|
|
||||||
"stage": st.column_config.TextColumn("执行阶段"),
|
|
||||||
"level": st.column_config.TextColumn("日志级别"),
|
|
||||||
"msg": st.column_config.TextColumn("日志消息", width="large")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# 下载功能
|
|
||||||
if not log_df.empty:
|
|
||||||
csv_data = log_df.to_csv(index=False).encode('utf-8')
|
|
||||||
st.download_button(
|
|
||||||
label="下载日志CSV",
|
|
||||||
data=csv_data,
|
|
||||||
file_name=f"logs_{start_date}_{end_date}.csv",
|
|
||||||
mime="text/csv",
|
|
||||||
key="download_logs"
|
|
||||||
)
|
|
||||||
|
|
||||||
# JSON下载
|
|
||||||
json_data = log_df.to_json(orient='records', force_ascii=False, indent=2)
|
|
||||||
st.download_button(
|
|
||||||
label="下载日志JSON",
|
|
||||||
data=json_data,
|
|
||||||
file_name=f"logs_{start_date}_{end_date}.json",
|
|
||||||
mime="application/json",
|
|
||||||
key="download_logs_json"
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
LOGGER.exception("加载日志失败", extra=LOG_EXTRA)
|
|
||||||
st.error(f"加载日志数据失败:{e}")
|
|
||||||
|
|
||||||
# 历史对比功能
|
|
||||||
st.subheader("历史对比")
|
|
||||||
st.write("选择两个时间点的日志进行对比分析。")
|
|
||||||
|
|
||||||
# 第一个时间点选择
|
|
||||||
col3, col4 = st.columns(2)
|
|
||||||
with col3:
|
|
||||||
compare_date1 = st.date_input("对比日期1", value=date.today() - timedelta(days=1))
|
|
||||||
with col4:
|
|
||||||
compare_date2 = st.date_input("对比日期2", value=date.today())
|
|
||||||
|
|
||||||
|
|
||||||
def render_pool_overview() -> None:
|
def render_pool_overview() -> None:
|
||||||
"""单独的投资池与仓位概览页面(从今日计划中提取)。"""
|
"""单独的投资池与仓位概览页面(从今日计划中提取)。"""
|
||||||
@ -2872,6 +2716,161 @@ def render_tests() -> None:
|
|||||||
st.write(response)
|
st.write(response)
|
||||||
|
|
||||||
|
|
||||||
|
def render_log_viewer() -> None:
|
||||||
|
"""渲染日志钻取与历史对比视图页面。"""
|
||||||
|
LOGGER.info("渲染日志视图页面", extra=LOG_EXTRA)
|
||||||
|
st.header("日志钻取与历史对比")
|
||||||
|
st.write("查看系统运行日志,支持时间范围筛选、关键词搜索和历史对比功能。")
|
||||||
|
|
||||||
|
col1, col2 = st.columns(2)
|
||||||
|
with col1:
|
||||||
|
start_date = st.date_input("开始日期", value=date.today() - timedelta(days=7))
|
||||||
|
with col2:
|
||||||
|
end_date = st.date_input("结束日期", value=date.today())
|
||||||
|
|
||||||
|
log_levels = ["ALL", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||||
|
selected_level = st.selectbox("日志级别", log_levels, index=1)
|
||||||
|
|
||||||
|
search_query = st.text_input("搜索关键词")
|
||||||
|
|
||||||
|
with db_session(read_only=True) as conn:
|
||||||
|
stages = [row["stage"] for row in conn.execute("SELECT DISTINCT stage FROM run_log").fetchall()]
|
||||||
|
stages = [s for s in stages if s]
|
||||||
|
stages.insert(0, "ALL")
|
||||||
|
selected_stage = st.selectbox("执行阶段", stages)
|
||||||
|
|
||||||
|
with st.spinner("加载日志数据中..."):
|
||||||
|
try:
|
||||||
|
with db_session(read_only=True) as conn:
|
||||||
|
query_parts = ["SELECT ts, stage, level, msg FROM run_log WHERE 1=1"]
|
||||||
|
params: list[object] = []
|
||||||
|
|
||||||
|
start_ts = f"{start_date.isoformat()}T00:00:00Z"
|
||||||
|
end_ts = f"{end_date.isoformat()}T23:59:59Z"
|
||||||
|
query_parts.append("AND ts BETWEEN ? AND ?")
|
||||||
|
params.extend([start_ts, end_ts])
|
||||||
|
|
||||||
|
if selected_level != "ALL":
|
||||||
|
query_parts.append("AND level = ?")
|
||||||
|
params.append(selected_level)
|
||||||
|
|
||||||
|
if search_query:
|
||||||
|
query_parts.append("AND msg LIKE ?")
|
||||||
|
params.append(f"%{search_query}%")
|
||||||
|
|
||||||
|
if selected_stage != "ALL":
|
||||||
|
query_parts.append("AND stage = ?")
|
||||||
|
params.append(selected_stage)
|
||||||
|
|
||||||
|
query_parts.append("ORDER BY ts DESC")
|
||||||
|
|
||||||
|
query = " ".join(query_parts)
|
||||||
|
rows = conn.execute(query, params).fetchall()
|
||||||
|
|
||||||
|
if rows:
|
||||||
|
rows_dict = [{key: row[key] for key in row.keys()} for row in rows]
|
||||||
|
log_df = pd.DataFrame(rows_dict)
|
||||||
|
log_df["ts"] = pd.to_datetime(log_df["ts"]).dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
for col in log_df.columns:
|
||||||
|
log_df[col] = log_df[col].astype(str)
|
||||||
|
else:
|
||||||
|
log_df = pd.DataFrame(columns=["ts", "stage", "level", "msg"])
|
||||||
|
|
||||||
|
st.dataframe(
|
||||||
|
log_df,
|
||||||
|
hide_index=True,
|
||||||
|
width="stretch",
|
||||||
|
column_config={
|
||||||
|
"ts": st.column_config.TextColumn("时间"),
|
||||||
|
"stage": st.column_config.TextColumn("执行阶段"),
|
||||||
|
"level": st.column_config.TextColumn("日志级别"),
|
||||||
|
"msg": st.column_config.TextColumn("日志消息", width="large"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if not log_df.empty:
|
||||||
|
csv_data = log_df.to_csv(index=False).encode("utf-8")
|
||||||
|
st.download_button(
|
||||||
|
label="下载日志CSV",
|
||||||
|
data=csv_data,
|
||||||
|
file_name=f"logs_{start_date}_{end_date}.csv",
|
||||||
|
mime="text/csv",
|
||||||
|
key="download_logs",
|
||||||
|
)
|
||||||
|
|
||||||
|
json_data = log_df.to_json(orient="records", force_ascii=False, indent=2)
|
||||||
|
st.download_button(
|
||||||
|
label="下载日志JSON",
|
||||||
|
data=json_data,
|
||||||
|
file_name=f"logs_{start_date}_{end_date}.json",
|
||||||
|
mime="application/json",
|
||||||
|
key="download_logs_json",
|
||||||
|
)
|
||||||
|
except Exception as exc: # noqa: BLE001
|
||||||
|
LOGGER.exception("加载日志失败", extra=LOG_EXTRA)
|
||||||
|
st.error(f"加载日志数据失败:{exc}")
|
||||||
|
|
||||||
|
st.subheader("历史对比")
|
||||||
|
st.write("选择两个时间点的日志进行对比分析。")
|
||||||
|
|
||||||
|
col3, col4 = st.columns(2)
|
||||||
|
with col3:
|
||||||
|
compare_date1 = st.date_input("对比日期1", value=date.today() - timedelta(days=1))
|
||||||
|
with col4:
|
||||||
|
compare_date2 = st.date_input("对比日期2", value=date.today())
|
||||||
|
|
||||||
|
comparison_stage = st.selectbox("对比阶段", stages, key="compare_stage")
|
||||||
|
st.write("选择需要比较的日志数量。")
|
||||||
|
compare_limit = st.slider("对比日志数量", min_value=10, max_value=200, value=50, step=10)
|
||||||
|
|
||||||
|
if st.button("生成历史对比报告"):
|
||||||
|
with st.spinner("生成对比报告中..."):
|
||||||
|
try:
|
||||||
|
with db_session(read_only=True) as conn:
|
||||||
|
def load_logs(d: date) -> pd.DataFrame:
|
||||||
|
start_ts = f"{d.isoformat()}T00:00:00Z"
|
||||||
|
end_ts = f"{d.isoformat()}T23:59:59Z"
|
||||||
|
query = ["SELECT ts, level, msg FROM run_log WHERE ts BETWEEN ? AND ?"]
|
||||||
|
params: list[object] = [start_ts, end_ts]
|
||||||
|
if comparison_stage != "ALL":
|
||||||
|
query.append("AND stage = ?")
|
||||||
|
params.append(comparison_stage)
|
||||||
|
query.append("ORDER BY ts DESC LIMIT ?")
|
||||||
|
params.append(compare_limit)
|
||||||
|
sql = " ".join(query)
|
||||||
|
rows = conn.execute(sql, params).fetchall()
|
||||||
|
if not rows:
|
||||||
|
return pd.DataFrame(columns=["ts", "level", "msg"])
|
||||||
|
df = pd.DataFrame([{k: row[k] for k in row.keys()} for row in rows])
|
||||||
|
df["ts"] = pd.to_datetime(df["ts"]).dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
return df
|
||||||
|
|
||||||
|
df1 = load_logs(compare_date1)
|
||||||
|
df2 = load_logs(compare_date2)
|
||||||
|
|
||||||
|
if df1.empty and df2.empty:
|
||||||
|
st.info("选定日期暂无日志可对比。")
|
||||||
|
else:
|
||||||
|
st.write("### 对比结果")
|
||||||
|
col_a, col_b = st.columns(2)
|
||||||
|
with col_a:
|
||||||
|
st.write(f"{compare_date1} 日日志")
|
||||||
|
st.dataframe(df1, hide_index=True, width="stretch")
|
||||||
|
with col_b:
|
||||||
|
st.write(f"{compare_date2} 日日志")
|
||||||
|
st.dataframe(df2, hide_index=True, width="stretch")
|
||||||
|
|
||||||
|
summary = {
|
||||||
|
"日期1日志条数": int(len(df1)),
|
||||||
|
"日期2日志条数": int(len(df2)),
|
||||||
|
"新增日志条数": max(len(df2) - len(df1), 0),
|
||||||
|
}
|
||||||
|
st.write("摘要:", summary)
|
||||||
|
except Exception as exc: # noqa: BLE001
|
||||||
|
LOGGER.exception("历史对比生成失败", extra=LOG_EXTRA)
|
||||||
|
st.error(f"生成历史对比失败:{exc}")
|
||||||
|
|
||||||
|
|
||||||
def render_data_settings() -> None:
|
def render_data_settings() -> None:
|
||||||
"""渲染数据源配置界面."""
|
"""渲染数据源配置界面."""
|
||||||
st.subheader("Tushare 数据源")
|
st.subheader("Tushare 数据源")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user