Files
tianxuan/scripts/test_e2e_full.py
T

129 lines
6.0 KiB
Python

"""天璇完整流程端到端测试"""
import sys, os, importlib
# 项目根目录 = 脚本目录的父目录
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
os.environ['DJANGO_SETTINGS_MODULE'] = 'tianxuan.settings'
import django; django.setup()
OK = '[OK]'
FAIL = '[!!]'
results = []
def check(name, ok, detail=""):
icon = OK if ok else FAIL
results.append((name, ok))
print(f' {icon} {name} {detail}')
print('=' * 60)
print('天璇 完整流程端到端测试')
print('=' * 60)
# ── 1. 生成复杂测试数据 ──────────────────────────────────
print('\n[1/7] 生成测试数据')
import importlib.util
spec = importlib.util.spec_from_file_location('gen_complex_test',
os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'scripts', 'gen_complex_test.py'))
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
df = mod.gen_flows(n_rows=2000)
csv_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'data', 'e2e_full.csv')
df.write_csv(csv_path, null_value='')
check('gen_complex_test', os.path.exists(csv_path), f'rows={len(df)} cols={len(df.columns)}')
# ── 2. 加载 + 清洗 ────────────────────────────────────────
print('\n[2/7] 加载 CSV + 自动清洗')
from analysis.data_loader import load_csv_directory
from analysis.session_store import SessionStore
store = SessionStore()
store.drop_all()
lf, schema, rc, fc, mem = load_csv_directory(csv_path)
store.store_dataset('ds', lf, schema=schema, metadata={'row_count': rc, 'csv_glob': csv_path})
# 检查旧经纬度列是否被丢弃
dropped_lat = not any('latitude' in c.lower() or 'longitude' in c.lower() for c in schema)
check('丢弃旧经纬度列', dropped_lat, f'columns={len(schema)}')
# 检查 HEX 列是否被预处理
schema_names = list(schema.keys())
check('schema 有效', len(schema_names) > 10, f'{len(schema_names)} columns')
# ── 3. 数据校验 ────────────────────────────────────────────
print('\n[3/7] 数据校验')
from analysis.data_validator import validate
v_result = validate('ds')
n_risks = len(v_result.get('risks', []))
check('数据校验完成', v_result.get('valid') is not None, f'valid={v_result["valid"]} risks={n_risks}')
# ── 4. 实体检测 ────────────────────────────────────────────
print('\n[4/7] 实体检测')
from analysis.entity_detector import detect_entity_column
detect_result = detect_entity_column('ds')
entity_col = detect_result.get('recommended', '')
n_candidates = len(detect_result.get('candidates', []))
check('实体检测', bool(entity_col), f'entity={entity_col} candidates={n_candidates}')
# ── 5. 类型分类 + 聚合 ─────────────────────────────────────
print('\n[5/7] 类型感知聚合')
from analysis.type_classifier import classify_schema, DataType
from analysis.entity_aggregator import aggregate_by_entity
# 类型分类
type_map = classify_schema(lf.head(500))
types_found = set(t.name for t in type_map.values())
check('类型分类', len(type_map) > 0, f'types={types_found}')
# 聚合
agg_lf, features = aggregate_by_entity(lf, entity_col, schema)
df_agg = agg_lf.collect()
entity_count = len(df_agg)
store.store_dataset('entity', agg_lf, schema=dict(zip(features, ['']*len(features))),
metadata={'row_count': entity_count, 'entity_column': entity_col})
check('实体聚合', entity_count > 0, f'{entity_count} entities, {len(features)} features')
# 检查 GeoIP 和类型特征
check('有特征列', len(features) > 5, f'features={features[:8]}')
# ── 6. 聚类 + 特征提取 ────────────────────────────────────
print('\n[6/7] 聚类 + 特征提取')
import asyncio
from analysis.tool_registry import _handle_run_clustering, _handle_extract_features
feature_cols = [c for c in features if not c.startswith('_')][:10]
clust_result = asyncio.run(_handle_run_clustering(
dataset_id='entity', cluster_columns=feature_cols,
algorithm='hdbscan', params={}, random_state=42
))
n_clusters = clust_result.get('n_clusters', 0)
check('HDBSCAN 聚类', n_clusters >= 0, f'n_clusters={n_clusters}')
if 'error' not in clust_result:
quality = clust_result.get('quality_metrics', {})
check('聚类质量', quality.get('silhouette_score') is not None, f'silhouette={quality.get("silhouette_score")}')
cid = clust_result.get('cluster_result_id', '')
if cid and 'error' not in clust_result:
feat_result = asyncio.run(_handle_extract_features(
dataset_id='entity', cluster_result_id=cid,
top_k=10, method='zscore', save_to_db=True
))
n_feats = feat_result.get('n_features', 0)
check('特征提取 + DB持久化', feat_result.get('db_saved'), f'{n_feats} features')
# ── 7. 验证 ORM 数据 ──────────────────────────────────────
print('\n[7/7] ORM 数据验证')
from analysis.models import ClusterResult, ClusterFeature, EntityProfile
n_clusters_db = ClusterResult.objects.count()
n_features_db = ClusterFeature.objects.count()
n_entities_db = EntityProfile.objects.count()
check('ClusterResult 表', n_clusters_db > 0, f'{n_clusters_db} records')
check('ClusterFeature 表', n_features_db > 0, f'{n_features_db} records')
check('EntityProfile 表', n_entities_db >= 0, f'{n_entities_db} records')
# ── 结论 ──────────────────────────────────────────────────
print('\n' + '=' * 60)
passed = sum(1 for _, ok in results if ok)
total = len(results)
print(f'\n结果: {passed}/{total} 通过')
print('=' * 60)