加入指令数据统计,指令看板内容

This commit is contained in:
liuwei
2025-03-18 17:40:59 +08:00
parent 21f8fffca8
commit 1d7ee9f953
23 changed files with 2407 additions and 1 deletions

View File

@@ -0,0 +1,64 @@
-- 插件统计汇总表
CREATE TABLE IF NOT EXISTS t_plugin_stats (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
plugin_name VARCHAR(50) NOT NULL COMMENT '插件名称',
command VARCHAR(50) NOT NULL COMMENT '触发的命令',
stat_date DATE NOT NULL COMMENT '统计日期',
total_calls INT NOT NULL DEFAULT 0 COMMENT '总调用次数',
success_calls INT NOT NULL DEFAULT 0 COMMENT '成功调用次数',
failed_calls INT NOT NULL DEFAULT 0 COMMENT '失败调用次数',
group_calls INT NOT NULL DEFAULT 0 COMMENT '群聊调用次数',
private_calls INT NOT NULL DEFAULT 0 COMMENT '私聊调用次数',
avg_process_time FLOAT NOT NULL DEFAULT 0 COMMENT '平均处理时间(毫秒)',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY uk_plugin_command_date (plugin_name, command, stat_date),
INDEX idx_stat_date (stat_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='插件统计汇总表';
-- 用户使用统计表
CREATE TABLE IF NOT EXISTS t_user_stats (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id VARCHAR(50) NOT NULL COMMENT '用户ID',
plugin_name VARCHAR(50) NOT NULL COMMENT '插件名称',
command VARCHAR(50) NOT NULL COMMENT '触发的命令',
total_calls INT NOT NULL DEFAULT 0 COMMENT '总调用次数',
success_calls INT NOT NULL DEFAULT 0 COMMENT '成功调用次数',
failed_calls INT NOT NULL DEFAULT 0 COMMENT '失败调用次数',
first_used_at DATETIME NOT NULL COMMENT '首次使用时间',
last_used_at DATETIME NOT NULL COMMENT '最后使用时间',
UNIQUE KEY uk_user_plugin_command (user_id, plugin_name, command),
INDEX idx_user_id (user_id),
INDEX idx_last_used_at (last_used_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户使用统计表';
-- 群组使用统计表
CREATE TABLE IF NOT EXISTS t_group_stats (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
group_id VARCHAR(50) NOT NULL COMMENT '群组ID',
plugin_name VARCHAR(50) NOT NULL COMMENT '插件名称',
command VARCHAR(50) NOT NULL COMMENT '触发的命令',
total_calls INT NOT NULL DEFAULT 0 COMMENT '总调用次数',
success_calls INT NOT NULL DEFAULT 0 COMMENT '成功调用次数',
failed_calls INT NOT NULL DEFAULT 0 COMMENT '失败调用次数',
unique_users INT NOT NULL DEFAULT 0 COMMENT '唯一用户数',
first_used_at DATETIME NOT NULL COMMENT '首次使用时间',
last_used_at DATETIME NOT NULL COMMENT '最后使用时间',
UNIQUE KEY uk_group_plugin_command (group_id, plugin_name, command),
INDEX idx_group_id (group_id),
INDEX idx_last_used_at (last_used_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='群组使用统计表';
-- 错误日志表
CREATE TABLE IF NOT EXISTS t_error_logs (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
plugin_name VARCHAR(50) NOT NULL COMMENT '插件名称',
command VARCHAR(50) NOT NULL COMMENT '触发的命令',
user_id VARCHAR(50) NOT NULL COMMENT '用户ID',
group_id VARCHAR(50) COMMENT '群组ID私聊为NULL',
error_message TEXT NOT NULL COMMENT '错误信息',
stack_trace TEXT COMMENT '堆栈跟踪',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
INDEX idx_plugin_name (plugin_name),
INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='错误日志表';

119
db/scripts/init.sql Normal file
View File

@@ -0,0 +1,119 @@
-- 创建数据库
CREATE DATABASE IF NOT EXISTS message_archive CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE message_archive;
CREATE TABLE IF NOT EXISTS message_archive.messages
(
id int auto_increment comment '自增主键ID'
primary key,
group_id varchar(20) null comment '群ID',
timestamp varchar(20) not null comment '消息时间戳',
sender varchar(255) not null comment '发送者微信ID',
content text null comment '消息内容',
message_type varchar(50) null comment '消息类型(文本、图片、视频等)',
attachment_url varchar(512) null comment '附件URL图片、视频链接',
message_id varchar(32) null comment '消息 id',
message_xml text null comment '消息 xml 部分',
message_thumb text null comment '视频或图片消息的缩略图路径'
)
comment '微信群消息存储表,记录所有群聊消息';
CREATE TABLE IF NOT EXISTS message_archive.speech_counts
(
id int auto_increment comment '自增主键ID'
primary key,
group_id text null comment '群聊ID',
wx_id text null comment '用户微信ID',
date text null comment '统计日期YYYY-MM-DD格式',
count int null comment '发言次数',
constraint speech_counts_group_id_wx_id_date_uindex
unique (group_id, wx_id, date) using hash
)
comment '群成员每日发言统计表';
CREATE TABLE IF NOT EXISTS message_archive.t_encyclopedia_active_tasks
(
active_task_id int auto_increment comment '任务ID'
primary key,
group_id varchar(50) not null comment '群聊ID',
question varchar(255) not null comment '问题内容',
answer varchar(100) not null comment '正确答案',
score int not null comment '答对可获得的分数',
description text null comment '问题描述或提示',
holder_id varchar(50) not null comment '出题人ID',
assigned_at datetime default current_timestamp() null comment '任务分配时间',
status enum ('pending', 'completed') default 'pending' null comment '任务状态',
question_id int null comment '问题ID'
)
comment '百科答题游戏活跃任务表';
create or replace index group_id
on message_archive.t_encyclopedia_active_tasks (group_id);
create or replace index question_id
on message_archive.t_encyclopedia_active_tasks (question_id);
CREATE TABLE IF NOT EXISTS message_archive.t_encyclopedia_groups
(
group_id varchar(50) not null comment '群聊ID'
primary key,
created_at datetime default current_timestamp() null comment '记录创建时间'
)
comment '百科答题游戏群聊表';
CREATE TABLE IF NOT EXISTS message_archive.t_encyclopedia_players
(
player_id varchar(50) not null comment '玩家ID微信ID',
group_id varchar(50) not null comment '群聊ID',
player_name varchar(50) not null comment '玩家名称',
points int default 0 null comment '玩家积分',
created_at datetime default current_timestamp() null comment '记录创建时间',
primary key (player_id, group_id)
)
comment '百科答题游戏玩家表';
CREATE TABLE IF NOT EXISTS message_archive.t_encyclopedia_task_history
(
history_id int auto_increment comment '历史记录ID'
primary key,
group_id varchar(50) not null comment '群聊ID',
active_task_id int not null comment '关联的任务ID',
player_id varchar(50) not null comment '回答者ID',
answer varchar(100) not null comment '玩家的回答',
is_correct tinyint(1) default 0 null comment '是否回答正确',
points_earned int default 0 null comment '获得的积分',
completed_at datetime default current_timestamp() null comment '完成时间'
)
comment '百科答题游戏任务历史表';
create or replace index group_id
on message_archive.t_encyclopedia_task_history (group_id);
CREATE TABLE IF NOT EXISTS message_archive.t_sign_record
(
id bigint auto_increment comment '自增主键ID'
primary key,
wx_id varchar(100) not null comment '用户微信ID',
group_id varchar(100) not null comment '群聊ID',
wx_nick_name varchar(100) not null comment '用户昵称',
points int default 0 null comment '积分数量',
sign_stat datetime null comment '最近签到时间',
signin_streak int default 0 null comment '连续签到天数',
create_time datetime default current_timestamp() null comment '记录创建时间',
update_time datetime default current_timestamp() null on update current_timestamp() comment '记录更新时间',
constraint unique_sign
unique (wx_id, group_id)
)
comment '用户群内签到记录表';
CREATE TABLE IF NOT EXISTS message_archive.tasks
(
task_id int auto_increment comment '任务ID'
primary key,
task_description varchar(255) not null comment '任务描述',
reminder_time time not null comment '提醒时间',
task_type enum ('single', 'recurring') default 'single' null comment '任务类型:单次或周期性',
status enum ('pending', 'completed') default 'pending' null comment '任务状态:待办或已完成',
created_at timestamp default current_timestamp() null comment '创建时间'
)
comment '机器人定时任务表';

540
db/stats_db.py Normal file
View File

@@ -0,0 +1,540 @@
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Union, Tuple
from db.base import BaseDBOperator
from db.connection import DBConnectionManager
class StatsDBOperator(BaseDBOperator):
"""统计数据库操作类"""
def __init__(self, db_manager: DBConnectionManager):
super().__init__(db_manager)
def record_plugin_call(self, plugin_name: str, command: str, user_id: str,
group_id: Optional[str], success: bool,
process_time_ms: float) -> bool:
"""记录插件调用信息
Args:
plugin_name: 插件名称
command: 触发的命令
user_id: 用户ID
group_id: 群组ID私聊为None
success: 是否调用成功
process_time_ms: 处理时间(毫秒)
Returns:
是否成功记录
"""
# 1. 更新插件统计汇总表
self._update_plugin_stats(plugin_name, command, success, process_time_ms,
True if group_id else False)
# 2. 更新用户使用统计表
self._update_user_stats(user_id, plugin_name, command, success)
# 3. 如果是群聊,更新群组使用统计表
if group_id:
self._update_group_stats(group_id, plugin_name, command, user_id, success)
return True
def record_error(self, plugin_name: str, command: str, user_id: str,
group_id: Optional[str], error_message: str,
stack_trace: Optional[str] = None) -> bool:
"""记录错误信息
Args:
plugin_name: 插件名称
command: 触发的命令
user_id: 用户ID
group_id: 群组ID私聊为None
error_message: 错误信息
stack_trace: 堆栈跟踪
Returns:
是否成功记录
"""
sql = """
INSERT INTO t_error_logs
(plugin_name, command, user_id, group_id, error_message, stack_trace)
VALUES (%s, %s, %s, %s, %s, %s)
"""
params = (plugin_name, command, user_id, group_id, error_message, stack_trace)
return self.execute_update(sql, params)
def _update_plugin_stats(self, plugin_name: str, command: str, success: bool,
process_time_ms: float, is_group: bool) -> bool:
"""更新插件统计汇总表
Args:
plugin_name: 插件名称
command: 触发的命令
success: 是否调用成功
process_time_ms: 处理时间(毫秒)
is_group: 是否群聊
Returns:
是否成功更新
"""
today = datetime.now().strftime("%Y-%m-%d")
# 先查询当前记录
query_sql = """
SELECT id, total_calls, success_calls, failed_calls, group_calls,
private_calls, avg_process_time
FROM t_plugin_stats
WHERE plugin_name = %s AND command = %s AND stat_date = %s
"""
query_params = (plugin_name, command, today)
result = self.execute_query(query_sql, query_params, fetch_one=True)
if result:
# 更新现有记录
total_calls = result['total_calls'] + 1
success_calls = result['success_calls'] + (1 if success else 0)
failed_calls = result['failed_calls'] + (0 if success else 1)
group_calls = result['group_calls'] + (1 if is_group else 0)
private_calls = result['private_calls'] + (0 if is_group else 1)
# 计算新的平均处理时间
old_avg = result['avg_process_time']
new_avg = ((old_avg * (total_calls - 1)) + process_time_ms) / total_calls
update_sql = """
UPDATE t_plugin_stats
SET total_calls = %s,
success_calls = %s,
failed_calls = %s,
group_calls = %s,
private_calls = %s,
avg_process_time = %s
WHERE id = %s
"""
update_params = (total_calls, success_calls, failed_calls, group_calls,
private_calls, new_avg, result['id'])
return self.execute_update(update_sql, update_params)
else:
# 插入新记录
insert_sql = """
INSERT INTO t_plugin_stats
(plugin_name, command, stat_date, total_calls, success_calls,
failed_calls, group_calls, private_calls, avg_process_time)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
insert_params = (
plugin_name, command, today, 1,
1 if success else 0,
0 if success else 1,
1 if is_group else 0,
0 if is_group else 1,
process_time_ms
)
return self.execute_update(insert_sql, insert_params)
def _update_user_stats(self, user_id: str, plugin_name: str,
command: str, success: bool) -> bool:
"""更新用户使用统计表
Args:
user_id: 用户ID
plugin_name: 插件名称
command: 触发的命令
success: 是否调用成功
Returns:
是否成功更新
"""
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 查询是否存在记录
query_sql = """
SELECT id, total_calls, success_calls, failed_calls, first_used_at
FROM t_user_stats
WHERE user_id = %s AND plugin_name = %s AND command = %s
"""
query_params = (user_id, plugin_name, command)
result = self.execute_query(query_sql, query_params, fetch_one=True)
if result:
# 更新现有记录
update_sql = """
UPDATE t_user_stats
SET total_calls = total_calls + 1,
success_calls = success_calls + %s,
failed_calls = failed_calls + %s,
last_used_at = %s
WHERE id = %s
"""
update_params = (1 if success else 0, 0 if success else 1, now, result['id'])
return self.execute_update(update_sql, update_params)
else:
# 插入新记录
insert_sql = """
INSERT INTO t_user_stats
(user_id, plugin_name, command, total_calls, success_calls,
failed_calls, first_used_at, last_used_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
"""
insert_params = (
user_id, plugin_name, command, 1,
1 if success else 0,
0 if success else 1,
now, now
)
return self.execute_update(insert_sql, insert_params)
def _update_group_stats(self, group_id: str, plugin_name: str,
command: str, user_id: str, success: bool) -> bool:
"""更新群组使用统计表
Args:
group_id: 群组ID
plugin_name: 插件名称
command: 触发的命令
user_id: 用户ID
success: 是否调用成功
Returns:
是否成功更新
"""
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 查询是否存在记录
query_sql = """
SELECT id, total_calls, success_calls, failed_calls, unique_users, first_used_at
FROM t_group_stats
WHERE group_id = %s AND plugin_name = %s AND command = %s
"""
query_params = (group_id, plugin_name, command)
result = self.execute_query(query_sql, query_params, fetch_one=True)
# 查询该命令的唯一用户
user_query_sql = """
SELECT COUNT(DISTINCT user_id) as user_count
FROM t_user_stats
WHERE plugin_name = %s AND command = %s
"""
user_query_params = (plugin_name, command)
user_result = self.execute_query(user_query_sql, user_query_params, fetch_one=True)
unique_users = user_result['user_count'] if user_result else 1
if result:
# 更新现有记录
update_sql = """
UPDATE t_group_stats
SET total_calls = total_calls + 1,
success_calls = success_calls + %s,
failed_calls = failed_calls + %s,
unique_users = %s,
last_used_at = %s
WHERE id = %s
"""
update_params = (1 if success else 0, 0 if success else 1,
unique_users, now, result['id'])
return self.execute_update(update_sql, update_params)
else:
# 插入新记录
insert_sql = """
INSERT INTO t_group_stats
(group_id, plugin_name, command, total_calls, success_calls,
failed_calls, unique_users, first_used_at, last_used_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
insert_params = (
group_id, plugin_name, command, 1,
1 if success else 0,
0 if success else 1,
unique_users, now, now
)
return self.execute_update(insert_sql, insert_params)
def get_plugin_stats(self, days: int = 7) -> List[Dict]:
"""获取插件使用统计
Args:
days: 统计天数
Returns:
插件统计列表
"""
sql = """
SELECT plugin_name, command,
SUM(total_calls) as total_calls,
SUM(success_calls) as success_calls,
SUM(failed_calls) as failed_calls,
AVG(avg_process_time) as avg_process_time
FROM t_plugin_stats
WHERE stat_date >= DATE_SUB(CURDATE(), INTERVAL %s DAY)
GROUP BY plugin_name, command
ORDER BY total_calls DESC
"""
return self.execute_query(sql, (days,)) or []
def get_user_stats(self, days: int = 7, limit: int = 20) -> List[Dict]:
"""获取用户使用统计
Args:
days: 统计天数
limit: 返回记录数量限制
Returns:
用户统计列表
"""
sql = """
SELECT user_id,
SUM(total_calls) as total_calls,
COUNT(DISTINCT plugin_name) as used_plugins
FROM t_user_stats
WHERE last_used_at >= DATE_SUB(CURDATE(), INTERVAL %s DAY)
GROUP BY user_id
ORDER BY total_calls DESC
LIMIT %s
"""
return self.execute_query(sql, (days, limit)) or []
def get_group_stats(self, days: int = 7, limit: int = 20) -> List[Dict]:
"""获取群组使用统计
Args:
days: 统计天数
limit: 返回记录数量限制
Returns:
群组统计列表
"""
sql = """
SELECT group_id,
SUM(total_calls) as total_calls,
COUNT(DISTINCT plugin_name) as used_plugins,
MAX(unique_users) as max_unique_users
FROM t_group_stats
WHERE last_used_at >= DATE_SUB(CURDATE(), INTERVAL %s DAY)
GROUP BY group_id
ORDER BY total_calls DESC
LIMIT %s
"""
return self.execute_query(sql, (days, limit)) or []
def get_error_logs(self, days: int = 7, limit: int = 100) -> List[Dict]:
"""获取错误日志
Args:
days: 统计天数
limit: 返回记录数量限制
Returns:
错误日志列表
"""
sql = """
SELECT id, plugin_name, command, user_id, group_id,
error_message, stack_trace, created_at
FROM t_error_logs
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL %s DAY)
ORDER BY created_at DESC
LIMIT %s
"""
return self.execute_query(sql, (days, limit)) or []
def get_error_detail(self, error_id: int) -> Optional[Dict]:
"""获取错误详情
Args:
error_id: 错误ID
Returns:
错误详情
"""
sql = """
SELECT id, plugin_name, command, user_id, group_id,
error_message, stack_trace, created_at
FROM t_error_logs
WHERE id = %s
"""
return self.execute_query(sql, (error_id,), fetch_one=True)
def get_dashboard_summary(self, days: int = 7) -> Dict:
"""获取仪表盘摘要数据
Args:
days: 统计天数
Returns:
仪表盘摘要数据
"""
# 获取时间范围
end_date = datetime.now()
start_date = end_date - timedelta(days=days)
start_date_str = start_date.strftime("%Y-%m-%d")
# 1. 总调用次数
total_calls_sql = """
SELECT SUM(total_calls) as total_calls
FROM t_plugin_stats
WHERE stat_date >= %s
"""
total_calls_result = self.execute_query(total_calls_sql, (start_date_str,), fetch_one=True)
total_calls = total_calls_result['total_calls'] if total_calls_result and total_calls_result['total_calls'] else 0
# 2. 成功率
success_rate_sql = """
SELECT SUM(success_calls) as success_calls, SUM(total_calls) as total_calls
FROM t_plugin_stats
WHERE stat_date >= %s
"""
success_rate_result = self.execute_query(success_rate_sql, (start_date_str,), fetch_one=True)
success_rate = 0
if success_rate_result and success_rate_result['total_calls']:
success_rate = (success_rate_result['success_calls'] / success_rate_result['total_calls']) * 100
# 3. 活跃用户数
active_users_sql = """
SELECT COUNT(DISTINCT user_id) as active_users
FROM t_user_stats
WHERE last_used_at >= %s
"""
active_users_result = self.execute_query(active_users_sql, (start_date_str,), fetch_one=True)
active_users = active_users_result['active_users'] if active_users_result else 0
# 4. 活跃群组数
active_groups_sql = """
SELECT COUNT(DISTINCT group_id) as active_groups
FROM t_group_stats
WHERE last_used_at >= %s
"""
active_groups_result = self.execute_query(active_groups_sql, (start_date_str,), fetch_one=True)
active_groups = active_groups_result['active_groups'] if active_groups_result else 0
# 5. 错误数量
error_count_sql = """
SELECT COUNT(*) as error_count
FROM t_error_logs
WHERE created_at >= %s
"""
error_count_result = self.execute_query(error_count_sql, (start_date_str,), fetch_one=True)
error_count = error_count_result['error_count'] if error_count_result else 0
# 6. 平均响应时间
avg_response_time_sql = """
SELECT AVG(avg_process_time) as avg_response_time
FROM t_plugin_stats
WHERE stat_date >= %s
"""
avg_response_time_result = self.execute_query(avg_response_time_sql, (start_date_str,), fetch_one=True)
avg_response_time = avg_response_time_result['avg_response_time'] if avg_response_time_result and avg_response_time_result['avg_response_time'] else 0
# 7. 最常用的插件
top_plugins_sql = """
SELECT plugin_name, SUM(total_calls) as total_calls
FROM t_plugin_stats
WHERE stat_date >= %s
GROUP BY plugin_name
ORDER BY total_calls DESC
LIMIT 5
"""
top_plugins = self.execute_query(top_plugins_sql, (start_date_str,)) or []
# 8. 最活跃的用户
top_users_sql = """
SELECT user_id, SUM(total_calls) as total_calls
FROM t_user_stats
WHERE last_used_at >= %s
GROUP BY user_id
ORDER BY total_calls DESC
LIMIT 5
"""
top_users = self.execute_query(top_users_sql, (start_date_str,)) or []
# 9. 最活跃的群组
top_groups_sql = """
SELECT group_id, SUM(total_calls) as total_calls
FROM t_group_stats
WHERE last_used_at >= %s
GROUP BY group_id
ORDER BY total_calls DESC
LIMIT 5
"""
top_groups = self.execute_query(top_groups_sql, (start_date_str,)) or []
# 返回汇总数据
return {
"total_calls": total_calls,
"success_rate": success_rate,
"active_users": active_users,
"active_groups": active_groups,
"error_count": error_count,
"avg_response_time": avg_response_time,
"top_plugins": top_plugins,
"top_users": top_users,
"top_groups": top_groups
}
def get_plugin_trend(self, plugin_name: str = "", days: int = 7) -> List[Dict]:
"""获取插件使用趋势数据
Args:
plugin_name: 插件名称,为空则获取所有插件
days: 统计天数
Returns:
插件使用趋势数据
"""
# 获取时间范围内的每一天
end_date = datetime.now().date()
start_date = end_date - timedelta(days=days-1) # 包含今天所以减1
if plugin_name:
# 获取特定插件的趋势
sql = """
SELECT stat_date, SUM(total_calls) as total_calls,
SUM(success_calls) as success_calls,
SUM(failed_calls) as failed_calls
FROM t_plugin_stats
WHERE plugin_name = %s AND stat_date >= %s
GROUP BY stat_date
ORDER BY stat_date
"""
params = (plugin_name, start_date)
else:
# 获取所有插件的趋势
sql = """
SELECT stat_date, SUM(total_calls) as total_calls,
SUM(success_calls) as success_calls,
SUM(failed_calls) as failed_calls
FROM t_plugin_stats
WHERE stat_date >= %s
GROUP BY stat_date
ORDER BY stat_date
"""
params = (start_date,)
results = self.execute_query(sql, params) or []
# 将结果转换为按日期的字典
trend_by_date = {r['stat_date'].strftime('%Y-%m-%d'): r for r in results}
# 确保每一天都有数据
trend_data = []
current_date = start_date
while current_date <= end_date:
date_str = current_date.strftime('%Y-%m-%d')
if date_str in trend_by_date:
data = trend_by_date[date_str]
trend_data.append({
'date': date_str,
'total_calls': data['total_calls'],
'success_calls': data['success_calls'],
'failed_calls': data['failed_calls'],
'success_rate': (data['success_calls'] / data['total_calls'] * 100) if data['total_calls'] > 0 else 0
})
else:
trend_data.append({
'date': date_str,
'total_calls': 0,
'success_calls': 0,
'failed_calls': 0,
'success_rate': 0
})
current_date += timedelta(days=1)
return trend_data