Databricks: Jak opublikować report w Power BI używając Pythona?

Problem:
Dostawca zewnętrzny umieszcza na Azure Storage Account raport w Power BI. Masz zadanie umieścić ten raport w serwisie Power BI. Będziesz to robił cyklicznie, więc chcesz uprościć sobie pracę. W jaki sposób to zrobisz?

Co masz dostępne?
Narzędzie, która masz dostępne to Databricks i całe dobrodziejstwo jakie z tym się wiąże.

Rozwiązanie:
Skrypt w Pythonie wykorzystujący Power BI Rest API, Key Vault i Azure Identity do automatycznego importu raportu w pbix.

Potrzebne oczywistości:

  1. Storage Account - tam przechowywany będzie plik pbix.
  2. Service Principal - do połączenia Databricks - Power BI. Pamiętaj o ustawieniu odpowiedniej roli.
  3. Biblioteka Azure Identity - do autentykacji
  4. Key Vault - do przechowywania sekretów. To nie jest "must have" ale to jest dobra praktyka.

Zanim zaczniemy

Odczytywanie pliku z Databricks z Azure Storage Account będzie prostsze gdy zamapujesz kontener, w którym znajduje się plik. To nie jest przedmiotem tego wpisu. O mountowaniu możesz poczytać tutaj:

https://learn.microsoft.com/en-us/azure/databricks/dbfs/mounts

Utworzenie Service Principala i połączenie go z obszarem roboczym (workspace) w Power BI, nie jest przedmiotem tego wpisu. Jednak, żeby nie zostawiać Cię samego z tym zadaniem, to co będzie przydatne znajdzie tutaj:

https://learn.microsoft.com/en-us/power-bi/enterprise/service-premium-service-principal

https://learn.microsoft.com/en-us/power-bi/collaborate-share/service-roles-new-workspaces

Daj znać w komentarzu, jeżeli chciałbyś, żebym rozwiną któryś z tych tematów.

TL;DR

Jeżeli przychodzisz po gotowy kod, który możesz skopiować i używać, proszę bardzo:

def publish_powerbi_report(workspace_id: str, report_name: str, report_content):
    payload = {"datasetDisplayName": report_name,
               "nameConflict":"Overwrite"
               }
    files = {report_name: report_content}
    return requests.post(url=f"{BASE_URL}groups/{workspace_id}/imports",headers=AUTHORIZATION,params=payload, files=files).json()

Jeżeli chcesz wiedzieć więcej, jak i dlaczego to działa, zapraszam. Będzie ciekawie 🙂

Co potrzebuje Power BI?

W dokumentacji czytamy:

To import a file, specify the content type multipart/form-data in the request headers and encode the file as form data in the request body.

Rozumiem to w ten sposób, że będziemy musieli ustawić nagłówek wiadomości na multipart/form-data i wysłać plik w body.

Następnie w wymaganiach jest:

This API call can be called by a service principal profile.

Zakłada, że mamy już stworzonego service principala, z dostępem do odpowiedniego obszaru roboczego (workspace) i ustawionymi odpowiednimi prawami na poziomie Azure Active Directory:

Dataset.ReadWrite.All

Potrzebne będą też parametry do wysłania w zapytaniu, URI Parameters:

groupId - identyfikator workspace, do którego będziesz publikował raport.

datasetDisplayName - nazwa pod którą raport będzie się wyświetlał

Opcjonalny jest parametr:
nameConflict - czyli określimy co ma się stać, gdy plik pbix już istnieje w grupie roboczej. Domyślnie ustawiona jest opcja Ignore, czyli nic nie zrobi, gdy plik już istnieje. Ten parametr przestawimy na CreateOrOverwrite. Czyli utworzymy dataset jeżeli nie istnieje albo nadpiszemy go jeżeli już istnieje.

Jak to ubrać w Python'a i requests.post?

Wysyłanie pliku przy użyciu Pythona i request.post jest proste.

files={'file':open('report.pbix','rb')}
r = requests.post(url, files=files)

Warto zauważyć, że plik jest otwarty w formacie binarnym. To jest bardzo ważne, żeby otworzyć plik w ten sposób inaczej możesz dostać błędy przy wysyłaniu pliku. Domyślnie plik będzie automatycznie wysłany w formacie Multipart-Encoded, więc w taki formacie jak oczekuje serwis Power Bi.

Ale to nie wszystko jako URL:

f"{BASE_URL}groups/{workspace_id}/imports"

Gdzie:

BASE_URL to adres twojego tenanta

workspace_id to identyfikator grupy roboczej, do której zapisujesz raport.

Parametry do wysyłki zdefiniujesz w ten sposób:

payload = {"datasetDisplayName": "report_name",
           "nameConflict":"CreateOrOverwrite"
          }

Jak opublikować raport w Power BI używając Pythona?

Definiowanie funkcji

def publish_powerbi_report(workspace_id: str, report_name: str, report_content):
    payload = {"datasetDisplayName": report_name,
               "nameConflict":"Overwrite"
               }
    files = {report_name: report_content}
    return requests.post(url=f"{BASE_URL}groups/{workspace_id}/imports",headers=AUTHORIZATION,params=payload, files=files).json()

Otwieranie pliku i wywoływanie funkcji:

with open('/dbfs/mnt/landing-zone/Next Level BI.pbix', 'rb') as f:
    print(publish_powerbi_report(group_id, "Next Level BI.pbix",f))

Cały kod znajdziesz jak zawsze na github.
https://github.com/rgogloza/nextlevelbi/blob/master/databricks/utils-pbi-publish-report.ipynb

Pytania, zażalenia, wnioski, prośby? Daj znać w komentarzu.