背景

最近工作上遇到一个需求,对BI报表进行权限修改。在原先报告的模型基础上,将原有的Role重新设计一套,让BI报表的权限控制更加灵活且具有扩展性,RLS的修改没有什么难度,但是修改后的数据对比确实极其的耗时耗力的工作。

思路

  1. python连接power bi本地模型

  2. 发送DAX语句并附带角色标识及customdata

  3. 将返回的表格进行存储

  4. 对存储的表格内容进行对比

实现步骤

连接本地打开的power bi,用外部工具DAX Studio找到power bi AS 部署的模型端口号(或者直接查找端口号)。还有本地Windows的登录时的账号密码。

接下来着手准备连接AS,在连接前,检查下C:\Windows\Microsoft.NET\assembly\GAC_MSIL目录下是否由Microsoft.AnalysisServices.AdomdClient这样的文件夹,这是因为我们将利用python去调用.NET的包来实现与power bi AS进行连接。如果没有,请自行查找安装方法。

下面是引用代码:

import clr

folder = r"C:\Windows\Microsoft.NET\assembly\GAC_MSIL"

# 引用Microsoft.AnalysisServices.AdomdClient
clr.AddReference(
    folder
    + r"\Microsoft.AnalysisServices.AdomdClient\v4.0_15.0.0.0__89845dcd8080cc91\Microsoft.AnalysisServices.AdomdClient.DLL"
)

# 导入Microsoft.AnalysisServices.AdomdClient命名空间
from Microsoft.AnalysisServices.AdomdClient import AdomdConnection, AdomdCommand  # type: ignore

连接信息拼接并发送查询:

# 从AS中获取数据
def getDataFromAS_df(workspace, username, password, customdata, role, query,admin= False):

    if admin:
        conn_string = f"DataSource={workspace};User ID={username};Password={password};CustomData={customdata};Roles={role};"
    else:
        conn_string = f"DataSource={workspace};User ID={username};Password={password};"
    conn = AdomdConnection(conn_string)

    # 创建连接
    try:
        # 建立连接
        conn.Open()
        # 创建命令
        cmd = AdomdCommand(query, conn)
        # 执行命令
        result = cmd.ExecuteReader()
        IsNext = True
        while IsNext:
            # 将结果集转换为DataFrame
            df, IsNext = readData(result, IsNext)
            # 将DataFrame添加到列表中
            yield df
    except Exception as ex:
        print("Error:", ex)
    finally:
        # 关闭连接
        conn.Close()

由于返回的结果是个对象,所以使用pandas的DataFrame来及接收

# 读取返回结果并format成DataFrame文件
def readData(reader, IsNext):
    # 获取列名
    column_names = [reader.GetName(i) for i in range(reader.FieldCount)]
    # 初始化数据存储
    data = []
    while reader.Read():
        row_data = []
        for column_name in column_names:
            index = reader.GetOrdinal(column_name)
            row_data.append(reader[index])  # 获取数据
        data.append(row_data)  # 将行数据添加到数据列表        
        # 将数据转换为 DataFrame
        df = pd.DataFrame(data, columns=column_names)
    if not data:
        df = pd.DataFrame(columns=column_names)
    IsNext = reader.NextResult()
    return df, IsNext

那么得到结果后,我们将结果写入excel,供后续的数据对比

import pandas as pd
from getDataFromAS import getDataFromAS_df


def writeTo(
    workspace,
    username,
    password,
    customdata,
    role,
    queries,
    output_file_name,
    error_list,
):
    # 创建一个ExcelWriter对象
    with pd.ExcelWriter(
        f"evaluateData/{output_file_name}.xlsx", engine="openpyxl"
    ) as writer:
        sheet_name_id = 1
        # 将DataFrame写入不同的工作表
        for query in queries:
            for df in getDataFromAS_df(
                workspace=workspace,
                username=username,
                password=password,
                customdata=customdata,
                role=role,
                query=query,
                admin=False
            ):
                try:

                    # 访问工作簿和工作表
                    workbook = writer.book
                    worksheet = workbook.create_sheet(str(sheet_name_id))
                    worksheet["A1"] = query

                    # 将DataFrame写入Excel文件
                    df.to_excel(
                        writer,
                        sheet_name=str(sheet_name_id),
                        index=True,
                        float_format="%.2f",
                        startrow=1,
                    )

                except Exception as e:
                    print(f"写入工作表 {sheet_name_id} 失败: {e}")
                    error_list.append(sheet_name_id)
                else:
                    print(output_file_name)
                    print(df.shape)
                    print(f"写入工作表 {sheet_name_id}")

                finally:
                    sheet_name_id += 1
                    print(
                        "------------------------------------------------------------"
                    )
        # 保存Excel文件
        writer.save()

未完成,待续。。。