studytime-analysis: 新增留存状态判定 — 最近14天无完课标记为流失,否则正常

This commit is contained in:
xiaoban 2026-05-24 10:44:49 +08:00
parent ed6c864197
commit cde77e762d
2 changed files with 24 additions and 4 deletions

View File

@ -7,4 +7,4 @@ lark_wiki_operate_as_bot 2a37701f568849f03eb46dd938baeda171380fe252b698ac8bda69c
vala_git_workspace_backup 4cf352bec88fe84af065ba1ffcbb06647b77df0e01860faaf0bca9fd64b968ec vala_git_workspace_backup 4cf352bec88fe84af065ba1ffcbb06647b77df0e01860faaf0bca9fd64b968ec
cron-schedule b1879fa59d60e3d99cea1138674f7abac84a4aecd32743b801d41bfd6ed7181d cron-schedule b1879fa59d60e3d99cea1138674f7abac84a4aecd32743b801d41bfd6ed7181d
study-analysis 33217dc132073ecd47b921800834f6df89494da9e7708fa90f15b3de7742e37f study-analysis 33217dc132073ecd47b921800834f6df89494da9e7708fa90f15b3de7742e37f
studytime-analysis 042178f9f7e97eeef366fd619403f4d0c8e0dce290abf17af662dddfa67c4e83 studytime-analysis a3b2570583717cf5af2720f047de23752ee42cba8ff4a3f859d216e0580026ae

View File

@ -13,7 +13,7 @@ import sys
import psycopg2 import psycopg2
import psycopg2.extras import psycopg2.extras
import pymysql import pymysql
from datetime import datetime, timedelta from datetime import datetime, timedelta, timezone
from collections import defaultdict, OrderedDict from collections import defaultdict, OrderedDict
# ── 配置 ────────────────────────────────────────────── # ── 配置 ──────────────────────────────────────────────
@ -148,6 +148,21 @@ def fetch_role_info(role_id):
} }
def check_retention(records, cutoff_days=14):
"""
检查角色留存状态
- 最近 cutoff_days 天内有完课记录 "正常"
- 否则 "流失"
当无任何完课记录时返回 "无完课记录"
"""
if not records:
return "无完课记录"
# 用本地时间比较(与 PG 存储时区一致 UTC+8
cutoff = datetime.now() - timedelta(days=cutoff_days)
has_recent = any(r["updated_at"].replace(tzinfo=None) >= cutoff for r in records)
return "正常" if has_recent else "流失"
def fetch_completion_records(role_id): def fetch_completion_records(role_id):
"""查询指定角色全部完课记录(包含寒暑假)""" """查询指定角色全部完课记录(包含寒暑假)"""
params = {} params = {}
@ -321,7 +336,7 @@ def analyze_weekly_trend(records):
# ── 输出格式化 ──────────────────────────────────────── # ── 输出格式化 ────────────────────────────────────────
def format_report(role_id, role_info, all_records, non_holiday_records, holiday_count, day_counts, weekday_periods, weeks_data, analysis): def format_report(role_id, role_info, retention_status, all_records, non_holiday_records, holiday_count, day_counts, weekday_periods, weeks_data, analysis):
"""生成 Markdown 格式分析报告 """生成 Markdown 格式分析报告
Args: Args:
@ -352,6 +367,8 @@ def format_report(role_id, role_info, all_records, non_holiday_records, holiday_
lines.append(f"| 年龄 | {role_info['age']} 岁 |") lines.append(f"| 年龄 | {role_info['age']} 岁 |")
if role_info['phone_tail']: if role_info['phone_tail']:
lines.append(f"| 账号手机号后4位 | {role_info['phone_tail']} |") lines.append(f"| 账号手机号后4位 | {role_info['phone_tail']} |")
if retention_status:
lines.append(f"| 最近留存状态 | {retention_status} |")
lines.append(f"") lines.append(f"")
# ── 数据概况 ── # ── 数据概况 ──
@ -566,12 +583,15 @@ def main():
# 角色基本信息MySQL # 角色基本信息MySQL
role_info = fetch_role_info(role_id) role_info = fetch_role_info(role_id)
# 留存状态判定
retention_status = check_retention(all_records)
# 一周分布分析:仅用非寒暑假数据 # 一周分布分析:仅用非寒暑假数据
day_counts, weekday_periods = analyze_weekly_distribution(non_holiday_records) day_counts, weekday_periods = analyze_weekly_distribution(non_holiday_records)
# 跨周趋势分析:用全部数据 # 跨周趋势分析:用全部数据
weeks_data, analysis = analyze_weekly_trend(all_records) weeks_data, analysis = analyze_weekly_trend(all_records)
report = format_report(role_id, role_info, all_records, non_holiday_records, holiday_count, day_counts, weekday_periods, weeks_data, analysis) report = format_report(role_id, role_info, retention_status, all_records, non_holiday_records, holiday_count, day_counts, weekday_periods, weeks_data, analysis)
print(report) print(report)