studytime-analysis: 报告开头增加角色基本信息(角色ID、账号ID、手机号后4位、名字、性别、年龄)

This commit is contained in:
xiaoban 2026-05-24 10:24:01 +08:00
parent 2e3caefacb
commit ed6c864197
3 changed files with 126 additions and 6 deletions

View File

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

View File

@ -38,8 +38,8 @@ python3 skills/studytime-analysis/scripts/studytime_analysis.py <role_id>
## 数据库说明
- **数据源**: PostgreSQL Online (`vala` 库)
- **核心表**: `user_chapter_play_record_0~7`完课记录8张分表
- **完课数据源**: PostgreSQL Online (`vala` 库)`user_chapter_play_record_0~7`8张分表
- **角色信息源**: MySQL Online (`vala_user` 库) — `vala_app_character` + `vala_app_account`
- **筛选**: `play_status = 1`(已完成),查询全量数据(不按月过滤)
- **寒暑假处理**: 1-2月寒假和 7-8月暑假的记录仅在一周时间分布中排除跨周趋势和完课明细完整保留
- **注意**: 表名无 `bi_` 前缀;课程通过 `level` + `chapter_id` 字段展示
@ -47,7 +47,8 @@ python3 skills/studytime-analysis/scripts/studytime_analysis.py <role_id>
## 输出说明
脚本输出为 Markdown 格式的分析报告,可直接发送给用户。包含:
- 基本信息角色ID、完课总数、寒暑假/非寒暑假数量)
- 角色基本信息角色ID、账号ID、手机号后4位、角色名字、性别、年龄
- 数据概况(完课总数、寒暑假/非寒暑假数量)
- 📌 一周时间分布数据范围说明
- 一周时间分布表 + 规律总结(仅非寒暑假)
- 跨周学习趋势表 + 趋势分析(全部数据)

View File

@ -12,6 +12,7 @@ import os
import sys
import psycopg2
import psycopg2.extras
import pymysql
from datetime import datetime, timedelta
from collections import defaultdict, OrderedDict
@ -24,6 +25,16 @@ PG_CONFIG = {
"dbname": os.environ.get("PG_DB_DATABASE", "vala"),
}
# MySQL Online (vala_user 库 — 角色基本信息)
MYSQL_CONFIG = {
"host": os.environ.get("MYSQL_HOST_online", "bj-cdb-dh2fkqa0.sql.tencentcdb.com"),
"port": int(os.environ.get("MYSQL_PORT_online", "27751")),
"user": os.environ.get("MYSQL_USERNAME_online", "read_only"),
"password": os.environ.get("MYSQL_PASSWORD_online", ""),
"db": "vala_user",
"charset": "utf8mb4",
}
EXCLUDED_MONTHS = (1, 2, 7, 8) # 寒假1-2月, 暑假7-8月
WEEKDAY_NAMES = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
@ -51,6 +62,92 @@ def get_connection():
return conn
def get_mysql_connection():
"""连接 MySQL Online (vala_user 库)"""
conn = pymysql.connect(
host=MYSQL_CONFIG["host"],
port=MYSQL_CONFIG["port"],
user=MYSQL_CONFIG["user"],
password=MYSQL_CONFIG["password"],
db=MYSQL_CONFIG["db"],
charset=MYSQL_CONFIG["charset"],
)
return conn
def fetch_role_info(role_id):
"""
MySQL vala_user 库查询角色基本信息
返回 dict: role_id, account_id, nickname, gender, age, phone_tail
"""
sql = """
SELECT
c.id AS role_id,
c.account_id,
c.nickname,
c.gender,
c.birthday,
a.tel
FROM vala_app_character c
LEFT JOIN vala_app_account a ON c.account_id = a.id
WHERE c.id = %s
"""
conn = get_mysql_connection()
try:
with conn.cursor() as cur:
cur.execute(sql, (role_id,))
row = cur.fetchone()
finally:
conn.close()
if not row:
return None
role_id_val, account_id, nickname, gender, birthday, tel = row
# 性别映射
gender_str = ""
if gender == 0:
gender_str = ""
elif gender == 1:
gender_str = ""
elif gender is not None:
gender_str = str(gender)
# 年龄(从 birthday 推算)
age = ""
if birthday:
try:
# birthday 可能是 "2015-5-28" 或 "2015-05-28" 格式
parts = str(birthday).split("-")
if len(parts) >= 1 and parts[0].isdigit():
birth_year = int(parts[0])
current_year = datetime.now().year
age = current_year - birth_year
except (ValueError, IndexError):
pass
# 手机号后4位
phone_tail = ""
if tel:
tel_str = str(tel)
# tel 可能是 "186****1625" 格式取最后4位
digits = ''.join(c for c in tel_str if c.isdigit())
if len(digits) >= 4:
phone_tail = digits[-4:]
elif digits:
phone_tail = digits
return {
"role_id": role_id_val,
"account_id": account_id,
"nickname": nickname or "",
"gender": gender_str,
"age": age,
"phone_tail": phone_tail,
}
def fetch_completion_records(role_id):
"""查询指定角色全部完课记录(包含寒暑假)"""
params = {}
@ -224,7 +321,7 @@ def analyze_weekly_trend(records):
# ── 输出格式化 ────────────────────────────────────────
def format_report(role_id, all_records, non_holiday_records, holiday_count, day_counts, weekday_periods, weeks_data, analysis):
def format_report(role_id, role_info, all_records, non_holiday_records, holiday_count, day_counts, weekday_periods, weeks_data, analysis):
"""生成 Markdown 格式分析报告
Args:
@ -239,6 +336,25 @@ def format_report(role_id, all_records, non_holiday_records, holiday_count, day_
lines.append(f"# 📊 学习时间分析报告 — 角色 {role_id}")
lines.append(f"")
# ── 角色基本信息 ──
if role_info:
lines.append(f"## 基本信息")
lines.append(f"")
lines.append(f"| 项目 | 详情 |")
lines.append(f"|------|------|")
lines.append(f"| 角色ID | {role_info['role_id']} |")
lines.append(f"| 账号ID | {role_info['account_id']} |")
if role_info['nickname']:
lines.append(f"| 角色名字 | {role_info['nickname']} |")
lines.append(f"| 性别 | {role_info['gender']} |")
if role_info['age']:
lines.append(f"| 年龄 | {role_info['age']} 岁 |")
if role_info['phone_tail']:
lines.append(f"| 账号手机号后4位 | {role_info['phone_tail']} |")
lines.append(f"")
# ── 数据概况 ──
lines.append(f"**分析时间**: {now_str}")
lines.append(f"**完课记录总数**: {len(all_records)}")
if holiday_count > 0:
@ -447,12 +563,15 @@ def main():
non_holiday_records, holiday_records = split_records(all_records)
holiday_count = len(holiday_records)
# 角色基本信息MySQL
role_info = fetch_role_info(role_id)
# 一周分布分析:仅用非寒暑假数据
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)
report = format_report(role_id, role_info, all_records, non_holiday_records, holiday_count, day_counts, weekday_periods, weeks_data, analysis)
print(report)