"""天璇完整流程端到端测试""" 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)