from abc import ABC, abstractmethod
import unittest
import nltk, spacy, re

class NLPInterface(ABC):
    """
    定义一个抽象基类，用于处理自然语言处理（NLP）任务。
    """

    @abstractmethod
    def __init__(self):
        """
        初始化方法，子类必须实现。
        """
        pass

    @abstractmethod
    def _split_sentences(self, text):
        """
        将输入的文本分割成句子列表。

        Args:
            text (str): 需要分割的文本。

        Returns:
            list: 分割后的文本块列表。
        """
        pass
   
    def calculate_sentence_size(self, text):
        """
        计算句子的大小。

        如果句子包含中文字符，则大小为字符数，否则大小为单词数。

        Args:
            text (str): 需要计算大小的句子。

        Returns:
            int: 句子的大小。
        """
        if re.match(r'[\u4e00-\u9fa5]+', text):
            return len(text)
        else:
            return len(text.split())

   

    def split_text_into_chunks(self, text, cmax_tokens=256):
        """
        将输入的文本分割成符合最大标记数限制的句子块列表，并返回这些句子块。

        Args:
            text (str): 需要分割的文本。
            cmax_tokens (int, optional): 最大标记数限制。 Defaults to 256.

        Returns:
            list: 分割后的句子块列表，每个块的长度不超过cmax_tokens。
        """
        # 初始化变量
        sentence_chunks = []
        current_sentence_chunk = []
        current_token_count = 0
        current_paragraph_count = 0
        current_paragraph_chunk = []

        # 分割文本成段落
        paragraphs = re.split(r'(\n\n)', text)
        paragraphs = [p for p in paragraphs if p.strip() and len(p) > 1]
        # 遍历每个段落
        for i, paragraph in enumerate(paragraphs):
            # 将当前段落添加到当前段落块
            current_paragraph_chunk.append(paragraph)
            # 如果当前段落块的长度不超过最大标记数，更新当前段落块的长度
            size = self.calculate_sentence_size(paragraph)
            if current_paragraph_count + size <= cmax_tokens:
                current_paragraph_count += size
            else:
                
                # 分割段落成句子
                docs = self._split_sentences(''.join(current_paragraph_chunk))
                for doc  in docs:
                    # 将句子列表合并成一个字符串
                    combined_sentence = "".join(doc)
                    # 检查合并后的句子是否包含中文字符
                    combined_sentence_size = self.calculate_sentence_size(combined_sentence)
                    # 如果句子长度超过限制，添加到新的段落
                    if current_token_count + combined_sentence_size <= cmax_tokens:
                        current_sentence_chunk.append(combined_sentence)
                        current_token_count += combined_sentence_size
                    else:
                        sentence_chunks.append("".join(current_sentence_chunk) + "\n\n")
                        current_sentence_chunk = [combined_sentence]
                        current_token_count = combined_sentence_size
                current_paragraph_chunk = []
        if current_sentence_chunk:
            sentence_chunks.append("".join(current_sentence_chunk))
                # current_sentence_chunk = []
        
        return  sentence_chunks           

class SpacyNLP(NLPInterface):
    """
    使用Spacy进行NLP处理的类。
    """

    def __init__(self):
        """
        初始化Spacy模型。
        """
        self.nlp = spacy.load('zh_core_web_sm')

    def _split_sentences(self, text):
        """
        使用Spacy分割文本成句子列表。

        Args:
            text (str): 需要分割的文本。

        Returns:
            list: 分割后的句子列表。
        """
        doc = self.nlp(text)
        sentences = [sent.text for sent in doc.sents]
        return sentences

class NLTKNLP(NLPInterface):
    """
    使用NLTK进行NLP处理的类。
    """

    def __init__(self):
        """
        初始化NLTK分割句子模型。
        """
        self.nlp = nltk.sent_tokenize

    def _split_sentences(self, text):
        """
        使用NLTK分割文本成句子列表。

        Args:
            text (str): 需要分割的文本。

        Returns:
            list: 分割后的句子列表。
        """
        sentences = [sent for sent in self.nlp(text) if sent]
        return sentences

class TestNLPProcessor(unittest.TestCase):
    """
    测试NLP处理器的类。
    """
    def setUp(self):
        """
        设置测试环境。
        """
        self.spacy_nlp = SpacyNLP()
        self.nltk_nlp = NLTKNLP()
        self.txt = """
造纸机的真空系统
造纸机的辅助系统中，真空系统的能耗占了整个系统的30%~40%，有的甚至更高。
真空系统面临的问题
造纸机辅助系统中，真空系统无论是在能源消耗，还是在设备投资方面都占据着极其重要的地位。
现代纸机其车速、定量变化范围越来越大，纸种也是随着市场变化而改变，真空系统面临严峻的挑战：
（1）真空系统必须精细化工作，以适应纸张定量和车速的变化；
（2）真空系统必须适应造纸机成形网和毛毯的不同使用阶段；
真空系统必须提供最优化的水电消耗，节约能源。
两种真空系统的对比
水环真空泵真空系统
水环真空泵的原理这里不再赘述，很多资料都有介绍。造纸机真空系统如图1 所示，在实际的设计过程中也由此衍生出其他一些真空系统，但是其基本的核心没有改变，主要包括汽水分离器、水环真空泵、真空泵水环水系统（真空泵水集池、自吸罐、冷却塔等设备）。
变频透平真空系统
透平真空泵工作原理：造纸机各真空抽吸点来的汽水混合物进入特殊的汽水分离器，液体以及流失的细小纤维通过气水分离器下部安装的泵抽出，气体经过滤器后进入透平式真空泵。气体伴随着透平式真空泵叶轮的旋转，进入到叶轮中间的孔眼，经叶轮径向旋转加速和增压。透平真空泵一般采用一台多级透平真空泵和两台单级透平真空泵联合使用，以满足造纸机不同抽吸点的真空度需求。
透平真空泵应用案例
以年产15 万t 两叠网箱纸板项目为例。
选用透平真空泵总装机容量1315kW，水环真空泵总装机容量1830 kW，透平真空泵系统节约28%左右的装机容量。究其原因主要在于透平真空泵有80%的超高效率，而传统的水环真空泵只有40%~50%，甚至更低。
在造纸机运行阶段，由于透平真空泵使用灵活，尤其是在生产低定量产品时，节能效果将更加明显。另外一方面，透平真空泵系统无水环真空泵水环水系统，进一步节省循环水泵和冷却塔的电耗，本项目中水环水系统共计67.5kW。
水耗对比
水环真空泵的水环水循环使用，仅考虑电耗，不考虑补水；本项目改造之前水环真空泵均为皮带传动，填料密封水40 L·min-1 左右，透平真空泵润滑油站冷却水共计30~40 L·min-1，大型造纸机真空系统中，水环真空泵一般采用联轴器传动，也会需要润滑油冷却水，透平真空泵节水有优势。
设备布置以及土建费用
常规造纸机厂房设计中，湿部传动侧通常布置流送系统、真空系统，管道密集，设备多，空间有限，尤其是采用复合压榨的纸机，湿部传动侧空间进一
步被压缩，设备布置非常紧凑，管道弯头多，压降损失大。透平真空泵系统采用一带多的流程设计，减少管路长度，其设备数量较水环真空泵少50%，甚
至更多，布置灵活，可以布置到一楼，也可以布置到二楼楼面，大大减少真空系统占地空间。
（1） 这里仅考虑泵房的占地面积，不考虑水环真空泵系统前置分离器的占地面积，水环真空泵原来占地6.5 m×36 m，透平真空泵占地仅6.5 m×18 m，占地面积节约50%。
（2） 透平真空泵系统没有水环水系统，不需要水环真空泵系统的水环水收集池，仅设计施工透平真空泵和汽水分离器的设备基础，较水环真空泵系统减少约90% 的土建费用。
热能回收
水环真空泵排气由于品位低，基本没有什么可以回收利用价值，大部分直接排空；而透平真空泵排气温度高（140℃左右），含湿量低，可以用于加热湿
部吊顶送风，也可以加热气罩送风，最终电能转化成热能，提高电能利用效率，以达到节能降耗的目的。
透平真空系统具有节水、节电、节地的特点。
在项目改造过程中，部分企业主怀疑其实际使用效果，建议压榨部先采用透平风机，因为压榨部脱水远远小于网部脱水。长期以来，水环真空泵以成熟
可靠作为造纸真空系统的首选，然而在国家大力推进节能减排的国家战略面前，企业也必须做出改变，透平真空泵价格和维护费用使很多企业望而却步，鉴于此种国情，部分设备供应商设计了一种能源管理模式，这种模式下企业可以省去对设备采购的投入，对企业来说是个不错的选择。
        """
        # self.txt = re.sub(r'\s+', '', self.txt)
    def test_split_sentences_spacy(self):
        """
        测试SpacyNLP的句子分割功能。
        """
        for x in self.spacy_nlp.print_sentences(self.txt):
            print(x)
            print('-'*50)


if __name__ == '__main__':
    unittest.main()
