Home Tags Posts tagged with "Power BI Workarounds"

Power BI Workarounds

In one of the projects I was working on, I received feedback saying it is hard to understand how many items they have selected in a slicer, and it is not the first time I came across this. It is a valid point, especially when you have quite a few items in a slicer, you use a search bar to look for items, you select a couple, but you were not sure how many were selected.

Obviously, there are many solutions to one problem. I have seen many reports displaying custom labels to handle this situation. Still, after some research for a better UX, I found this example on the dribble, which looked like an excellent design method.

shot.gif (800×600) (dribbble.com)

And 

And doing this in 

 

The logic here, I will use a DAX measure to figure out how many items are selected and count those Items to display in an object. I can display this many ways, but I went for a Shape. Having shape allows me to use a shape type as a background; I can use a dynamic DAX expression to show the value. The only thing to note is, my DAX expression has to be a string type. Hence, I created my measure like below. Next is the slicer visual. By default, I enabled the slicer header and used an empty character as text in the slicer header. If I keep the slicer header off, whenever I hover over a slicer, it jumps down/up to make more options visible. If I keep the slicer header on, that won’t be an issue. The only way to have the slicer header off with no title and no tooltip to display is by using Empty Character. So, I used an empty character as the title for the slicer header. Then I went to change the title to DAX measure. And then I grouped altogether, slicer and the shape. Now when I select multiple items, it nicely shows how many items were selected. When none is selected, it shows all the items available in the slicer. In this example, I don’t have many values, so a nice round shape works but based on several results, I may go for different Shape types and keep my shape. But to make it work perfectly, I mean to make this shape to show the right number irrespective of other visuals cross-filtering can be a tiring job. One way is to make all the rest of the visuals do not interact with this shape. That can be time-consuming when you have a lot of objects on a page. The other way to handle this is by creating a new table and creating a relationship. So, in this example, I have Artists and their Total streams. I am using Artist in my slicer. When I select an Artist in another visual, by default, my measure also gets filters. If I create another table with a distinct Artist use that in my slicer, and use that column to count, and in all other visuals, I use the Artist from a different table; then I get the result I want.1. Created an Artist table2. Create a relationship between my streams table and Artist table3. Update my slicer to get Artist names from Artist table4. Update measure to get Artist name from Artist table now. Cross-filtering doesn’t impact the value I’m showing. I hope this inspires someone out there.

4 comments
2 FacebookTwitterPinterestEmail

It has been a while since I written a blog post, even my ten years old daughter started teasing saying “Motionless blog”, so decided to get back to blogging with a short and simple blog post based on a question I received about one of my Power BI portfolio report Global Landslides Data. Few people asked me how I created the slicer in this report,

So how I created the Dynamic Legend in Power BI Visual, Line Chart :

I was using slicer as a button to dynamically choose the legend I wanted to show in the line Chart. In fact, it was relatively straightforward:

First I created an aggregated table like below

Table =
UNION(
//Incidents
SUMMARIZE('Global Landslide Data'
,'Global Landslide Data'[ID]
,'Global Landslide Data'[Date]
,"Slicer","Incidents"
,"Incidents",'Global Landslide Data'[Incidents]
,"Distance",BLANK()
,"Injuries",BLANK()
,"Fatalities",BLANK()
)
,//Distance

SUMMARIZE('Global Landslide Data'
,'Global Landslide Data'[ID]
,'Global Landslide Data'[Date]
,"Slicer","Distance"
,"Incidents",BLANK()
,"Distance",CALCULATE(SUM('Global Landslide Data'[Distance]))
,"Injuries",BLANK()
,"Fatalities",BLANK()
)
,//Injuries

SUMMARIZE('Global Landslide Data'
,'Global Landslide Data'[ID]
,'Global Landslide Data'[Date]
,"Slicer","Injuries"
,"Incidents",BLANK()
,"Distance",BLANK()
,"Injuries",CALCULATE(SUM('Global Landslide Data'[Injuries]))
,"Fatalities",BLANK()
)
,//Fatalities

SUMMARIZE('Global Landslide Data'
,'Global Landslide Data'[ID]
,'Global Landslide Data'[Date]
,"Slicer","Fatalities"
,"Incidents",BLANK()
,"Distance",BLANK()
,"Injuries",BLANK()
,"Fatalities",CALCULATE(SUM('Global Landslide Data'[Fatalities]))
)
)

The logic is to create a table with the DAX function UNION. Each Table expression in UNION function represents a value of slicer. Apart from that slicer related value, all the rest of the values are blanks.  It is key to have them as blanks than zero’s, we don’t see any data.

Then I chose the slicer column as the value in my Slicer visual and allowed slicer to have multiple selections. Then in the visual, I have used all measures as values, so based on the slicer selection it shows the trend line.

You can see it in the report here – https://app.powerbi.com/view?r=eyJrIjoiN2I4YWI4MjMtYmIyYy00ZjRkLWFjYTktZjM1ZjIwODk0ZjkzIiwidCI6IjM4YmU3MjU5LTMyMGQtNDZkYy04YzNiLWNkZDA5OTM1NGExMyIsImMiOjh9

Till next time,

Prathy 🙂

4 comments
2 FacebookTwitterPinterestEmail

(Ideally, I would like to do this as a Power BI custom data connector. I have tried, for some reason, my Visual Studio has been crashing when I click on connect to PBI Service using OAuth2. Once I succeed, it would become another blog post)

Why Calling Power BI service using Power BI Desktop?

We all know Power BI is a great tool and easily adaptable. Using Power BI Desktop and service, we can create models then we can publish or save them to Power BI Service. In service, we have workspaces, dashboards, reports, datasets, workbooks and apps; Power BI users create and/or access all these features based on their access levels. Which means all your users will be creating and/or accessing content from Power BI service. Which is great, but how can I answer questions like below:

  • Number of workspaces
  • Who has access to that particular workspace and their level of access
  • Number of Dashboards
  • Number of Reports
  • Dataset Last Refresh time
  • Average refresh time of X data set? ( You can plan, do you need premium or not based on this value)
  • How many Gateways ( For me, it’s not working at the moment)

And much more

Of course, I can get this information by going to Power BI service but how about getting answers to all those questions in one report.With extensive Power BI documentation, we know we could use Power BI API to Embedded Power BI reports or do data refresh. So I thought to call same Power BI API using Power BI Desktop to get the information I need for documenting my Power BI.

Calling Power BI service using Power BI Desktop

Before we dwell into this, let’s understand my approach for API Access. So when using Power BI API, the user will be able to see whatever they have access to. When a user creates a workspace in Power BI mean they are creating an office 365 group, which means they are creating loads of things in Office 365. I will talk more about this in another blog post. For now, just assume I have an SA User. The only user who can create workspaces, which means this user has access to every single workspace in my Power BI tenant. Instead of using an OAuth authentication, I am using the hard coded username and password for my API calls. As I mentioned earlier, I will talk more about Power BI workspaces and my so-called SA User in another blog post (Power BI publishing Strategy)

To call Power BI API, first I need to register an application for Power BI. You can create an application using this URL – https://dev.powerbi.com/apps. You can find more information about how to create and edit an App here – https://powerbi.microsoft.com/en-us/documentation/powerbi-developer-register-app/.

Don’t forget to save your Client ID and Client Secret safe; I still don’t know how to find client secret using Azure Portal (Maybe I have not looked well enough!)

Then in Power BI Desktop, I created two parameters one for Client ID and one for Client Secret. Next, I created a query called getToken with following M

GetToken:

let
    //App Details
    client_id = ClientID,
    client_secret=ClientSecret,

    //Authentication
    
    //URI's
    redirect_uri = "http://localhost:13526/redirect",
    token_uri = "https://login.windows.net/common/oauth2/token", 
    authorize_uri = "https://login.windows.net/common/oauth2/authorize",
    resource="https://analysis.windows.net/powerbi/api",
    
    //User Details  
    username="xxxxxxxxxxxxxxx.com",
    password="xxxxxxxxx",
    

   tokenResponse = Json.Document(Web.Contents(token_uri,          
                                [
                                    Content=
                                    Text.ToBinary(Uri.BuildQueryString(
                                    [
                                        client_id = client_id
                                        ,client_secret=client_secret
                                        ,username=username
                                        ,password=password
                                        ,resource=resource
                                        ,grant_type = "password"
                                        ,redirect_uri = redirect_uri
                                    ]))
                                    ,Headers=
                                        [Accept="application/json"]
                                        , ManualStatusHandling={400}
                                ])),
                                access_token = tokenResponse[access_token],
    token = tokenResponse[access_token]
in
    token

By using above query, I get an access token to call Power BI API using the given user access details. Then it’s a matter of calling various Power BI API GET operations. There is a plenty of documentation available, but Power BI Apiary was most useful one for me. By going to http://docs.powerbi.apiary.io/#introduction/what-can-i-do-with-the-power-bi-api? You can quickly see different operations you could do with Power BI API. I want to start with workspaces.

List all Groups / List all Workspaces:

Once up on time Workspaces were called Groups, may be that’s why in API they are still called Groups. The GET call for Groups is https://api.powerbi.com/v1.0/myorg/groups. I created a query called Workspaces with following M Code to get workspaces data.

let
    getgroups_uri ="https://api.powerbi.com/v1.0/myorg/groups",
    GetGroups = Json.Document(Web.Contents(getgroups_uri, 
                [
                Headers=
                    [
                    Authorization="Bearer "& refToken
                    ], 
                ManualStatusHandling={400}
                ]
                )),
    value = GetGroups[value],
    #"Converted to Table" = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"id", "isReadOnly", "name"}, {"id", "isReadOnly", "name"}),
    #"Renamed Columns" = Table.RenameColumns(#"Expanded Column1",{{"id", "Workspace ID"}, {"name", "Workspace name"}})
in
    #"Renamed Columns"

List all Datasets:

Next all datasets under each workspace. Datasets GET request need GroupID query parameter. Hence I created a function called fnGetdatasets with following M Code

(groupid as text)=>

let
getdatasets_uri =" https://api.powerbi.com/v1.0/myorg/groups/"&groupid&"/datasets",

 GetGroups = Json.Document(Web.Contents(getdatasets_uri ,

 [

 Headers=

 [

 Authorization="Bearer "&getToken

 ],

 ManualStatusHandling={400}

 ]

 )),

 value = GetGroups[value],

 #"Converted to Table" = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error)

in

 #"Converted to Table"

Then created a new query with following M code to get all datasets under each workspace.

let

 Source = Workspaces,

 #"Invoked Custom Function" = Table.AddColumn(Source, "fnDatasets", each fnDatasets([id])),

 #"Expanded fnDatasets" = Table.ExpandTableColumn(#"Invoked Custom Function", "fnDatasets", {"Column1"}, {"Column1"}),

 #"Expanded Column1" = Table.ExpandRecordColumn(#"Expanded fnDatasets", "Column1", {"id", "name", "addRowsAPIEnabled", "configuredBy", "isRefreshable", "isEffectiveIdentityRequired", "isEffectiveIdentityRolesRequired", "isOnPremGatewayRequired"}, {"id.1", "name.1", "addRowsAPIEnabled", "configuredBy", "isRefreshable", "isEffectiveIdentityRequired", "isEffectiveIdentityRolesRequired", "isOnPremGatewayRequired"})

in

 #"Expanded Column1"

All Reports:

Again Reports API needs Workspace ID to get reports. There are two ways you can get Reports data. However, I went for the one who needs GroupID. I created function and then called that function for each GroupID

fnReports Function:
(groupid as text)=>
let
getdatasets_uri ="https://api.powerbi.com/v1.0/myorg/groups/"&groupid&"/reports",
GetGroups = Json.Document(Web.Contents(getdatasets_uri , 
[
Headers=
[
Authorization="Bearer "&refToken
], 
ManualStatusHandling={400}
]
)),
value = GetGroups[value],
#"Converted to Table" = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error)
in
#"Converted to Table"

Reports Query:

let
getgroups_uri ="https://api.powerbi.com/v1.0/myorg/groups",
GetGroups = Json.Document(Web.Contents(getgroups_uri, 
[
Headers=
[
Authorization="Bearer "& refToken
], 
ManualStatusHandling={400}
]
)),
value = GetGroups[value],
#"Converted to Table" = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"id", "isReadOnly", "name"}, {"id", "isReadOnly", "name"}),
#"Renamed Columns" = Table.RenameColumns(#"Expanded Column1",{{"id", "Workspace ID"}, {"name", "Workspace name"}}),
#"Invoked Custom Function" = Table.AddColumn(#"Renamed Columns", "fnReports", each fnReports([Workspace ID])),
#"Expanded fnReports" = Table.ExpandTableColumn(#"Invoked Custom Function", "fnReports", {"Column1"}, {"Column1"}),
#"Expanded Column2" = Table.ExpandRecordColumn(#"Expanded fnReports", "Column1", {"id", "modelId", "name", "webUrl", "embedUrl", "isOwnedByMe", "isOriginalPbixReport", "datasetId"}, {"Report.id", "Report.modelId", "Report.name", "Report.webUrl", "Report.embedUrl", "Report.isOwnedByMe", "Report.isOriginalPbixReport", "Report.datasetId"})
in
#"Expanded Column2"

Like this, I can also get List of users under each workspace, Dataset refresh history. In the documentation I can see, I can get all gateways and much more information about Gateways – https://msdn.microsoft.com/en-us/library/mt809129.aspx. I think this feature is not available yet, as it is not shown in the apiary and I get an error when I call this service.(Something for future!)

A sample report below:

Summary:

I have to admit, it was not easy. The main difficulty for me was too much information out there. I struggled to understand which API to use, what redirect URL and headers etc. I don’t know much about API’s. All these headers and scopes are very new to me; I still don’t understand why or how they work. So part of the problem was me and part was various documentation about Power BI Embedded. However, at the end of the day, I learned something new. By calling API, I can answer many questions with one refresh in Power BI Desktop.

This post is already too long; I will create a nicer looking template with all queries and post it soon. It’s pretty difficult to get much time to blog during school summer holidays. I hope I create a template soon and also hope this gives some ideas and help someone out there.

Some References for Power BI API :

https://msdn.microsoft.com/en-us/library/mt147898.aspx

http://docs.powerbi.apiary.io/#introduction/what-can-i-do-with-the-power-bi-api?

https://powerbi.microsoft.com/en-us/documentation/powerbi-developer-get-azuread-access-token/

Till next time,

Prathy 🙂

13 comments
2 FacebookTwitterPinterestEmail

Recently one of my business users came to me asking “how can I get a Clickable Card in Power BI”? He wants a card where he can show the measured value, but when he clicks on that, he wanted to cross filter other visuals. To be fair, A valid ask, but not something I have done before. After deep thought, I came up with a work around, so thought to write a quick blog post for future reference.

Let’s understand the need, assume we are looking at Products data, and this is how report looks like

In the above report, if we have 50000 rows instead of 5, it would be difficult to find those two products which are causing significant loss. Of course, we can click on the chart on the right and interact, but that’s not the point here. So how can we make that card clickable? Well, for now, we don’t have an inbuilt clickable visual. So the question is how can we get something which looks like the card but interactive.  (I am sure there are several ways of doing this, but I have gone for Treemap visual.)

Let’s see how I created a Clickable Card:

First I created a calculated table

LossTable =
CALCULATETABLE (
ADDCOLUMNS (
SUMMARIZE ( Products, Products[Product] ),
"Prod", "Prod",
"LossCount", CALCULATE ( COUNT ( Products[Profit/Loss] ), Products[Profit/Loss] = "L" )
),
Products[Profit/Loss] = "L"
)

Then I created a relationship between Products table and a new Loss table, also I have hidden LossTable for users.

Replaced existing card with a treemap, Product column on Group and Sum of Loss count on values. As I had same values for all rows in Prod column, as expected there was only one tile in Treemap.

Now I have a clickable Card look-alike, which can interact with other visuals.

Hope this helps someone out there!

Prathy 🙂

2 comments
0 FacebookTwitterPinterestEmail