#!/usr/bin/env python3
"""
Tab5内容生成器 - 实质与形式的双向支撑关系图
展示:选题点(来自实质) ← 实质点 → 形式点 → 选题点(来自形式)
"""
import html as html_module
import json
from typing import Dict, Any, List
def get_intent_support_data(element: Dict[str, Any]) -> Dict[str, Any]:
"""
获取元素的意图支撑数据(兼容新旧数据结构)
优先使用"意图支撑"字段,如果没有则使用"多维度评分"字段
这两个字段内部存储的都是意图支撑数据
Args:
element: 元素数据
Returns:
意图支撑数据字典,格式:{"灵感点": [...], "目的点": [...], "关键点": [...]}
"""
# 优先使用"意图支撑"字段
intent_support = element.get('意图支撑')
if intent_support and isinstance(intent_support, dict):
return intent_support
# 如果没有"意图支撑",则使用"多维度评分"字段(兼容旧数据)
multi_scores = element.get('多维度评分')
if multi_scores and isinstance(multi_scores, dict):
return multi_scores
# 都没有则返回空字典
return {}
def generate_tab5_content(data: Dict[str, Any]) -> str:
"""生成Tab5内容:实质与形式的双向支撑关系图(4列布局)"""
html = '
\n'
# 提取数据
script_data = data.get('脚本理解', {})
form_list = script_data.get('形式列表', [])
substance_list = script_data.get('实质列表', [])
# 处理灵感点:可能是列表,也可能在对象中
inspiration_data = data.get('灵感点', [])
if isinstance(inspiration_data, list):
inspiration_points = inspiration_data
elif isinstance(inspiration_data, dict):
inspiration_points = inspiration_data.get('inspiration_points', [])
else:
inspiration_points = []
# 处理目的点:可能是列表,也可能在对象的purposes字段中
purpose_data = data.get('目的点', [])
if isinstance(purpose_data, list):
purpose_points = purpose_data
elif isinstance(purpose_data, dict):
purpose_points = purpose_data.get('purposes', [])
else:
purpose_points = []
# 处理关键点:可能是列表,也可能在对象的key_points字段中
keypoint_data = data.get('关键点', [])
if isinstance(keypoint_data, list):
key_points = keypoint_data
elif isinstance(keypoint_data, dict):
key_points = keypoint_data.get('key_points', [])
else:
key_points = []
if not substance_list and not form_list:
html += '
暂无实质点和形式点数据
\n'
html += '
\n'
return html
# 分类实质点
concrete_elements = []
concrete_concepts = []
implicit_concepts = []
abstract_concepts = []
for substance in substance_list:
dimension_2 = substance.get('维度', {}).get('二级', '')
elem_type = substance.get('类型', '')
# 隐含概念:优先通过类型判断(因为维度二级可能是"隐含概念"或"抽象概念")
if elem_type == '隐含概念':
implicit_concepts.append(substance)
elif dimension_2 == '具体元素':
concrete_elements.append(substance)
elif dimension_2 == '具象概念': # 修改:从"具体概念"改为"具象概念"
concrete_concepts.append(substance)
elif dimension_2 == '抽象概念':
abstract_concepts.append(substance)
# 分类形式点
concrete_element_forms = []
concrete_concept_forms = []
overall_forms = []
for form in form_list:
dimension_2 = form.get('维度', {}).get('二级', '')
if dimension_2 == '具体元素形式':
concrete_element_forms.append(form)
elif dimension_2 == '具象概念形式': # 修改:从"具体概念形式"改为"具象概念形式"
concrete_concept_forms.append(form)
elif dimension_2 == '整体形式':
overall_forms.append(form)
# 构建关系数据
relationships = build_bidirectional_relationships(
concrete_elements, concrete_concepts, implicit_concepts, abstract_concepts,
concrete_element_forms, concrete_concept_forms, overall_forms,
inspiration_points, purpose_points, key_points
)
# 添加标题和说明
html += '\n'
# SVG 连线容器(放在4列布局之前,作为背景层)
html += '\n'
# 第1列:左侧选题点(来自实质点的支撑关系)
html += '
\n'
html += '
选题点
(实质支撑)
\n'
# 灵感点(左)
if inspiration_points:
html += '
\n'
html += '
灵感点
\n'
html += '
\n'
for idx, point in enumerate(inspiration_points, 1):
# 确保 point 是字典
if not isinstance(point, dict):
continue
# 使用提取的特征中的特征名称,每个特征名称一个卡片
features = point.get('提取的特征', [])
feature_names = [f.get('特征名称', '') for f in features if f.get('特征名称')]
if feature_names:
# 为每个特征名称创建一个独立ID的卡片
for feature_idx, feature_name in enumerate(feature_names, 1):
feature_id = f'inspiration-{idx}-{feature_idx}'
html += f'
\n'
html += f'
#{idx}-{feature_idx}
\n'
html += f'
{html_module.escape(feature_name)}
\n'
html += '
\n'
else:
# 如果没有特征,使用原始的灵感点文本
display_text = point.get('灵感点', '')
html += f'
\n'
html += f'
#{idx}
\n'
html += f'
{html_module.escape(display_text)}
\n'
html += '
\n'
html += '
\n'
html += '
\n'
# 关键点(左)
if key_points:
html += '
\n'
html += '
关键点
\n'
html += '
\n'
for idx, point in enumerate(key_points, 1):
# 确保 point 是字典
if not isinstance(point, dict):
continue
# 使用提取的特征中的特征名称,每个特征名称一个卡片
features = point.get('提取的特征', [])
feature_names = [f.get('特征名称', '') for f in features if f.get('特征名称')]
if feature_names:
# 为每个特征名称创建一个独立ID的卡片
for feature_idx, feature_name in enumerate(feature_names, 1):
feature_id = f'keypoint-{idx}-{feature_idx}'
html += f'
\n'
html += f'
#{idx}-{feature_idx}
\n'
html += f'
{html_module.escape(feature_name)}
\n'
html += '
\n'
else:
# 如果没有特征,使用原始的关键点文本
display_text = point.get('关键点', '')
html += f'
\n'
html += f'
#{idx}
\n'
html += f'
{html_module.escape(display_text)}
\n'
html += '
\n'
html += '
\n'
html += '
\n'
# 目的点(左)
if purpose_points:
html += '
\n'
html += '
目的点
\n'
html += '
\n'
for idx, point in enumerate(purpose_points, 1):
# 确保 point 是字典
if not isinstance(point, dict):
continue
# 使用提取的特征中的特征名称,每个特征名称一个卡片
features = point.get('提取的特征', [])
feature_names = [f.get('特征名称', '') for f in features if f.get('特征名称')]
if feature_names:
# 为每个特征名称创建一个独立ID的卡片
for feature_idx, feature_name in enumerate(feature_names, 1):
feature_id = f'purpose-{idx}-{feature_idx}'
html += f'
\n'
html += f'
#{idx}-{feature_idx}
\n'
html += f'
{html_module.escape(feature_name)}
\n'
html += '
\n'
else:
# 如果没有特征,使用原始的目的点文本
display_text = point.get('目的点', '')
html += f'
\n'
html += f'
#{idx}
\n'
html += f'
{html_module.escape(display_text)}
\n'
html += '
\n'
html += '
\n'
html += '
\n'
html += '
\n'
# 第2列:实质点
html += '
\n'
html += '
实质点
\n'
# 具体元素
if concrete_elements:
html += '
\n'
html += '
具体元素
\n'
html += '
\n'
for substance in concrete_elements:
html += render_substance_card(substance, 'concrete-element')
html += '
\n'
html += '
\n'
# 具象概念
if concrete_concepts:
html += '
\n'
html += '
具象概念
\n'
html += '
\n'
for substance in concrete_concepts:
html += render_substance_card(substance, 'concrete-concept')
html += '
\n'
html += '
\n'
# 隐含概念
if implicit_concepts:
html += '
\n'
html += '
隐含概念
\n'
html += '
\n'
for substance in implicit_concepts:
html += render_substance_card(substance, 'implicit-concept')
html += '
\n'
html += '
\n'
# 抽象概念
if abstract_concepts:
html += '
\n'
html += '
抽象概念
\n'
html += '
\n'
for substance in abstract_concepts:
html += render_substance_card(substance, 'abstract-concept')
html += '
\n'
html += '
\n'
html += '
\n'
# 第3列:形式点
html += '
\n'
# 第4列:右侧选题点(来自形式点的支撑关系)
html += '
\n'
html += '
选题点
(形式支撑)
\n'
# 灵感点(右)
if inspiration_points:
html += '
\n'
html += '
灵感点
\n'
html += '
\n'
for idx, point in enumerate(inspiration_points, 1):
# 确保 point 是字典
if not isinstance(point, dict):
continue
# 使用提取的特征中的特征名称,每个特征名称一个卡片
features = point.get('提取的特征', [])
feature_names = [f.get('特征名称', '') for f in features if f.get('特征名称')]
if feature_names:
# 为每个特征名称创建一个独立ID的卡片
for feature_idx, feature_name in enumerate(feature_names, 1):
feature_id = f'inspiration-{idx}-{feature_idx}'
html += f'
\n'
html += f'
#{idx}-{feature_idx}
\n'
html += f'
{html_module.escape(feature_name)}
\n'
html += '
\n'
else:
# 如果没有特征,使用原始的灵感点文本
display_text = point.get('灵感点', '')
html += f'
\n'
html += f'
#{idx}
\n'
html += f'
{html_module.escape(display_text)}
\n'
html += '
\n'
html += '
\n'
html += '
\n'
# 关键点(右)
if key_points:
html += '
\n'
html += '
关键点
\n'
html += '
\n'
for idx, point in enumerate(key_points, 1):
# 确保 point 是字典
if not isinstance(point, dict):
continue
# 使用提取的特征中的特征名称,每个特征名称一个卡片
features = point.get('提取的特征', [])
feature_names = [f.get('特征名称', '') for f in features if f.get('特征名称')]
if feature_names:
# 为每个特征名称创建一个独立ID的卡片
for feature_idx, feature_name in enumerate(feature_names, 1):
feature_id = f'keypoint-{idx}-{feature_idx}'
html += f'
\n'
html += f'
#{idx}-{feature_idx}
\n'
html += f'
{html_module.escape(feature_name)}
\n'
html += '
\n'
else:
# 如果没有特征,使用原始的关键点文本
display_text = point.get('关键点', '')
html += f'
\n'
html += f'
#{idx}
\n'
html += f'
{html_module.escape(display_text)}
\n'
html += '
\n'
html += '
\n'
html += '
\n'
# 目的点(右)
if purpose_points:
html += '
\n'
html += '
目的点
\n'
html += '
\n'
for idx, point in enumerate(purpose_points, 1):
# 确保 point 是字典
if not isinstance(point, dict):
continue
# 使用提取的特征中的特征名称,每个特征名称一个卡片
features = point.get('提取的特征', [])
feature_names = [f.get('特征名称', '') for f in features if f.get('特征名称')]
if feature_names:
# 为每个特征名称创建一个独立ID的卡片
for feature_idx, feature_name in enumerate(feature_names, 1):
feature_id = f'purpose-{idx}-{feature_idx}'
html += f'
\n'
html += f'
#{idx}-{feature_idx}
\n'
html += f'
{html_module.escape(feature_name)}
\n'
html += '
\n'
else:
# 如果没有特征,使用原始的目的点文本
display_text = point.get('目的点', '')
html += f'
\n'
html += f'
#{idx}
\n'
html += f'
{html_module.escape(display_text)}
\n'
html += '
\n'
html += '
\n'
html += '
\n'
html += '
\n'
html += '
\n'
# 嵌入关系数据
html += '\n'
html += '\n'
return html
def render_substance_card(substance: Dict[str, Any], css_class: str) -> str:
"""渲染实质点卡片"""
substance_id = substance.get('id', '')
substance_name = substance.get('名称', '')
description = substance.get('描述', '')
html = f'\n'
html += f'\n'
if description:
html += f'
{html_module.escape(description[:50])}{"..." if len(description) > 50 else ""}
\n'
html += '
\n'
return html
def render_form_card(form: Dict[str, Any], css_class: str) -> str:
"""渲染形式点卡片"""
form_id = form.get('id', '')
form_name = form.get('名称', '')
description = form.get('描述', '')
weight_score = form.get('权重分')
html = f'