修改 studytime-analysis: 寒暑假数据仅在一周分布中排除,跨周趋势和明细表保留全量数据
This commit is contained in:
parent
aa6b689d52
commit
2e3caefacb
@ -7,4 +7,4 @@ lark_wiki_operate_as_bot 2a37701f568849f03eb46dd938baeda171380fe252b698ac8bda69c
|
||||
vala_git_workspace_backup 4cf352bec88fe84af065ba1ffcbb06647b77df0e01860faaf0bca9fd64b968ec
|
||||
cron-schedule b1879fa59d60e3d99cea1138674f7abac84a4aecd32743b801d41bfd6ed7181d
|
||||
study-analysis 33217dc132073ecd47b921800834f6df89494da9e7708fa90f15b3de7742e37f
|
||||
studytime-analysis f83392be24766898c5cfc206cc0edaf4f44b81ed0cb353c3f31157154964a7c2
|
||||
studytime-analysis ab15a60381dd281a229004977d3137a9ac59bca048743069dc6e2875e1075682
|
||||
|
||||
@ -20,11 +20,11 @@ version: 1.0.0
|
||||
## 工作流程
|
||||
|
||||
1. **参数提取** — 从用户消息中提取角色ID
|
||||
2. **数据采集** — 执行 Python 脚本查询该角色全部完课记录(自动排除寒假1-2月、暑假7-8月)
|
||||
2. **数据采集** — 执行 Python 脚本查询该角色全部完课记录(包含寒暑假,非按月过滤)
|
||||
3. **分析输出** — 脚本自动完成以下分析并输出格式化报告:
|
||||
- 一周分布:周一至周日各天完课数量 + 周一至周五上课时段(上午/中午/晚上)
|
||||
- 跨周趋势:覆盖总周数、周均完课数、连续性、断档、趋势变化
|
||||
- 完课记录明细表格
|
||||
- **一周分布**(仅非寒暑假数据):周一至周日各天完课数量 + 周一至周五上课时段(上午/中午/下午/晚上),排除寒暑假作息差异的干扰
|
||||
- **跨周趋势**(全部数据):覆盖总周数、周均完课数、连续性、中断周、趋势变化(含寒暑假期间的学习节奏变化)
|
||||
- **完课记录明细**(全部数据):完整的完课时间表
|
||||
|
||||
## 调用方式
|
||||
|
||||
@ -40,13 +40,15 @@ python3 skills/studytime-analysis/scripts/studytime_analysis.py <role_id>
|
||||
|
||||
- **数据源**: PostgreSQL Online (`vala` 库)
|
||||
- **核心表**: `user_chapter_play_record_0~7`(完课记录,8张分表)
|
||||
- **筛选**: `play_status = 1`(已完成),`EXTRACT(MONTH FROM updated_at) NOT IN (1,2,7,8)`(排除寒假1-2月、暑假7-8月)
|
||||
- **筛选**: `play_status = 1`(已完成),查询全量数据(不按月过滤)
|
||||
- **寒暑假处理**: 1-2月(寒假)和 7-8月(暑假)的记录仅在一周时间分布中排除,跨周趋势和完课明细完整保留
|
||||
- **注意**: 表名无 `bi_` 前缀;课程通过 `level` + `chapter_id` 字段展示
|
||||
|
||||
## 输出说明
|
||||
|
||||
脚本输出为 Markdown 格式的分析报告,可直接发送给用户。包含:
|
||||
- 基本信息(角色ID、有效完课总数、排除的寒暑假记录数)
|
||||
- 一周时间分布表 + 规律总结
|
||||
- 跨周学习趋势表 + 趋势分析
|
||||
- 完课记录明细表(日期、时间、星期、时段、课程、课时)
|
||||
- 基本信息(角色ID、完课总数、寒暑假/非寒暑假数量)
|
||||
- 📌 一周时间分布数据范围说明
|
||||
- 一周时间分布表 + 规律总结(仅非寒暑假)
|
||||
- 跨周学习趋势表 + 趋势分析(全部数据)
|
||||
- 完课记录明细表(全部数据,含寒暑假标记)
|
||||
|
||||
@ -52,7 +52,7 @@ def get_connection():
|
||||
|
||||
|
||||
def fetch_completion_records(role_id):
|
||||
"""查询指定角色全部完课记录(排除寒暑假)"""
|
||||
"""查询指定角色全部完课记录(包含寒暑假)"""
|
||||
params = {}
|
||||
union_parts = []
|
||||
for i in range(8):
|
||||
@ -63,7 +63,6 @@ def fetch_completion_records(role_id):
|
||||
FROM user_chapter_play_record_{i}
|
||||
WHERE user_id = %({param_name})s
|
||||
AND play_status = 1
|
||||
AND EXTRACT(MONTH FROM updated_at) NOT IN (1, 2, 7, 8)
|
||||
""")
|
||||
|
||||
union_sql = " UNION ALL ".join(union_parts)
|
||||
@ -84,32 +83,31 @@ def fetch_completion_records(role_id):
|
||||
return rows
|
||||
|
||||
|
||||
def count_excluded_records(role_id):
|
||||
"""统计被寒暑假排除的记录数"""
|
||||
params = {}
|
||||
union_parts = []
|
||||
for i in range(8):
|
||||
param_name = f"rid_{i}"
|
||||
params[param_name] = role_id
|
||||
union_parts.append(f"""
|
||||
SELECT COUNT(*) as cnt
|
||||
FROM user_chapter_play_record_{i}
|
||||
WHERE user_id = %({param_name})s
|
||||
AND play_status = 1
|
||||
AND EXTRACT(MONTH FROM updated_at) IN (1, 2, 7, 8)
|
||||
""")
|
||||
def is_holiday(dt):
|
||||
"""判断是否为寒暑假月份(1-2月寒假, 7-8月暑假)"""
|
||||
if dt is None:
|
||||
return False
|
||||
return dt.month in EXCLUDED_MONTHS
|
||||
|
||||
union_sql = " UNION ALL ".join(union_parts)
|
||||
sql = f"SELECT SUM(cnt) as total FROM ({union_sql}) t"
|
||||
|
||||
conn = get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(sql, params)
|
||||
result = cur.fetchone()
|
||||
finally:
|
||||
conn.close()
|
||||
return result[0] if result and result[0] else 0
|
||||
def split_records(records):
|
||||
"""
|
||||
拆分记录:
|
||||
- non_holiday: 非寒暑假记录(用于一周分布分析)
|
||||
- holiday: 寒暑假记录
|
||||
返回统计信息
|
||||
"""
|
||||
non_holiday = []
|
||||
holiday = []
|
||||
for r in records:
|
||||
dt = r["updated_at"]
|
||||
if dt is None:
|
||||
continue
|
||||
if is_holiday(dt):
|
||||
holiday.append(r)
|
||||
else:
|
||||
non_holiday.append(r)
|
||||
return non_holiday, holiday
|
||||
|
||||
|
||||
# ── 分析函数 ──────────────────────────────────────────
|
||||
@ -226,26 +224,41 @@ def analyze_weekly_trend(records):
|
||||
|
||||
# ── 输出格式化 ────────────────────────────────────────
|
||||
|
||||
def format_report(role_id, records, excluded_count, day_counts, weekday_periods, weeks_data, analysis):
|
||||
"""生成 Markdown 格式分析报告"""
|
||||
def format_report(role_id, all_records, non_holiday_records, holiday_count, day_counts, weekday_periods, weeks_data, analysis):
|
||||
"""生成 Markdown 格式分析报告
|
||||
|
||||
Args:
|
||||
all_records: 全部完课记录(用于明细表)
|
||||
non_holiday_records: 非寒暑假记录(用于一周分布分析)
|
||||
holiday_count: 寒暑假记录数(仅统计,不参与一周分布)
|
||||
day_counts, weekday_periods: 基于 non_holiday_records 的分析结果
|
||||
weeks_data, analysis: 基于 all_records 的分析结果
|
||||
"""
|
||||
lines = []
|
||||
now_str = datetime.now().strftime('%Y-%m-%d %H:%M')
|
||||
|
||||
lines.append(f"# 📊 学习时间分析报告 — 角色 {role_id}")
|
||||
lines.append(f"")
|
||||
lines.append(f"**分析时间**: {now_str}")
|
||||
lines.append(f"**有效完课记录**: {len(records)} 条")
|
||||
if excluded_count > 0:
|
||||
lines.append(f"**已排除寒暑假记录**: {excluded_count} 条(寒假1-2月、暑假7-8月,不算入分析)")
|
||||
lines.append(f"**完课记录总数**: {len(all_records)} 条")
|
||||
if holiday_count > 0:
|
||||
lines.append(f"**其中寒暑假记录**: {holiday_count} 条(寒假1-2月、暑假7-8月)")
|
||||
lines.append(f"**非寒暑假记录**: {len(non_holiday_records)} 条")
|
||||
lines.append(f"")
|
||||
lines.append(f"> ⚠️ **说明**: 「一周时间分布」仅基于非寒暑假数据(共 {len(non_holiday_records)} 条),排除寒暑假作息差异的干扰。")
|
||||
lines.append(f"> 「跨周趋势」和「完课明细」包含全部数据(共 {len(all_records)} 条),反映完整学习轨迹。")
|
||||
lines.append(f"")
|
||||
|
||||
if not records:
|
||||
lines.append("> ⚠️ 该角色没有非寒暑假期间的完课记录,无法进行分析。")
|
||||
if not all_records:
|
||||
lines.append("> ⚠️ 该角色没有任何完课记录。")
|
||||
return "\n".join(lines)
|
||||
|
||||
if not non_holiday_records:
|
||||
lines.append("> ⚠️ 该角色在非寒暑假期间没有完课记录,一周时间分布无法分析。")
|
||||
|
||||
# ═══ 一、一周时间分布 ═══
|
||||
lines.append(f"---")
|
||||
lines.append(f"## 一、一周时间分布")
|
||||
lines.append(f"## 一、一周时间分布(仅非寒暑假,{len(non_holiday_records)} 条记录)")
|
||||
lines.append(f"")
|
||||
|
||||
# 日分布表
|
||||
@ -395,12 +408,12 @@ def format_report(role_id, records, excluded_count, day_counts, weekday_periods,
|
||||
|
||||
# ═══ 三、完课记录明细 ═══
|
||||
lines.append(f"---")
|
||||
lines.append(f"## 三、完课记录明细")
|
||||
lines.append(f"## 三、完课记录明细(全部 {len(all_records)} 条记录)")
|
||||
lines.append(f"")
|
||||
lines.append(f"| 序号 | 日期 | 时间 | 星期 | 时段 | 级别 | 课程ID |")
|
||||
lines.append(f"|------|------|------|------|------|------|--------|")
|
||||
|
||||
for i, r in enumerate(records, 1):
|
||||
for i, r in enumerate(all_records, 1):
|
||||
dt = r["updated_at"]
|
||||
if dt is None:
|
||||
continue
|
||||
@ -430,11 +443,16 @@ def main():
|
||||
print(f"错误: 角色ID必须是数字,收到: {sys.argv[1]}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
records = fetch_completion_records(role_id)
|
||||
excluded_count = count_excluded_records(role_id)
|
||||
day_counts, weekday_periods = analyze_weekly_distribution(records)
|
||||
weeks_data, analysis = analyze_weekly_trend(records)
|
||||
report = format_report(role_id, records, excluded_count, day_counts, weekday_periods, weeks_data, analysis)
|
||||
all_records = fetch_completion_records(role_id)
|
||||
non_holiday_records, holiday_records = split_records(all_records)
|
||||
holiday_count = len(holiday_records)
|
||||
|
||||
# 一周分布分析:仅用非寒暑假数据
|
||||
day_counts, weekday_periods = analyze_weekly_distribution(non_holiday_records)
|
||||
# 跨周趋势分析:用全部数据
|
||||
weeks_data, analysis = analyze_weekly_trend(all_records)
|
||||
|
||||
report = format_report(role_id, all_records, non_holiday_records, holiday_count, day_counts, weekday_periods, weeks_data, analysis)
|
||||
|
||||
print(report)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user