Home Tags Posts tagged with "Power BI"

Power BI

REPLACE ALL NUMERIC VALUES IN MULTIPLE COLUMNS USING #POWERQUERY

A quick post more for my future reference. Hoping it helps some one out there!

While I was working on a Power BI report with Commonwealth Games dataset, I came across an issue where I had to replace all numeric values with a text value. There are some very useful blogs posts about replacing multiple values in Power Query. With my issue, I wanted to replace only numeric values. Instead of replacing each column, I tried to replace all!
My data looked like below:

I looked upon all available Replace functions of Text, Lists and Tables. As Imke mentioned on her blog, I decided to go for List.ReplaceMatchingItems. As I wanted to replace only numeric values, I used Value.Is(Value.FromText(_), type number)  to check the Type of value for each row. I replaced value only when my condition returns true. So I combined all required columns into a single column and used Text.ToList function to convert to a List. The condition I used for Type checking works great. But, when I converted the combined columns to Text, then to a List; each character in Text value converted into List as a different List Item. E.g. 12 would convert to {{1},{2}}. When I replace each by checking the data type, I will be replacing 1 and 2 individually, which will cause duplication in the result, like below:

 

I have to admit; I searched so much to figure out a way to covert Text value to a List using a delimiter.I felt silly after I looked at one my earlier post where I was using Text.Split() function. After a good number of failed attempts, I used Text.Split() function and then I used that List for replacements. And my condition Value.Is(Value.FromText(_), type number) works great.

 

Here is the script:

let
    Source = Web.Page(Web.Contents("https://en.wikipedia.org/wiki/2018_Commonwealth_Games")),
    Data4 = Source{4}[Data],
    #"Replaced Value" = Table.ReplaceValue(Data4,"#(cr)#(lf)"," -",Replacer.ReplaceText,{"April", "April2", "4 #(lf)Wed", "5 #(lf)Thu", "6 #(lf)Fri", "7 #(lf)Sat", "8 #(lf)Sun", "9 #(lf)Mon", "10 #(lf)Tue", "11 #(lf)Wed", "12 #(lf)Thu", "13 #(lf)Fri", "14 #(lf)Sat", "15 #(lf)Sun", "Events"}),
    #"Replaced Value2" = Table.ReplaceValue(#"Replaced Value","#(lf)","",Replacer.ReplaceText,{"April", "April2", "4 #(lf)Wed", "5 #(lf)Thu", "6 #(lf)Fri", "7 #(lf)Sat", "8 #(lf)Sun", "9 #(lf)Mon", "10 #(lf)Tue", "11 #(lf)Wed", "12 #(lf)Thu", "13 #(lf)Fri", "14 #(lf)Sat", "15 #(lf)Sun", "Events"}),
    #"Promoted Headers" = Table.PromoteHeaders(#"Replaced Value2", [PromoteAllScalars=true]),
    #"Renamed Columns" = Table.RenameColumns(#"Promoted Headers",{{"April_1", "Type"}}),
    #"Removed Bottom Rows" = Table.RemoveLastN(#"Renamed Columns",1),
    #"Filtered Rows" = Table.SelectRows(#"Removed Bottom Rows", each ([Type] <> null)),
    #"Replaced Value1" = Table.ReplaceValue(#"Filtered Rows", each {"4 -Wed", "5 -Thu", "6 -Fri", "7 -Sat", "8 -Sun", "9 -Mon", "10 -Tue", "11 -Wed", "12 -Thu", "13 -Fri", "14 -Sat", "15 -Sun"} ,each if Value.Is(Value.FromText(_), type number) = true then "_" else "_",Replacer.ReplaceText,{"4 -Wed", "5 -Thu", "6 -Fri", "7 -Sat", "8 -Sun", "9 -Mon", "10 -Tue", "11 -Wed", "12 -Thu", "13 -Fri", "14 -Sat", "15 -Sun"}),
    #"Added Custom" = Table.AddColumn(#"Replaced Value1", "Custom", each Value.Is(Value.FromText([#"15 -Sun"]), type number)),
    ColumnNames = List.Select(Table.ColumnNames(#"Filtered Rows"), each Text.Contains(_,"-")= true),
    CombineColumns = Table.CombineColumns(#"Filtered Rows",ColumnNames ,Combiner.CombineTextByDelimiter(",", QuoteStyle.None),"Merged"),
    #"Added Custom1" = Table.AddColumn(CombineColumns, "Custom", each Text.Split([Merged],",")),
  #"Added Custom2" = Table.AddColumn(#"Added Custom1", "Custom.1", each List.Transform([Custom], each if Value.Is(Value.FromText(_), type number) = true then "GME" else _)),
    #"Removed Other Columns" = Table.SelectColumns(#"Added Custom2",{"April", "Type", "Events", "Custom.1"}),
    #"Extracted Values" = Table.TransformColumns(#"Removed Other Columns", {"Custom.1", each Text.Combine(List.Transform(_, Text.From), ","), type text}),
    #"Split Column by Delimiter" = Table.SplitColumn(#"Extracted Values", "Custom.1", Splitter.SplitTextByDelimiter(",", QuoteStyle.Csv), ColumnNames )
in
    #"Split Column by Delimiter"

This may not be worthy to be a blog post especially when there are already some good posts covering the similar topic, but I spent a fair bit of time on this so thought of writing a post!

Keep Smiling,

Prathy 🙂

0 comment
0 FacebookTwitterPinterestEmail

Have you ever ended up in a situation where someone already did all the work to get the right brand colours, created a Power BI theme file and left the company. Then you go there to do some quick reports; business wants that long time signed off  theme that they used in other reports, but no one knows where that theme file exists? I have!  I was asked to create Power BI files which looked similar to the ones they had; existing ones had themes and backgrounds, but no one knows where they were.

Where are my Power BI theme file and background images?

By now, many of us know Power BI file can be renamed to a .zip file to see the contents. I previously blogged here how to zip PBI file and how we can use this for Drill Through functionality. So, I was pretty sure; if I imported a Jason file as a theme into Power BI Desktop, it should be saved somewhere inside. I can unzip the  model and find all imported files; I was not wrong 😊

So the steps to see imported files are simple:

  1. Copy the existing Power BI file
  2. Change file type to .zip
  3. Unzip it
  4. In the unzipped file, go to \Report\StaticResources\RegisteredResources
  5. That’s all; that’s where your theme file, background images or any other images you used in the model resides

Hoe this helps!

Prathy 🙂

0 comment
2 FacebookTwitterPinterestEmail

Recently, I worked on an interesting problem. Datasource I was working with was a SharePoint list, working with SharePoint lists always turns out to be a difficult task than I first assumed. On top of that, with this particular data source; data was coming as semicolon delimited text. Basically, there were many columns like Mile Stones and Sub Milestones, their related data and so on.

Initially, when I looked at it, I thought, I could just split columns in a jiffy; apparently, I didn’t think through it. To cut a long story short, I had to find a way to split multiple columns into rows at the same time so that I can visualise it in a Gantt chart format.

Split multiple columns into rows in #PowerQuery:

Here is an example dataset. My data looked something like below; the real data set has far too many columns and much more data in each cell.

Name Milestones Sub Milestones
Prathy m1;M2;M3;M4;M5 s1;s2;s3;s4;s5

If I split Milestones and Sub Milestones one column after other by using Splitter.SplitTextByDelimiter function, this is how my data will look like. There is no way I can find the which sub-milestones belongs to what.( Well, there must be away, but def, not an easy way)

So, instead of splitting each column, I Split each row using function Text.Split and added it as a new column. Text.Split returns a list. Later, I used all these lists to create a column using Table.FromColumns() function which returned a table for me. When I expand the table, I have all my columns split nicely the way I want.

Power Query Script:

let
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WCihKLMmoVNJRyjW09jWy9jW29jWx9jUFChQbWhcbWRcbWxebWBebKsXGAgA=", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type text) meta [Serialized.Text = true]) in type table [Name = _t, Milestones = _t, SubMilestones = _t]),
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Name", type text}, {"Milestones", type text}, {"SubMilestones", type text}}),
    #"Added Custom" = Table.AddColumn(#"Changed Type", "Custom", each Text.Split([Milestones],";")),
    #"Added Custom1" = Table.AddColumn(#"Added Custom", "Custom.1", each Text.Split([SubMilestones],";")),
    #"Added Custom2" = Table.AddColumn(#"Added Custom1", "Custom.2", each Table.FromColumns({[Custom],[Custom.1]})),
    #"Expanded Custom.2" = Table.ExpandTableColumn(#"Added Custom2", "Custom.2", {"Column1", "Column2"}, {"Column1", "Column2"}),
    #"Removed Other Columns" = Table.SelectColumns(#"Expanded Custom.2",{"Name", "Column1", "Column2"})
in
    #"Removed Other Columns"

I did this with multiple steps for better readability, but you can fit all statements in a single statement as well. I kind of feel like, there must be an inbuilt function which does this, but I haven’t discovered it yet, if you knew, please do let me know 😊

You can download the sample file here – https://1drv.ms/u/s!Avm7gbgZtlMlvjUCCvb981Ga_AVB

Keep smiling 😊

Prathy

 

 

 

 

 

5 comments
4 FacebookTwitterPinterestEmail

Back in September, I promised I would write another blog post with a beautiful looking Power BI template. Even though I did the model long ago, I did not get a chance to write a post until recently one of my blog readers reminded me. Thanks, Martin 🙂

Quick Recap:

As I mentioned in my previous blog post Calling Power BI API using Power BI Desktop to document Power BI Service, we can get a lot of metadata from Power BI service. I feel like, it is essential to have an overview of what’s happening on your tenant. The only place I know to get this information is Power BI API. I used Power BI API a data source in Power BI, I needed a bearer token to get any data from Power BI Service, so I used “https://login.windows.net/common/oauth2/token” URL to get the token. Which mean I looked at two different URL’s which restricted me to manual refresh in Power BI Desktop than having an automated schedule. Hopefully, it will change shortly.

You can use this template to answer many questions like:

  • Show me all dashboards in my organization
  • Total number of reports in the organization
  • Users who have admin rights
  • Number of refreshes
  • Number of failures
  • Why some refresh failed
  • When was the last time a certain dataset refreshed, if it’s failed why
  • And many more…

Template to Document the Power BI Service:

You can download the template here – https://1drv.ms/u/s!Avm7gbgZtlMlqGprbKHS6zHd0aPf

Above download link has a zip file with PBIT file and a CSV file. CSV file has user details required for authentication. This blog post covers the approach I used to call the API – https://powerbi.microsoft.com/en-us/documentation/powerbi-developer-register-app/.Once you updated “UserDetails.CSV” file with essential information, save it to your preferred location.

When you open the template, you will see a pop message asking Full path of the UserDetails file, e.g. C:\Users\xxxx\OneDrive\xxx\PBI Portal\PBI\UserDetails.csv. Enter file location and click on the Load button. That’s all; it should just work. Sometimes, you may get a timeout error. Most of the times refreshing again will do the trick if not go to edit queries and choose “Refresh All queries”.

The first page has drill through functionality enabled where it shows the lil driller icon. Are you impressed with above screenshots, want to know how I designed this model? Find it here – https://wp.me/p5u6Pw-AQ 

Summary

Remember, you can see all data points only if you have enough rights and level of access is maintained through Azure App. Also, you can only retrieve the data from workspaces where the username you used to call API has the access. This Power BI model gives exposure to a lot of information but not all. Using this model, I can find all users who have access to a Workspace but not to Power BI Apps. If you want to know all users on your tenant, use Graph API as I showed here – http://prathy.com/2017/11/find-power-bi-users-organisation/. Ideally, I would love to see an OData compatible API like Microsoft Graph API so that I can schedule the refresh. Also, I don’t think we have an endpoint to get Power BI Apps or username of data source connections. Power BI hides Username in data source connections, so this would be something handy to understand which username we used to set up the data source. You may have noticed many placeholders, I would like to add much more information to it, and design reports a bit nicer.  If I make any changes to the template, I will keep this post updated or write another with changes. Hope this helps someone out there! Thanks for reading 🙂

Keep smiling,

Prathy 🙂

 

10 comments
2 FacebookTwitterPinterestEmail

When it comes to writing, I have the least trust in my writing skills. I always wanted my blog to sound more like a personal journey than some docs, whenever I look back mostly it doesn’t. I hope at least this post will sound like a journey. I am not an expert with visualizations; this post is all about my thought processing when I designed this template. Presentation slides or Power BI templates are like babies, you like yours. So, I would really like to hear your feedback on my approach. Feel free to use the comments section or send an email to Prathy@outlook.com

For me, one of the hidden pleasures of working with Power BI is working with colours and choosing the layouts. Of course, primary usage is the ability to design something to promote data-driven decisions in an organisation. I am not encouraging anyone to splash lots of colours on to your report. Designing reports in Power BI remind me of my Digital scrapbooking days. An empty page looks like a blank canvas except, the gesso smell. While I work with business obviously I follow branding and give importance to the user input; when it comes to my templates like my previous blog post “How to find all Power Users” and “POWER BI TEMPLATE TO DOCUMENT THE POWER BI SERVICE“, it gets difficult I keep on change things. This Template is still an unfinished project. I don’t think it will ever get finished. However, I learned a lot while creating this one. Hopefully, I will use my learnings in the future blog posts.

Let’s look at the first Page:

I usually create background images, decide the layout and then use that image as back ground in Power BI. I used image background for this template too. The background colour and left pane are part of image background. I could use frame and all other things as an image as well. That will look and work better. Although for this template, I kind of went with the flow and it was too late to change to the image. Frames: The sole reason behind the Frames was just because I like Frames. Anyone who worked with Power BI can easily guess why I went for the frame which has a lil gap on the top. It’s because of that little bar you get on top of every visual in Power BI  https://ideas.powerbi.com/forums/265200-power-bi-ideas/suggestions/12247677-optionally-remove-focus-mode-box

I tried following a pattern with the look and feel of below image

Most of the times, I used left pane for slicers or any valuable information I wanted to show. I kind of followed inverted pyramid concept, but excluding the left pane, I suppose. Colours: I have various data categories and I wanted to differentiate all those categories with different colours. I looked for popular colour combos and went for the colours I used in the below image. You can find popular colour combos here – https://www.colorcombos.com/popular-color-combinations/2

Pushpin affect, that’s just to show off my digital scrapbooking tact. I used treemap visual and layered that with a pushpin image.

The left pane, there is a lot of information here. Bottom table is something unnecessary, I needed something to fill up that space so I added by creating a calculated table. It’s a bit misleading or needs a better naming. It has Failed, Failures
Today and I also have a dynamic title with failure information. I was duplicating the same content which was not a wise choice.

Next comes the drill through affect: I added a drill through icon to show users that it has Drill Through functionality. Not sure that’s doing any justice. When I googled for drill through, that was the first image came up in my search feed also it was a perfect image to add a background colour. As I wanted to follow my pattern, so I went for colours which were good to show black foreground. When the user uses navigates to drill through page, that page will have the drill through icon background colour theme. Each frame which has some characters are separate table visuals. I formatted them to show like labels.

First, I changed Column Headers Font colour and Background colour to the background I used in the left pane, next increased Row padding to 11 so it aligns nicely with my drill icon. Then bottom to top, I formatted them to send Back, so when the user right clicks to choose drill through it will not interfere. Only when a user clicks on the table, they will be able to see the column header interfering with previous one otherwise it looks nice. Then I used a ribbon chart to show the number of refreshes and number of failures by Date to get an overview. Another pattern I followed is on each page, I added Last refresh data time, obviously to show when was the last time report refreshed.

rest of the pages are pretty much similar apart from few extra affects.

Page Workspaces:

Noticeably assorted colour pattern. Then, I used Bookmarks functionality.

I added an effect like clicking check boxes. Basically, I had several visuals on this page, I created three bookmarks by selecting the visuals I wanted to show. Then on each relevant bookmark, I went for ticked checkbox image than an unticked one. So when users access it using Power Bi service, it will look like a proper checkbox. You can find more information about bookmarks here – https://docs.microsoft.com/en-us/power-bi/desktop-bookmarks

Page Datasets:

Two noticeable approaches here are, using images and numbers to highlight some measures. I used https://www.iconfinder.com/ to get all my icons and I think, they do stand out quite well. Then, I used slicers as a lil button with full frame and I edited interactions to work only with the chart. I also separated them with other visuals using lines so it kind of explains they belong to chart only.

Page Gateways:

I have a lot of placeholders here. I do want to add more information from Gateway Data source query. One noticeable thing I did here is, in the Gateway contact information, I was showing a green circle to show it is live.

I am using an image reference here. I created a measure with following DAX

Status = IF(Gateways[Gateway Status]="Live","https://upload.wikimedia.org/wikipedia/commons/0/0e/Ski_trail_rating_symbol-green_circle.svg",Blank())

Then, I updated table properties to use image height as 24, 24 is the minimum in the current version.

All rest of the pages are similar, nothing too exciting. Hope it gives some ideas to someone out there. Let me know what you think about it.

Prathy

2 comments
2 FacebookTwitterPinterestEmail

If you are the goto person for Power BI in your organisation, then I am sure at least once you may have been asked about Power BI users question, I get asked many times. Of course, I can go to Power BI Service, check each workspace and Power BI Apps and see who has access to what; not a productive way when I am working with many workspaces. Or as I blogged before, I can use Power BI API to get all users data, probably a better way when I am working with many workspaces. If I share content using Power BI Apps and given access using security group, It gets difficult to understand who has access to what, who is a member of what groups.

If you have enough rights, you can view users information and members of which groups in Office 365 portal, again not a very productive way.  However, by using Power BI and Graph API, we can create a report to show all users information. I will be able to see all users who have Power BI license. I can slice this data by Location, Team and many more. It’s not only Power BI; I can view all other licenses. For example, Power Apps and Microsoft Flow. Now I can easily see who has access and monitor users and license, If you want to know about Graph API and how to use it with Power BI, check out my earlier blog post – Access Microsoft Graph API using Power BI.

The Template:

By using Microsoft Graph API, I created a template, where you can see all users, their groups and their license details. You can download the template here – https://1drv.ms/u/s!Avm7gbgZtlMlqB12UF4WXFGzzgXk .

Using template:

When you open the Pbit file, you will see a pop up window with two parameters.

Enter these values Graph API URL = https://graph.microsoft.com/  and Graph API  Version =  beta. After a while, based on size, the template should get populated with data.  If you want to understand the logic behind the template, carry on reading …

10 comments
2 FacebookTwitterPinterestEmail

Recently I came across Microsoft Graph API through Power BI Custom data connectors documentation. Amazed by the amount of information provided by this API. I didn’t find an appropriate use case right away, but later I found a very interesting one “a Power BI template to find all Power BI users and their Groups”. In this post, I am going to cover a bit about Graph API and how to extract data into Power BI, more like an antecedent for my next blog post.

Microsoft Graph API is a RESTful Web API; we can use this to get access to data from the Microsoft Cloud services like Active Directory, Sharepoint, Onedrive and much more. You can find all documentation here – https://developer.microsoft.com/en-us/graph/docs/concepts/overview. What one of the main benefits of this API compares to many other Microsoft API’s is it has a single endpoint https://graph.microsoft.com.  I could use this as a data source in my Power BI Desktop, publish the Model to Power BI service and schedule the data source. Currently, we can not schedule Power BI custom data connectors, but once this feature gets implemented, I think it is better to access Graph API using custom data connector as documented here –  https://github.com/Microsoft/DataConnectors/tree/master/samples/MyGraph

For now, if you want you can extract data from this API using OData Connector. The best place I found useful to start with Microsoft Graph API is the Graph Explorer – https://developer.microsoft.com/en-us/graph/graph-explorer

Microsoft Graph now supports two versions. I found beta useful as it has more data than version v1.0. You can switch between versions easily in Graph explorer and see what data available in each version. A user needs the right level of permission to get access to the correct data, and these permissions are at very granular level. I don’t think; I understood permissions very well if you want you can find permissions information here – https://developer.microsoft.com/en-us/graph/docs/concepts/permissions_reference

You can use the Graph Explorer, to see what data is available to view, modify permissions or just to get an overview of the API.

Access Microsoft Graph API using Power BI

Microsoft Graph has a unified OData endpoint, so I can just use this as OData Feed data source connection, and with few simple clicks, I could import data int Power BI Desktop.

I just copied the API URL from Graph Explorer, pasted in the OData feed data source. I used organizational account authentication. That’s all; I can see all users 🙂

You can download a simple template here – https://1drv.ms/u/s!Avm7gbgZtlMlqCk02OFlwX2ANOfs 

Summary:

This is a fascinating data source, and it gets more interesting with Power BI. As I mentioned earlier, I started playing with this API to get an overview of all Power BI users available. One can get a lot of insights from this data source, hope you find it useful.

Prathy 🙂

 

9 comments
1 FacebookTwitterPinterestEmail

SharePoint is a killer; front end looks nice and beautiful. Once you start exploring behind the screens, that is when it shows the true colours. Whenever you view a list in the SharePoint mostly, you are looking at a SharePoint list View, so it does not show many underlined columns, not even columns with their real names. When you use SharePoint list as a source for a Power BI model, apparently it extracts all the information from the backend, which means you see far more columns than you expected and most of the times column names look obscure.

For example, look at following columns, how anyone can interpret s6hu meant to be column Jan-17

Original Name  Display Name
Title AI Number
cybf Aug-17
Notes1 Notes
s6hu Jan-17
a6ua Feb-17
dnxw Mar-17
uv0g Apr-17
yqkw May-17
ContentType Content Type

If you have faced this problem, want a quick fix, then just download this template. It should rename columns with display names wherever possible, or you may want to read rest of the post, you will know how I created the Template.

SharePoint List Power BI Template

Step 1:

First I created two parameters one for SharePoint URL and one for SharePoint list ID.

[ Description = "Sharepoint List URL" ] SPListURL = null meta [IsParameterQuery=true, Type="Any", IsParameterQueryRequired=true];
[ Description = "Sharepoint List ID" ] ListID = null meta [IsParameterQuery=true, Type="Any", IsParameterQueryRequired=true];

If you are not sure about getting List ID follow this URL or download this template – Get Sharepoint List ID’s. I used following M Code in the Get Sharepoint List ID’s template.

#"SharepointList IDs" = let
    Source = SharePoint.Tables(SPListURL, [ApiVersion = 15])
in
    Source;

Bingo, you get all lists from the given SharePoint site.

Step 2:

Once I know the URL and ID, I created another query with following M Code

 SPList = let
//SPListURL is the URL of sharepoint list
Source = SharePoint.Tables(SPListURL, [ApiVersion = 15]),
/* ListID is the ID of Sharepoint list */
SPList = Source{[Id=ListID]}[Items],
#"Keep only FieldValuesAsText column" = Table.SelectColumns(SPList,{"FieldValuesAsText"}),
RecordColumnNames= Record.FieldNames(#"Keep only FieldValuesAsText column"{0}[FieldValuesAsText]),
ExpandColumn= Table.ExpandRecordColumn(#"Keep only FieldValuesAsText column", "FieldValuesAsText",RecordColumnNames)
in
ExpandColumn;

I navigated to the list I want to import data from, chose FieldValuesAsText column only. FieldValuesAsText has text representation of all list data. Then I used function Record.FieldNames to get all values from Record. This makes it easy for future proofing. If any user changes columns in the future, Model will be clever enough to know the new column names. Then Expanded FieldValuesAsText column with the RecordColumnNames list.

Step 3:

I referenced the previous query and this time extracted all available column names using function Table.ColumnNames. Later I am going to use this query to join the query from Step 4.

 ListColumnNames = let
    Source = SPList,
    ColumnsNames= Table.ColumnNames(Source ),
    #"Converted to Table" = Table.FromList(ColumnsNames, Splitter.SplitByNothing(), null, null, ExtraValues.Error)
in
    #"Converted to Table";

Step 4:

I created another query with the same source as step 1 but in this query, I navigated to field ContentType and then Fields. Apparently, this is where SharePoint hiding all its secrets 🙂

 Title = let
    //SPListURL is the URL of sharepoint list
    Source = SharePoint.Tables(SPListURL, [ApiVersion = 15]),
    /*    ListID is the ID of Sharepoint list       */
    SPList = Source{[Id=ListID]}[Items],
    #"Removed Other Columns" = Table.SelectColumns(SPList,{"ContentType"}),
    ContentType1 = #"Removed Other Columns"{0}[ContentType],
    Fields = ContentType1[Fields],
    #"Removed Other Columns1" = Table.SelectColumns(Fields,{"EntityPropertyName", "InternalName", "StaticName", "Title"})
in
    #"Removed Other Columns1";

Step 5:

I think this step is where the important stuff is.

First, I did a left outer join with ListColumnNames (Step3) and Title (Step 4) on EntityPropertyName and Column1. Then expanded and removed unnecessary columns. I ended up with Origin Name and Title (Display Name name in the SharePoint list). Then I created a custom column with a List of original column names.

In step 6, I want to rename all columns based on the display columns, but if we have duplicate columns or if there is already a column with the same name, Table.RenameColumns function will fail.

So I created a custom column with a list of original column names to see if the column already exists. Then I used List.Contains to check if the column already exists and I filtered out all columns which already exists.

In the end, I left again with Original Name and Title but excluded duplicates and columns already exist in the SharePoint List, then I transformed rows to list using Table.TransformRows function.

RenamedColumns = let
    Source = Table.NestedJoin(ListColumnNames,{"Column1"},Title,{"EntityPropertyName"},"Title",JoinKind.LeftOuter),
    #"Expanded Title" = Table.ExpandTableColumn(Source, "Title", {"EntityPropertyName", "Title"}, {"EntityPropertyName", "Title.1"}),
    #"Renamed Columns" = Table.RenameColumns(#"Expanded Title",{{"Column1", "Original Name"}, {"Title.1", "Title"}}),
    #"Filtered Rows" = Table.SelectRows(#"Renamed Columns", each ([EntityPropertyName] <> null)),
    #"Removed Other Columns" = Table.SelectColumns(#"Filtered Rows",{"Original Name", "Title"}),
    AddListColumns= Table.AddColumn(#"Removed Other Columns", "Custom", each Table.ColumnNames(SPList)),
    CheckNameAlreadyExists = Table.AddColumn(AddListColumns,"TF",each List.Contains([Custom],[Title])),
    #"Filter already existing column names" = Table.SelectRows(CheckNameAlreadyExists, each [TF] = false),
    #"Removed Duplicates" = Table.Distinct(#"Filter already existing column names", {"Title"}),
    #"Removed Other Columns1" = Table.SelectColumns(#"Removed Duplicates",{"Original Name", "Title"}),
    MakeAList= Table.TransformRows(#"Removed Other Columns1", Record.FieldValues)
in 

    MakeAList;

Step 6:

The final step for the template!

Again I referenced my query from Step1, SPList. Then used function Table.RenameColumns to rename columns as display names.  That’s all! Your lovely SharePoint list is ready for the transformations and analysis.

 #"Sharepoint List" = let
    Source = SPList,
    RenameColumns= Table.RenameColumns(Source,RenamedColumns,MissingField.UseNull)
in
    RenameColumns;

Summary :

Lately, one slogan I have heard among people is “We are in digital transformation”. The first place they start or probably the easiest place to start is moving all important Excel workbooks to SharePoint Online then analyse them using Power BI. But it was not that simple to make it work, at least not for me. I could write stories about it. I hope I will find some time to write about my adventures of successfully scheduling a Power BI Model with SharePoint list data source…

Sometimes you face irritating problems, If a user changes a column, it breaks the model, because of renaming columns to friendly names as it looks like in the Sharepoint List. I can’t imagine the frustration of a user when a model brakes with a name change error. Luckily I did not work with really wide lists. But with the very first model I designed with SP list, I realised this issue. My best friend google betrayed me, and I left with no choice other than exploring all the metadata I extracted from Sharepoint List.

This Template helped me to speed up the process. Now I don’t need to create Models for users; if they want they can design their own models, the way they want. It’s a win win 🙂

It’s not only me; there are other are others had similar issues

https://community.powerbi.com/t5/Desktop/Column-Name-Changes-when-Sharepoint-list-is-imported-in-PBI/td-p/183814

https://community.powerbi.com/t5/Issues/Sharepoint-list-connector-pulling-in-incorrect-column-names/idi-p/138924

I hope this helps someone out there. This is just a workaround; it worked great for me, hope it works for you too 😊

Sharepoint Power BI Template  – https://1drv.ms/u/s!Avm7gbgZtlMlqCHE55X7yJaDuBx0

Until next time,

Prathy 🙂

 

6 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