223 lines
8.8 KiB
Python
223 lines
8.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
测试指数算法任务
|
|
"""
|
|
import sys
|
|
import os
|
|
|
|
# 添加项目路径
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
import logging
|
|
from config.settings import AppConfig
|
|
from database.connection import DatabaseConnection
|
|
from database.operations import DatabaseOperations
|
|
from tasks.dws.index import RecallIndexTask, IntimacyIndexTask
|
|
|
|
# 配置日志
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
)
|
|
logger = logging.getLogger('test_index')
|
|
|
|
def test_recall_index():
|
|
"""测试召回指数任务"""
|
|
logger.info("=" * 60)
|
|
logger.info("测试客户召回指数任务 (DWS_RECALL_INDEX)")
|
|
logger.info("=" * 60)
|
|
|
|
# 加载配置
|
|
config = AppConfig.load()
|
|
|
|
# 连接数据库
|
|
db_conn = DatabaseConnection(config.config["db"]["dsn"])
|
|
db = DatabaseOperations(db_conn)
|
|
|
|
try:
|
|
# 创建任务实例
|
|
task = RecallIndexTask(config, db, None, logger)
|
|
|
|
# 执行任务
|
|
result = task.execute(None)
|
|
|
|
logger.info("任务执行结果: %s", result)
|
|
|
|
# 查询结果
|
|
if result.get('status') == 'success':
|
|
sql = """
|
|
SELECT
|
|
COUNT(*) as total_count,
|
|
ROUND(AVG(display_score)::numeric, 2) as avg_score,
|
|
ROUND(MIN(display_score)::numeric, 2) as min_score,
|
|
ROUND(MAX(display_score)::numeric, 2) as max_score,
|
|
ROUND(AVG(raw_score)::numeric, 4) as avg_raw_score,
|
|
ROUND(AVG(score_overdue)::numeric, 4) as avg_overdue,
|
|
ROUND(AVG(score_new_bonus)::numeric, 4) as avg_new_bonus,
|
|
ROUND(AVG(score_recharge_bonus)::numeric, 4) as avg_recharge_bonus,
|
|
ROUND(AVG(score_hot_drop)::numeric, 4) as avg_hot_drop
|
|
FROM billiards_dws.dws_member_recall_index
|
|
"""
|
|
rows = db.query(sql)
|
|
if rows:
|
|
stats = dict(rows[0])
|
|
logger.info("-" * 40)
|
|
logger.info("召回指数统计:")
|
|
logger.info(" 总记录数: %s", stats['total_count'])
|
|
logger.info(" Display Score: 平均=%.2f, 最小=%.2f, 最大=%.2f",
|
|
stats['avg_score'] or 0, stats['min_score'] or 0, stats['max_score'] or 0)
|
|
logger.info(" Raw Score 平均: %.4f", stats['avg_raw_score'] or 0)
|
|
logger.info(" 分项得分平均:")
|
|
logger.info(" - 超期紧急性: %.4f", stats['avg_overdue'] or 0)
|
|
logger.info(" - 新客户加分: %.4f", stats['avg_new_bonus'] or 0)
|
|
logger.info(" - 充值加分: %.4f", stats['avg_recharge_bonus'] or 0)
|
|
logger.info(" - 热度断档: %.4f", stats['avg_hot_drop'] or 0)
|
|
|
|
# 查询Top 5
|
|
logger.info("-" * 40)
|
|
logger.info("召回优先级 Top 5:")
|
|
top_sql = """
|
|
SELECT member_id, display_score, raw_score,
|
|
days_since_last_visit, visit_interval_median
|
|
FROM billiards_dws.dws_member_recall_index
|
|
ORDER BY display_score DESC
|
|
LIMIT 5
|
|
"""
|
|
top_rows = db.query(top_sql)
|
|
for i, row in enumerate(top_rows or [], 1):
|
|
r = dict(row)
|
|
logger.info(" %d. 会员%s: %.2f分 (Raw=%.4f, 最近到店=%s天前, 周期=%.1f天)",
|
|
i, r['member_id'], r['display_score'] or 0, r['raw_score'] or 0,
|
|
r['days_since_last_visit'], r['visit_interval_median'] or 0)
|
|
|
|
return result
|
|
|
|
finally:
|
|
db_conn.close()
|
|
|
|
|
|
def test_intimacy_index():
|
|
"""测试亲密指数任务"""
|
|
logger.info("")
|
|
logger.info("=" * 60)
|
|
logger.info("测试客户-助教亲密指数任务 (DWS_INTIMACY_INDEX)")
|
|
logger.info("=" * 60)
|
|
|
|
# 加载配置
|
|
config = AppConfig.load()
|
|
|
|
# 连接数据库
|
|
db_conn = DatabaseConnection(config.config["db"]["dsn"])
|
|
db = DatabaseOperations(db_conn)
|
|
|
|
try:
|
|
# 创建任务实例
|
|
task = IntimacyIndexTask(config, db, None, logger)
|
|
|
|
# 执行任务
|
|
result = task.execute(None)
|
|
|
|
logger.info("任务执行结果: %s", result)
|
|
|
|
# 查询结果
|
|
if result.get('status') == 'success':
|
|
sql = """
|
|
SELECT
|
|
COUNT(*) as total_count,
|
|
COUNT(DISTINCT member_id) as unique_members,
|
|
COUNT(DISTINCT assistant_id) as unique_assistants,
|
|
ROUND(AVG(display_score)::numeric, 2) as avg_score,
|
|
ROUND(MIN(display_score)::numeric, 2) as min_score,
|
|
ROUND(MAX(display_score)::numeric, 2) as max_score,
|
|
ROUND(AVG(raw_score)::numeric, 4) as avg_raw_score,
|
|
ROUND(AVG(score_frequency)::numeric, 4) as avg_frequency,
|
|
ROUND(AVG(score_recency)::numeric, 4) as avg_recency,
|
|
ROUND(AVG(score_recharge)::numeric, 4) as avg_recharge,
|
|
ROUND(AVG(burst_multiplier)::numeric, 4) as avg_burst
|
|
FROM billiards_dws.dws_member_assistant_intimacy
|
|
"""
|
|
rows = db.query(sql)
|
|
if rows:
|
|
stats = dict(rows[0])
|
|
logger.info("-" * 40)
|
|
logger.info("亲密指数统计:")
|
|
logger.info(" 总记录数: %s (客户-助教对)", stats['total_count'])
|
|
logger.info(" 唯一会员: %s, 唯一助教: %s", stats['unique_members'], stats['unique_assistants'])
|
|
logger.info(" Display Score: 平均=%.2f, 最小=%.2f, 最大=%.2f",
|
|
stats['avg_score'] or 0, stats['min_score'] or 0, stats['max_score'] or 0)
|
|
logger.info(" Raw Score 平均: %.4f", stats['avg_raw_score'] or 0)
|
|
logger.info(" 分项得分平均:")
|
|
logger.info(" - 频次强度: %.4f", stats['avg_frequency'] or 0)
|
|
logger.info(" - 最近温度: %.4f", stats['avg_recency'] or 0)
|
|
logger.info(" - 充值强度: %.4f", stats['avg_recharge'] or 0)
|
|
logger.info(" - 激增放大: %.4f", stats['avg_burst'] or 0)
|
|
|
|
# 查询Top亲密关系
|
|
logger.info("-" * 40)
|
|
logger.info("亲密度 Top 5 客户-助教对:")
|
|
top_sql = """
|
|
SELECT member_id, assistant_id, display_score, raw_score,
|
|
session_count, attributed_recharge_amount
|
|
FROM billiards_dws.dws_member_assistant_intimacy
|
|
ORDER BY display_score DESC
|
|
LIMIT 5
|
|
"""
|
|
top_rows = db.query(top_sql)
|
|
for i, row in enumerate(top_rows or [], 1):
|
|
r = dict(row)
|
|
logger.info(" %d. 会员%s-助教%s: %.2f分 (会话%d次, 归因充值%.2f元)",
|
|
i, r['member_id'], r['assistant_id'],
|
|
r['display_score'] or 0, r['session_count'] or 0,
|
|
r['attributed_recharge_amount'] or 0)
|
|
|
|
return result
|
|
|
|
finally:
|
|
db_conn.close()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
print("=" * 60)
|
|
print("指数算法任务测试")
|
|
print("=" * 60)
|
|
print()
|
|
|
|
# 先检查表是否存在
|
|
config = AppConfig.load()
|
|
db_conn = DatabaseConnection(config.config["db"]["dsn"])
|
|
db = DatabaseOperations(db_conn)
|
|
|
|
check_sql = """
|
|
SELECT table_name
|
|
FROM information_schema.tables
|
|
WHERE table_schema = 'billiards_dws'
|
|
AND table_name IN ('dws_member_recall_index', 'dws_member_assistant_intimacy', 'cfg_index_parameters')
|
|
"""
|
|
tables = db.query(check_sql)
|
|
existing_tables = [dict(r)['table_name'] for r in (tables or [])]
|
|
|
|
if 'cfg_index_parameters' not in existing_tables:
|
|
print("警告: cfg_index_parameters 表不存在,请先执行 schema_dws.sql")
|
|
print("需要执行的表:")
|
|
print(" - cfg_index_parameters")
|
|
print(" - dws_member_recall_index")
|
|
print(" - dws_member_assistant_intimacy")
|
|
print(" - dws_index_percentile_history")
|
|
db_conn.close()
|
|
sys.exit(1)
|
|
|
|
db_conn.close()
|
|
|
|
# 测试召回指数
|
|
recall_result = test_recall_index()
|
|
|
|
# 测试亲密指数
|
|
intimacy_result = test_intimacy_index()
|
|
|
|
print()
|
|
print("=" * 60)
|
|
print("测试完成")
|
|
print("=" * 60)
|
|
print(f"召回指数: {recall_result.get('status', 'unknown')}")
|
|
print(f"亲密指数: {intimacy_result.get('status', 'unknown')}")
|