#!/usr/bin/env python3
"""
Tab3内容生成器 - 脚本点(元素列表)
"""
import html as html_module
from typing import Dict, Any, List
def calculate_intent_support_count(element: Dict[str, Any]) -> int:
"""
计算元素的意图支撑数量
统计元素支撑的意图点总数
支撑的意图点越多,说明该元素与意图的关联越强
Args:
element: 元素数据
Returns:
支撑的意图点总数
"""
# 区分实质元素和形式元素的统计方式
dimension = element.get('维度') or {}
if isinstance(dimension, dict) and dimension.get('一级') == '形式':
# 形式元素:基于多维度评分,使用最高相似度作为"意图支撑强度"的代表
# 兼容两种结构:
# 1)新版:{"名称": "...", "相似度结果": [{"点","语义相似度","文本相似度",...}, ...]}
# 2)旧版:直接列表 [{"点","语义相似度","文本相似度",...}, ...]
multi_scores = element.get('多维度评分') or {}
best_score = 0.0
for point_type in ['灵感点', '目的点', '关键点']:
for item in multi_scores.get(point_type, []) or []:
if not isinstance(item, dict):
continue
similarity_results = item.get('相似度结果')
# 新结构:在相似度结果列表里取最大值
if similarity_results:
for sim in similarity_results or []:
if not isinstance(sim, dict):
continue
semantic = float(sim.get('语义相似度', 0) or 0)
text_sim = float(sim.get('文本相似度', 0) or 0)
best_score = max(best_score, semantic, text_sim)
else:
# 旧结构:当前item本身就带语义/文本相似度
semantic = float(item.get('语义相似度', 0) or 0)
text_sim = float(item.get('文本相似度', 0) or 0)
best_score = max(best_score, semantic, text_sim)
return best_score
else:
# 实质元素:按意图支撑的数量统计
intent_support = element.get('意图支撑') or {}
total_support_count = 0
for point_type in ['灵感点', '目的点', '关键点']:
if point_type in intent_support and intent_support[point_type]:
total_support_count += len(intent_support[point_type])
return total_support_count
def determine_dominant_factor(element: Dict[str, Any], all_elements: List[Dict[str, Any]]) -> str:
"""
判断元素排序的主导因素
排序规则:覆盖率 > 频次 > 意图支撑数
主导因素判断:哪个指标在当前元素中相对最显著
Args:
element: 当前元素
all_elements: 同组所有元素
Returns:
主导因素: 'coverage' | 'frequency' | 'intent_support'
"""
if not all_elements:
return 'coverage'
# 获取当前元素的指标
commonality = element.get('共性分析') or {}
coverage = commonality.get('段落覆盖率', 0.0)
frequency = commonality.get('出现频次', 0)
intent_count = calculate_intent_support_count(element)
# 将所有指标归一化到同一量级,然后比较
# 覆盖率已经是0-1范围
# 频次归一化:假设最大频次为10
normalized_frequency = min(frequency / 10.0, 1.0)
# 意图支撑数归一化:假设最大支撑数为10
normalized_intent = min(intent_count / 10.0, 1.0)
# 比较归一化后的值,取最大的作为主导因素
scores = {
'coverage': coverage,
'frequency': normalized_frequency,
'intent_support': normalized_intent
}
return max(scores, key=scores.get)
def sort_elements_by_coverage_and_frequency(elements: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
按照覆盖率、频次和意图支撑数对元素排序
排序规则:
1. 第一优先级:共性(段落覆盖率)- 倒序
2. 第二优先级:共性(出现频次)- 倒序
3. 第三优先级:意图支撑数 - 倒序
Args:
elements: 元素列表
Returns:
排序后的元素列表
"""
def get_sort_key(elem):
# 获取共性分析,防止为None
commonality = elem.get('共性分析') or {}
# 获取段落覆盖率
coverage = commonality.get('段落覆盖率', 0.0)
# 获取出现频次
frequency = commonality.get('出现频次', 0)
# 计算意图支撑数
intent_count = calculate_intent_support_count(elem)
# 返回排序键(负数用于倒序)
return (-coverage, -frequency, -intent_count)
return sorted(elements, key=get_sort_key)
def get_element_category(element: Dict[str, Any]) -> str:
"""
获取元素的分类名称(支持新旧两种数据结构)
Args:
element: 元素数据
Returns:
分类名称字符串
"""
category = element.get('分类', '未分类')
if isinstance(category, dict):
# 新结构:分类是对象,包含一级分类和二级分类
level1 = category.get('一级分类', '')
level2 = category.get('二级分类', '')
if level1 and level2:
return f"{level1} - {level2}"
elif level1:
return level1
else:
return '未分类'
else:
# 旧结构:分类是字符串或列表
if isinstance(category, list):
return ' - '.join(category) if category else '未分类'
return category if category else '未分类'
def group_elements_by_hierarchical_category(elements: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
按树形分类结构组织元素(一级分类 → 二级分类 → 元素)
优化规则:同一个父节点下的所有子节点采用统一的分类格式展示
- 如果一级分类下既有元素又有分类,将元素归入"未分类"二级分类
Args:
elements: 元素列表
Returns:
树形分类结构字典
"""
# 1. 按一级分类和二级分类分组
level1_groups = {}
for elem in elements:
category_data = elem.get('分类', {})
if isinstance(category_data, dict):
level1 = category_data.get('一级分类', '未分类')
level2 = category_data.get('二级分类', '')
elif isinstance(category_data, list):
# 列表格式:第一个元素作为一级分类,第二个作为二级分类
level1 = category_data[0] if len(category_data) > 0 else '未分类'
level2 = category_data[1] if len(category_data) > 1 else ''
else:
# 旧结构:分类是字符串
level1 = str(category_data) if category_data else '未分类'
level2 = ''
# 初始化一级分类
if level1 not in level1_groups:
level1_groups[level1] = {
'elements': [],
'level2_groups': {}
}
# 如果有二级分类,放入二级分类组;否则放入一级分类的直接元素列表(临时)
if level2:
if level2 not in level1_groups[level1]['level2_groups']:
level1_groups[level1]['level2_groups'][level2] = []
level1_groups[level1]['level2_groups'][level2].append(elem)
else:
level1_groups[level1]['elements'].append(elem)
# 1.5 优化:仅当一级分类下既有直接元素又有二级分类时,才将直接元素移到"未分类"二级分类中
# 如果一级分类下只有直接元素,没有二级分类,则保持原样(不需要"未分类"概念)
for level1_name, level1_data in level1_groups.items():
if level1_data['elements'] and level1_data['level2_groups']:
# 将直接元素移到"未分类"分类
if '未分类' not in level1_data['level2_groups']:
level1_data['level2_groups']['未分类'] = []
level1_data['level2_groups']['未分类'].extend(level1_data['elements'])
level1_data['elements'] = []
# 如果只有直接元素,没有二级分类,则保持level1_data['elements']不变
# 2. 对每个分类内的元素排序
for level1_data in level1_groups.values():
# 排序一级分类直接包含的元素
if level1_data['elements']:
level1_data['elements'] = sort_elements_by_coverage_and_frequency(level1_data['elements'])
# 排序每个二级分类的元素
for level2_name in level1_data['level2_groups']:
level1_data['level2_groups'][level2_name] = sort_elements_by_coverage_and_frequency(
level1_data['level2_groups'][level2_name]
)
# 3. 计算每个一级分类的统计信息用于排序
level1_scores = {}
for level1_name, level1_data in level1_groups.items():
# 收集该一级分类下的所有元素(包括二级分类下的)
all_elements = level1_data['elements'][:]
for level2_elements in level1_data['level2_groups'].values():
all_elements.extend(level2_elements)
if not all_elements:
level1_scores[level1_name] = (0.0, 0, 0.0)
continue
# 计算统计指标
avg_coverage = sum((e.get('共性分析') or {}).get('段落覆盖率', 0.0) for e in all_elements) / len(all_elements)
avg_frequency = sum((e.get('共性分析') or {}).get('出现频次', 0) for e in all_elements) / len(all_elements)
avg_intent_count = sum(calculate_intent_support_count(e) for e in all_elements) / len(all_elements)
level1_scores[level1_name] = (avg_coverage, avg_frequency, avg_intent_count)
# 4. 对一级分类排序
sorted_level1 = sorted(
level1_scores.keys(),
key=lambda c: (-level1_scores[c][0], -level1_scores[c][1], -level1_scores[c][2])
)
# 5. 对每个一级分类内的二级分类排序
for level1_name in sorted_level1:
level1_data = level1_groups[level1_name]
level2_groups = level1_data['level2_groups']
if not level2_groups:
continue
# 计算二级分类的统计信息
level2_scores = {}
for level2_name, level2_elements in level2_groups.items():
if not level2_elements:
level2_scores[level2_name] = (0.0, 0, 0.0)
continue
avg_coverage = sum((e.get('共性分析') or {}).get('段落覆盖率', 0.0) for e in level2_elements) / len(level2_elements)
avg_frequency = sum((e.get('共性分析') or {}).get('出现频次', 0) for e in level2_elements) / len(level2_elements)
avg_intent_count = sum(calculate_intent_support_count(e) for e in level2_elements) / len(level2_elements)
level2_scores[level2_name] = (avg_coverage, avg_frequency, avg_intent_count)
# 排序二级分类
sorted_level2_names = sorted(
level2_scores.keys(),
key=lambda c: (-level2_scores[c][0], -level2_scores[c][1], -level2_scores[c][2])
)
# 重新组织为有序字典
sorted_level2_groups = {name: level2_groups[name] for name in sorted_level2_names}
level1_data['level2_groups'] = sorted_level2_groups
# 6. 返回排序后的结构
return {level1_name: level1_groups[level1_name] for level1_name in sorted_level1}
def render_element_item(element: Dict[str, Any], all_elements: List[Dict[str, Any]] = None) -> str:
"""渲染单个元素项的HTML(支持详情展开,兼容新旧数据结构)
Args:
element: 元素数据
all_elements: 同组所有元素(用于计算主导因素)
"""
elem_id = element.get('id', '')
name = element.get('名称') or '' # 处理None的情况
description = element.get('描述') or '' # 处理None的情况
# 获取类型和维度(兼容新旧结构)
dimension = element.get('维度', {})
if isinstance(dimension, dict):
elem_type = dimension.get('一级', '')
elem_type_level2 = dimension.get('二级', '')
else:
elem_type = element.get('类型', '')
elem_type_level2 = ''
# 获取分类(兼容新旧结构)
category_data = element.get('分类', '')
if isinstance(category_data, dict):
category_level1 = category_data.get('一级分类', '')
category_level2 = category_data.get('二级分类', '')
category = get_element_category(element)
elif isinstance(category_data, list):
category_level1 = category_data[0] if len(category_data) > 0 else ''
category_level2 = category_data[1] if len(category_data) > 1 else ''
category = get_element_category(element)
else:
category = category_data
category_level1 = ''
category_level2 = ''
category_def = element.get('分类定义', '')
# 获取共性分析(防止为None)
commonality = element.get('共性分析') or {}
coverage = commonality.get('段落覆盖率', 0.0)
frequency = commonality.get('出现频次', 0)
paragraphs_list = commonality.get('出现段落列表', [])
source = element.get('来源', [])
intent_count = calculate_intent_support_count(element)
intent_support = element.get('意图支撑', {})
# 检查是否有详细信息
has_details = bool(elem_type or category or category_def or paragraphs_list or source or intent_support)
# 计算主导因素
dominant_factor = 'coverage' # 默认
if all_elements:
dominant_factor = determine_dominant_factor(element, all_elements)
# 根据主导因素确定边框颜色
border_color_class = f'dominant-{dominant_factor}'
html = f'
\n'
html += '
\n'
# 添加展开/收起图标
if has_details:
html += '▶\n'
# 显示ID和名称
if elem_id:
html += f'#{elem_id}\n'
html += f'{html_module.escape(name)}\n'
# 显示统计指标(根据主导因素高亮)
# 判断是否为形式元素
is_form = isinstance(dimension, dict) and dimension.get('一级') == '形式'
html += '
\n'
if is_form:
# 形式元素:显示最高相似度(基于多维度评分)
html += f'最高相似度: {intent_count:.2f}\n'
else:
# 实质元素显示全部三个指标
coverage_highlight = 'stat-highlight' if dominant_factor == 'coverage' else ''
frequency_highlight = 'stat-highlight' if dominant_factor == 'frequency' else ''
intent_highlight = 'stat-highlight' if dominant_factor == 'intent_support' else ''
html += f'覆盖率: {coverage:.2%}\n'
html += f'频次: {frequency}\n'
html += f'意图支撑: {intent_count}\n'
html += '
\n'
html += '
\n'
# 描述(始终显示)
if description:
html += f'
{html_module.escape(description)}
\n'
# 详细信息(可展开)
if has_details:
html += '
\n'
# 维度/类型 - 已移除,不再展示
# if elem_type:
# html += '
\n'
# html += '维度:\n'
# html += '
\n'
# html += f'{html_module.escape(elem_type)}\n'
# if elem_type_level2:
# html += f'{html_module.escape(elem_type_level2)}\n'
# html += '
\n'
# html += '
\n'
# 分类 - 已移除,不再展示
# if category_level1 or category:
# html += '
\n'
# html += '分类:\n'
# html += '
\n'
# if category_level1:
# html += f'{html_module.escape(category_level1)}\n'
# if category_level2:
# html += f'{html_module.escape(category_level2)}\n'
# else:
# html += f'{html_module.escape(str(category))}\n'
# html += '
\n'
# html += '
\n'
# 分类定义
if category_def:
html += '
\n'
html += '分类定义:\n'
html += f'
{html_module.escape(category_def)}
\n'
html += '
\n'
# 针对"形式"维度,显示"支撑"、"推理"和"多维度评分"
if isinstance(dimension, dict) and dimension.get('一级') == '形式':
# 支撑
zhicheng = element.get('支撑')
if zhicheng:
html += '
\n'
html += '支撑:\n'
html += '
\n'
if isinstance(zhicheng, list):
for item in zhicheng:
if isinstance(item, dict):
item_id = item.get('id', '')
item_name = item.get('名称', '')
html += f'{html_module.escape(f"{item_id}: {item_name}")}\n'
else:
html += f'{html_module.escape(str(item))}\n'
elif isinstance(zhicheng, dict):
# 支撑可能是对象(包含具体元素、具象概念等)
for key, values in zhicheng.items():
if isinstance(values, list):
html += f'
{html_module.escape(key)}
\n'
for item in values:
if isinstance(item, dict):
item_id = item.get('id', '')
item_name = item.get('名称', '')
html += f'{html_module.escape(f"{item_id}: {item_name}")}\n'
else:
html += f'{html_module.escape(str(item))}\n'
else:
html += f'{html_module.escape(str(values))}\n'
else:
html += f'{html_module.escape(str(zhicheng))}\n'
html += '
\n'
html += '
\n'
# 推理
tuili = element.get('推理')
if tuili:
html += '
\n'
html += '推理:\n'
html += f'
{html_module.escape(tuili)}
\n'
html += '
\n'
# 多维度评分(形式元素专用,简化为分类列表展示)
multi_scores = element.get('多维度评分') or {}
if multi_scores:
html += '
\n'
html += '多维度评分:\n'
for score_type in ['灵感点', '目的点', '关键点']:
score_items = multi_scores.get(score_type) or []
if not score_items:
continue
html += f'
{score_type}
\n'
html += '
\n'
for score_item in score_items:
if not isinstance(score_item, dict):
continue
# 兼容两种结构:
# 1)新结构:{"名称": "...", "相似度结果": [ {...}, ... ]}
# 2)旧结构:直接列表 [{"点","语义相似度","文本相似度",...}, ...]
similarity_results = score_item.get('相似度结果')
if similarity_results:
# 新结构:对每个相似度结果生成一条列表项
for sim in similarity_results or []:
if not isinstance(sim, dict):
continue
point = sim.get('点', '')
semantic = sim.get('语义相似度', 0)
text_sim = sim.get('文本相似度', 0)
html += '
\n'
if point:
html += f'{html_module.escape(point)}\n'
html += f'语义 {semantic:.2f}\n'
html += f'文本 {text_sim:.2f}\n'
html += '
\n'
else:
# 旧结构:当前条目本身就是一个点的评分
point = score_item.get('点', '')
semantic = score_item.get('语义相似度', 0)
text_sim = score_item.get('文本相似度', 0)
html += '
\n'
if point:
html += f'{html_module.escape(point)}\n'
html += f'语义 {semantic:.2f}\n'
html += f'文本 {text_sim:.2f}\n'
html += '
\n'
html += '
\n' # end score-list
html += '
\n' # end detail-section
# 针对"隐含概念",显示"来源"(声音特征、语气语调、BGM、音效等)和"时间范围"
elem_type = element.get('类型', '')
if elem_type == '隐含概念':
# 来源(隐含概念的来源包含声音特征、语气语调、背景音乐、音效等)
laiyuan = element.get('来源')
if laiyuan and isinstance(laiyuan, dict):
html += '
\n'
html += '来源:\n'
html += '
\n'
for key, values in laiyuan.items():
if isinstance(values, list) and values:
html += f'
{html_module.escape(key)}
\n'
for item in values:
html += f'{html_module.escape(str(item))}\n'
elif values:
html += f'{html_module.escape(str(values))}\n'
html += '
\n'
html += '
\n'
# 时间范围
time_range = element.get('时间范围')
if time_range:
html += '
\n'
html += '时间范围:\n'
html += '
\n'
if isinstance(time_range, list):
for tr in time_range:
html += f'{html_module.escape(str(tr))}\n'
else:
html += f'{html_module.escape(str(time_range))}\n'
html += '
\n'
html += '
\n'
# 针对"抽象概念"(实质-抽象概念),显示"类型"、"来源"和"推理过程"
# 注意:排除隐含概念(隐含概念有自己的显示逻辑)
elif isinstance(dimension, dict) and dimension.get('二级') == '抽象概念':
# 类型
leixing = element.get('类型')
if leixing:
html += '
\n'
html += '类型:\n'
html += f'
{html_module.escape(str(leixing))}
\n'
html += '
\n'
# 来源(抽象概念的来源可能是复杂对象)
laiyuan = element.get('来源')
if laiyuan and isinstance(laiyuan, dict):
html += '
\n'
html += '来源:\n'
html += '
\n'
for key, values in laiyuan.items():
if isinstance(values, list):
html += f'
{html_module.escape(key)}
\n'
for item in values:
if isinstance(item, dict):
item_id = item.get('id', '')
item_name = item.get('名称', '')
html += f'{html_module.escape(f"{item_id}: {item_name}")}\n'
else:
html += f'{html_module.escape(str(item))}\n'
else:
html += f'{html_module.escape(str(values))}\n'
html += '
\n'
html += '
\n'
# 推理过程
tuili_guocheng = element.get('推理过程')
if tuili_guocheng:
html += '
\n'
html += '推理过程:\n'
html += f'
{html_module.escape(tuili_guocheng)}
\n'
html += '
\n'
else:
if source:
html += '
\n'
html += '来源:\n'
html += '
\n'
for src in source:
html += f'{html_module.escape(str(src))}\n'
html += '
\n'
html += '
\n'
# 上下文验证(适用于具象概念)
context_verification = element.get('上下文验证')
if context_verification:
html += '
\n'
html += '上下文验证:\n'
html += '
\n'
# 原文位置
original_position = context_verification.get('原文位置', '')
if original_position:
html += '
\n'
html += '原文位置:\n'
html += f'
{html_module.escape(original_position)}
\n'
html += '
\n'
# 语法成分
grammar_component = context_verification.get('语法成分', '')
if grammar_component:
html += '
\n'
html += '语法成分:\n'
html += f'{html_module.escape(grammar_component)}\n'
html += '
\n'
# 语境判断
context_judgment = context_verification.get('语境判断', '')
if context_judgment:
html += '
\n'
html += '语境判断:\n'
html += f'
{html_module.escape(context_judgment)}
\n'
html += '
\n'
html += '
\n'
html += '
\n'
# 出现段落
if paragraphs_list:
html += '
\n'
html += '出现段落:\n'
html += '
\n'
for para in paragraphs_list:
if isinstance(para, dict):
# 新结构:对象包含段落ID和如何体现
para_id = para.get('段落ID', '')
how = para.get('如何体现', '')
html += '
\n'
html += f'{html_module.escape(para_id)}\n'
if how:
html += f'
{html_module.escape(how)}
\n'
html += '
\n'
else:
# 旧结构:字符串
html += f'{html_module.escape(str(para))}\n'
html += '
\n'
html += '
\n'
# 意图支撑
if intent_support:
html += '
\n'
html += '意图支撑:\n'
for point_type in ['灵感点', '目的点', '关键点']:
if point_type in intent_support and intent_support[point_type]:
html += f'
{point_type}
\n'
html += '
\n'
for item in intent_support[point_type]:
point = item.get('点', '')
point_intention = item.get('点的意图', '')
support_reason = item.get('支撑理由', '')
html += '
\n'
html += f'
{html_module.escape(point)}
\n'
# 显示点的意图
if point_intention:
html += '
\n'
html += f'点的意图:{html_module.escape(point_intention)}\n'
html += '
\n'
# 显示支撑理由
if support_reason:
html += '
\n'
html += f'支撑理由:\n'
html += f'
{html_module.escape(support_reason)}
\n'
html += '
\n'
html += '
\n'
html += '
\n'
html += '
\n'
html += '
\n'
html += '
\n'
return html
def generate_tab3_content(data: Dict[str, Any]) -> str:
"""生成Tab3内容:按层次展示(实质/形式 → 具体元素/具体概念/抽象概念 → 树形展示)"""
html = '
\n'
# 添加全局控制按钮
html += '
\n'
html += '
\n'
html += ' 颜色图例(主导因素):\n'
html += ' 覆盖率\n'
html += ' 频次\n'
html += ' 意图支撑\n'
html += '
\n'
html += ' \n'
html += ' \n'
html += '
\n'
if '脚本理解' in data:
script = data['脚本理解']
# 尝试获取元素列表,如果不存在则合并实质列表和形式列表
elements = script.get('元素列表', [])
if not elements:
substance_list = script.get('实质列表', [])
form_list = script.get('形式列表', [])
elements = substance_list + form_list
# 第一层:按维度.一级分组(实质 vs 形式)
level1_groups = {}
for elem in elements:
dimension = elem.get('维度', {})
if isinstance(dimension, dict):
level1 = dimension.get('一级', '实质')
else:
# 兼容旧结构
level1 = elem.get('类型', '实质')
if level1 not in level1_groups:
level1_groups[level1] = []
level1_groups[level1].append(elem)
# 按顺序渲染:实质、形式
for level1_name in ['实质', '形式']:
if level1_name not in level1_groups:
continue
level1_elements = level1_groups[level1_name]
html += '
\n'
html += f'
\n'
html += '▼\n'
html += f'
{level1_name} ({len(level1_elements)}个)
\n'
html += '
\n'
html += '
\n'
# 第二层:按维度.二级分组
level2_groups = {}
for elem in level1_elements:
dimension = elem.get('维度', {})
elem_type = elem.get('类型', '')
# 隐含概念:优先通过类型判断(因为维度二级可能是"隐含概念"或"抽象概念")
if elem_type == '隐含概念':
level2 = '隐含概念'
elif isinstance(dimension, dict):
level2 = dimension.get('二级', '具体元素')
else:
# 兼容旧结构
elem_type_old = elem.get('类型', '实质')
if elem_type_old == '实质':
level2 = '具体元素'
elif elem_type_old == '具象概念':
level2 = '具体概念'
else:
level2 = '抽象概念'
if level2 not in level2_groups:
level2_groups[level2] = []
level2_groups[level2].append(elem)
# 根据一级维度确定二级维度遍历顺序
if level1_name == '实质':
level2_order = ['具体元素', '具象概念', '隐含概念', '抽象概念']
else: # 形式
level2_order = ['具体元素形式', '具象概念形式', '整体形式']
# 按顺序渲染二级维度
for level2_name in level2_order:
if level2_name not in level2_groups:
continue
level2_elements = level2_groups[level2_name]
html += '
\n'
html += f'
\n'
html += '▼\n'
html += f'
{level2_name} ({len(level2_elements)}个)
\n'
html += '
\n'
html += '
\n'
# 第三层:按树形分类结构组织
hierarchical_categories = group_elements_by_hierarchical_category(level2_elements)
# 渲染树形分类结构
for cat_level1_name, cat_level1_data in hierarchical_categories.items():
# 收集该一级分类下的所有元素
all_cat_elements = cat_level1_data['elements'][:]
for level2_elems in cat_level1_data['level2_groups'].values():
all_cat_elements.extend(level2_elems)
if not all_cat_elements:
continue
# 计算一级分类的统计信息
avg_coverage = sum((e.get('共性分析') or {}).get('段落覆盖率', 0.0) for e in all_cat_elements) / len(all_cat_elements)
avg_intent_count = sum(calculate_intent_support_count(e) for e in all_cat_elements) / len(all_cat_elements)
html += '
\n'
html += f'平均覆盖率: {avg_coverage:.2%}\n'
html += f'平均意图支撑: {avg_intent_count:.1f}\n'
html += '
\n'
html += '
\n'
html += '
\n'
# 渲染一级分类直接包含的元素
if cat_level1_data['elements']:
html += '
\n'
for elem in cat_level1_data['elements']:
html += render_element_item(elem, all_cat_elements)
html += '
\n'
# 渲染二级分类
for cat_level2_name, cat_level2_elements in cat_level1_data['level2_groups'].items():
if not cat_level2_elements:
continue
# 计算二级分类的统计信息
avg_coverage_l2 = sum((e.get('共性分析') or {}).get('段落覆盖率', 0.0) for e in cat_level2_elements) / len(cat_level2_elements)
avg_intent_count_l2 = sum(calculate_intent_support_count(e) for e in cat_level2_elements) / len(cat_level2_elements)
html += '