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

import re, os
import copy
import json

import chevron
import datetime
import requests
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 utils.database_mysql import DatabaseMySQL
from pathlib import Path
# from app_run import doc2docx
# from transform_doc_to_docx import doc2docx
UPLOAD_FOLDER = r'../data'       # 上传路径
Path(UPLOAD_FOLDER).mkdir(parents=True, exist_ok=True)
# 定义数据库链接基础信息
# database_config = {
#     'host': '114.115.159.144',
#     'port': 3306,
#     'user': 'root',
#     'password': 'zzsn9988',
#     'database': 'clb_project'
# }
database_config = {
    'host': '114.115.205.50',
    # 'host': '114.116.44.11',
    'port': 3306,
    'user': 'root',
    # 'password': 'f7s0&7qqtK',
    'password': 'yotop@123456',
    'database': 'clb_project'
}
dbm = DatabaseMySQL(database_config=database_config)
temp_url = "http://114.115.215.96/"
# temp_url = "http://114.115.205.50/"
# task_id = '1641261934625521666'
# dataset_id = '1641045122365317122'


class GeneralUserReportAutoGenerated(object):
    """
    1. 扫描数据源
    2. 拉取数据
    3. 数据填充
    """
    def __init__(self, project_name: str, input_template_path: str, output_report_path: str,
                 start_time: datetime, end_time: datetime, parameter='',
                 is_handle_success='ynHandleSuccess', result_data='resultData'):
        super(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._input_template_path = input_template_path
        self._output_report_path = output_report_path
        self._document = Document(self._input_template_path)
        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) -> 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)
        # 2. 获取数据信息
        logger.info('获取数据信息')
        # self._getting_data(data_result)
        self._dict_padding_data = data_result
        logger.info("处理第一部分普通的info.year后的结果: {}".format(self._dict_template_label))
        # todo：当前数据内容
        logger.info("当前待填充数据内容{}".format(self._dict_padding_data))

        # 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(data_result)
        # 多段渲染 [统一渲染]
        self._process_multi_para_render()
        # 4. 表格生成引擎(表格第一行Json + 表格内对象型数据集 + 填充)
        logger.info('启动表格生成引擎(表格第一行Json + 表格内对象型数据集 + 填充)')
        # 5. 表格数据填充
        # todo： 基于self._getting_data 函数得到 self._dict_padding_data
        self._process_table()
        # 6. 报告生成完毕 保存
        logger.info('报告生成完毕! 保存......')
        self._save()

    # todo: 先找到待填充数据的位置（相应的段落索引，索引下标默认从0开始计数）
    def _scanning_data(self, set_dataset_name: set, data_result: dict) -> None:
        """
        扫描数据
        :param set_dataset_name: index已经重定位好[已填充]的数据 仅有data_type=1会插入其中
                                                            补充: 若数据集未注册，则也会在拉取数据集时存入，以后便不再扫描该数据源
        :return:
        """
        start_index, end_index, dataset_name = None, None, 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:
                # 原始步骤1 扫描列表渲染 [start_index: end_index]
                # todo: 先处理“#zhanlue”样式  找到所有待渲染的数据集名称并将其相应的段落索引（起始）加入待渲染列表self._dict_list_render中，数据对象为第二部分的数据集列表
                # {'zhanlue': [{'start_index': 12, 'end_index': 14}], 'caiwu': [{'start_index': 17, 'end_index': 19}], 'shichang': [{'start_index': 22, 'end_index': 24}], 'yunying': [{'start_index': 27, 'end_index': 29}], 'falv': [{'start_index': 32, 'end_index': 34}], 'centerHonest': [{'start_index': 37, 'end_index': 39}], 'localHonest': [{'start_index': 40, 'end_index': 42}], 'private': [{'start_index': 46, 'end_index': 48}]}
                if len(result) > 0 and result.strip()[0] == '#':
                    start_index = index
                    dataset_name = result.strip()[1:]
                    continue
                elif len(result) > 0 and result.strip()[0] == '/':
                    end_index = index
                    if result.strip()[1:] == dataset_name and dataset_name not in set_dataset_name:
                        if dataset_name not in self._dict_list_render:
                            self._dict_list_render[dataset_name] = []
                        self._dict_list_render[dataset_name].append(
                            {
                                'start_index': start_index,
                                'end_index': end_index
                            }
                        )
                    start_index, end_index, dataset_name = None, None, None
                    continue

                # 原始步骤2 特殊文本型渲染: {{ ^dataset.title }} 若title为None或''则删除该行(不进行渲染)。 数据对象为第一部分的info数据集   待处理样式 ^info.zlRisk
                if len(result) > 0 and result[0] == '^':
                    list_template_label = result[1:].split('.')
                    if len(list_template_label) == 2:
                        template_dataset_name = list_template_label[0]
                        template_dataset_label = list_template_label[1]
                        if template_dataset_name not in set_dataset_name:
                            # print("当前模板标签索引对象为: {}".format(self._dict_template_label_del))
                            if template_dataset_name in self._dict_template_label_del:
                                # print(self._dict_template_label_del[template_dataset_name])
                                # 如果info数据集的label 对象已存在，则追加段落索引位置，反之则插入新的label对象到模板标签集合中，当前该情况不存在。
                                if template_dataset_label in self._dict_template_label_del[template_dataset_name]:
                                    self._dict_template_label_del[template_dataset_name][template_dataset_label].add(index)
                                else:
                                    # 当前info数据集的label 对象不存在，将新的标签对象插入到模板标签集合中
                                    self._dict_template_label_del[template_dataset_name][template_dataset_label] = {index}
                            else:
                                # 由于数据集名称都是第一部分的对象“info”，则只有初始的时候进入该部分，处理后的结果为{"info": {'zlRisk': {11}}}
                                self._dict_template_label_del[template_dataset_name] = {template_dataset_label: {index}}
                    continue

                # 图片型渲染
                try:
                    json_result = json.loads(result)
                    logger.info('Picture Json: {}'.format(json_result))
                    # todo: 基于传过来的数据生成相应的数据图
                    self._process_picture(index=index, json_pic_config=json_result, data_result=data_result)
                    continue
                except Exception as e:
                    pass
                # 文本渲染(对象型)
                # todo： 处理第一部分info.year普通标签
                list_template_label = result.split('.')
                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}}
                    continue

    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, data_result) -> 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或[]
            '''

            '''
            在此判断:
                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]:
                    # print(self._dict_template_label)
                    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, data_result=data_result)

    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)
                        logger.info('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].strip()
                        template_label = list_template_label[1].strip()
                        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}}
            logger.info("表格型数据对象{}".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'].strip() if 'table.dataset' in json_table_config else None

                # 3). 组织请求接口参数 + 拉取数据 + cv表单 + 数据填充
                """
                    先处理字典型表格: 不需要cv 不需要更新padding index 直接padding
                """
                # cv表单 + 数据填充
                if type(self._dict_padding_data[dataset_name]) is dict:
                    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 type(self._dict_padding_data[dataset_name]) is list:
                    """
                    列表型表格:   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) -> None:
        runs = self._paragraphs[index].runs
        for run in runs:
            run.text = ''
        run = runs[0]
        # 解析json
        dataset_name = json_pic_config['chart.dataset'] if 'chart.dataset' in json_pic_config else None
        # 获取数据对象
        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 = [], []
                # todo: 不能放在模板里来获取， 将key 和 vlaue 作为参数来进行赋值
                for data in result_data:
                    keys.append(data[keys_name])
                    values.append(data[values_name])
                pic_name = pic_echarts_pie(
                    keys=keys,
                    values=values,
                    title=json_pic_config['chart.title'] if 'chart.title' in json_pic_config else None
                )
                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(
                    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(
                    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 '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

            if pic_type == 'bar_line' or pic_type == 'line_bar':
                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(
                    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
                )

        if pic_name:
            run.add_picture(pic_name, width=shared.Cm(15))
            self._paragraphs[index].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

    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 pl_process_pro(task_id):
#     project_name = "平台报告"
#     report_name = "平台模板样例报告.docx"
#     # template_dir, template_name = os.path.split(template_path)
#     # todo: 先基于任务id获取数据集信息  包括数据集id， 参数值， 模板地址
#     # dataset_sql = '''SELECT ds.id,ds.param_value,te.file_path FROM clb_report_task t inner join clb_report_template te on t.template_id = te.id
#     # inner join clb_report_data_set ds on te.data_set_id = ds.id
#     # where t.id  = {};'''.format(task_id)
#     dataset_sql = """SELECT ds.id,te.file_path FROM clb_report_task t inner join clb_report_template te on t.template_id = te.id
#                         inner join clb_report_data_set ds on te.data_set_id = ds.id
#                         where t.id  = {};""".format(task_id)
#
#     dataset_result = dbm.query(sql=dataset_sql)[0]
#     dataset_id, temp_path = dataset_result["id"], dataset_result["file_path"]
#     # dataset_id, param_value, temp_path = dataset_result["id"], dataset_result["param_value"], dataset_result["file_path"]
#     # print(type(param_value))
#     # param_value = json.loads(param_value)
#     # todo: 再基于数据集id 获取数据集地址，参数，返回数据类型，数据对象
#     # data_source_sql = """select ds.url,ds.params,ds.type,ds.data_name from clb_report_data_source ds inner join clb_report_data_set_source_map m on ds.id = m.data_source_id
#     #  where m.data_set_id = {};""".format(dataset_id)
#
#     data_source_sql = """select ds.url,m.param_value,ds.type,m.return_object from clb_report_data_source ds inner join clb_report_data_set_source_map m on ds.id = m.data_source_id
#     where m.data_set_id = {};""".format(dataset_id)
#
#     datasource_result_list = dbm.query(sql=data_source_sql)
#     # 关闭数据库连接
#     dbm.close()
#     # todo: 第一步：基于模板路径和模板url 获取下载模板链接
#     template_request = temp_url + "/" + temp_path
#     # 先判断是否是docx 格式
#     template_filename = template_request.split("/")[-1]
#     # 文件先下载再判断是否转换
#     if ".doc" in template_request:
#         r = requests.get(template_request, stream=True)
#         with open(os.path.join(UPLOAD_FOLDER, template_filename), "wb") as f:
#             for chunk in r.iter_content(chunk_size=512):
#                 f.write(chunk)
#
#         temp_template_path = os.path.join(UPLOAD_FOLDER, template_filename)
#         # 获取文件路径前缀
#         template_path = os.path.splitext(temp_template_path)[0] + '.docx'
#         # 将doc转换为docx
#         # doc2docx(temp_template_path, template_path)
#
#     elif ".docx" in template_request:
#         r = requests.get(template_request, stream=True)
#         with open(os.path.join(UPLOAD_FOLDER, template_filename), "wb") as f:
#             for chunk in r.iter_content(chunk_size=512):
#                 f.write(chunk)
#         template_path = os.path.join(UPLOAD_FOLDER, template_filename)
#
#     # todo: 第二步：基于数据源信息获取数据对象
#     dict_data = dict()
#     for datasource_result in datasource_result_list:
#         dataset_url = datasource_result["url"]
#         params = datasource_result["param_value"]
#         # 4月23号调整
#         dict_param = json.loads(params)
#         # {"id": 2, "name": "nihao"}
#         connect_list = []
#         for key, value in dict_param.items():
#             connect_param = key + "=" + value
#             connect_list.append(connect_param)
#
#         # connect_list = []
#         # for temp_param in params_list:
#         #     connect_param = temp_param + "=" + param_value[temp_param]
#         #     connect_list.append(connect_param)
#         request_str = "&".join(connect_list)
#         dataset_request = dataset_url + "?" + request_str
#         str_dataset_info = requests.get(dataset_request, stream=True)
#         # logger.info(str_dataset_info.content)
#         dataset_info = json.loads(str_dataset_info.content)
#         data_name = datasource_result["return_object"]
#         dict_data[data_name] = dataset_info["result"]
#     logger.info(dict_data)
#     template_dir, template_name = os.path.split(template_path)
#     gurag = GeneralUserReportAutoGenerated(
#         project_name=project_name,
#         input_template_path=template_path,
#         output_report_path=os.path.join(template_dir, report_name),
#         start_time=datetime.datetime.now(), end_time=datetime.datetime.now()
#     )
#     gurag.process(dict_data)
#     return None


def pl_process(template_path, dict_data, report_name):
    project_name = "平台报告"
    template_dir, template_name = os.path.split(template_path)
    gurag = GeneralUserReportAutoGenerated(
        project_name=project_name,
        input_template_path=template_path,
        output_report_path=os.path.join(template_dir, report_name),
        start_time=datetime.datetime.now(), end_time=datetime.datetime.now()
    )
    gurag.process(dict_data)
    return None


def new_pl_process_pro(task_id):
    project_name = "平台报告"
    report_name = "平台模板样例报告.docx"
    # template_dir, template_name = os.path.split(template_path)
    # todo: 先基于任务id获取数据集信息  包括数据集id， 参数值， 模板地址
    # dataset_sql = '''SELECT ds.id,ds.param_value,te.file_path FROM clb_report_task t inner join clb_report_template te on t.template_id = te.id
    # inner join clb_report_data_set ds on te.data_set_id = ds.id
    # where t.id  = {};'''.format(task_id)
    dataset_sql = """SELECT ds.id,te.file_path FROM clb_report_task t inner join clb_report_template te on t.template_id = te.id 
                        inner join clb_report_data_set ds on te.data_set_id = ds.id
                        where t.id  = {};""".format(task_id)

    dataset_result = dbm.query(sql=dataset_sql)[0]
    dataset_id, temp_path = dataset_result["id"], dataset_result["file_path"]
    # dataset_id, param_value, temp_path = dataset_result["id"], dataset_result["param_value"], dataset_result["file_path"]
    # print(type(param_value))
    # param_value = json.loads(param_value)
    # todo: 再基于数据集id 获取数据集地址，参数，返回数据类型，数据对象
    # data_source_sql = """select ds.url,ds.params,ds.type,ds.data_name from clb_report_data_source ds inner join clb_report_data_set_source_map m on ds.id = m.data_source_id
    #  where m.data_set_id = {};""".format(dataset_id)

    data_source_sql = """select ds.url,m.param_value,ds.type,m.return_object from clb_report_data_source ds inner join clb_report_data_set_source_map m on ds.id = m.data_source_id
    where m.data_set_id = {};""".format(dataset_id)

    datasource_result_list = dbm.query(sql=data_source_sql)
    # 关闭数据库连接
    dbm.close()
    # todo: 第一步：基于模板路径和模板url 获取下载模板链接
    template_request = temp_url + "/" + temp_path
    # 先判断是否是docx 格式
    template_filename = template_request.split("/")[-1]
    # 文件先下载再判断是否转换
    if ".doc" in template_request:
        r = requests.get(template_request, stream=True)
        with open(os.path.join(UPLOAD_FOLDER, template_filename), "wb") as f:
            for chunk in r.iter_content(chunk_size=512):
                f.write(chunk)

        temp_template_path = os.path.join(UPLOAD_FOLDER, template_filename)
        # 获取文件路径前缀
        template_path = os.path.splitext(temp_template_path)[0] + '.docx'
        # 将doc转换为docx
        # doc2docx(temp_template_path, template_path)

    elif ".docx" in template_request:
        r = requests.get(template_request, stream=True)
        with open(os.path.join(UPLOAD_FOLDER, template_filename), "wb") as f:
            for chunk in r.iter_content(chunk_size=512):
                f.write(chunk)
        template_path = os.path.join(UPLOAD_FOLDER, template_filename)

    # todo: 第二步：基于数据源信息获取数据对象
    dict_data = dict()
    for datasource_result in datasource_result_list:
        dataset_url = datasource_result["url"]
        params = datasource_result["param_value"]
        print(params)
        # 4月23号调整
        dict_param = json.loads(params)
        # {"id": 2, "name": "nihao"}
        connect_list = []
        for key, value in dict_param.items():
            connect_param = key + "=" + value
            connect_list.append(connect_param)

        # connect_list = []
        # for temp_param in params_list:
        #     connect_param = temp_param + "=" + param_value[temp_param]
        #     connect_list.append(connect_param)
        request_str = "&".join(connect_list)
        dataset_request = dataset_url + "?" + request_str
        str_dataset_info = requests.get(dataset_request, stream=True)
        # logger.info(str_dataset_info.content)
        dataset_info = json.loads(str_dataset_info.content)
        data_name = datasource_result["return_object"]
        dict_data[data_name] = dataset_info["result"]
    logger.info(dict_data)
    template_dir, template_name = os.path.split(template_path)
    gurag = GeneralUserReportAutoGenerated(
        project_name=project_name,
        input_template_path=template_path,
        output_report_path=os.path.join(template_dir, report_name),
        start_time=datetime.datetime.now(), end_time=datetime.datetime.now()
    )
    gurag.process(dict_data)
    return None

# print(pl_process(task_id="1641261934625521666"))


if __name__ == "__main__":
    new_pl_process_pro("1650786947993526274")
    # import os
    # project_name = "平台报告"
    # # save_path = "../data/new_平台模板定义样例.docx"
    # save_path = "../data/4月25日周报模板(2)(2).docx"
    # template_dir, template_name = os.path.split(save_path)
    # report_name = "4月25平台模板样例报告.docx"
    # gurag = GeneralUserReportAutoGenerated(
    #     project_name=project_name,
    #     input_template_path=save_path,
    #     output_report_path=os.path.join(template_dir, report_name),
    #     start_time=datetime.datetime.now(), end_time=datetime.datetime.now()
    # )
    # data_result = {'info': {'year': 2023, 'num': '3', 'beginDate': '2023年04月17日', 'endDate': '2023年04月23日'},
    #                'zhongyaojianghua': [{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'},{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'}],
    #                'zhongdahuiyi': [{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'},{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'}],
    #                'diyiyiti': [{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'},{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'}],
    #                'qqjjfzqs': [{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'},{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'}],
    #                'cyzcjzx': [{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'},{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'}],
    #                'zhongdashijian': [{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'},{'title': "11", 'summary': '22', 'origin': '33', 'publishDate': '2023年04月23日'}],
    #                }
    # # data_result = {'originDateRatio': [{'date': '2023-02', '网易新闻': 551, '新浪财经': 145, '占比': '26.32%'}, {'date': '2023-03', '网易新闻': 68, '新浪财经': 83, '占比': '122.06%'}], 'originDate': [{'name': '澎湃新闻', '2023-02': 100, '2023-03': 39}, {'name': '光明网', '2023-02': 78, '2023-03': 14}, {'name': '网易新闻', '2023-02': 551, '2023-03': 68}, {'name': '新浪财经', '2023-02': 145, '2023-03': 83}, {'name': '新浪新闻', '2023-02': 112, '2023-03': 72}, {'name': '国际在线', '2023-02': 50, '2023-03': 25}, {'name': '手机光明网', '2023-02': 73, '2023-03': 25}, {'name': '腾讯新闻', '2023-02': 54, '2023-03': 16}, {'name': '环球网', '2023-02': 85, '2023-03': 35}, {'name': '网易', '2023-02': 326, '2023-03': 159}], 'origin': [{'name': '网易新闻', 'value': 619}, {'name': '网易', 'value': 485}, {'name': '新浪财经', 'value': 228}, {'name': '新浪新闻', 'value': 184}, {'name': '澎湃新闻', 'value': 139}, {'name': '环球网', 'value': 120}, {'name': '手机光明网', 'value': 98}, {'name': '光明网', 'value': 92}, {'name': '国际在线', 'value': 75}, {'name': '腾讯新闻', 'value': 70}], 'newList': [{'title': '联合国安理会就叙利亚局势举行公开会', 'summary': '当地时间3月23日，联合国安理会就叙利亚局势举行定期公开会。中方代表表示对阿勒颇机场再度遭袭感到关切，并呼吁有关国家取消所有非法单边制裁。', 'origin': '网易', 'publishDate': '2023-03-24 10:50:11'}, {'title': '总台记者探访丨强震后土耳其货币贬值 加剧药物短缺困境', 'summary': '当地时间2月6日土耳其南部发生强震。由于投资者担心强震对经济的影响，近期，土耳其里拉持续贬值，里拉对美元汇率跌破1美元兑换19里拉关口，创下历史新低。近日，中央广播电视总台记者探访后发现，里拉持续贬值，给多行业带来负面连锁反应，其中，本已非常严重的药物短缺问题也因为里拉贬值进一步加剧。', 'origin': '荆楚网', 'publishDate': '2023-03-24 08:43:00'}, {'title': '外媒:沙特与叙利亚就重开两国大使馆达成一致', 'summary': '央视新闻客户端消息，据路透社3月23日报道，沙特阿拉伯与叙利亚近期已就重新开放两国大使馆达成一致。报道称，两国使馆预计将于4月下旬恢复工作，而这一行动也将为叙利亚重返阿拉伯国家联盟“铺平道路”。', 'origin': '上游新闻', 'publishDate': '2023-03-24 08:23:18'}, {'title': '强震后土耳其农业生产遭重创 农户无力恢复生产', 'summary': '东北新闻网微博\n 北斗融媒\n *本网站有关内容转载自合法授权网站，如果您认为转载内容侵犯了您的权益，\n 请您来信来电(024-23187042)声明，本网站将在收到信息核实后24小时内删除相关内容。', 'origin': '东北新闻网', 'publishDate': '2023-03-24 07:52:01'}, {'title': '中方敦促消除对叙利亚人道救援的障碍', 'summary': '新华社联合国3月23日电 中国常驻联合国副代表耿爽23日在安理会叙利亚政治人道问题公开会上发言时指出，进一步改善对叙人道救援工作，需要克服三方面的障碍。', 'origin': '新京报网', 'publishDate': '2023-03-24 07:02:00'}, {'title': '中方敦促消除对叙利亚人道救援的障碍', 'summary': '新华社联合国3月23日电 中国常驻联合国副代表耿爽23日在安理会叙利亚政治人道问题公开会上发言时指出，进一步改善对叙人道救援工作，需要克服三方面的障碍。', 'origin': '手机光明网', 'publishDate': '2023-03-24 06:59:00'}, {'title': '外媒:沙特与叙利亚就重开两国大使馆达成一致', 'summary': '每经AI快讯，据路透社3月23日报道，沙特阿拉伯与叙利亚近期已就重新开放两国大使馆达成一致。报道称，两国使馆预计将于4月下旬恢复工作，而这一行动也将为叙利亚重返阿拉伯国家联盟“铺平道路”。', 'origin': '每日经济新闻', 'publishDate': '2023-03-24 05:12:35'}, {'title': '中方敦促消除对叙利亚人道救援的障碍-中新网', 'summary': '新华社联合国3月23日电 中国常驻联合国副代表耿爽23日在安理会叙利亚政治人道问题公开会上发言时指出，进一步改善对叙人道救援工作，需要克服三方面的障碍。', 'origin': '中国新闻网', 'publishDate': '2023-03-24 03:07:57'}, {'title': '强震后土耳其制造业损失惨重,恢复生产困难重重', 'summary': '近日，记者探访了土耳其强震灾区的一个制鞋业中心，发现强震后当地大量厂房坍塌、生产设备受损严重，大批劳动力流失，目前恢复生产面临重重困难。', 'origin': '澎湃新闻', 'publishDate': '2023-03-23 22:35:00'}, {'title': '程昊:我是记者,也是名救援队员_中安在线', 'summary': '合肥—武汉—土耳其—合肥，7天7夜，安徽新媒体集团记者、合肥市蓝天救援队队员程昊在土耳其完成了自己的很多的第一次。第一次跨出国门，第一次在异国他乡升起无人机。', 'origin': '中安在线网站', 'publishDate': '2023-03-23 21:07:00'}], 'info': {'total': 4295, 'createTime': '2023年02月25日', 'name': '叙利亚地震'}}
    #
    # gurag.process(data_result)
