Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
187 lines
5.7 KiB
Python
187 lines
5.7 KiB
Python
"""类别管理器
|
||
|
||
统一管理评分项类别相关的所有逻辑,包括分组、排序、匹配等。
|
||
"""
|
||
|
||
import logging
|
||
from typing import Dict, List
|
||
|
||
from ...tools.parser import ScoringCriteria, DocumentChapter
|
||
from .constants import CATEGORY_NAMES, CATEGORY_ORDER, CATEGORY_TITLE_HINTS
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class CategoryManager:
|
||
"""类别管理器
|
||
|
||
提供统一的类别相关操作方法。
|
||
"""
|
||
|
||
@staticmethod
|
||
def group_criteria_by_category(criteria: List[ScoringCriteria]) -> Dict[str, List[ScoringCriteria]]:
|
||
"""按类别分组评分项
|
||
|
||
Args:
|
||
criteria: 评分项列表
|
||
|
||
Returns:
|
||
分组后的字典,key为类别,value为该类别下的评分项列表
|
||
"""
|
||
# 初始化所有预定义类别
|
||
category_groups = {category: [] for category in CATEGORY_ORDER}
|
||
|
||
# 分组
|
||
for criterion in criteria:
|
||
category_key = criterion.category.value
|
||
if category_key in category_groups:
|
||
category_groups[category_key].append(criterion)
|
||
else:
|
||
# 未知类别默认归入技术方案
|
||
category_groups["technical_solution"].append(criterion)
|
||
logger.warning(f"未知类别 {category_key},归入技术方案类别")
|
||
|
||
# 只保留有评分项的类别
|
||
filtered_groups = {k: v for k, v in category_groups.items() if v}
|
||
|
||
# 记录分组统计
|
||
for category, items in filtered_groups.items():
|
||
total_score = sum(item.max_score for item in items)
|
||
logger.info(f"类别 {category}: {len(items)}项,总分 {total_score}")
|
||
|
||
return filtered_groups
|
||
|
||
@staticmethod
|
||
def get_category_first_index(category: str, technical_criteria: List[ScoringCriteria]) -> int:
|
||
"""获取类别在原始评分项中的首次出现位置
|
||
|
||
Args:
|
||
category: 类别名称
|
||
technical_criteria: 技术评分项列表
|
||
|
||
Returns:
|
||
首次出现的索引位置,未找到时返回999
|
||
"""
|
||
for i, criteria in enumerate(technical_criteria):
|
||
if criteria.category.value == category:
|
||
return i
|
||
return 999
|
||
|
||
@staticmethod
|
||
def sort_categories_by_order(category_groups: Dict[str, List[ScoringCriteria]],
|
||
technical_criteria: List[ScoringCriteria]) -> List[str]:
|
||
"""按原始出现顺序排序类别
|
||
|
||
Args:
|
||
category_groups: 类别分组字典
|
||
technical_criteria: 原始技术评分项列表
|
||
|
||
Returns:
|
||
排序后的类别键名列表
|
||
"""
|
||
return sorted(
|
||
category_groups.keys(),
|
||
key=lambda cat: CategoryManager.get_category_first_index(cat, technical_criteria)
|
||
)
|
||
|
||
@staticmethod
|
||
def extract_category_from_chapter_id(chapter_id: str) -> str:
|
||
"""从章节ID中提取类别信息
|
||
|
||
Args:
|
||
chapter_id: 章节ID(格式:chapter_XX_category)
|
||
|
||
Returns:
|
||
类别名称,如果解析失败返回空字符串
|
||
"""
|
||
if "_" not in chapter_id:
|
||
return ""
|
||
|
||
parts = chapter_id.split("_")
|
||
if len(parts) < 3:
|
||
return ""
|
||
|
||
return "_".join(parts[2:])
|
||
|
||
@staticmethod
|
||
def infer_category_for_chapter(chapter: DocumentChapter) -> str:
|
||
"""通过章节ID或标题推断类别"""
|
||
|
||
category = CategoryManager.extract_category_from_chapter_id(chapter.id)
|
||
if category:
|
||
return category
|
||
|
||
title = (chapter.title or "").lower()
|
||
if not title:
|
||
return ""
|
||
|
||
for category_key, hints in CATEGORY_TITLE_HINTS.items():
|
||
for hint in hints:
|
||
if not hint:
|
||
continue
|
||
if hint.lower() in title:
|
||
return category_key
|
||
|
||
return ""
|
||
|
||
@staticmethod
|
||
def find_corresponding_criteria(chapter: DocumentChapter,
|
||
technical_criteria: List[ScoringCriteria]) -> List[ScoringCriteria]:
|
||
"""查找章节对应的评分项
|
||
|
||
Args:
|
||
chapter: 章节对象
|
||
technical_criteria: 技术评分项列表
|
||
|
||
Returns:
|
||
对应的评分项列表
|
||
"""
|
||
category = CategoryManager.infer_category_for_chapter(chapter)
|
||
if not category:
|
||
return []
|
||
|
||
return [
|
||
c for c in technical_criteria
|
||
if c.category.value == category
|
||
]
|
||
|
||
@staticmethod
|
||
def format_criteria_summary(technical_criteria: List[ScoringCriteria]) -> str:
|
||
"""格式化评分项摘要用于显示
|
||
|
||
Args:
|
||
technical_criteria: 技术评分项列表
|
||
|
||
Returns:
|
||
格式化后的字符串
|
||
"""
|
||
lines = []
|
||
|
||
# 按类别分组
|
||
category_groups = {}
|
||
for criteria in technical_criteria:
|
||
category = criteria.category.value
|
||
if category not in category_groups:
|
||
category_groups[category] = []
|
||
category_groups[category].append(criteria)
|
||
|
||
# 格式化输出
|
||
for category, items in category_groups.items():
|
||
category_name = CATEGORY_NAMES.get(category, category)
|
||
lines.append(f"【{category_name}】({len(items)}项):")
|
||
for item in items:
|
||
lines.append(f" - {item.item_name} ({item.max_score}分)")
|
||
|
||
return "\n".join(lines)
|
||
|
||
@staticmethod
|
||
def calculate_category_score(criteria_list: List[ScoringCriteria]) -> int:
|
||
"""计算类别总分
|
||
|
||
Args:
|
||
criteria_list: 该类别下的评分项列表
|
||
|
||
Returns:
|
||
总分值
|
||
"""
|
||
return sum(c.max_score for c in criteria_list) |