开发者

使用Python解析FineReport模板数据集

目录
  • 背景
  • 应用场景
  • 操作对象
  • 解析过程
    • 查找目标模板文件
    • 获取 数据集
    • 获取数据集下的内容
  • 完整代码

    背景

    在使用 FineReport 作为报表平台过程中,

    当报表项目过多,或者报表项目需要迁移,又或者报表数据源需要迁移时,

    我们通常需要知道报表用到的表有哪些,或者需要修改的SQL语句有哪些。

    这时我们在 FineReport 设计器中需要将模板的数据集提取出来。

    但目前设计器中没有 数据集导出 的功能。

    所以我们使用python 开发程式以解析报表模板文件获取 数据集 和 SQL语句,并保存为Excel,

    以提供给后续解析SQL获取调用内容的项目使用。

    应用场景

    • 查找调用内容
    • FineReport 模板迁移
    • 元数据
    • 数据血缘

    操作对象

    本示例中使用 本地工作目录 中的 报表模板,FineReport 平台版本为 11

    本地工作目录路径

    D:\FineReport_11\webapps\webroot\WEB-INF\reportlets

    示例报表模板路径

    01_dev\record_monitor\record_scheduler.cpt

    示例报表模板数据集名称

    log_record_execute

    示例报表模板数据链接名称

    LogDB

    示例报表模板截图

    使用Python解析FineReport模板数据集

    解析过程

    查找目标模板文件

    为保证数据和模板安全,请先下载 正式环境reports文件夹,在本地进行匹配

    工作过程中,平台的报表目录不一定之后报表模板文件,故需要用代码先查找匹配,再打开;

    代码

    import pathlib
    
    # 请先下载 正式环境reports文件夹,在本地进行匹配
    
    work_directory = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets")
    
    top = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets\\01_dev\\record_monitor")
    
    # 递归获取 工作目录 本路径和子路径所有扩展名为 .cpt 的 文件
    for f in top.rglob("*.cpt"):
        # 模板文件的绝对路径
        abs_path = f.absolute()
        # 为保证对象准确,使用绝对路径
        print(abs_path.__str__())
    

    运行结果

    D:\FineReport_11\webapppythons\webroot\WEB-INF\reportlets\01_dev\record_monitor\record_scheduler.cpt

    获取 数据集

    FineReport 报表模板 .cpt .frm 本身是 XML 文件,故可直接使用解析XML的方式解析;

    解析方式是XPATH语法定位目标元素

    代码

    import pathlib
    
    from lxml import etree
    
    # 请先下载 正式环境reports文件夹,在本地进行匹配
    
    work_directory = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets")
    top = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets\\01_dev\\record_monitor")
    
    # 解析结果 收集
    result_list = []
    
    # 递归获取 工作目录 本路径和子路径所有扩展名为 .cpt 的 文件
    for f in top.rglob("*.cpt"):
        # 模板文件的绝对路径
        abs_path = f.absolute()
    
        with open(abs_path, mode="rb") as fr:
            XSLT_content = fr.read()
        # 开始解析 模板文件 XML
        xml_root = etree.XML(xslt_content)
        # 使用 XPATH 定位 XML 中 数据集内容
        tabel_data_el = xml_root.xpath("//*/TableDataMap//TableData[@class='com.fr.data.impl.DBTableData']")
    
        for db_data in tabel_data_el:
            # 数据集名称
            date_source_name = db_data.get("name")
            print(date_source_name)
    

    运行结果

    log_record_execute

    我们可以用 Vs Code 或者其他文本编辑器从资源管理器打开这个模板文件,

    可以看到 数据集 在模板XML中的样子,

    数据集都在 标签名为 TableDataMap 的 xml 元素下,

    数据集本身就是 标签名为 TableData 的 xml 元素,所以使用 XPATH 能够根据这种结构定位到。

    同时数据集的内容也能以相同方式定位到。

    部分 XML

    <?xml version="1.0" encoding="UTF-8"?>
    <WorkBook xmlVersion="20211223" releaseVersion="11.0.0">
        <TableDataMap>
            <TableData name="log_record_execute" class="com.fr.data.impl.DBTableData">
                <Parameters>
                    <Parameter>
                        <Attributes name="start_date"/>
                        <O>
                            <![CDATA[2023-07-01 00:00:00]]></O>
                    </Parameter>
                    <javascriptParameter>
                        <Attributes name="end_date"/>
                        <O>
                            <![CDATA[2023-09-07 00:00:00]]></O>
                    </Parameter>
                </Parameters>
                <Attributes maxMemRowCount="-1"/>
                <Connection class="com.fr.data.impl.NameDatabaseConnection">
                    <DatabaseName>
                        <![CDATA[LogDB]]></DatabaseName>
                </Connection>
                <Query>
                    <![CDATA[select * from fine_record_execute where todate(time) >= '${start_date}'  and todate(time) <= '${end_date}']]></Query>
                <PageQuery>
                    <![CDATA[]]></PageQuery>
            </TableData>
        </TableDataMap>
        ...
    </WorkBook>
    

    获取数据集下的内容

    获取到数据集的XML之后,变不需要从整个模板文件定位了,因为数据集的各种内容都在这串XML里了,

    所以直接解析 数据集XML 的 字符串就行;

    代码

    import json
    import pathlib
    
    from lxml import etree
    
    # 请先下载 正式环境reports文件夹,在本地进行匹配
    
    work_directory = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets")
    top = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets\\01_dev\\record_monitor")
    
    # 递归获取 工作目录 本路径和子路径所有扩展名为 .cpt 的 文件
    for f in top.rglob("*.cpt"):
        # 模板文件的绝对路径
        abs_path = f.absolute()
    
        with open(abs_path, mode="rb") as fr:
            xslt_content = fr.read()
        # 开始解析 模板文件 XML
        xml_root = etree.XML(xslt_content)
        # 使用 XPATH 定位 XML 中 数据集内容
        tabel_data_el = xml_root.xpath("//*/TableDataMap//TableData[@class='com.fr.data.impl.DBTableData']")
    
        for db_data in tabel_data_el:
    
            # 数据集名称
            date_source_name = db_data.get("name")
    
            # 转 string 取巧,重新定位数据集内容
            string = etree.tostring(db_data, encoding='utf-8').decode('utf-8')
    
            data_source = etree.XML(string)
            # 即使 XPATH 定位到的是单个对象,但是返回值是一个list,需要将 list 里的对象 重新拼成 string
            conn = data_source.xpath("/TableData/Connection/DatabaseName/text()")
    
            conn_collect = []
    
            for j in conn:
                # 数据链接名字
                conn_collect.append(str(j).strip())
    
            conn_string = "".join(conn_collect)
    
            query = data_source.xpath("/TableData/Query/text()")
    
            query_collect = []
    
            for j in query:
                # 数据集SQL
                query_collect.append(str(j).strip())
    
            query_string = "".join(query_collect)
    
            # 将 解析结果 拼接到 dict
            data_source_json = {
                "report": pathlib.Path.relative_to(abs_path, work_directory).__str__(),
                "data_source": date_source_name,
                "conn": conn_string,
                "query": query_string
            }
    
            print(json.dumps(data_source_json, ensure_ascii=False))
    

    运行结果

    {

        "report": "01_dev\\record_monitor\\record_scheduler.cpt",

        "data_source": "log_record_execute",

        "conn": "LogDB",

        "query": "select * from fine_record_execute where todate(time) >= '${start_date}'  and todate(time) <= '${end_date}'"

    }

    可以看到,报表模板路径,数据集名称,数据链接名称,SQL语句都www.devze.com完整获取到了。

    完整代码

    最后将获取到的内容转成 pandas 的 DataFrame,然后输出到Excel

    import pathlib
    
    import pandas as pd
    from lxml import etree
    
    # 请先下载 正式环境reports文件夹,在本地进行匹配
    
    work_directory = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets")
    top = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets\\01_dev\\record_monitor")
    
    # 解析结果 收集
    result_list = []
    # fineReport .cpt .frm 本身是xml文件,故可直接按行匹配字符查找是否与对象有关
    # 递归获取 工作目录 本路径和子路径所有扩展名为 .cpt 的 文件
    for f in top.rglob("*.cpt"):
        # 模板文件的绝对路径
        abs_path = f.absolute()
    
        with open(abs_path, mode="rb") as fr:
            xslt_content = fr.read()
        # 开始解析 模板文件 XML
        xml_root = etree.XML(xslt_content)
        # 使用 XPATH 定位 XML 中 数据集内容
        tabel_data_el = xml_root.xpath("//*/TableDataMap//TableData[@class='com.fr.data.impl.DBTableData']")
    
        for db_data in tabel_data_el:
    
            # 数据集名称
            date_source_name = db_data.get("name")
    
            # 转 string 取巧,重新定位数据集内容
            string = etree.tostring(db_data, encoding='utf-8').decode('utf-8')
    
            data_source = etree.XML(string)
            # 即使 XPATH 定位到的是单个对象,但是返回值是一个list,需要将 list 里的对象 重新拼成 string
    
            conn = data_source.xpath("/TableData/Connection/DatabaseName/text()")
    
            conn_collect = []
    
            for j in conn:
                # 数据链接名字
                conn_collect.append(str(j).strip())
    
            conn_string = "".join(conn_collect)
    
            query = data_source.xpath("/TableData/Query/text()")
    
            query_collect = []
    
            for j in query:
                # 数据集SQL
                query_collect.append(str(j).strip())
    
            query_string = "".join(query_collect)
    
            # 将 解析结果 拼接到 dict
            data_source_jspythonon = {
                "report": pathlib.Path.relative_to(abs_path, work_directory).__str__(),
                "data_source": date_source_name,
                "conn": conn_string,
                "query": query_string
            }
    
            result_list.append(data_source_json)
    
    df = pd.DataFrame(result_list)
    
    df.to_excel("./data_source.xlsx", engine="openpyxl", index=FalsBnYonJDvLae)
    

    运行结果

    使用Python解析FineReport模板数据集

    到此这篇关于使用Python解析FineReport模板数据集的文章就介绍到这了,更多相关Python解析FineReport数据集内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜