Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
120 lines
4.0 KiB
Python
120 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
完整报告生成脚本:Excel → 赋分 → 统计 → LLM并行生成 → HTML报告
|
|
"""
|
|
import sys
|
|
import time
|
|
import argparse
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "backend"))
|
|
|
|
from app.engines.data_engine import DataEngine
|
|
from app.engines.scoring_engine import ScoringEngine
|
|
from app.engines.stats_engine import StatsEngine
|
|
from app.engines.llm_engine import LLMEngine
|
|
from app.engines.report_renderer import ReportRenderer
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s [%(levelname)s] %(message)s",
|
|
datefmt="%H:%M:%S",
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def progress_callback(completed, total, segment_id):
|
|
"""进度回调"""
|
|
pct = completed / total * 100
|
|
bar = "█" * int(pct / 5) + "░" * (20 - int(pct / 5))
|
|
print(f"\r [{bar}] {pct:.0f}% ({completed}/{total}) {segment_id:<40}", end="", flush=True)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="生成学校课程实施监测报告")
|
|
parser.add_argument("--school", type=str, default="延安中学", help="学校名称")
|
|
parser.add_argument("--no-cache", action="store_true", help="不使用LLM缓存")
|
|
parser.add_argument("--no-llm", action="store_true", help="跳过LLM生成(仅图表+数据)")
|
|
parser.add_argument("--list-schools", action="store_true", help="列出所有可用学校")
|
|
args = parser.parse_args()
|
|
|
|
start_time = time.time()
|
|
|
|
# ===== Step 1: 加载数据 =====
|
|
print("=" * 70)
|
|
print("📊 课程实施监测报告生成系统")
|
|
print("=" * 70)
|
|
|
|
logger.info("[1/5] 加载Excel数据...")
|
|
data_engine = DataEngine()
|
|
data_engine.load_all()
|
|
|
|
if args.list_schools:
|
|
print("\n可用学校:")
|
|
for s in data_engine.schools:
|
|
from app.config import SCHOOL_TYPE_MAP
|
|
info = SCHOOL_TYPE_MAP.get(s, {})
|
|
print(f" {s} ({info.get('type', '')})")
|
|
return
|
|
|
|
school = args.school
|
|
if school not in data_engine.schools:
|
|
print(f"\n❌ 学校 '{school}' 不在数据中。可用学校:")
|
|
for s in data_engine.schools:
|
|
print(f" - {s}")
|
|
return
|
|
|
|
print(f"\n🏫 目标学校: {school}")
|
|
|
|
# ===== Step 2: 赋分 =====
|
|
logger.info("[2/5] 全区赋分计算...")
|
|
scoring_engine = ScoringEngine(data_engine)
|
|
raw_scores = scoring_engine.score_all_schools()
|
|
|
|
# ===== Step 3: 统计分析 =====
|
|
logger.info("[3/5] PCA合成 + 标准化 + 统计分析...")
|
|
stats_engine = StatsEngine()
|
|
sub_scores = stats_engine.compute_dimension_scores(raw_scores)
|
|
dim_scores = stats_engine.compute_dimension_aggregates(sub_scores)
|
|
report_data = stats_engine.compute_school_report_data(school, sub_scores, dim_scores)
|
|
|
|
# ===== Step 4: LLM生成 =====
|
|
if args.no_llm:
|
|
logger.info("[4/5] 跳过LLM生成(--no-llm模式)")
|
|
llm_sections = {}
|
|
else:
|
|
logger.info("[4/5] LLM并行生成报告文字...")
|
|
llm_engine = LLMEngine()
|
|
llm_engine.set_progress_callback(progress_callback)
|
|
llm_sections = llm_engine.generate_report_segments(
|
|
report_data,
|
|
use_cache=not args.no_cache,
|
|
)
|
|
print() # 换行
|
|
|
|
# ===== Step 5: 渲染HTML =====
|
|
logger.info("[5/5] 渲染HTML报告...")
|
|
renderer = ReportRenderer()
|
|
|
|
output_dir = Path(__file__).parent.parent / "output"
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
output_path = output_dir / f"{school}_报告.html"
|
|
|
|
renderer.render_to_file(report_data, llm_sections, output_path)
|
|
|
|
elapsed = time.time() - start_time
|
|
|
|
print(f"\n{'=' * 70}")
|
|
print(f"✅ 报告生成完成!")
|
|
print(f" 学校: {school}")
|
|
print(f" 总体得分: {report_data['overall']['score']}分 (区内第{report_data['overall']['rank_in_district']}名)")
|
|
print(f" LLM段落: {len(llm_sections)}个")
|
|
print(f" 耗时: {elapsed:.1f}秒")
|
|
print(f" 输出: {output_path}")
|
|
print(f"{'=' * 70}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|