소스 검색

refactor: 移除旧版可视化脚本,仅保留v2版本

删除 visualize_how_results.py (v1),保留功能更完善的 v2 版本

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
yangxiaohui 2 주 전
부모
커밋
84f80b2ebf
1개의 변경된 파일0개의 추가작업 그리고 573개의 파일을 삭제
  1. 0 573
      script/data_processing/visualize_how_results.py

+ 0 - 573
script/data_processing/visualize_how_results.py

@@ -1,573 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-How解构结果可视化脚本
-
-将 how 解构结果转化为 HTML 格式,使用标签页展示多个帖子
-"""
-
-import json
-from pathlib import Path
-from typing import Dict, List
-import sys
-
-# 添加项目根目录到路径
-project_root = Path(__file__).parent.parent.parent
-sys.path.insert(0, str(project_root))
-
-
-def get_relation_color(relation: str) -> str:
-    """根据关系类型返回对应的颜色"""
-    color_map = {
-        "same": "#10b981",           # 绿色 - 同义
-        "contains": "#3b82f6",       # 蓝色 - 包含
-        "contained_by": "#8b5cf6",   # 紫色 - 被包含
-        "coordinate": "#f59e0b",     # 橙色 - 同级
-        "overlap": "#ec4899",        # 粉色 - 部分重叠
-        "related": "#6366f1",        # 靛蓝 - 相关
-        "unrelated": "#9ca3af"       # 灰色 - 无关
-    }
-    return color_map.get(relation, "#9ca3af")
-
-
-def get_relation_label(relation: str) -> str:
-    """返回关系类型的中文标签"""
-    label_map = {
-        "same": "同义",
-        "contains": "包含",
-        "contained_by": "被包含",
-        "coordinate": "同级",
-        "overlap": "部分重叠",
-        "related": "相关",
-        "unrelated": "无关"
-    }
-    return label_map.get(relation, relation)
-
-
-def generate_match_item_html(match: Dict) -> str:
-    """生成单个匹配项的HTML"""
-    persona_name = match.get("人设特征名称", "")
-    match_result = match.get("匹配结果", {})
-    relation = match_result.get("relation", "unrelated")
-    score = match_result.get("score", 0.0)
-    explanation = match_result.get("explanation", "")
-
-    color = get_relation_color(relation)
-    label = get_relation_label(relation)
-
-    # 根据分数设置背景透明度
-    opacity = min(score, 1.0)
-    bg_color = f"rgba({int(color[1:3], 16)}, {int(color[3:5], 16)}, {int(color[5:7], 16)}, {opacity * 0.15})"
-
-    html = f"""
-        <div class="match-item" style="border-left: 3px solid {color}; background: {bg_color};">
-            <div class="match-header">
-                <span class="persona-name">{persona_name}</span>
-                <span class="relation-badge" style="background: {color};">{label}</span>
-                <span class="score-badge">分数: {score:.2f}</span>
-            </div>
-            <div class="match-explanation">{explanation}</div>
-        </div>
-    """
-    return html
-
-
-def generate_feature_html(feature_data: Dict) -> str:
-    """生成单个特征的HTML"""
-    feature_name = feature_data.get("特征名称", "")
-    match_results = feature_data.get("匹配结果", [])
-
-    # 按分数排序(从高到低)
-    sorted_matches = sorted(match_results, key=lambda x: x.get("匹配结果", {}).get("score", 0), reverse=True)
-
-    # 统计匹配类型
-    relation_counts = {}
-    for match in match_results:
-        relation = match.get("匹配结果", {}).get("relation", "unrelated")
-        relation_counts[relation] = relation_counts.get(relation, 0) + 1
-
-    # 生成统计信息
-    stats_html = "<div class='relation-stats'>"
-    for relation, count in sorted(relation_counts.items(), key=lambda x: x[1], reverse=True):
-        label = get_relation_label(relation)
-        color = get_relation_color(relation)
-        stats_html += f"<span class='stat-item' style='color: {color};'>{label}: {count}</span>"
-    stats_html += "</div>"
-
-    matches_html = "".join(generate_match_item_html(match) for match in sorted_matches)
-
-    html = f"""
-        <div class="feature-section">
-            <div class="feature-header">
-                <h3>特征: {feature_name}</h3>
-                {stats_html}
-            </div>
-            <div class="matches-container">
-                {matches_html}
-            </div>
-        </div>
-    """
-    return html
-
-
-def generate_inspiration_point_html(point_data: Dict) -> str:
-    """生成单个灵感点的HTML"""
-    name = point_data.get("名称", "")
-    desc = point_data.get("描述", "")
-    how_steps = point_data.get("how步骤列表", [])
-
-    steps_html = ""
-    for step in how_steps:
-        step_name = step.get("步骤名称", "")
-        features = step.get("特征列表", [])
-
-        features_html = "".join(generate_feature_html(f) for f in features)
-
-        steps_html += f"""
-            <div class="step-section">
-                <h4 class="step-name">{step_name}</h4>
-                {features_html}
-            </div>
-        """
-
-    html = f"""
-        <div class="inspiration-point">
-            <div class="point-header">
-                <h2>{name}</h2>
-            </div>
-            <div class="point-description">{desc}</div>
-            {steps_html}
-        </div>
-    """
-    return html
-
-
-def generate_post_content_html(post_data: Dict) -> str:
-    """生成单个帖子的内容HTML(不包含完整页面结构)"""
-    post_id = post_data.get("帖子id", "")
-    post_detail = post_data.get("帖子详情", {})
-    publish_time = post_detail.get("publish_time", "")
-    like_count = post_detail.get("like_count", 0)
-    link = post_detail.get("link", "")
-
-    how_result = post_data.get("how解构结果", {})
-    inspiration_list = how_result.get("灵感点列表", [])
-
-    inspiration_html = "".join(generate_inspiration_point_html(p) for p in inspiration_list)
-
-    html = f"""
-        <div class="post-meta-bar">
-            <div class="meta-item">
-                <span class="meta-label">帖子ID:</span>
-                <span class="meta-value">{post_id}</span>
-            </div>
-            <div class="meta-item">
-                <span class="meta-label">发布时间:</span>
-                <span class="meta-value">{publish_time}</span>
-            </div>
-            <div class="meta-item">
-                <span class="meta-label">点赞数:</span>
-                <span class="meta-value">{like_count}</span>
-            </div>
-            <div class="meta-item">
-                <a href="{link}" target="_blank" class="view-link">查看原帖 →</a>
-            </div>
-        </div>
-        {inspiration_html}
-    """
-    return html
-
-
-def generate_combined_html(posts_data: List[Dict]) -> str:
-    """生成包含所有帖子的单一HTML(带标签页)"""
-
-    # 生成标签页按钮
-    tabs_html = ""
-    for i, post in enumerate(posts_data):
-        post_detail = post.get("帖子详情", {})
-        title = post_detail.get("title", "无标题")
-        active_class = "active" if i == 0 else ""
-        tabs_html += f'<button class="tab-button {active_class}" onclick="openTab(event, \'post-{i}\')">{title}</button>\n'
-
-    # 生成标签页内容
-    contents_html = ""
-    for i, post in enumerate(posts_data):
-        active_class = "active" if i == 0 else ""
-        content = generate_post_content_html(post)
-        contents_html += f"""
-        <div id="post-{i}" class="tab-content {active_class}">
-            {content}
-        </div>
-        """
-
-    html = f"""
-    <!DOCTYPE html>
-    <html lang="zh-CN">
-    <head>
-        <meta charset="UTF-8">
-        <meta name="viewport" content="width=device-width, initial-scale=1.0">
-        <title>How解构结果可视化</title>
-        <style>
-            * {{
-                margin: 0;
-                padding: 0;
-                box-sizing: border-box;
-            }}
-
-            body {{
-                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
-                background: #f5f5f5;
-                color: #333;
-                line-height: 1.6;
-            }}
-
-            .container {{
-                max-width: 1400px;
-                margin: 0 auto;
-                background: white;
-                min-height: 100vh;
-                box-shadow: 0 0 40px rgba(0,0,0,0.1);
-            }}
-
-            .header {{
-                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-                color: white;
-                padding: 30px;
-                text-align: center;
-            }}
-
-            .header h1 {{
-                font-size: 32px;
-                font-weight: bold;
-                margin-bottom: 10px;
-            }}
-
-            .header p {{
-                font-size: 16px;
-                opacity: 0.9;
-            }}
-
-            .tabs-container {{
-                display: flex;
-                background: #f9fafb;
-                border-bottom: 2px solid #e5e7eb;
-                overflow-x: auto;
-                position: sticky;
-                top: 0;
-                z-index: 100;
-            }}
-
-            .tab-button {{
-                flex: 1;
-                min-width: 200px;
-                padding: 18px 30px;
-                background: transparent;
-                border: none;
-                border-bottom: 3px solid transparent;
-                cursor: pointer;
-                font-size: 15px;
-                font-weight: 500;
-                color: #6b7280;
-                transition: all 0.3s;
-                white-space: nowrap;
-                overflow: hidden;
-                text-overflow: ellipsis;
-            }}
-
-            .tab-button:hover {{
-                background: #f3f4f6;
-                color: #374151;
-            }}
-
-            .tab-button.active {{
-                color: #667eea;
-                border-bottom-color: #667eea;
-                background: white;
-            }}
-
-            .tab-content {{
-                display: none;
-                padding: 30px;
-                animation: fadeIn 0.3s;
-            }}
-
-            .tab-content.active {{
-                display: block;
-            }}
-
-            @keyframes fadeIn {{
-                from {{ opacity: 0; transform: translateY(10px); }}
-                to {{ opacity: 1; transform: translateY(0); }}
-            }}
-
-            .post-meta-bar {{
-                display: flex;
-                flex-wrap: wrap;
-                gap: 25px;
-                padding: 20px;
-                background: #f9fafb;
-                border-radius: 8px;
-                margin-bottom: 30px;
-                border: 1px solid #e5e7eb;
-            }}
-
-            .meta-item {{
-                display: flex;
-                align-items: center;
-                gap: 8px;
-            }}
-
-            .meta-label {{
-                font-weight: 600;
-                color: #6b7280;
-                font-size: 14px;
-            }}
-
-            .meta-value {{
-                color: #111827;
-                font-size: 14px;
-            }}
-
-            .view-link {{
-                color: #667eea;
-                text-decoration: none;
-                font-weight: 600;
-                font-size: 14px;
-                transition: color 0.2s;
-            }}
-
-            .view-link:hover {{
-                color: #764ba2;
-            }}
-
-            .inspiration-point {{
-                margin-bottom: 40px;
-                border: 1px solid #e5e7eb;
-                border-radius: 8px;
-                overflow: hidden;
-            }}
-
-            .point-header {{
-                background: #f9fafb;
-                padding: 20px;
-                border-bottom: 2px solid #e5e7eb;
-            }}
-
-            .point-header h2 {{
-                font-size: 22px;
-                color: #1f2937;
-            }}
-
-            .point-description {{
-                padding: 20px;
-                background: #fefefe;
-                font-size: 15px;
-                color: #4b5563;
-                line-height: 1.8;
-                border-bottom: 1px solid #e5e7eb;
-            }}
-
-            .step-section {{
-                padding: 20px;
-            }}
-
-            .step-name {{
-                font-size: 18px;
-                color: #374151;
-                margin-bottom: 20px;
-                padding-bottom: 10px;
-                border-bottom: 2px solid #e5e7eb;
-            }}
-
-            .feature-section {{
-                margin-bottom: 30px;
-            }}
-
-            .feature-header {{
-                display: flex;
-                justify-content: space-between;
-                align-items: center;
-                margin-bottom: 15px;
-                padding: 15px;
-                background: #f3f4f6;
-                border-radius: 6px;
-            }}
-
-            .feature-header h3 {{
-                font-size: 18px;
-                color: #111827;
-            }}
-
-            .relation-stats {{
-                display: flex;
-                gap: 15px;
-                font-size: 13px;
-            }}
-
-            .stat-item {{
-                font-weight: 600;
-            }}
-
-            .matches-container {{
-                display: grid;
-                gap: 10px;
-            }}
-
-            .match-item {{
-                padding: 15px;
-                border-radius: 6px;
-                transition: transform 0.2s;
-            }}
-
-            .match-item:hover {{
-                transform: translateX(5px);
-            }}
-
-            .match-header {{
-                display: flex;
-                align-items: center;
-                gap: 10px;
-                margin-bottom: 8px;
-            }}
-
-            .persona-name {{
-                font-weight: 600;
-                font-size: 15px;
-                color: #111827;
-            }}
-
-            .relation-badge {{
-                padding: 3px 10px;
-                border-radius: 12px;
-                color: white;
-                font-size: 12px;
-                font-weight: 600;
-            }}
-
-            .score-badge {{
-                padding: 3px 10px;
-                border-radius: 12px;
-                background: #e5e7eb;
-                color: #374151;
-                font-size: 12px;
-                font-weight: 600;
-            }}
-
-            .match-explanation {{
-                font-size: 14px;
-                color: #6b7280;
-                line-height: 1.6;
-            }}
-
-            @media (max-width: 768px) {{
-                .header {{
-                    padding: 20px;
-                }}
-
-                .header h1 {{
-                    font-size: 24px;
-                }}
-
-                .tab-button {{
-                    min-width: 150px;
-                    padding: 15px 20px;
-                    font-size: 14px;
-                }}
-
-                .tab-content {{
-                    padding: 15px;
-                }}
-
-                .post-meta-bar {{
-                    flex-direction: column;
-                    gap: 10px;
-                }}
-
-                .feature-header {{
-                    flex-direction: column;
-                    align-items: flex-start;
-                    gap: 10px;
-                }}
-
-                .relation-stats {{
-                    flex-wrap: wrap;
-                }}
-            }}
-        </style>
-    </head>
-    <body>
-        <div class="container">
-            <div class="header">
-                <h1>How 解构结果可视化</h1>
-                <p>灵感点特征匹配分析</p>
-            </div>
-            <div class="tabs-container">
-                {tabs_html}
-            </div>
-            {contents_html}
-        </div>
-
-        <script>
-            function openTab(evt, tabId) {{
-                // 隐藏所有标签页内容
-                var tabContents = document.getElementsByClassName("tab-content");
-                for (var i = 0; i < tabContents.length; i++) {{
-                    tabContents[i].classList.remove("active");
-                }}
-
-                // 移除所有按钮的 active 类
-                var tabButtons = document.getElementsByClassName("tab-button");
-                for (var i = 0; i < tabButtons.length; i++) {{
-                    tabButtons[i].classList.remove("active");
-                }}
-
-                // 显示当前标签页并添加 active 类
-                document.getElementById(tabId).classList.add("active");
-                evt.currentTarget.classList.add("active");
-            }}
-        </script>
-    </body>
-    </html>
-    """
-    return html
-
-
-def main():
-    """主函数"""
-    # 输入输出路径
-    script_dir = Path(__file__).parent
-    project_root = script_dir.parent.parent
-    data_dir = project_root / "data" / "data_1117"
-
-    input_dir = data_dir / "当前帖子_how解构结果"
-    output_file = data_dir / "当前帖子_how解构结果_可视化.html"
-
-    print(f"读取 how 解构结果: {input_dir}")
-
-    # 获取所有 JSON 文件
-    json_files = list(input_dir.glob("*_how.json"))
-    print(f"找到 {len(json_files)} 个文件\n")
-
-    # 读取所有帖子数据
-    posts_data = []
-    for i, file_path in enumerate(json_files, 1):
-        print(f"读取文件 [{i}/{len(json_files)}]: {file_path.name}")
-        with open(file_path, "r", encoding="utf-8") as f:
-            post_data = json.load(f)
-            posts_data.append(post_data)
-
-    # 生成合并的 HTML
-    print(f"\n生成合并的 HTML...")
-    html_content = generate_combined_html(posts_data)
-
-    # 保存 HTML 文件
-    print(f"保存到: {output_file}")
-    with open(output_file, "w", encoding="utf-8") as f:
-        f.write(html_content)
-
-    print(f"\n完成! 可视化文件已保存")
-    print(f"请在浏览器中打开: {output_file}")
-
-
-if __name__ == "__main__":
-    main()