273 lines
7.8 KiB
Python
273 lines
7.8 KiB
Python
from lxml import etree
|
|
from typing import List, Dict
|
|
|
|
|
|
global_ast_path = r"E:\storyline.xast"
|
|
|
|
|
|
def items_filter(items, proc):
|
|
values = []
|
|
for it in items:
|
|
if proc(it):
|
|
values.append(it)
|
|
pass
|
|
pass
|
|
return values
|
|
|
|
|
|
def items_map(items: List['StoryLine']) -> Dict[str, 'StoryLine']:
|
|
map: Dict[str, 'StoryLine'] = {}
|
|
for inst in items:
|
|
map[inst.signature] = inst
|
|
pass
|
|
return map
|
|
|
|
|
|
class FragmentSlice():
|
|
"""
|
|
故事情节片段
|
|
"""
|
|
def __init__(self, signature: str, file_path: str):
|
|
self.signature = signature
|
|
self.file_path = file_path
|
|
self.text_sections = []
|
|
self.refer_items: 'FragmentSlice' = []
|
|
pass
|
|
|
|
def append_text_section(self, section: str):
|
|
"""
|
|
添加描述文本段落
|
|
|
|
:param section: 文本段落
|
|
:return: None
|
|
"""
|
|
self.text_sections.append(section)
|
|
pass
|
|
|
|
def append_refer_slice(self, refer:'FragmentSlice'):
|
|
"""
|
|
添加引用描述段落内容
|
|
|
|
:param refer: 引用段落
|
|
:return: None
|
|
"""
|
|
self.refer_items.append(refer)
|
|
pass
|
|
|
|
def content_equal(self, other: 'FragmentSlice') -> bool:
|
|
"""
|
|
内容比较
|
|
|
|
:param other: 其他片段
|
|
:return: 比较结果
|
|
"""
|
|
if self.signature != other.signature:
|
|
return False
|
|
|
|
if len(self.text_sections) != len(other.text_sections):
|
|
return False
|
|
|
|
for line0 in self.text_sections:
|
|
if not line0 in other.text_sections:
|
|
return False
|
|
pass
|
|
|
|
if len(self.refer_items) != len(other.refer_items):
|
|
return False
|
|
|
|
for slice in self.refer_items:
|
|
find: bool = False
|
|
for slice_1 in other.refer_items:
|
|
if slice.content_equal(slice_1):
|
|
find = True
|
|
pass
|
|
pass
|
|
|
|
if not find:
|
|
return False
|
|
pass
|
|
|
|
return True
|
|
|
|
|
|
class StoryLine():
|
|
"""
|
|
故事线结果
|
|
"""
|
|
def __init__(self, signature: str, file_path: str):
|
|
self.signature = signature
|
|
self.file_path = file_path
|
|
self.text_sections = []
|
|
self.fragment_slices = []
|
|
pass
|
|
|
|
def append_text_section(self, section: str):
|
|
"""
|
|
添加文本描述
|
|
|
|
:param section: 文本段落
|
|
:return: None
|
|
"""
|
|
self.text_sections.append(section)
|
|
pass
|
|
|
|
def append_fragment_slice(self, slice: FragmentSlice):
|
|
"""
|
|
添加情节
|
|
|
|
:param slice: 情节实例
|
|
:return: None
|
|
"""
|
|
self.fragment_slices.append(slice)
|
|
pass
|
|
|
|
def content_equal(self, other: "StoryLine") -> bool:
|
|
"""
|
|
内容比较
|
|
|
|
:param other: 其他实例
|
|
:return: 比较结果
|
|
"""
|
|
if self.signature != other.signature:
|
|
return False
|
|
|
|
if len(self.text_sections) != len(other.text_sections):
|
|
return False
|
|
|
|
for line in self.text_sections:
|
|
if not line in other.text_sections:
|
|
return False
|
|
pass
|
|
|
|
if len(self.fragment_slices) != len(other.fragment_slices):
|
|
return False
|
|
|
|
for slice0 in self.fragment_slices:
|
|
find: bool = False
|
|
for slice1 in other.fragment_slices:
|
|
if slice0.content_equal(slice1):
|
|
find = True
|
|
pass
|
|
pass
|
|
if not find:
|
|
return False
|
|
pass
|
|
|
|
return True
|
|
|
|
|
|
class AstParse():
|
|
"""
|
|
Ast解析器
|
|
"""
|
|
def __init__(self, ast_path: str):
|
|
self.ast_path = ast_path
|
|
ast_file = open(ast_path, "rb")
|
|
ast_text = ast_file.read()
|
|
self.ast_inst = etree.XML(ast_text)
|
|
pass
|
|
|
|
def generate_time(self) -> str:
|
|
"""
|
|
获取生成时间
|
|
|
|
:return: 生成时间字符串
|
|
"""
|
|
attr_time = self.ast_inst.xpath("/ast[1]/@time")[0]
|
|
return str(attr_time)
|
|
|
|
def peak_storylines(self) -> List[StoryLine]:
|
|
"""
|
|
提取所有故事线
|
|
|
|
:return: 故事线列表
|
|
"""
|
|
story_list = self.ast_inst.xpath("/ast/story") # 获取所有故事节点
|
|
|
|
story_items = []
|
|
fragm_map: Dict[str, FragmentSlice] = {}
|
|
for story in story_list:
|
|
story_name = story.xpath("@name")[0]
|
|
story_file = story.xpath("@file-path")[0]
|
|
story_slice = StoryLine("story:" + story_name, story_file)
|
|
story_items.append(story_slice)
|
|
|
|
story_text_lines = story.xpath("text-section/@text") #获取所有描述
|
|
for line in story_text_lines:
|
|
story_slice.append_text_section(line)
|
|
pass
|
|
|
|
fragment_items = story.xpath("fragment") #获取所有情节定义
|
|
for fragm in fragment_items:
|
|
fragment_name = fragm.xpath("@name")[0]
|
|
fragm_slice = FragmentSlice(f"fragment:{story_name}#{fragment_name}", story_file)
|
|
story_slice.append_fragment_slice(fragm_slice)
|
|
fragm_map[fragm_slice.signature] = fragm_slice
|
|
|
|
fragm_text_lines = fragm.xpath("text-section/@text")
|
|
for line in fragm_text_lines:
|
|
fragm_slice.append_text_section(line)
|
|
pass
|
|
pass
|
|
pass
|
|
|
|
fragm_refer_items = self.ast_inst.xpath("//refer")
|
|
for refer_item in fragm_refer_items:
|
|
story_name_refer = refer_item.xpath("@story")[0]
|
|
fragm_name_refer = refer_item.xpath("@fragment")[0]
|
|
fragm_file = refer_item.xpath("@file-path")[0]
|
|
fragm_signature = f"fragment:{story_name_refer}#{fragm_name_refer}"
|
|
fragment_target = fragm_map[fragm_signature]
|
|
|
|
fragm_pnode = refer_item.xpath("..")[0]
|
|
|
|
if fragm_pnode.xpath("name()") == "story":
|
|
this_story_name = fragm_pnode.xpath("@name")[0]
|
|
refer_slice = FragmentSlice(f"refer:<{this_story_name}>{story_name_refer}#{fragm_name_refer}", fragm_file)
|
|
fragment_target.append_refer_slice(refer_slice)
|
|
|
|
refer_lines = refer_item.xpath("text-section/@text")
|
|
for line in refer_lines:
|
|
refer_slice.append_text_section(line)
|
|
pass
|
|
pass
|
|
|
|
if fragm_pnode.xpath("name()") == "article":
|
|
this_article_name = fragm_pnode.xpath("@name")[0]
|
|
this_volume_node = fragm_pnode.xpath("..")[0]
|
|
this_volume_name = this_volume_node.xpath("@name")[0]
|
|
refer_slice = FragmentSlice(f"refer-article<{this_volume_name}#{this_article_name}>{story_name_refer}#{fragm_name_refer}", fragm_file)
|
|
fragment_target.append_refer_slice(refer_slice)
|
|
|
|
refer_lines = refer_item.xpath("text-section/@text")
|
|
for line in refer_lines:
|
|
refer_slice.append_text_section(line)
|
|
pass
|
|
pass
|
|
|
|
|
|
return story_items
|
|
|
|
def storylines_compare(self, base_list: List[StoryLine], other_list: List[StoryLine]) -> List[str]:
|
|
changed_list: List[str] = []
|
|
base_map = items_map(base_list)
|
|
|
|
for other_item in other_list:
|
|
if not other_item.signature in base_map:
|
|
changed_list.append(other_item.signature)
|
|
continue
|
|
|
|
base_item = base_map[other_item.signature]
|
|
if not other_item.content_equal(base_item):
|
|
changed_list.append(other_item.signature)
|
|
pass
|
|
|
|
base_map.pop(other_item.signature)
|
|
pass
|
|
|
|
for addit in base_map.values():
|
|
changed_list.append(addit.signature)
|
|
pass
|
|
|
|
return changed_list
|