#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @File     : gen_user_report_auto_generated
# @Author   : LiuYan
# @Time     : 2021/12/7 14:50

import re, os, time
import copy
import json

import chevron
import datetime

from docx import Document, shared
from docx.oxml.ns import qn
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from generate.regular_extract import RegularExtract
from generate.pic_echarts import pic_echarts_pie, pic_echarts_bar, pic_echarts_line, pic_echarts_bar_line
# from generate.pic_plt import pic_plt_pie
from utils.log import logger
from copy_table import get_choose_table, generate_report
from copy_content import copy_content_main
from base.config.base_config import root_dir
import threading


class GeneralUserReportAutoGenerated(object):
    """
    1. 扫描数据源
    2. 拉取数据
    3. 数据填充
    """

    def __init__(self, project_name: str, template_document, output_report_path: str,
                 start_time: datetime, end_time: datetime, parameter='',
                 is_handle_success='ynHandleSuccess', result_data='resultData',
                 lock=threading.Lock()):
        super(GeneralUserReportAutoGenerated, self).__init__()
        self._lock = lock
        self._project_name = project_name
        self._start_time = start_time
        self._end_time = end_time
        self._parameters = {
            'start_time': self._start_time,
            'end_time': self._end_time
        }
        self._parameter = parameter
        self._is_handle_success = is_handle_success
        self._result_data = result_data
        self._output_report_path = output_report_path
        self._document = template_document
        # self._paragraphs = self._document.paragraphs
        # self._tables = self._document.tables
        self._regular_extract = RegularExtract()
        self._table_dict_template_label = dict()  # 模板标签索引 [表格]
        # self._dict_template_label_del = dict()      # 模板标签索引 [del]
        self._dict_template_label = dict()  # 模板标签索引——普通类型的标签索引
        self._dict_padding_data = dict()  # 填充数据
        self._dict_dataset = dict()  # 数据集信息
        self._dict_list_render = dict()  # 列表渲染
        self._set_dataset_name = set()  # 用于存放已填充数据集名称和未配置的数据集名称
        self._list_multi_para_render = list()  # 用于存放多段渲染
        self._build()

    def _build(self) -> None:
        """
        创建 / 更新 段落 + 表格 [列表]
        :return:
        """
        self._paragraphs = self._document.paragraphs
        self._tables = self._document.tables

    def _build_parameter(self, parameter: str, json_config: dict) -> str:
        """
        组织请求参数
        :param parameter:   参数
        :param json_config: 参数配置
        :return: parameter [str]
        """
        self._parameters = {
            'start_time': self._start_time,
            'end_time': self._end_time
        }
        for json_config_key in json_config:
            list_para = json_config_key.split('.')
            if len(list_para) == 2:
                self._parameters[list_para[1]] = json_config[json_config_key]
        parameter = chevron.render(parameter, self._parameters)

        return parameter

    def process(self, data_result: dict, report_id: str) -> None:
        """
        1. 扫描文本数据集 + 图片生成引擎(Json + Data + Generate)
        2. 拉取文本数据 + 删除未注册数据集
        3. 填充对象型数据集 + 列表型数据集 [含多段渲染]
        4. 表格生成引擎(表格第一行Json + 表格内对象型数据集 + 填充)
        5. 报告生成完毕 保存
        [1. 图片 2. 文本(列表型) 3. 文本 4. 表格]
        :return: None
        """
        # 1. 扫描文本数据集 + 图片生成引擎(Json + Data + Generate)
        logger.info('开始扫描文本数据集 + 启动图片生成引擎(Json + Data + Generate)')
        self._scanning_data(set_dataset_name=self._set_dataset_name, data_result=data_result, report_id=report_id)
        # 2. 获取数据信息
        logger.info('获取数据信息')
        self._getting_data(data_result)
        # todo：当前数据内容
        # logger.info("当前待填充数据内容{}".format(self._dict_padding_data))
        # todo： 删除数据为空的内容
        dict_finance = self._dict_padding_data["finance"]
        dict_info = self._dict_padding_data["info"]
        for key in list(dict_finance.keys()):
            if not dict_finance.get(key):
                del dict_finance[key]
        for key in list(dict_info.keys()):
            if not dict_info.get(key):
                del dict_info[key]
        self._dict_padding_data.update({"info": dict_info, "finance": dict_finance})

        # 3. 文本数据填充
        # todo： 基于self._getting_data 函数得到 self._dict_padding_data
        for dataset_name in self._dict_template_label:
            self._padding_dict(dataset_name=dataset_name)
        # # 列表型填充
        # self._process_text_list_render()
        # 多段渲染 [统一渲染]
        # self._process_multi_para_render()
        # 4. 表格生成引擎(表格第一行Json + 表格内对象型数据集 + 填充)
        logger.info('启动表格生成引擎(表格第一行Json + 表格内对象型数据集 + 填充)')
        self._process_table()
        # 5. 表格数据填充
        # # todo： 基于self._getting_data 函数得到 self._dict_padding_data
        # for dataset_name in self._table_dict_template_label:
        #     print(dataset_name)
        #     self._padding_table(dataset_name=dataset_name)
        # 6. 报告生成完毕 保存
        logger.info('报告生成完毕! 保存......')
        self._save()

    # todo: 先找到待填充数据的位置（相应的段落索引，索引下标默认从0开始计数）
    def _scanning_data(self, set_dataset_name: set, data_result: dict, report_id: str) -> None:
        """
        扫描数据
        :param set_dataset_name: index已经重定位好[已填充]的数据 仅有data_type=1会插入其中
                                                            补充: 若数据集未注册，则也会在拉取数据集时存入，以后便不再扫描该数据源
        :return:
        """
        for index, para in enumerate(self._paragraphs):
            set_result = self._regular_extract.match_pattern(para_text=para.text, patterns=['(?<={{).*?(?=}})'])
            # logger.info(len(set_result))
            for result in set_result:
                """
                result = {"chart.type": "pie", "chart.dataset": "资产占比", "chart.xaxis_column": "indexName", "chart.yaxis_column": "dataValue", "chart.title": "", "chart.top_n": " "}
                """
                # 原始步骤1 扫描列表渲染 [start_index: end_index]
                #
                # 原始步骤2 特殊文本型渲染: {{ ^dataset.title }} 若title为None或''则删除该行(不进行渲染)。 数据对象为第一部分的info数据集   待处理样式 ^info.zlRisk
                #
                # 图片型渲染
                try:
                    json_result = json.loads(result)  #
                    logger.info(json_result)
                    # todo: 基于传过来的数据生成相应的数据图
                    self._process_picture(index=index, json_pic_config=json_result, data_result=data_result,
                                          report_id=report_id)
                except Exception as e:
                    try:
                        # 文本渲染(对象型)
                        # todo： 处理第一部分info.year普通标签
                        list_template_label = result.split('.')  # result: info.year
                        if len(list_template_label) == 2:
                            # dataset 为数据对象名称  初版有info  和 finance
                            template_dataset = list_template_label[0]
                            # label 为相应的属性名称
                            template_label = list_template_label[1].strip()
                            if template_dataset not in set_dataset_name:
                                if template_dataset in self._dict_template_label:
                                    if template_label in self._dict_template_label[template_dataset]:
                                        self._dict_template_label[template_dataset][template_label].add(index)
                                    else:
                                        self._dict_template_label[template_dataset][template_label] = {index}
                                else:
                                    self._dict_template_label[template_dataset] = {template_label: {index}}
                    except Exception as e2:
                        logger.warning('Error：{}插入图片失败！'.format(e2))

    def _getting_data(self, data_result: dict) -> None:
        """
        本项目无需拉取数据，只需要存储数据结果
        :return:
        """
        list_dataset_name = list(
            set(list(self._dict_template_label.keys()))
        )

        # 遍历扫描到的所有文本型数据集 [拉取 / 删除]
        for dataset_name in list_dataset_name:
            try:
                result = data_result[dataset_name]
                # 4. 存储数据集的数据信息
                self._dict_padding_data[dataset_name] = result
            except Exception as e:
                logger.warning('Error: {}'.format(e))

    def _process_text_list_render(self) -> None:
        """
        文本(列表型): 1. 渲染(cv)/删除(Delete) 2. 更新(update index) 3. 填充(padding data)
        :return:
        """
        list_render_dataset_name = list(self._dict_list_render.keys())
        for dataset_name in list_render_dataset_name:
            '''
            在此判断:
                    1. 该数据集在已注册字典中
                    2. 该数据集类型是列表型
                    3. 该数据集数据不为None或[]
            '''
            if dataset_name in self._dict_dataset and self._dict_dataset[dataset_name]['dataset_type'] == 1:
                '''
                在此判断:
                    1. 该数据集数据不为None或[] -> 正常进行列表渲染 cv
                    2. 如果为None或[] -> 列表渲染部分删除[开头 -> 结尾] 重定位index
                '''
                # 1) 渲染(cv)/删除(Delete)
                if self._dict_padding_data[dataset_name]:
                    render_sum_number = 0
                    # 列表渲染: 以正置负
                    for _label in self._dict_template_label[dataset_name]:
                        set_label_index = self._dict_template_label[dataset_name][_label]
                        set_label_index_update = set()
                        for label_index in set_label_index:
                            set_label_index_update.add(-label_index)
                        self._dict_template_label[dataset_name][_label] = set_label_index_update
                    # dict -> list
                    self._dict_template_label[dataset_name] = [
                        copy.deepcopy(self._dict_template_label[dataset_name]) for _ in range(
                            len(self._dict_padding_data[dataset_name])
                        )
                    ]
                    # 遍历待渲染的列表 [同一列表型数据源在模板多处配置]
                    for render in self._dict_list_render[dataset_name]:
                        # 列表渲染: 0
                        for _label in self._dict_template_label[dataset_name][0]:
                            set_label_index = self._dict_template_label[dataset_name][0][_label]
                            set_label_index_update = set()
                            for label_index in set_label_index:
                                if render['start_index'] < -label_index < render['end_index']:
                                    # 以负置正，并更新
                                    set_label_index_update.add(-label_index + render_sum_number)
                                else:
                                    set_label_index_update.add(label_index)
                            self._dict_template_label[dataset_name][0][_label] = set_label_index_update

                        para_index = render['end_index'] - 1 + render_sum_number
                        for padding_list_index in range(len(self._dict_padding_data[dataset_name]) - 1):
                            for index in range(
                                    render['start_index'] + 1 + render_sum_number,
                                    render['end_index'] + render_sum_number
                            ):
                                # 新型cv 完美复制
                                para = self._paragraphs[para_index]
                                _para = copy.deepcopy(self._paragraphs[index]._p)
                                para._p.addnext(_para)
                                para_index += 1
                                self._build()
                                # 老版cv 中文字体以及小标题编号格式丢失
                                # _para = self._paragraphs[index]
                                # self._add_para(para_before=para_before, para=para)

                            # 更新padding index
                            for _label in self._dict_template_label[dataset_name][padding_list_index + 1]:
                                set_label_index = self._dict_template_label[dataset_name][padding_list_index + 1][
                                    _label]
                                set_label_index_update = set()
                                for label_index in set_label_index:
                                    if render['start_index'] < -label_index < render['end_index']:
                                        # 以负为正，并更新
                                        set_label_index_update.add(
                                            -label_index + render_sum_number + (padding_list_index + 1) * (
                                                    render['end_index'] - render['start_index'] - 1
                                            )
                                        )
                                    else:
                                        set_label_index_update.add(label_index)
                                self._dict_template_label[dataset_name][padding_list_index + 1][
                                    _label] = set_label_index_update

                        # 删除列表渲染的开头与结尾
                        start_index = render['start_index'] + render_sum_number
                        end_index = para_index + 1
                        self._delete_paragraph(para=self._paragraphs[start_index])
                        self._delete_paragraph(para=self._paragraphs[end_index - 1])
                        # 更新列表渲染总计数器
                        render_sum_number += (
                                                     render['end_index'] - render['start_index'] - 1
                                             ) * (len(self._dict_padding_data[dataset_name]) - 1) - 2
                        # 因开头结尾渲染被删除，需重新更新该列表型数据集所有padding index
                        for dict_label_index in self._dict_template_label[dataset_name]:
                            for _label in dict_label_index:
                                set_output = set()
                                set_input = dict_label_index[_label]
                                for index in set_input:
                                    if index > start_index:
                                        index -= 1
                                    if index > end_index:
                                        index -= 1
                                    set_output.add(index)
                                dict_label_index[_label] = set_output
                    # 2) 填充该列表数据
                    self._padding_list(dataset_name=dataset_name)
                else:
                    '''
                    列表数据为None或[] 删除模板配置
                    '''
                    render_sum_number = 0
                    for render in self._dict_list_render[dataset_name]:
                        start_index = render['start_index'] - render_sum_number
                        end_index = render['end_index'] - render_sum_number
                        for _ in range(start_index, end_index + 1):
                            self._delete_paragraph(para=self._paragraphs[start_index])
                        render_sum_number += (end_index + 1 - start_index)
                        '''
                        *** 视情况加与不加 现版本采用 暂无副作用 ***
                        支持多加换行 如果列表不渲染[不存在] 一并删除待渲染块的末尾换行
                        '''
                        if self._paragraphs[start_index].text == '':
                            self._delete_paragraph(para=self._paragraphs[start_index])
                            render_sum_number += 1

                # 对已渲染填充/删除的列表型数据集 -> 列入白名单
                self._set_dataset_name.add(dataset_name)
                # 3) update index 需重新扫描除该列表填充外的数据源
                for dataset_ in self._dict_template_label:
                    # if dataset_ not in set_dataset:
                    self._dict_template_label[dataset_] = dict()
                for dataset_ in list_render_dataset_name:
                    self._dict_list_render[dataset_] = []
                self._scanning_data(set_dataset_name=self._set_dataset_name)

    def _process_multi_para_render(self) -> None:
        """
        1. 存储 [已存储] -> [self._list_multi_para_render]
        2. 根据para_index升序 [在此实现]
        3. 渲染 + style [在此实现]
        :return:
        """
        multi_para_render_num = 0
        self._list_multi_para_render.sort(key=lambda k: k['para_index'])
        for dict_multi_para_render in self._list_multi_para_render:
            '''
            {
                'dataset_name': dataset_name,
                'field_name': _label,
                'para_index': index,
                'padding_data': padding_data,
                'para_num': len(padding_data)
            }
            1. 考虑首尾 [xxx, {{ key.value }}, yyy.] -> [xxx, key.value[0]] ... [key.value[-1], yyy.]
            2. 考虑中间 [循环 + 删除首尾 + 渲染即可]
            '''
            dataset_name = dict_multi_para_render['dataset_name']
            field_name = dict_multi_para_render['field_name']
            para_index = dict_multi_para_render['para_index']
            list_padding_data = dict_multi_para_render['list_padding_data']
            para_num = dict_multi_para_render['para_num']

            if len(dataset_name) > 0 and dataset_name[0] == '^':
                pattern_template_label = '\^' + dataset_name[1:] + '\.' + field_name
            else:
                pattern_template_label = dataset_name + '\.' + field_name
            pattern_label = re.compile(r'' + '{{{{\s*{}\s*}}}}'.format(pattern_template_label))

            # 定位多段渲染run_index
            list_run_index = self._position_run_index(
                para_index=para_index + multi_para_render_num,
                dataset_name=dataset_name,
                field_name=field_name
            )
            list_run_index_new = []
            list_para_style_index = []
            if len(list_padding_data) > 0:
                '''
                    1. cv run
                    2. cv para
                '''
                # 1. cv run
                run_num = 0
                for run_index in list_run_index:
                    # runs[run_index] = 'xx{{ dataset_name.field_name }}yy'切分为['xx', '{{ dataset_name.field_name }}', 'yy']
                    run = self._paragraphs[para_index + multi_para_render_num].runs[run_index + run_num]
                    results = re.finditer(pattern_label, run.text)
                    run_text = copy.deepcopy(run.text)
                    # 改到这里了，总算是不乱了
                    # 1. cv run -> run_index_new
                    for _ in results:
                        result = re.search(pattern_label, run_text)
                        if result:
                            start_index, end_index = result.regs[0]
                        else:
                            continue
                        # text_head
                        run = self._paragraphs[para_index + multi_para_render_num].runs[run_index + run_num]
                        run.text = run_text[: start_index]
                        # text_middle
                        _run = copy.deepcopy(run)
                        _run.text = run_text[start_index: end_index]
                        run._r.addnext(_run._r)
                        self._build()
                        # text_tail
                        run = self._paragraphs[para_index + multi_para_render_num].runs[run_index + run_num + 1]
                        _run = copy.deepcopy(run)
                        _run.text = run_text[end_index:]
                        run._r.addnext(_run._r)
                        self._build()
                        list_run_index_new.append(run_index + run_num + 1)
                        run_text = run_text[end_index:]
                        run_num += 2
                # 2. cv para
                para_org = copy.deepcopy(self._paragraphs[para_index + multi_para_render_num])
                for run_index in list_run_index_new:
                    # para_head
                    padding_data = str(list_padding_data[0]) if type(list_padding_data[0]) is int else \
                    list_padding_data[0]
                    runs = self._paragraphs[para_index + multi_para_render_num].runs
                    for i in range(run_index + 1, len(runs)):
                        runs[i].text = ''
                    runs[run_index].text = re.sub(pattern_label, padding_data, runs[run_index].text)
                    list_para_style_index.append(para_index + multi_para_render_num)
                    # para_middle
                    for index, padding_data in enumerate(list_padding_data[1: -1]):
                        padding_data = str(padding_data) if type(padding_data) is int else padding_data
                        para = self._paragraphs[para_index + multi_para_render_num + index]
                        _para = copy.deepcopy(para_org)
                        runs = _para.runs
                        for i in range(run_index):
                            runs[i].text = ''
                        for i in range(run_index + 1, len(runs)):
                            runs[i].text = ''
                        runs[run_index].text = re.sub(pattern_label, padding_data, runs[run_index].text)
                        para._p.addnext(_para._p)
                        self._build()
                        list_para_style_index.append(para_index + multi_para_render_num + index)
                    # para_tail
                    if len(list_padding_data) > 1:
                        padding_data = str(list_padding_data[-1]) if type(list_padding_data[-1]) is int else \
                        list_padding_data[-1]
                        para = self._paragraphs[para_index + multi_para_render_num + len(list_padding_data) - 2]
                        _para = copy.deepcopy(para_org)
                        runs = _para.runs
                        for i in range(run_index):
                            runs[i].text = ''
                        runs[run_index].text = re.sub(pattern_label, padding_data, runs[run_index].text)
                        para._p.addnext(_para._p)
                        self._build()

                    multi_para_render_num += len(list_padding_data) - 1

                # 2) 定制style
                if 'dict_style' in dict_multi_para_render:
                    for para_style_index in list_para_style_index:
                        self._process_style(
                            para_index=para_style_index,
                            list_padding_index=list_run_index_new,
                            dict_style=dict_multi_para_render['dict_style']
                        )

    def _position_run_index(self, para_index, dataset_name: str, field_name: str) -> list:
        begin, tmp = -100, ''
        template_label = dataset_name + '.' + field_name
        pattern_mustache = re.compile(r'{{.*?}}')

        runs = self._paragraphs[para_index].runs
        list_run_index = []
        for index, run in enumerate(runs):
            '''
            runs[0].text = '{{ list'
            runs[1].text = '_multi.info }}{{ list_multi.info }}
            '''
            if '{{' in run.text and begin == -100:
                begin, tmp = index, run.text
            elif '{' in run.text and begin == -100:
                begin, tmp = index, run.text
            else:
                tmp += run.text

            if pattern_mustache.findall(tmp):
                if template_label in tmp:
                    # 如果存在匹配的字符串，那么将当前的run替换成合并后得字符串tmp
                    run.text = run.text.replace(run.text, tmp)

                    if begin != -100:
                        for i in range(begin, index):
                            runs[i].text = ''

                    list_run_index.append(index)
                    begin, tmp = -100, ''
                elif '{{' in run.text:
                    begin, tmp = index, run.text
                elif '{' in run.text:
                    begin, tmp = index, run.text
                else:
                    begin, tmp = -100, ''

        return list_run_index

    def _process_table(self) -> None:
        """
        Table
        表格处理:
            表格第一行Json -> [单一数据集模板配置]
            表格内部 -> [已实现同一表格存在单一列表型数据集和多个对象型数据集配置]
        处理过程: table: tables [一个一个扫描获取填充 获取可能为多次GET请求]
        :return:
        """
        for table in self._tables:
            """
            1. 扫描列表型数据集 [只存在于table第一行, 且为JSON]
            2. 扫描对象型数据集 + 定位padding index
            3. 拉取对象型数据 + 数据填充 [对比dict_dataset, 以补全的策略拉取对象型数据集] 如数据集未注册, 同样删去
            4. 拉取列表型数据 + 根据List长度cv表单 + 数据填充
            """
            # 1. 扫描列表型数据集
            json_table_config = None
            row = table.rows[0]
            for cell in row.cells:
                set_result = self._regular_extract.match_pattern(
                    para_text=cell.text, patterns=['(?<={{).*?(?=}})']
                )
                for result in set_result:
                    try:
                        # Table Json
                        json_table_config = json.loads(result)
                        # print('Table Json: {}'.format(json_table_config))
                        row._element.getparent().remove(row._element)
                    except Exception as e:
                        pass
                if json_table_config:
                    break
            # 2. 扫描对象型数据集 + 定位padding index
            self._table_dict_template_label = dict()
            for index, cell in enumerate(table._cells):
                set_result = self._regular_extract.match_pattern(
                    para_text=cell.text, patterns=['(?<={{).*?(?=}})']
                )
                for result in set_result:
                    list_template_label = result.split('.')
                    if len(list_template_label) == 2:
                        template_dataset = list_template_label[0]
                        template_label = list_template_label[1]
                        if template_dataset in self._table_dict_template_label:
                            if template_label in self._table_dict_template_label[template_dataset]:
                                self._table_dict_template_label[template_dataset][template_label].add(index)
                            else:
                                self._table_dict_template_label[template_dataset][template_label] = {index}
                        else:
                            self._table_dict_template_label[template_dataset] = {template_label: {index}}
            # print("表格型数据对象{}".format(self._table_dict_template_label))
            # 3. 拉取对象型数据 + 数据填充
            list_dataset_name = list(self._table_dict_template_label.keys())
            # print(list_dataset_name)
            for dataset_name in self._table_dict_template_label:
                table_dict_label_index = self._table_dict_template_label[dataset_name]
                table_dict_padding_data = self._dict_padding_data[dataset_name]
                for _label in table_dict_label_index:
                    if _label in table_dict_padding_data:
                        for index in table_dict_label_index[_label]:
                            paragraphs = table._cells[index].paragraphs
                            for para in paragraphs:
                                self._padding_runs(
                                    runs=para.runs, dataset_name=dataset_name, field_name=_label,
                                    padding_data=table_dict_padding_data[_label]
                                )
            # # 4. 拉取列表型数据 + cv表单 + 数据填充
            # if json_table_config:
            #     # 1). 解析Json
            #     table_type = json_table_config['table.type'] if 'table.type' in json_table_config else None
            #     dataset_name = json_table_config['table.dataset'] if 'table.dataset' in json_table_config else None
            #     # 2). 查询数据集请求接口
            #     dataset = Dataset.query.filter(
            #         Dataset.project_name == self._project_name, Dataset.dataset_name == dataset_name
            #     ).first()
            #     # 3). 组织请求接口参数 + 拉取数据 + cv表单 + 数据填充
            #     if dataset:
            #         # 对于已注册的数据集: 信息存储 + 组织参数 + 请求数据
            #         self._request_data(
            #             dataset_name=dataset_name, dataset=dataset, json_config=json_table_config
            #         )
            #         # cv表单 + 数据填充
            #         if self._dict_dataset[dataset_name]['dataset_type'] == 0:
            #             """
            #             字典型表格: 不需要cv 不需要更新padding index 直接padding
            #             """
            #             self._padding_table(
            #                 table=table, dataset_name=dataset_name,
            #                 table_dict_label_index=self._table_dict_template_label[dataset_name],
            #                 table_dict_padding_data=self._dict_padding_data[dataset_name]
            #             )
            #         elif self._dict_dataset[dataset_name]['dataset_type'] == 1:
            #             """
            #             列表型表格:   1. cv
            #                         2. 更新padding index
            #                         3. padding
            #             """
            #             # 完美cv
            #             self._table_dict_template_label[dataset_name] = [self._table_dict_template_label[dataset_name]]
            #             for _ in range(len(self._dict_padding_data[dataset_name]) - 1):
            #                 row = table.rows[-1]
            #                 _row = copy.deepcopy(table.rows[-1]._tr)
            #                 row._tr.addnext(_row)
            #                 # 更新 padding index
            #                 dict_label_index = dict()
            #                 for _label in self._table_dict_template_label[dataset_name][0]:
            #                     set_label_index = self._table_dict_template_label[dataset_name][0][_label]
            #                     dict_label_index[_label] = set()
            #                     for label_index in set_label_index:
            #                         dict_label_index[_label].add(
            #                             label_index + (_ + 1) * len(
            #                                 list(self._table_dict_template_label[dataset_name][0].keys()))
            #                         )
            #                 self._table_dict_template_label[dataset_name].append(dict_label_index)
            #             # 填充Table数据
            #             for table_dict_label_index, table_dict_padding_data in zip(
            #                 self._table_dict_template_label[dataset_name], self._dict_padding_data[dataset_name]
            #             ):
            #                 self._padding_table(
            #                     table=table, dataset_name=dataset_name,
            #                     table_dict_label_index=table_dict_label_index,
            #                     table_dict_padding_data=table_dict_padding_data
            #                 )
            #         else:
            #             pass

    def _process_picture(self, index: int, json_pic_config: dict, data_result: dict, report_id: str) -> None:
        logger.info("=====进入图片加工程序=====")

        runs = self._paragraphs[index].runs
        for run in runs:
            run.text = ''

        run = runs[0]
        # todo: 基于报告id创建临时文件夹
        # picture_dir = os.path.join(root_dir, 'generate/echarts/{}'.format(report_id))
        # os.makedirs(picture_dir)
        picture_dir = report_id
        logger.info("正在处理中====")
        os.makedirs(picture_dir, exist_ok=True)
        logger.info("====创建目录结束====")

        # todo: 定义图片缓存名字
        time_stamp = str(datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
        # 解析json
        dataset_name = json_pic_config['chart.dataset'] if 'chart.dataset' in json_pic_config else None
        temp_file_name = "{}_{}_{}.png".format(report_id,
                                               time_stamp,
                                               dataset_name)
        picture_save_path = os.path.join(picture_dir, temp_file_name)
        # logger.info(picture_save_path)

        # 获取数据对象
        result_data = data_result[dataset_name]
        # 绘图引擎
        pic_name, pic_type = None, None
        if 'chart.type' in json_pic_config and 'chart.type2' not in json_pic_config:
            # 单图表 现版本支持: 饼图(pie) / 柱状图(bar) / 折线图(line)
            if json_pic_config['chart.type'] == 'pie':
                # echarts 饼图
                keys_name = json_pic_config['chart.xaxis_column'] if 'chart.xaxis_column' in json_pic_config else None
                values_name = json_pic_config['chart.yaxis_column'] if 'chart.yaxis_column' in json_pic_config else None
                keys, values = [], []
                for data in result_data:
                    keys.append(data[keys_name])
                    values.append(data[values_name])

                # logger.info("当前处理的数据集key{}，和value{}".format(keys, values))

                pic_name = pic_echarts_pie(
                    pic_echarts_path=picture_save_path,
                    keys=keys,
                    values=values,
                    title=json_pic_config['chart.title'] if 'chart.title' in json_pic_config else None
                )
                # logger.info("当前生成的图片id{}，数据集名称为{}".format(report_id, dataset_name))
                # logger.info("===={}--饼状图已生成====".format(dataset_name))
            elif json_pic_config['chart.type'] == 'bar':
                # echarts 柱状图
                keys_name = json_pic_config[
                    'chart.xaxis_column'] if 'chart.xaxis_column' in json_pic_config else None
                list_values_name = json_pic_config[
                    'chart.yaxis_columns'] if 'chart.yaxis_columns' in json_pic_config else []
                keys, dict_values = [], {values_name: [] for values_name in list_values_name}
                for data in result_data:
                    keys.append(data[keys_name])
                    for values_name in list_values_name:
                        dict_values[values_name].append(data[values_name])
                pic_name = pic_echarts_bar(
                    temp_file_name=temp_file_name,
                    keys=keys,
                    dict_values=dict_values,
                    title=json_pic_config['chart.title'] if 'chart.title' in json_pic_config else None,
                    x_name=json_pic_config['chart.xaxis_name'] if 'chart.xaxis_name' in json_pic_config else None,
                    y_name=json_pic_config['chart.yaxis_name'] if 'chart.yaxis_name' in json_pic_config else None
                )
            elif json_pic_config['chart.type'] == 'line':
                # echarts 折线图
                keys_name = json_pic_config[
                    'chart.xaxis_column'] if 'chart.xaxis_column' in json_pic_config else None
                list_values_name = json_pic_config[
                    'chart.yaxis_columns'] if 'chart.yaxis_columns' in json_pic_config else []
                keys, dict_values = [], {values_name: [] for values_name in list_values_name}
                for data in result_data:
                    keys.append(data[keys_name])
                    for values_name in list_values_name:
                        dict_values[values_name].append(data[values_name])
                pic_name = pic_echarts_line(
                    temp_file_name=temp_file_name,
                    keys=keys,
                    dict_values=dict_values,
                    title=json_pic_config['chart.title'] if 'chart.title' in json_pic_config else None,
                    x_name=json_pic_config['chart.xaxis_name'] if 'chart.xaxis_name' in json_pic_config else None,
                    y_name=json_pic_config['chart.yaxis_name'] if 'chart.yaxis_name' in json_pic_config else None
                )
            else:
                pic_name = None

        elif 'chart.type' in json_pic_config and 'chart.type2' in json_pic_config:
            # 组合图表 现版本支持: 双轴图: 柱状图(bar) + 折线图(line)
            list_bar_values_name, list_line_values_name = [], []
            y_name_left, y_name_right = None, None
            if json_pic_config['chart.type'] == 'bar' and json_pic_config['chart.type2'] == 'line':
                pic_type = 'bar_line'
                list_bar_values_name = json_pic_config[
                    'chart.yaxis_columns'] if 'chart.yaxis_columns' in json_pic_config else []
                list_line_values_name = json_pic_config[
                    'chart.yaxis2_columns'] if 'chart.yaxis2_columns' in json_pic_config else []
                y_name_left = json_pic_config[
                    'chart.yaxis_name'] if 'chart.yaxis_name' in json_pic_config else None
                y_name_right = json_pic_config[
                    'chart.yaxis2_name'] if 'chart.yaxis2_name' in json_pic_config else None

            elif json_pic_config['chart.type'] == 'line' and json_pic_config['chart.type2'] == 'bar':
                pic_type = 'line_bar'
                list_bar_values_name = json_pic_config[
                    'chart.yaxis2_columns'] if 'chart.yaxis2_columns' in json_pic_config else []
                list_line_values_name = json_pic_config[
                    'chart.yaxis_columns'] if 'chart.yaxis_columns' in json_pic_config else []
                y_name_left = json_pic_config[
                                  'chart.yaxis2_name'] if 'chart.yaxis2_name' in json_pic_config else None,
                y_name_right = json_pic_config[
                    'chart.yaxis_name'] if 'chart.yaxis_name' in json_pic_config else None
            else:
                pic_type = None

            if pic_type:
                keys_name = json_pic_config[
                    'chart.xaxis_column'] if 'chart.xaxis_column' in json_pic_config else None
                keys = []
                dict_bar_values = {bar_values_name: [] for bar_values_name in list_bar_values_name}
                dict_line_values = {line_values_name: [] for line_values_name in list_line_values_name}
                for data in result_data:
                    keys.append(data[keys_name])
                    for bar_values_name in list_bar_values_name:
                        dict_bar_values[bar_values_name].append(data[bar_values_name])
                    for line_values_name in list_line_values_name:
                        dict_line_values[line_values_name].append(data[line_values_name])
                pic_name = pic_echarts_bar_line(
                    temp_file_name=temp_file_name,
                    keys=keys,
                    dict_bar_values=dict_bar_values,
                    dict_line_values=dict_line_values,
                    colors=['#d14a61', '#5793f3', '#2f4554'],
                    title=json_pic_config['chart.title'] if 'chart.title' in json_pic_config else None,
                    x_name=json_pic_config['chart.xaxis_name'] if 'chart.xaxis_name' in json_pic_config else None,
                    y_name_left=y_name_left,
                    y_name_right=y_name_right
                )
            else:
                pic_name = None

        else:
            pic_name = None


        if pic_name:
            run.add_picture(pic_name, width=shared.Cm(15))
            self._paragraphs[index].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            logger.error("当前报告id为：{}，图片{} 插入已完成".format(report_id, pic_name))

            try:
                os.remove(pic_name)
                logger.error("当前报告id为：{}，图片{} 删除已完成".format(report_id, pic_name))
            except:
                logger.info('file: {} 删除失败！'.format(pic_name))
        else:
            logger.warning("pic_name is NONE， 请检查程序！")

    def _process_style(self, para_index: int, list_padding_index: list, dict_style: dict) -> None:
        """
        支持定制加粗
        list_padding_index:
        list_bold:
        1. 降序 去除重复子串 √
        2. 遍历list_padding_index 待加粗字符串[max]
        3. 两种思路 [均未实现]
            1) 全程拆分 操作list
            2) 记录start_index end_index列表[需控制start_index&end_index不在记录列表所有SE间] 最后切分
            3) 先拆分为一个run存放一个char 并记录该label起始结束下标
            选用3) 原因是方便后续支持颜色 下划线等等等
            注意！！！若想支持其他 在1. 2.间加list即可 若太多可考虑dict 其余操作同2. 3.
        :param para_index:
        :param dict_style:  特定风格的字典{'bold': []..} [需加粗的文本列表]
        :return:
        """
        # 1. 降序 去除重复子串
        list_bold = dict_style['bold'] if 'bold' in dict_style else []
        list_bold_new = sorted(list_bold, key=lambda k: len(k), reverse=True)
        '''
        2022年04月01日 16:09:33修改
        1. 降序问题 -> 按照字符串长度降序
        2. 重复子串 -> 不可直接去除 因为公司全称不一定出现在文本中，也可能是简称
        3. 策略为按照 长->短 对run进行切分 特定加粗
        list_bold_new = []
        for bold in list_bold:
            bool_bold = False
            for bold_new in list_bold_new:
                if bold in bold_new:
                    bool_bold = True
            if not bool_bold:
                list_bold_new.append(bold)
        '''

        # 2. 拆分为一个run存放一个char 并记录该label起始结束下标
        style_sum_number = 0
        list_padding_index_new = []
        for padding_index in list_padding_index:
            start_index = padding_index + style_sum_number
            run = self._paragraphs[para_index].runs[start_index]
            run_text = copy.deepcopy(run.text)
            len_run_rext = len(run_text)
            run.text = run_text[: 1]
            for i in range(len_run_rext - 1):
                run = self._paragraphs[para_index].runs[padding_index + style_sum_number]
                _run = copy.deepcopy(run._r)
                _run.text = run_text[i + 1: i + 2]
                run._r.addnext(_run)
                style_sum_number += 1
                self._build()
            # 存放runs的[start_index, end_index]
            list_padding_index_new.append(
                {
                    'start_index': start_index,
                    'end_index': padding_index + style_sum_number
                }
            )
        # 3. 设置加粗 run.font.bold = True
        runs = self._paragraphs[para_index].runs
        for dict_padding_index in list_padding_index_new:
            padding_text = ''
            for run_index in range(dict_padding_index['start_index'], dict_padding_index['end_index'] + 1):
                padding_text += runs[run_index].text
            for bold_new in list_bold_new:
                for dict_index in self._regular_extract.match_index(para_text=padding_text, pattern_str=bold_new):
                    start_index = dict_index['start_index'] + dict_padding_index['start_index']
                    end_index = dict_index['end_index'] + dict_padding_index['start_index']
                    for i in range(start_index, end_index):
                        # todo: 加粗函数
                        runs[i].font.bold = True
                    '''
                    开放break: 只加粗首次出现的字体
                    注释break: 加粗所有匹配到的字体
                    '''
                    # break

    def _padding_dict(self, dataset_name: str) -> None:
        """
        填充数据 类型为: Dict(0)
        :param dataset_name: 数据集名称  info  | finance
        :return:
        """
        # todo: 此处由于对象有些区别，先获取待填充数据

        dict_label_index = self._dict_template_label[dataset_name]
        dict_padding_data = self._dict_padding_data[dataset_name]
        # print(dict_padding_data)
        # print(dict_label_index)

        self._padding(
            dataset_name=dataset_name,
            dict_label_index=dict_label_index,
            dict_padding_data=dict_padding_data
        )

    def _padding_list(self, dataset_name: str) -> None:
        """
        填充数据 类型为: List(1)
        :param dataset_name: 数据集名称
        :return:
        """
        list_label_index = self._dict_template_label[dataset_name]
        list_padding_data = self._dict_padding_data[dataset_name]
        for dict_label_index, dict_padding_data in zip(list_label_index, list_padding_data):
            self._padding(
                dataset_name=dataset_name, dict_label_index=dict_label_index, dict_padding_data=dict_padding_data
            )

    def _padding(self, dataset_name: str, dict_label_index: dict, dict_padding_data: dict) -> None:
        """
        模板填充
        填充文本为 str 或 list
        str:
            1. 填充
            2. 定制style
        list:
            多段渲染[正文常用]
            1. 存储 [在此实现]
            2. 根据para_index升序
            3. 渲染 + style
        :param dataset_name:        数据集名称
        :param dict_label_index:    待替换模板所在位置
        :param dict_padding_data:   待填充数据集
        :return:
        """
        for _label in dict_label_index:
            if _label in dict_padding_data:
                for index in dict_label_index[_label]:
                    padding_data = dict_padding_data[_label]
                    # 防止返回的数据为None类型[且没配置删除'^'] (为None经测试也没任何问题, 填充替换结果会变成'')
                    if type(padding_data) is str or type(padding_data) is int or type(padding_data) is list:
                        padding_data = padding_data
                    else:
                        padding_data = ''
                    # print(padding_data, type(padding_data))
                    if type(padding_data) is str or type(padding_data) is int:
                        '''
                        1. 填充
                        2. 定制style
                        '''
                        # 1) 填充
                        para = self._paragraphs[index]
                        list_padding_index = self._padding_runs(
                            runs=para.runs, dataset_name=dataset_name, field_name=_label,
                            padding_data=padding_data
                        )
                        # print(list_padding_index)
                        # 2) 定制style
                        if 'style' in dict_padding_data:
                            for dict_style in dict_padding_data['style']:
                                if _label == dict_style['field']:
                                    self._process_style(
                                        para_index=index,
                                        list_padding_index=list_padding_index,
                                        dict_style=dict_style
                                    )
                                    break
                    elif type(padding_data) is list:
                        '''
                        多段渲染[正文常用]
                        1. 存储 [在此实现]
                        2. 根据para_index升序
                        3. 渲染 + style
                        '''
                        dict_multi_para_render = {
                            'dataset_name': dataset_name,
                            'field_name': _label,
                            'para_index': index,
                            'list_padding_data': padding_data,
                            'para_num': len(padding_data)
                        }
                        if 'style' in dict_padding_data:
                            for dict_style in dict_padding_data['style']:
                                if _label == dict_style['field']:
                                    dict_multi_para_render['dict_style'] = dict_style
                        self._list_multi_para_render.append(dict_multi_para_render)

    def _padding_runs(self, runs, dataset_name: str, field_name: str, padding_data: str or int) -> list:
        """
        核心替换 [重点关注] 后续可能问题多多！！！
        功能描述:
                文档某一段落 分runs进行替换
                runs: list[]
                padding_data_str: str
                存在多处替换，但都是同一个dataset_name + dataset_label
        :param runs:            代替换文本
        :param dataset_name:    数据集名称 [模板配置前缀] (dataset).
        :param field_name:      数据集字段 [模板配置后缀] .(company_name)
        :param padding_data:    可用于填充的数据
        :return: 返回runs中填充label的定位下标列表 [供给特定style使用]
        """
        if type(padding_data) is int:
            padding_data = str(padding_data)

        begin, tmp = -100, ''
        template_label = dataset_name + '.' + field_name  # 模板标签全称[用于二次确认+替换]

        pattern_mustache = re.compile(r'{{.*?}}')
        if len(dataset_name) > 0 and dataset_name[0] == '^':
            pattern_template_label = '\^' + dataset_name[1:] + '\.' + field_name
        else:
            pattern_template_label = dataset_name + '\.' + field_name
        pattern_label = re.compile(r'' + '{{{{\s*{}\s*}}}}'.format(pattern_template_label))

        list_padding_index = []
        for index, run in enumerate(runs):
            '''
                重难点
                有标题格式 开头出现的 {{ 会分为两个run
                如果后续支持计算，需修改此处代码
                这里为何用if 而不是 elif
                run.text = '}}{{' 连一起这种情况
            '''
            if '{{' in run.text and begin == -100:
                begin, tmp = index, run.text
            elif '{' in run.text and begin == -100:
                begin, tmp = index, run.text
            else:
                tmp += run.text

            if pattern_mustache.findall(tmp):
                if template_label in tmp:
                    # 如果存在匹配的字符串，那么将当前的run替换成合并后得字符串tmp
                    run.text = re.sub(pattern_label, padding_data, tmp)
                    '''
                    len(runs) = 1
                    runs[0].text = '{{ base_info.risk_title }}'
                    或者 runs[i].text = ' }}' runs[i + 1].text = '{{ dataset_name.filed_name }}'
                    此时会出现 begin = -100, end_index = 0
                    IndexError: list index out of range
                    '''
                    if begin != -100:
                        for i in range(begin, index):
                            runs[i].text = ''

                    list_padding_index.append(index)
                    begin, tmp = -100, ''
                elif '{{' in run.text:
                    begin, tmp = index, run.text
                elif '{' in run.text:
                    begin, tmp = index, run.text
                else:
                    begin, tmp = -100, ''

        return list_padding_index

    def _padding_table(
            self, table, dataset_name: str,
            table_dict_label_index: dict,
            table_dict_padding_data: dict
    ) -> None:
        for _label in table_dict_label_index:
            if _label in table_dict_padding_data:
                for index in table_dict_label_index[_label]:
                    paragraphs = table._cells[index].paragraphs
                    for para in paragraphs:
                        self._padding_runs(
                            runs=para.runs, dataset_name=dataset_name, field_name=_label,
                            padding_data=table_dict_padding_data[_label]
                        )

    def _delete_annotation(self) -> None:
        """
        删除模板中注释: {{ !xxx }}
        :return: None
        """
        for index, para in enumerate(self._paragraphs):
            set_result = self._regular_extract.match_pattern(para_text=para.text, patterns=['(?<={{).*?(?=}})'])
            for result in set_result:
                if len(result) > 0 and result[0] == '!':
                    self._delete_paragraph(para=para)
        pass

    def _delete_paragraph(self, para) -> None:
        """
        删除word文档某段落
        :param para:
        :return: None
        """
        p = para._element
        p.getparent().remove(p)
        para._p = para._element = None
        self._build()

    def _save(self) -> None:
        """
        将word文档保存至本地
        :return:
        """
        # self._document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
        # self._document.styles['Normal'].font.name = 'Times New Roman'
        self._delete_annotation()
        self._document.save(self._output_report_path)

    def _add_para(self, para_before, para) -> None:
        """
        新插入段落 [已弃用]
        :param para_before:
        :param para:
        :return:
        """
        # 插入一个段落，代码换行，word中就是一个段落，不换行
        new_para = para_before.insert_paragraph_before()
        self._add_runs(new_para=new_para, para=para)

        """ Paragraph 段落格式设置 """
        # 设置段落文本右对齐
        new_para.paragraph_format.alignment = para.paragraph_format.alignment

        """ 段落缩进 """
        # 左缩进
        new_para.paragraph_format.left_indent = para.paragraph_format.left_indent
        # 右缩进
        new_para.paragraph_format.right_indent = para.paragraph_format.right_indent
        # 首行缩进
        new_para.paragraph_format.first_line_indent = para.paragraph_format.first_line_indent

        """ 行间距 """
        new_para.paragraph_format.line_spacing = para.paragraph_format.line_spacing
        "当line_spacing设置为长度值时表示绝对距离，"
        "设置为浮点数时表示行高的倍数"

        # 段前间距
        new_para.paragraph_format.space_before = para.paragraph_format.space_before
        # 段后间距
        new_para.paragraph_format.space_after = para.paragraph_format.space_after

        """ 设置段落内部文字在遇到需分页情况时处理状态 """
        new_para.paragraph_format.keep_together = para.paragraph_format.keep_together  # 段中不分页
        new_para.paragraph_format.keep_with_next = para.paragraph_format.keep_with_next  # 与下段同页
        new_para.paragraph_format.page_break_before = para.paragraph_format.page_break_before  # 段前分页
        new_para.paragraph_format.widow_control = para.paragraph_format.widow_control  # 孤行控制

        # 获取段落的左缩进，首行缩进，段前间距：
        l_space = new_para.paragraph_format.left_indent
        h_space = new_para.paragraph_format.first_line_indent
        b_space = new_para.paragraph_format.space_before
        # print(l_space, h_space, b_space)  # 457200 914400 63500
        self._build()

    def _add_runs(self, new_para, para) -> None:
        """
        新建段落[由runs组成] 新建run [已弃用]
        :param new_para:
        :param para:
        :return:
        """
        for run in para.runs:
            # 新建一个段落，增加一段文字
            new_run = new_para.add_run(run.text)
            new_run.bold = run.bold
            new_run.element = run.element
            """ 设置字体格式 """
            # new_run.font.name = run.font.name  # 注：这个好像设置 run 中的西文字体
            # new_run.font.element = run.font.element

            # new_run._element.rPr.rFonts = run._element.rPr.rFonts
            # new_run._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
            new_run.font.name = 'Times New Roman'  # 注：这个好像设置 run 中的西文字体
            # new_run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
            new_run._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')

            # new_run.style._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
            # new_run.style._element.rPr.rFonts.set(qn('w:ascii'), 'Times New Roman')
            # print(new_run.font.name)

            # 设置中文字体
            # new_run.font.element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
            # 设置字体大小
            new_run.font.size = run.font.size
            # 设置加粗
            new_run.font.bold = run.font.bold
            # 设置斜体
            new_run.font.italic = run.font.italic
            # 设置字体颜色
            new_run.font.color.rgb = run.font.color.rgb
            # 设置背景颜色
            new_run.font.highlight_color = run.font.highlight_color

            # 设置下划线
            new_run.font.underline = run.font.underline
            # 设置轮廓线
            new_run.font.outline = run.font.outline
            # 设置阴影
            new_run.font.shadow = run.font.shadow
            # 删除线
            new_run.font.strike = run.font.strike
            # 双删除线
            new_run.font.double_strike = run.font.double_strike
            # 设置下标
            new_run.font.subscript = run.font.subscript
            # 设置上标
            new_run.font.superscript = run.font.superscript

            new_run.italic = run.italic
            new_run.style.name = run.style.name
            new_run.underline = run.underline


class new_GeneralUserReportAutoGenerated(object):
    """
    1. 扫描数据源
    2. 拉取数据
    3. 数据填充
    """

    def __init__(self, project_name: str, template_document, output_report_path: str,
                 start_time: datetime, end_time: datetime, parameter='',
                 is_handle_success='ynHandleSuccess', result_data='resultData'):
        super(new_GeneralUserReportAutoGenerated, self).__init__()
        self._project_name = project_name
        self._start_time = start_time
        self._end_time = end_time
        self._parameters = {
            'start_time': self._start_time,
            'end_time': self._end_time
        }
        self._parameter = parameter
        self._is_handle_success = is_handle_success
        self._result_data = result_data
        self._output_report_path = output_report_path
        self._document = template_document
        self._regular_extract = RegularExtract()
        self._table_dict_template_label = dict()  # 模板标签索引 [表格]
        # self._dict_template_label_del = dict()      # 模板标签索引 [del]
        self._dict_template_label = dict()  # 模板标签索引——普通类型的标签索引
        self._dict_padding_data = dict()  # 填充数据
        self._dict_dataset = dict()  # 数据集信息
        self._dict_list_render = dict()  # 列表渲染
        self._set_dataset_name = set()  # 用于存放已填充数据集名称和未配置的数据集名称
        self._list_multi_para_render = list()  # 用于存放多段渲染
        self._build()

    def _build(self) -> None:
        """
        创建 / 更新 段落 + 表格 [列表]
        :return:
        """
        self._paragraphs = self._document.paragraphs
        self._tables = self._document.tables

    def _build_parameter(self, parameter: str, json_config: dict) -> str:
        """
        组织请求参数
        :param parameter:   参数
        :param json_config: 参数配置
        :return: parameter [str]
        """
        self._parameters = {
            'start_time': self._start_time,
            'end_time': self._end_time
        }
        for json_config_key in json_config:
            list_para = json_config_key.split('.')
            if len(list_para) == 2:
                self._parameters[list_para[1]] = json_config[json_config_key]
        parameter = chevron.render(parameter, self._parameters)

        return parameter

    def process(self, data_result: dict, report_id:str) -> None:
        """
        1. 扫描文本数据集 + 图片生成引擎(Json + Data + Generate)
        2. 拉取文本数据 + 删除未注册数据集
        3. 填充对象型数据集 + 列表型数据集 [含多段渲染]
        4. 表格生成引擎(表格第一行Json + 表格内对象型数据集 + 填充)
        5. 报告生成完毕 保存
        [1. 图片 2. 文本(列表型) 3. 文本 4. 表格]
        :return: None
        """
        # 1. 扫描文本数据集 + 图片生成引擎(Json + Data + Generate)
        logger.info('开始扫描文本数据集 + 启动图片生成引擎(Json + Data + Generate)')
        self._scanning_data(set_dataset_name=self._set_dataset_name, data_result=data_result, report_id=report_id)
        # 2. 获取数据信息
        logger.info('获取数据信息')
        self._getting_data(data_result)
        # print("处理第一部分普通的info.year后的结果: {}".format(self._dict_template_label))
        # todo：当前数据内容
        logger.info("当前待填充数据内容{}".format(self._dict_padding_data))
        # todo： 删除数据为空的内容
        dict_info = self._dict_padding_data["info"]

        for key in list(dict_info.keys()):
            if not dict_info.get(key):
                del dict_info[key]
        self._dict_padding_data.update({"info": dict_info})

        # 3. 文本数据填充
        # todo： 基于self._getting_data 函数得到 self._dict_padding_data
        for dataset_name in self._dict_template_label:
            # print(dataset_name)
            self._padding_dict(dataset_name=dataset_name)
        # # 列表型填充
        # self._process_text_list_render()
        # 多段渲染 [统一渲染]
        # self._process_multi_para_render()
        # 4. 表格生成引擎(表格第一行Json + 表格内对象型数据集 + 填充)
        logger.info('启动表格生成引擎(表格第一行Json + 表格内对象型数据集 + 填充)')
        self._process_table()
        # 5. 表格数据填充
        # # todo： 基于self._getting_data 函数得到 self._dict_padding_data
        # for dataset_name in self._table_dict_template_label:
        #     print(dataset_name)
        #     self._padding_table(dataset_name=dataset_name)
        # 6. 报告生成完毕 保存
        logger.info('报告生成完毕! 保存......')
        self._save()

    # todo: 先找到待填充数据的位置（相应的段落索引，索引下标默认从0开始计数）
    def _scanning_data(self, set_dataset_name: set, data_result: dict, report_id: str) -> None:
        """
        扫描数据
        :param set_dataset_name: index已经重定位好[已填充]的数据 仅有data_type=1会插入其中
                                                            补充: 若数据集未注册，则也会在拉取数据集时存入，以后便不再扫描该数据源
        :return:
        """
        for index, para in enumerate(self._paragraphs):
            set_result = self._regular_extract.match_pattern(para_text=para.text, patterns=['(?<={{).*?(?=}})'])
            # logger.info(len(set_result))
            for result in set_result:
                """
                result = {"chart.type": "pie", "chart.dataset": "资产占比", "chart.xaxis_column": "indexName", "chart.yaxis_column": "dataValue", "chart.title": "", "chart.top_n": " "}
                """
                # 原始步骤1 扫描列表渲染 [start_index: end_index]
                #
                # 原始步骤2 特殊文本型渲染: {{ ^dataset.title }} 若title为None或''则删除该行(不进行渲染)。 数据对象为第一部分的info数据集   待处理样式 ^info.zlRisk
                #
                # 图片型渲染
                try:
                    json_result = json.loads(result)  #
                    logger.info(json_result)
                    # logger.info("当前数据对象为：{}".format(data_result))
                    # logger.info('Picture Json: {}'.format(json_result))
                    # logger.info("当前处理的段落索引 {}".format(index))
                    # todo: 基于传过来的数据生成相应的数据图
                    self._process_picture(index=index, json_pic_config=json_result, data_result=data_result,
                                          report_id=report_id)
                except Exception as e:
                    try:
                        # 文本渲染(对象型)
                        # todo： 处理第一部分info.year普通标签
                        list_template_label = result.split('.')  # result: info.year
                        if len(list_template_label) == 2:
                            # dataset 为数据对象名称  初版有info  和 finance
                            template_dataset = list_template_label[0]
                            # label 为相应的属性名称
                            template_label = list_template_label[1].strip()
                            if template_dataset not in set_dataset_name:
                                if template_dataset in self._dict_template_label:
                                    if template_label in self._dict_template_label[template_dataset]:
                                        self._dict_template_label[template_dataset][template_label].add(index)
                                    else:
                                        self._dict_template_label[template_dataset][template_label] = {index}
                                else:
                                    self._dict_template_label[template_dataset] = {template_label: {index}}
                    except Exception as e2:
                        logger.warning('Error：{}插入图片失败！'.format(e2))

    def _getting_data(self, data_result: dict) -> None:
        """
        本项目无需拉取数据，只需要存储数据结果
        :return:
        """
        list_dataset_name = list(
            set(list(self._dict_template_label.keys()))
        )
        # 遍历扫描到的所有文本型数据集 [拉取 / 删除]
        for dataset_name in list_dataset_name:
            result = data_result[dataset_name]
            # 4. 存储数据集的数据信息
            self._dict_padding_data[dataset_name] = result

    def _process_text_list_render(self) -> None:
        """
        文本(列表型): 1. 渲染(cv)/删除(Delete) 2. 更新(update index) 3. 填充(padding data)
        :return:
        """
        list_render_dataset_name = list(self._dict_list_render.keys())
        for dataset_name in list_render_dataset_name:
            '''
            在此判断:
                    1. 该数据集在已注册字典中
                    2. 该数据集类型是列表型
                    3. 该数据集数据不为None或[]
            '''
            if dataset_name in self._dict_dataset and self._dict_dataset[dataset_name]['dataset_type'] == 1:
                '''
                在此判断:
                    1. 该数据集数据不为None或[] -> 正常进行列表渲染 cv
                    2. 如果为None或[] -> 列表渲染部分删除[开头 -> 结尾] 重定位index
                '''
                # 1) 渲染(cv)/删除(Delete)
                if self._dict_padding_data[dataset_name]:
                    render_sum_number = 0
                    # 列表渲染: 以正置负
                    for _label in self._dict_template_label[dataset_name]:
                        set_label_index = self._dict_template_label[dataset_name][_label]
                        set_label_index_update = set()
                        for label_index in set_label_index:
                            set_label_index_update.add(-label_index)
                        self._dict_template_label[dataset_name][_label] = set_label_index_update
                    # dict -> list
                    self._dict_template_label[dataset_name] = [
                        copy.deepcopy(self._dict_template_label[dataset_name]) for _ in range(
                            len(self._dict_padding_data[dataset_name])
                        )
                    ]
                    # 遍历待渲染的列表 [同一列表型数据源在模板多处配置]
                    for render in self._dict_list_render[dataset_name]:
                        # 列表渲染: 0
                        for _label in self._dict_template_label[dataset_name][0]:
                            set_label_index = self._dict_template_label[dataset_name][0][_label]
                            set_label_index_update = set()
                            for label_index in set_label_index:
                                if render['start_index'] < -label_index < render['end_index']:
                                    # 以负置正，并更新
                                    set_label_index_update.add(-label_index + render_sum_number)
                                else:
                                    set_label_index_update.add(label_index)
                            self._dict_template_label[dataset_name][0][_label] = set_label_index_update

                        para_index = render['end_index'] - 1 + render_sum_number
                        for padding_list_index in range(len(self._dict_padding_data[dataset_name]) - 1):
                            for index in range(
                                    render['start_index'] + 1 + render_sum_number,
                                    render['end_index'] + render_sum_number
                            ):
                                # 新型cv 完美复制
                                para = self._paragraphs[para_index]
                                _para = copy.deepcopy(self._paragraphs[index]._p)
                                para._p.addnext(_para)
                                para_index += 1
                                self._build()
                                # 老版cv 中文字体以及小标题编号格式丢失
                                # _para = self._paragraphs[index]
                                # self._add_para(para_before=para_before, para=para)

                            # 更新padding index
                            for _label in self._dict_template_label[dataset_name][padding_list_index + 1]:
                                set_label_index = self._dict_template_label[dataset_name][padding_list_index + 1][
                                    _label]
                                set_label_index_update = set()
                                for label_index in set_label_index:
                                    if render['start_index'] < -label_index < render['end_index']:
                                        # 以负为正，并更新
                                        set_label_index_update.add(
                                            -label_index + render_sum_number + (padding_list_index + 1) * (
                                                    render['end_index'] - render['start_index'] - 1
                                            )
                                        )
                                    else:
                                        set_label_index_update.add(label_index)
                                self._dict_template_label[dataset_name][padding_list_index + 1][
                                    _label] = set_label_index_update

                        # 删除列表渲染的开头与结尾
                        start_index = render['start_index'] + render_sum_number
                        end_index = para_index + 1
                        self._delete_paragraph(para=self._paragraphs[start_index])
                        self._delete_paragraph(para=self._paragraphs[end_index - 1])
                        # 更新列表渲染总计数器
                        render_sum_number += (
                                                     render['end_index'] - render['start_index'] - 1
                                             ) * (len(self._dict_padding_data[dataset_name]) - 1) - 2
                        # 因开头结尾渲染被删除，需重新更新该列表型数据集所有padding index
                        for dict_label_index in self._dict_template_label[dataset_name]:
                            for _label in dict_label_index:
                                set_output = set()
                                set_input = dict_label_index[_label]
                                for index in set_input:
                                    if index > start_index:
                                        index -= 1
                                    if index > end_index:
                                        index -= 1
                                    set_output.add(index)
                                dict_label_index[_label] = set_output
                    # 2) 填充该列表数据
                    self._padding_list(dataset_name=dataset_name)
                else:
                    '''
                    列表数据为None或[] 删除模板配置
                    '''
                    render_sum_number = 0
                    for render in self._dict_list_render[dataset_name]:
                        start_index = render['start_index'] - render_sum_number
                        end_index = render['end_index'] - render_sum_number
                        for _ in range(start_index, end_index + 1):
                            self._delete_paragraph(para=self._paragraphs[start_index])
                        render_sum_number += (end_index + 1 - start_index)
                        '''
                        *** 视情况加与不加 现版本采用 暂无副作用 ***
                        支持多加换行 如果列表不渲染[不存在] 一并删除待渲染块的末尾换行
                        '''
                        if self._paragraphs[start_index].text == '':
                            self._delete_paragraph(para=self._paragraphs[start_index])
                            render_sum_number += 1

                # 对已渲染填充/删除的列表型数据集 -> 列入白名单
                self._set_dataset_name.add(dataset_name)
                # 3) update index 需重新扫描除该列表填充外的数据源
                for dataset_ in self._dict_template_label:
                    # if dataset_ not in set_dataset:
                    self._dict_template_label[dataset_] = dict()
                for dataset_ in list_render_dataset_name:
                    self._dict_list_render[dataset_] = []
                self._scanning_data(set_dataset_name=self._set_dataset_name)

    def _process_multi_para_render(self) -> None:
        """
        1. 存储 [已存储] -> [self._list_multi_para_render]
        2. 根据para_index升序 [在此实现]
        3. 渲染 + style [在此实现]
        :return:
        """
        multi_para_render_num = 0
        self._list_multi_para_render.sort(key=lambda k: k['para_index'])
        for dict_multi_para_render in self._list_multi_para_render:
            '''
            {
                'dataset_name': dataset_name,
                'field_name': _label,
                'para_index': index,
                'padding_data': padding_data,
                'para_num': len(padding_data)
            }
            1. 考虑首尾 [xxx, {{ key.value }}, yyy.] -> [xxx, key.value[0]] ... [key.value[-1], yyy.]
            2. 考虑中间 [循环 + 删除首尾 + 渲染即可]
            '''
            dataset_name = dict_multi_para_render['dataset_name']
            field_name = dict_multi_para_render['field_name']
            para_index = dict_multi_para_render['para_index']
            list_padding_data = dict_multi_para_render['list_padding_data']
            para_num = dict_multi_para_render['para_num']

            if len(dataset_name) > 0 and dataset_name[0] == '^':
                pattern_template_label = '\^' + dataset_name[1:] + '\.' + field_name
            else:
                pattern_template_label = dataset_name + '\.' + field_name
            pattern_label = re.compile(r'' + '{{{{\s*{}\s*}}}}'.format(pattern_template_label))

            # 定位多段渲染run_index
            list_run_index = self._position_run_index(
                para_index=para_index + multi_para_render_num,
                dataset_name=dataset_name,
                field_name=field_name
            )
            list_run_index_new = []
            list_para_style_index = []
            if len(list_padding_data) > 0:
                '''
                    1. cv run
                    2. cv para
                '''
                # 1. cv run
                run_num = 0
                for run_index in list_run_index:
                    # runs[run_index] = 'xx{{ dataset_name.field_name }}yy'切分为['xx', '{{ dataset_name.field_name }}', 'yy']
                    run = self._paragraphs[para_index + multi_para_render_num].runs[run_index + run_num]
                    results = re.finditer(pattern_label, run.text)
                    run_text = copy.deepcopy(run.text)
                    # 改到这里了，总算是不乱了
                    # 1. cv run -> run_index_new
                    for _ in results:
                        result = re.search(pattern_label, run_text)
                        if result:
                            start_index, end_index = result.regs[0]
                        else:
                            continue
                        # text_head
                        run = self._paragraphs[para_index + multi_para_render_num].runs[run_index + run_num]
                        run.text = run_text[: start_index]
                        # text_middle
                        _run = copy.deepcopy(run)
                        _run.text = run_text[start_index: end_index]
                        run._r.addnext(_run._r)
                        self._build()
                        # text_tail
                        run = self._paragraphs[para_index + multi_para_render_num].runs[run_index + run_num + 1]
                        _run = copy.deepcopy(run)
                        _run.text = run_text[end_index:]
                        run._r.addnext(_run._r)
                        self._build()
                        list_run_index_new.append(run_index + run_num + 1)
                        run_text = run_text[end_index:]
                        run_num += 2
                # 2. cv para
                para_org = copy.deepcopy(self._paragraphs[para_index + multi_para_render_num])
                for run_index in list_run_index_new:
                    # para_head
                    padding_data = str(list_padding_data[0]) if type(list_padding_data[0]) is int else \
                    list_padding_data[0]
                    runs = self._paragraphs[para_index + multi_para_render_num].runs
                    for i in range(run_index + 1, len(runs)):
                        runs[i].text = ''
                    runs[run_index].text = re.sub(pattern_label, padding_data, runs[run_index].text)
                    list_para_style_index.append(para_index + multi_para_render_num)
                    # para_middle
                    for index, padding_data in enumerate(list_padding_data[1: -1]):
                        padding_data = str(padding_data) if type(padding_data) is int else padding_data
                        para = self._paragraphs[para_index + multi_para_render_num + index]
                        _para = copy.deepcopy(para_org)
                        runs = _para.runs
                        for i in range(run_index):
                            runs[i].text = ''
                        for i in range(run_index + 1, len(runs)):
                            runs[i].text = ''
                        runs[run_index].text = re.sub(pattern_label, padding_data, runs[run_index].text)
                        para._p.addnext(_para._p)
                        self._build()
                        list_para_style_index.append(para_index + multi_para_render_num + index)
                    # para_tail
                    if len(list_padding_data) > 1:
                        padding_data = str(list_padding_data[-1]) if type(list_padding_data[-1]) is int else \
                        list_padding_data[-1]
                        para = self._paragraphs[para_index + multi_para_render_num + len(list_padding_data) - 2]
                        _para = copy.deepcopy(para_org)
                        runs = _para.runs
                        for i in range(run_index):
                            runs[i].text = ''
                        runs[run_index].text = re.sub(pattern_label, padding_data, runs[run_index].text)
                        para._p.addnext(_para._p)
                        self._build()

                    multi_para_render_num += len(list_padding_data) - 1

                # 2) 定制style
                if 'dict_style' in dict_multi_para_render:
                    for para_style_index in list_para_style_index:
                        self._process_style(
                            para_index=para_style_index,
                            list_padding_index=list_run_index_new,
                            dict_style=dict_multi_para_render['dict_style']
                        )

    def _position_run_index(self, para_index, dataset_name: str, field_name: str) -> list:
        begin, tmp = -100, ''
        template_label = dataset_name + '.' + field_name
        pattern_mustache = re.compile(r'{{.*?}}')

        runs = self._paragraphs[para_index].runs
        list_run_index = []
        for index, run in enumerate(runs):
            '''
            runs[0].text = '{{ list'
            runs[1].text = '_multi.info }}{{ list_multi.info }}
            '''
            if '{{' in run.text and begin == -100:
                begin, tmp = index, run.text
            elif '{' in run.text and begin == -100:
                begin, tmp = index, run.text
            else:
                tmp += run.text

            if pattern_mustache.findall(tmp):
                if template_label in tmp:
                    # 如果存在匹配的字符串，那么将当前的run替换成合并后得字符串tmp
                    run.text = run.text.replace(run.text, tmp)

                    if begin != -100:
                        for i in range(begin, index):
                            runs[i].text = ''

                    list_run_index.append(index)
                    begin, tmp = -100, ''
                elif '{{' in run.text:
                    begin, tmp = index, run.text
                elif '{' in run.text:
                    begin, tmp = index, run.text
                else:
                    begin, tmp = -100, ''

        return list_run_index

    def _process_table(self) -> None:
        """
        Table
        表格处理:
            表格第一行Json -> [单一数据集模板配置]
            表格内部 -> [已实现同一表格存在单一列表型数据集和多个对象型数据集配置]
        处理过程: table: tables [一个一个扫描获取填充 获取可能为多次GET请求]
        :return:
        """
        for table in self._tables:
            """
            1. 扫描列表型数据集 [只存在于table第一行, 且为JSON]
            2. 扫描对象型数据集 + 定位padding index
            3. 拉取对象型数据 + 数据填充 [对比dict_dataset, 以补全的策略拉取对象型数据集] 如数据集未注册, 同样删去
            4. 拉取列表型数据 + 根据List长度cv表单 + 数据填充
            """
            # 1. 扫描列表型数据集
            json_table_config = None
            row = table.rows[0]
            for cell in row.cells:
                set_result = self._regular_extract.match_pattern(
                    para_text=cell.text, patterns=['(?<={{).*?(?=}})']
                )
                for result in set_result:
                    try:
                        # Table Json
                        json_table_config = json.loads(result)
                        # print('Table Json: {}'.format(json_table_config))
                        row._element.getparent().remove(row._element)
                    except Exception as e:
                        pass
                if json_table_config:
                    break
            # 2. 扫描对象型数据集 + 定位padding index
            self._table_dict_template_label = dict()
            for index, cell in enumerate(table._cells):
                set_result = self._regular_extract.match_pattern(
                    para_text=cell.text, patterns=['(?<={{).*?(?=}})']
                )
                for result in set_result:
                    list_template_label = result.split('.')
                    if len(list_template_label) == 2:
                        template_dataset = list_template_label[0]
                        template_label = list_template_label[1]
                        if template_dataset in self._table_dict_template_label:
                            if template_label in self._table_dict_template_label[template_dataset]:
                                self._table_dict_template_label[template_dataset][template_label].add(index)
                            else:
                                self._table_dict_template_label[template_dataset][template_label] = {index}
                        else:
                            self._table_dict_template_label[template_dataset] = {template_label: {index}}
            # print("表格型数据对象{}".format(self._table_dict_template_label))
            # 3. 拉取对象型数据 + 数据填充
            list_dataset_name = list(self._table_dict_template_label.keys())
            # print(list_dataset_name)
            for dataset_name in self._table_dict_template_label:
                table_dict_label_index = self._table_dict_template_label[dataset_name]
                table_dict_padding_data = self._dict_padding_data[dataset_name]
                for _label in table_dict_label_index:
                    if _label in table_dict_padding_data:
                        for index in table_dict_label_index[_label]:
                            paragraphs = table._cells[index].paragraphs
                            for para in paragraphs:
                                self._padding_runs(
                                    runs=para.runs, dataset_name=dataset_name, field_name=_label,
                                    padding_data=table_dict_padding_data[_label]
                                )

    def _process_picture(self, index: int, json_pic_config: dict, data_result: dict, report_id: str) -> None:
        logger.info("=====进入图片加工程序=====")

        runs = self._paragraphs[index].runs
        for run in runs:
            run.text = ''

        run = runs[0]
        # todo: 基于报告id创建临时文件夹
        # picture_dir = os.path.join(root_dir, 'generate/echarts/{}'.format(report_id))
        # os.makedirs(picture_dir)
        picture_dir = report_id
        logger.info("正在处理中====")
        os.makedirs(picture_dir, exist_ok=True)
        logger.info("====创建目录结束====")

        # todo: 定义图片缓存名字
        time_stamp = str(datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
        # 解析json
        dataset_name = json_pic_config['chart.dataset'] if 'chart.dataset' in json_pic_config else None
        temp_file_name = "{}_{}_{}.png".format(report_id,
                                               time_stamp,
                                               dataset_name)
        picture_save_path = os.path.join(picture_dir, temp_file_name)
        logger.info(picture_save_path)

        # 获取数据对象
        result_data = data_result[dataset_name]
        # 绘图引擎
        # pic_name, pic_type = None, None
        if 'chart.type' in json_pic_config and 'chart.type2' not in json_pic_config:
            # 单图表 现版本支持: 饼图(pie) / 柱状图(bar) / 折线图(line)
            if json_pic_config['chart.type'] == 'pie':
                # echarts 饼图
                keys_name = json_pic_config['chart.xaxis_column'] if 'chart.xaxis_column' in json_pic_config else None
                values_name = json_pic_config['chart.yaxis_column'] if 'chart.yaxis_column' in json_pic_config else None
                keys, values = [], []
                for data in result_data:
                    keys.append(data[keys_name])
                    values.append(data[values_name])

                logger.info("当前处理的数据集key{}，和value{}".format(keys, values))

                pic_name = pic_echarts_pie(
                    pic_echarts_path=picture_save_path,
                    keys=keys,
                    values=values,
                    title=json_pic_config['chart.title'] if 'chart.title' in json_pic_config else None
                )
                # logger.info("当前生成的图片id{}，数据集名称为{}".format(report_id, dataset_name))
                # logger.info("===={}--饼状图已生成====".format(dataset_name))
            elif json_pic_config['chart.type'] == 'bar':
                # echarts 柱状图
                keys_name = json_pic_config[
                    'chart.xaxis_column'] if 'chart.xaxis_column' in json_pic_config else None
                list_values_name = json_pic_config[
                    'chart.yaxis_columns'] if 'chart.yaxis_columns' in json_pic_config else []
                keys, dict_values = [], {values_name: [] for values_name in list_values_name}
                for data in result_data:
                    keys.append(data[keys_name])
                    for values_name in list_values_name:
                        dict_values[values_name].append(data[values_name])
                pic_name = pic_echarts_bar(
                    temp_file_name=temp_file_name,
                    keys=keys,
                    dict_values=dict_values,
                    title=json_pic_config['chart.title'] if 'chart.title' in json_pic_config else None,
                    x_name=json_pic_config['chart.xaxis_name'] if 'chart.xaxis_name' in json_pic_config else None,
                    y_name=json_pic_config['chart.yaxis_name'] if 'chart.yaxis_name' in json_pic_config else None
                )
            elif json_pic_config['chart.type'] == 'line':
                # echarts 折线图
                keys_name = json_pic_config[
                    'chart.xaxis_column'] if 'chart.xaxis_column' in json_pic_config else None
                list_values_name = json_pic_config[
                    'chart.yaxis_columns'] if 'chart.yaxis_columns' in json_pic_config else []
                keys, dict_values = [], {values_name: [] for values_name in list_values_name}
                for data in result_data:
                    keys.append(data[keys_name])
                    for values_name in list_values_name:
                        dict_values[values_name].append(data[values_name])
                pic_name = pic_echarts_line(
                    temp_file_name=temp_file_name,
                    keys=keys,
                    dict_values=dict_values,
                    title=json_pic_config['chart.title'] if 'chart.title' in json_pic_config else None,
                    x_name=json_pic_config['chart.xaxis_name'] if 'chart.xaxis_name' in json_pic_config else None,
                    y_name=json_pic_config['chart.yaxis_name'] if 'chart.yaxis_name' in json_pic_config else None
                )
            else:
                pic_name = None

        elif 'chart.type' in json_pic_config and 'chart.type2' in json_pic_config:
            # 组合图表 现版本支持: 双轴图: 柱状图(bar) + 折线图(line)
            list_bar_values_name, list_line_values_name = [], []
            y_name_left, y_name_right = None, None
            if json_pic_config['chart.type'] == 'bar' and json_pic_config['chart.type2'] == 'line':
                pic_type = 'bar_line'
                list_bar_values_name = json_pic_config[
                    'chart.yaxis_columns'] if 'chart.yaxis_columns' in json_pic_config else []
                list_line_values_name = json_pic_config[
                    'chart.yaxis2_columns'] if 'chart.yaxis2_columns' in json_pic_config else []
                y_name_left = json_pic_config[
                    'chart.yaxis_name'] if 'chart.yaxis_name' in json_pic_config else None
                y_name_right = json_pic_config[
                    'chart.yaxis2_name'] if 'chart.yaxis2_name' in json_pic_config else None

            elif json_pic_config['chart.type'] == 'line' and json_pic_config['chart.type2'] == 'bar':
                pic_type = 'line_bar'
                list_bar_values_name = json_pic_config[
                    'chart.yaxis2_columns'] if 'chart.yaxis2_columns' in json_pic_config else []
                list_line_values_name = json_pic_config[
                    'chart.yaxis_columns'] if 'chart.yaxis_columns' in json_pic_config else []
                y_name_left = json_pic_config[
                                  'chart.yaxis2_name'] if 'chart.yaxis2_name' in json_pic_config else None,
                y_name_right = json_pic_config[
                    'chart.yaxis_name'] if 'chart.yaxis_name' in json_pic_config else None
            else:
                pic_type = None

            if pic_type:
                keys_name = json_pic_config[
                    'chart.xaxis_column'] if 'chart.xaxis_column' in json_pic_config else None
                keys = []
                dict_bar_values = {bar_values_name: [] for bar_values_name in list_bar_values_name}
                dict_line_values = {line_values_name: [] for line_values_name in list_line_values_name}
                for data in result_data:
                    keys.append(data[keys_name])
                    for bar_values_name in list_bar_values_name:
                        dict_bar_values[bar_values_name].append(data[bar_values_name])
                    for line_values_name in list_line_values_name:
                        dict_line_values[line_values_name].append(data[line_values_name])
                pic_name = pic_echarts_bar_line(
                    temp_file_name=temp_file_name,
                    keys=keys,
                    dict_bar_values=dict_bar_values,
                    dict_line_values=dict_line_values,
                    colors=['#d14a61', '#5793f3', '#2f4554'],
                    title=json_pic_config['chart.title'] if 'chart.title' in json_pic_config else None,
                    x_name=json_pic_config['chart.xaxis_name'] if 'chart.xaxis_name' in json_pic_config else None,
                    y_name_left=y_name_left,
                    y_name_right=y_name_right
                )
            else:
                pic_name = None

        else:
            pic_name = None

        if pic_name:
            run.add_picture(pic_name, width=shared.Cm(15))
            self._paragraphs[index].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            logger.error("当前报告id为：{}，图片{} 插入已完成".format(report_id, pic_name))
            try:
                os.remove(pic_name)
                logger.error("当前报告id为：{}，图片{} 删除已完成".format(report_id, pic_name))
            except:
                logger.info('file: {} 删除失败！'.format(pic_name))
        else:
            logger.warning("pic_name is NONE， 请检查程序！")

    def _process_style(self, para_index: int, list_padding_index: list, dict_style: dict) -> None:
        """
        支持定制加粗
        list_padding_index:
        list_bold:
        1. 降序 去除重复子串 √
        2. 遍历list_padding_index 待加粗字符串[max]
        3. 两种思路 [均未实现]
            1) 全程拆分 操作list
            2) 记录start_index end_index列表[需控制start_index&end_index不在记录列表所有SE间] 最后切分
            3) 先拆分为一个run存放一个char 并记录该label起始结束下标
            选用3) 原因是方便后续支持颜色 下划线等等等
            注意！！！若想支持其他 在1. 2.间加list即可 若太多可考虑dict 其余操作同2. 3.
        :param para_index:
        :param dict_style:  特定风格的字典{'bold': []..} [需加粗的文本列表]
        :return:
        """
        # 1. 降序 去除重复子串
        list_bold = dict_style['bold'] if 'bold' in dict_style else []
        list_bold_new = sorted(list_bold, key=lambda k: len(k), reverse=True)
        '''
        2022年04月01日 16:09:33修改
        1. 降序问题 -> 按照字符串长度降序
        2. 重复子串 -> 不可直接去除 因为公司全称不一定出现在文本中，也可能是简称
        3. 策略为按照 长->短 对run进行切分 特定加粗
        list_bold_new = []
        for bold in list_bold:
            bool_bold = False
            for bold_new in list_bold_new:
                if bold in bold_new:
                    bool_bold = True
            if not bool_bold:
                list_bold_new.append(bold)
        '''

        # 2. 拆分为一个run存放一个char 并记录该label起始结束下标
        style_sum_number = 0
        list_padding_index_new = []
        for padding_index in list_padding_index:
            start_index = padding_index + style_sum_number
            run = self._paragraphs[para_index].runs[start_index]
            run_text = copy.deepcopy(run.text)
            len_run_rext = len(run_text)
            run.text = run_text[: 1]
            for i in range(len_run_rext - 1):
                run = self._paragraphs[para_index].runs[padding_index + style_sum_number]
                _run = copy.deepcopy(run._r)
                _run.text = run_text[i + 1: i + 2]
                run._r.addnext(_run)
                style_sum_number += 1
                self._build()
            # 存放runs的[start_index, end_index]
            list_padding_index_new.append(
                {
                    'start_index': start_index,
                    'end_index': padding_index + style_sum_number
                }
            )
        # 3. 设置加粗 run.font.bold = True
        runs = self._paragraphs[para_index].runs
        for dict_padding_index in list_padding_index_new:
            padding_text = ''
            for run_index in range(dict_padding_index['start_index'], dict_padding_index['end_index'] + 1):
                padding_text += runs[run_index].text
            for bold_new in list_bold_new:
                for dict_index in self._regular_extract.match_index(para_text=padding_text, pattern_str=bold_new):
                    start_index = dict_index['start_index'] + dict_padding_index['start_index']
                    end_index = dict_index['end_index'] + dict_padding_index['start_index']
                    for i in range(start_index, end_index):
                        # todo: 加粗函数
                        runs[i].font.bold = True
                    '''
                    开放break: 只加粗首次出现的字体
                    注释break: 加粗所有匹配到的字体
                    '''
                    # break

    def _padding_dict(self, dataset_name: str) -> None:
        """
        填充数据 类型为: Dict(0)
        :param dataset_name: 数据集名称  info  | finance
        :return:
        """
        # todo: 此处由于对象有些区别，先获取待填充数据

        dict_label_index = self._dict_template_label[dataset_name]
        dict_padding_data = self._dict_padding_data[dataset_name]
        # print(dict_padding_data)
        # print(dict_label_index)

        self._padding(
            dataset_name=dataset_name,
            dict_label_index=dict_label_index,
            dict_padding_data=dict_padding_data
        )

    def _padding_list(self, dataset_name: str) -> None:
        """
        填充数据 类型为: List(1)
        :param dataset_name: 数据集名称
        :return:
        """
        list_label_index = self._dict_template_label[dataset_name]
        list_padding_data = self._dict_padding_data[dataset_name]
        for dict_label_index, dict_padding_data in zip(list_label_index, list_padding_data):
            self._padding(
                dataset_name=dataset_name, dict_label_index=dict_label_index, dict_padding_data=dict_padding_data
            )

    def _padding(self, dataset_name: str, dict_label_index: dict, dict_padding_data: dict) -> None:
        """
        模板填充
        填充文本为 str 或 list
        str:
            1. 填充
            2. 定制style
        list:
            多段渲染[正文常用]
            1. 存储 [在此实现]
            2. 根据para_index升序
            3. 渲染 + style
        :param dataset_name:        数据集名称
        :param dict_label_index:    待替换模板所在位置
        :param dict_padding_data:   待填充数据集
        :return:
        """
        for _label in dict_label_index:
            if _label in dict_padding_data:
                for index in dict_label_index[_label]:
                    padding_data = dict_padding_data[_label]
                    # 防止返回的数据为None类型[且没配置删除'^'] (为None经测试也没任何问题, 填充替换结果会变成'')
                    if type(padding_data) is str or type(padding_data) is int or type(padding_data) is list:
                        padding_data = padding_data
                    else:
                        padding_data = ''
                    # print(padding_data, type(padding_data))
                    if type(padding_data) is str or type(padding_data) is int:
                        '''
                        1. 填充
                        2. 定制style
                        '''
                        # 1) 填充
                        para = self._paragraphs[index]
                        list_padding_index = self._padding_runs(
                            runs=para.runs, dataset_name=dataset_name, field_name=_label,
                            padding_data=padding_data
                        )
                        # print(list_padding_index)
                        # 2) 定制style
                        if 'style' in dict_padding_data:
                            for dict_style in dict_padding_data['style']:
                                if _label == dict_style['field']:
                                    self._process_style(
                                        para_index=index,
                                        list_padding_index=list_padding_index,
                                        dict_style=dict_style
                                    )
                                    break
                    elif type(padding_data) is list:
                        '''
                        多段渲染[正文常用]
                        1. 存储 [在此实现]
                        2. 根据para_index升序
                        3. 渲染 + style
                        '''
                        dict_multi_para_render = {
                            'dataset_name': dataset_name,
                            'field_name': _label,
                            'para_index': index,
                            'list_padding_data': padding_data,
                            'para_num': len(padding_data)
                        }
                        if 'style' in dict_padding_data:
                            for dict_style in dict_padding_data['style']:
                                if _label == dict_style['field']:
                                    dict_multi_para_render['dict_style'] = dict_style
                        self._list_multi_para_render.append(dict_multi_para_render)

    def _padding_runs(self, runs, dataset_name: str, field_name: str, padding_data: str or int) -> list:
        """
        核心替换 [重点关注] 后续可能问题多多！！！
        功能描述:
                文档某一段落 分runs进行替换
                runs: list[]
                padding_data_str: str
                存在多处替换，但都是同一个dataset_name + dataset_label
        :param runs:            代替换文本
        :param dataset_name:    数据集名称 [模板配置前缀] (dataset).
        :param field_name:      数据集字段 [模板配置后缀] .(company_name)
        :param padding_data:    可用于填充的数据
        :return: 返回runs中填充label的定位下标列表 [供给特定style使用]
        """
        if type(padding_data) is int:
            padding_data = str(padding_data)

        begin, tmp = -100, ''
        template_label = dataset_name + '.' + field_name  # 模板标签全称[用于二次确认+替换]

        pattern_mustache = re.compile(r'{{.*?}}')
        if len(dataset_name) > 0 and dataset_name[0] == '^':
            pattern_template_label = '\^' + dataset_name[1:] + '\.' + field_name
        else:
            pattern_template_label = dataset_name + '\.' + field_name
        pattern_label = re.compile(r'' + '{{{{\s*{}\s*}}}}'.format(pattern_template_label))

        list_padding_index = []
        for index, run in enumerate(runs):
            '''
                重难点
                有标题格式 开头出现的 {{ 会分为两个run
                如果后续支持计算，需修改此处代码
                这里为何用if 而不是 elif
                run.text = '}}{{' 连一起这种情况
            '''
            if '{{' in run.text and begin == -100:
                begin, tmp = index, run.text
            elif '{' in run.text and begin == -100:
                begin, tmp = index, run.text
            else:
                tmp += run.text

            if pattern_mustache.findall(tmp):
                if template_label in tmp:
                    # 如果存在匹配的字符串，那么将当前的run替换成合并后得字符串tmp
                    run.text = re.sub(pattern_label, padding_data, tmp)
                    '''
                    len(runs) = 1
                    runs[0].text = '{{ base_info.risk_title }}'
                    或者 runs[i].text = ' }}' runs[i + 1].text = '{{ dataset_name.filed_name }}'
                    此时会出现 begin = -100, end_index = 0
                    IndexError: list index out of range
                    '''
                    if begin != -100:
                        for i in range(begin, index):
                            runs[i].text = ''

                    list_padding_index.append(index)
                    begin, tmp = -100, ''
                elif '{{' in run.text:
                    begin, tmp = index, run.text
                elif '{' in run.text:
                    begin, tmp = index, run.text
                else:
                    begin, tmp = -100, ''

        return list_padding_index

    def _padding_table(
            self, table, dataset_name: str,
            table_dict_label_index: dict,
            table_dict_padding_data: dict
    ) -> None:
        for _label in table_dict_label_index:
            if _label in table_dict_padding_data:
                for index in table_dict_label_index[_label]:
                    paragraphs = table._cells[index].paragraphs
                    for para in paragraphs:
                        self._padding_runs(
                            runs=para.runs, dataset_name=dataset_name, field_name=_label,
                            padding_data=table_dict_padding_data[_label]
                        )

    def _delete_annotation(self) -> None:
        """
        删除模板中注释: {{ !xxx }}
        :return: None
        """
        for index, para in enumerate(self._paragraphs):
            set_result = self._regular_extract.match_pattern(para_text=para.text, patterns=['(?<={{).*?(?=}})'])
            for result in set_result:
                if len(result) > 0 and result[0] == '!':
                    self._delete_paragraph(para=para)
        pass

    def _delete_paragraph(self, para) -> None:
        """
        删除word文档某段落
        :param para:
        :return: None
        """
        p = para._element
        p.getparent().remove(p)
        para._p = para._element = None
        self._build()

    def _save(self) -> None:
        """
        将word文档保存至本地
        :return:
        """
        # self._document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
        # self._document.styles['Normal'].font.name = 'Times New Roman'
        self._delete_annotation()
        self._document.save(self._output_report_path)

    def _add_para(self, para_before, para) -> None:
        """
        新插入段落 [已弃用]
        :param para_before:
        :param para:
        :return:
        """
        # 插入一个段落，代码换行，word中就是一个段落，不换行
        new_para = para_before.insert_paragraph_before()
        self._add_runs(new_para=new_para, para=para)

        """ Paragraph 段落格式设置 """
        # 设置段落文本右对齐
        new_para.paragraph_format.alignment = para.paragraph_format.alignment

        """ 段落缩进 """
        # 左缩进
        new_para.paragraph_format.left_indent = para.paragraph_format.left_indent
        # 右缩进
        new_para.paragraph_format.right_indent = para.paragraph_format.right_indent
        # 首行缩进
        new_para.paragraph_format.first_line_indent = para.paragraph_format.first_line_indent

        """ 行间距 """
        new_para.paragraph_format.line_spacing = para.paragraph_format.line_spacing
        "当line_spacing设置为长度值时表示绝对距离，"
        "设置为浮点数时表示行高的倍数"

        # 段前间距
        new_para.paragraph_format.space_before = para.paragraph_format.space_before
        # 段后间距
        new_para.paragraph_format.space_after = para.paragraph_format.space_after

        """ 设置段落内部文字在遇到需分页情况时处理状态 """
        new_para.paragraph_format.keep_together = para.paragraph_format.keep_together  # 段中不分页
        new_para.paragraph_format.keep_with_next = para.paragraph_format.keep_with_next  # 与下段同页
        new_para.paragraph_format.page_break_before = para.paragraph_format.page_break_before  # 段前分页
        new_para.paragraph_format.widow_control = para.paragraph_format.widow_control  # 孤行控制

        # 获取段落的左缩进，首行缩进，段前间距：
        l_space = new_para.paragraph_format.left_indent
        h_space = new_para.paragraph_format.first_line_indent
        b_space = new_para.paragraph_format.space_before
        # print(l_space, h_space, b_space)  # 457200 914400 63500
        self._build()

    def _add_runs(self, new_para, para) -> None:
        """
        新建段落[由runs组成] 新建run [已弃用]
        :param new_para:
        :param para:
        :return:
        """
        for run in para.runs:
            # 新建一个段落，增加一段文字
            new_run = new_para.add_run(run.text)
            new_run.bold = run.bold
            new_run.element = run.element
            """ 设置字体格式 """
            # new_run.font.name = run.font.name  # 注：这个好像设置 run 中的西文字体
            # new_run.font.element = run.font.element

            # new_run._element.rPr.rFonts = run._element.rPr.rFonts
            # new_run._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
            new_run.font.name = 'Times New Roman'  # 注：这个好像设置 run 中的西文字体
            # new_run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
            new_run._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')

            # new_run.style._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
            # new_run.style._element.rPr.rFonts.set(qn('w:ascii'), 'Times New Roman')
            # print(new_run.font.name)

            # 设置中文字体
            # new_run.font.element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
            # 设置字体大小
            new_run.font.size = run.font.size
            # 设置加粗
            new_run.font.bold = run.font.bold
            # 设置斜体
            new_run.font.italic = run.font.italic
            # 设置字体颜色
            new_run.font.color.rgb = run.font.color.rgb
            # 设置背景颜色
            new_run.font.highlight_color = run.font.highlight_color

            # 设置下划线
            new_run.font.underline = run.font.underline
            # 设置轮廓线
            new_run.font.outline = run.font.outline
            # 设置阴影
            new_run.font.shadow = run.font.shadow
            # 删除线
            new_run.font.strike = run.font.strike
            # 双删除线
            new_run.font.double_strike = run.font.double_strike
            # 设置下标
            new_run.font.subscript = run.font.subscript
            # 设置上标
            new_run.font.superscript = run.font.superscript

            new_run.italic = run.italic
            new_run.style.name = run.style.name
            new_run.underline = run.underline


def new_main_process(half_document, tables_dict, template_document, data_object, report_id, output_report_path):
    # todo： 步骤2——从半成品中复制需要的内容到模板中
    from copy_content import new_copy_content_main
    template_document = new_copy_content_main(half_document, template_document)
    # todo: 步骤3——将半成品中提取到的表格内容填充到模板中
    from copy_table import new_generate_report
    # todo: 先从半成品中提取需要的表格（基于 tables_dict 定义的表格内容）
    data_result = get_choose_table(half_document, list(tables_dict.values()))
    if not data_result["以名义金额计量的资产名称、数量等情况，以及以名义金额计量理由的说明"]:
        del data_result["以名义金额计量的资产名称、数量等情况，以及以名义金额计量理由的说明"]  # 删除key，vule
        tables_dict.update({"table13": "本单位无以名义金额计量的资产"})
        temp_table_result = get_choose_table(half_document, ["本单位无以名义金额计量的资产"])
        data_result.update(temp_table_result)
    logger.info("提取表格数据成功！")
    template_document = new_generate_report(data_result, template_document, tables_dict)
    # todo： 步骤4——填充基本数据到模板中
    project_name = str(report_id) + "四川报告"

    gurag = GeneralUserReportAutoGenerated(
        project_name=project_name,
        template_document=template_document,
        output_report_path=output_report_path,
        start_time=datetime.datetime.now(), end_time=datetime.datetime.now()
    )
    gurag.process(data_result=data_object,
                  report_id=report_id)

    return output_report_path


if __name__ == "__main__":
    import os

    data_object = {
        "finance": {
            "publicInfrastructureRatio": "",
            "affordabelHouseNewRatio": "",
            "assetLiabilityRatio": "24.31%",
            "assetLiabilityRatioRemark": "说明本单位财务风险低",
            "beInDebt": "264.31",
            "beInDebtChangeRatio": "24.88%",
            "beInDebtChangeRatioRemark": "主要原因是本年的其他应付款的减少",
            "cashRatio": "248.35%",
            "cashRatioRemark": "说明本单位利用现金和现金等价物偿还短期债务的能力强",
            "composition": "流动资产占100.00%",
            "currentAssetsCompose": "货币资金",
            "currentLiabilitiesCompose": "其他应付款",
            "currentRatio": "248.35%",
            "currentRatioRemark": "说明本单位流动资产偿还短期债务的能力强",
            "debtComparisonRatio": "减少87.56万元，减少24.88%",
            "debtComposition": "流动负债占100.00%",
            "fixedAssetsDepreciationRatio": "77.75%",
            "fixedAssetsDepreciationRatioRemark": "说明本单位固定资产持续服务能力较强",
            "netAssets": "823.10",
            "nonCurrentAssetsCompose": "固定资产原值、固定资产净值、无形资产原价和无形资产净值",
            "otherRemark": "资产总额较上年增减变动幅度超过20%主要原因是货币资金减少265.35万元;负债总额较上年增减变动幅度超过20%主要原因是其他应付款减少87.56万元",
            "revenueExpensesRatio": "100.00%",
            "revenueExpensesRatioRemark": "小于",
            "surplus": "",
            "totalAssets": "1087.40",
            "totalAssetsChangeRatio": "20.53%",
            "totalAssetsChangeRatioRemark": "主要原因是本年的货币资金的减少",
            "totalAssetsComparison": "减少280.84万元，减少20.53%",
            "totalExpenses": "1890.01",
            "totalExpensesChangeRatio": "0.20%",
            "totalExpensesChangeRatioRemark": "主要原因是本年的业务活动费用的减少",
            "totalExpensesComparison": "减少3.77万元，减少0.20%",
            "totalExpensesCompose": "业务活动费用",
            "totalRevenue": "1890.01",
            "totalRevenueChangeRatio": "15.49%",
            "totalRevenueChangeRatioRemark": "主要原因是本年的财政拨款收入的减少",
            "totalRevenueComparison": "减少346.51万元，减少15.49%",
            "totalRevenueCompose": "财政拨款收入和其他收入",
            "totalRevenueComposeDetail": "财政拨款收入占比99.88%、其他收入占比0.12%",
            "unitAssetComposition": "流动资产",
            "unitDebtComposition": "流动负债"
        },
        "收入占比": [
            {
                "beforeDataValue": "1050.89",
                "dataValue": "1107.31",
                "indexName": "财政拨款收入",
                "subtractValue": "56.42"
            }
        ],
        "流动负债占比": [
            {
                "beforeDataValue": "0.28",
                "dataValue": "0.28",
                "indexName": "应交增值税",
                "subtractValue": "0.00"
            },
            {
                "beforeDataValue": "3.05",
                "dataValue": "3.06",
                "indexName": "其他应交税费",
                "subtractValue": "0.01"
            },
            {
                "beforeDataValue": "0.28",
                "dataValue": "0.28",
                "indexName": "应付职工薪酬",
                "subtractValue": "0.00"
            },
            {
                "beforeDataValue": "10.31",
                "dataValue": "7.32",
                "indexName": "其他应付款",
                "subtractValue": "2.99"
            }
        ],
        "流动资产占比": [
            {
                "beforeDataValue": "0.15",
                "dataValue": "0.07",
                "indexName": "货币资金",
                "subtractValue": "0.08"
            },
            {
                "beforeDataValue": "0.26",
                "dataValue": "0.26",
                "indexName": "应收账款净额",
                "subtractValue": "0.00"
            },
            {
                "beforeDataValue": "508.44",
                "dataValue": "481.68",
                "indexName": "其他应收款净额",
                "subtractValue": "26.76"
            }
        ],
        "负债占比": [
            {
                "beforeDataValue": "13.92",
                "dataValue": "10.94",
                "indexName": "流动负债合计"
            }
        ],
        "费用占比": [
            {
                "beforeDataValue": "939.43",
                "dataValue": "1012.68",
                "indexName": "业务活动费用",
                "subtractValue": "73.25"
            },
            {
                "beforeDataValue": "189.10",
                "dataValue": "192.33",
                "indexName": "单位管理费用",
                "subtractValue": "3.23"
            }
        ],
        "资产占比": [
            {
                "beforeDataValue": "508.85",
                "dataValue": "482.00",
                "indexName": "流动资产合计"
            },
            {
                "beforeDataValue": "1236.49",
                "dataValue": "1122.16",
                "indexName": "非流动资产合计"
            }
        ],
        "非流动资产占比": [
            {
                "beforeDataValue": "200.00",
                "dataValue": "160.00",
                "indexName": "长期股权投资",
                "subtractValue": "40.00"
            },
            {
                "beforeDataValue": "1018.90",
                "dataValue": "962.16",
                "indexName": "固定资产净值",
                "subtractValue": "56.74"
            }
        ],
        "info": {
            "internalControl": "2021年，本单位加强学习国家和省关于内部控制的文件。建立健全了单位层面的内部控制体系和制度，健全了预算、收支、采购、建设、资产和合同的内控流程和制度，把内部控制落实在业务流程中，实现了不相容岗位相互分离、形成相互制约、相互监督的工作机制；实现了内部授权审批控制。",
            "unitName": "安岳县元坝镇人民政府",
            "unitCall": "本部门",
            "mainFunctions": """1．主要职能。&&&坚持依法行政，切实转变经济发展方式，进一步强化社会管理和公共服务职能，努力实现从“管理型”政府向“法治型”和“服务型”政府转变，承办好上级党委、政府交办的工作，现阶段，主要围绕以下几个方面履行职能。 &&&(1)贯彻执行党和国家的有关方针、政策、法律和法规；执行上级政府的决定和命令；拟定适合本镇实际的具体政策措施，并有效地组织实施。&&&(2)负责全镇经济和社会的宏观管理，编制和执行镇域经济和社会发展计划工作。&&&(3)负责本行政区域内的经济、教育、科学、文化、卫生等事业发展和计划生育工作；负责本辖区社会治安综合治理，维护社会稳定。&&&(4)依法保护和管理国有资产和集体财产，保障公民合法权利，保护各种经济组织的合法权益。&&&(5)承办县人民政府交办的其它事项。&&&2．机构情况。&&&我镇2022年机构为3个单位，其中行政单位1个（元坝镇人民政府），事业单位2个（元坝镇社会事务中心、元坝镇农业服务中心）。&&&(1)党政综合办公室&&&负责镇党委、政府机关日常工作。负责起草文件、材料和会议组织及有关决定决议的落实、督办、信息收集和报送；负责办理日常公文、档案管理和保密工作；承担规范性文件的审核、把关和管理工作；具体负责纪检监察、组织人事、青年、宣传、群团等工作；负责镇机关的行政事务和后勤保障服务工作；负责镇应急和综合协调工作，督促检查工作落实；承办镇党委、政府及上级部门交办的其他事项。&&&(2)党建办公室&&&统筹基层党组织建设，负责纪检监察、组织人事、宣传、统战、机构编制、机关党建、群团事务等工作，负责人大、政协相关服务工作，承办镇党委、政府及上级部门交办的其他事项。&&&(3)综合行政执法办公室&&&负责统筹协调基层站所、县级部门派出执法力量和资源，综合开展综合行政执法工作，承办镇党委、政府及上级部门交办的其他事项。&&&(4)社会事务管理办公室&&&负责民政、教育体育、文化旅游、卫生健康、社会保障、医疗保障等工作，持扶贫恤孤、助残敬老，为各种自然灾害造成的受灾户、特困户、临时困难户提供救济；做好“一保五难”工作；为军烈属、复退军人提供优抚、补助和救济，安置退伍军人和军休干部；建设和管理好敬老院，建立社会服务体系；宣传残疾人就业、学习、优惠政策，协助老龄委工；协调有关部门处理违法婚姻，抓好殡葬改革，提倡节约；协调有关部门搞好政务公开、村务公开等工作；参与农村基层政权建设和基层群众自治建设，搞好村委会村民自治和社区居委会建设工作；承办镇党委、政府及上级部门交办的其他事项。&&&(5)经济发展和乡村振兴办公室&&&负责经济发展、科学技术、生态环境、村镇建设、交通运输、农业农村、商务经济合作、统计、扶贫开发、产业发展和项目推进等工作推进农业、工业、第三产业发展规划的制定实施及强农惠农政策措施的落实，统筹产业发展布局和结构调整工作，负责土地流转、农村集体资产财务管理、市场监管、安全生产、环境保护、国民经济和社会产业综合统计等工作，协调与经济发展相关的其他工作；承办镇党委、政府及上级部门交办的其他事项。&&&(6)社会治理办公室&&&负责社会治安综合治理、矛盾纠纷化解、信访维稳、安全生产、应急抢险救灾、掌握、分析辖区内的治安、稳定形势和工作动态，及时向上反馈信息，重大问题提请镇党委、政府研究；组织协调各有关部门共同解决辖区内突出的治安、稳定问题。组织开展矛盾纠纷排查调处工作，定期开展排查调处工作；定期上报矛盾纠纷排查调处报表，重要情况及时上报；组织开展基层平安建设活动和治安防范工作及军、警民联防活动；加强防控体系建设；组织开展防范和处理邪教问题工作，加强警示教育，做好外来流动人口服务管理工作；承办镇党委、政府及上级部门交办的其他事项。&&&(7)财政所&&&进一步贯彻执行国家有关财政管理等方面的法律、法规和规章；拟定和执行镇财政发展规划及其他有关政策；编制镇年度财政预算草案并组织执行；向镇人大报告财政决算；管理和监督镇各项财政收支；管理各类政策性补贴等资金，进一步完善财政补贴农民资金“一卡（折）通”发放机制；负责对各类专项资金的监管，负责财政、税收政策法规的宣传工作；严格按照上级财政部门规定的工作程序开展工作，充分发挥财政资金效益；负责本镇行政事业单位的国有资产监督管理工作；负责做好农村综合改革和社会主义新农村建设相关工作；承办镇党委、政府及上级部门交办的其他事项。&&&(8)便民服务中心&&&牵头乡镇便民服务工作，负责退役军人、文化宣传、就业和社会保障、社会保险等相关服务工作；承办镇党委、政府及上级部门交办的其他事项。&&&(9)农业综合服务中心&&&负责脱贫攻坚、农技推广、农业机械化、农产品质量监管、水利等相关服务工作；承办镇党委、政府及上级部门交办的其他事项。&&&(10)村镇建设综合服务中心&&&负责村镇建设环卫、城乡环境综合治理、农房改造等相关服务工作；承办镇党委、政府及上级部门交办的其他事项。&&&(11)农民工服务中心&&&负责农民工输出、培训、维权、回引及返乡创业等相关服务工作；承办镇党委、政府及上级部门交办的其他事项。&&&3.人员情况。&&&我镇2022年年末在职人数61人，其中：行政在职34人，机关工勤在职2人，事业在职人数25人。
""",
            "year": "2021",
            "amountDescription": "",
            "unitBudgetLevel": "二级预算单位",
            "institutionalSituation": "无资料数据",
            "performanceManagement": "2021年，本单位按照绩效管理要求对照设定预算绩效目标、绩效指标的成本指标、产出指标、效益指标、满意度指标等具体内容，开展项目绩效目标申报、运行监控和自评工作。通过预算绩效管理对工作中存在的薄弱环节作出针对性查漏补缺和持续完善。",
            "LastYear": "2020",
            "personnelSituation": "",
            "unitType": "行政单位",
            "budgetManagement": "2021年，本单位严格按照《预算法》、《会计法》、《政府会计制度》和上级的文件建立健全财务制度；严格执行财经纪律和各项财务制度；强化预算管理，加强对银行存款和现金的管理；单位对年终决算高度重视，组织专人负责编制决算报告，对决算数据进行了严格审核，认真分析并应用到下年的预算工作。",
            "assetManagement": "2021年，本单位资产实行分类管理，建立健全了资产内部管理制度；单位加强对实物资产和无形资产的管理，明确相关部门和岗位的职责权限，强化对配置、使用和处置等关键环节的管控；明确资产使用和保管责任人，落实资产使用人在资产管理中的责任。",
            "pppProject": "本单位无PPP项目。",
            "careerAchievements": """坚持以经济建设为中心，抢抓各种发展机遇，全面推进基础设施建设及重点项目建设，全力抓好农村剩余劳动力转移、城镇新增就业、党员创业带富工程及各类社会救助保障体系建设等工作，大力支持和鼓励辖区内中小企业发展，千方百计增加就业岗位，不断提高群众可支配收入，全镇综合实力得到进一步提升。
"""
        }
    }
    half_work_path = "../data/5月9号半成品.docx"
    template_path = "../data/5月9号模板.docx"
    template_document = Document(template_path)
    half_document = Document(half_work_path)
    save_path = "../data" + str(datetime.datetime.now().strftime('%Y%m%d%H%M%S')) + ".docx"
    tables_dict = {
        "table13": "以名义金额计量的资产名称、数量等情况，以及以名义金额计量理由的说明",
        "table5": "收入费用表（2）",
        "table4": "收入费用表（1）",
        "table3": "资产负债表续表2",
        "table2": "资产负债表续表1",
        "table1": "资产负债表",
    }
    report_id = "1234567"
    report_name = "5月10号财务报告测试"
    output_report_path = os.path.join("../data",
                                      '{}_{}.docx'.format(str(datetime.datetime.now().strftime('%Y%m%d%H%M%S')),
                                      report_name))
    report_processed_path = new_main_process(half_document,
                                             tables_dict,
                                             template_document,
                                             data_object,
                                             report_id,
                                             output_report_path)

