Quantcast
Channel: All Developer posts
Viewing all 48175 articles
Browse latest View live

Re: Adding a Second Measure to Custom Visual

$
0
0

Great!

I've added all files to this gist for you to download the full code, as I'd like to focus on explaining the changes I made so that you can build on them accordingly.

capabilities.json - dataRoles

I've modified the dataRoles to the following:

 

"dataRoles": [ { "displayName": "Bar Grouping", "name": "myCategory", "kind": "Grouping" }, { "displayName": "Actual", "name": "actual", "kind": "Measure" }, { "displayName": "Budget", "name": "budget", "kind": "Measure" } ]

 

 This will modify the visual's data roles as follows:

image.png

Notethat your development visual will likely still have the now removed myMeasure role in there, so your best bet is to ensure everything is fully removed and re-applied when working with the new code I've provided.

capabilities.json - dataViewMappings

The dataViewMappings now look as follows:

 

"dataViewMappings": [ { "conditions": [ { "myCategory": { "max": 1 }, "actual": { "max": 1 }, "budget": { "max": 1 } } ], "categorical": { "categories": { "for": { "in": "myCategory" }, "dataReductionAlgorithm": { "top": {} } }, "values": { "select": [ { "bind": { "to": "actual" } }, { "bind": { "to": "budget" } } ] } } } ]

 

This will group by myCategory and then bind both measures to each. For my test data, the visual table looks as follows:

image.png

Note that this is a public dataset and I'm treating Tip as actual and Total Bill as budget.

Let's take a look at the dataViewMapping in the developer visual for the data I've added:

image.png

The values array now has two entries - one for actual and one for budget. We can see this if we expand the source object for each and then roles - and this is how you can determine which entry is performing which role. The measure values for each are then in the associated values array (which will match the order of myCategory). For brevity, I've only expanded this for actual.

visual.ts - BarchartDataPoint interface

We need to change the interface spec to ensure that the 'shape' of each data point is correct. This now looks as follows:

 

export interface BarchartDataPoint{ category: string; actual: number; budget: number; }

 

(it's not essential, but good practice to use camelCase for properties, so I have modified all to suit)

This now means that our view model expects two numeric values for each category, which matches our dataViewMappings, so we need to map them into the view model next.

visual.ts - createViewModel function

If you're making the above changes incrementally, you'll now see errors in your code, because the BarchartDataPoint interface 'shape' does not match the object you're assigning in the createViewModel function. We need to update this.

With the above in mind, and how the dataViewMapping looks, we need to change the way we iterate over it.

  • You were previously iterating over the measure values; I have modified this to iterate over the category values just to make the next bit easier.
  • I'm going to do the simplest possible solution for the measures using array accessors of [0] for actual and [1] for budget.
  • To make your code super-resilient, you would filter the values array by role to ensure the value is definitely provided, but we can worry about that waaaaay later 😉

The portion of the function code to map the view model now looks as follows:

 

categoryNames.map((c, ci) => { /** c= category, ci = category array index */ BarchartDataPoints.push({ category: <string>c, actual: <number>categoricalDataView.values[0].values[ci], budget: <number>categoricalDataView.values[1].values[ci] }); });

 

You have a sort lower down, so that's been modified to use the actual value:

 

if(SortBySize){ BarchartDataPoints.sort((x,y) =>{return y.actual - x.actual}) }

 

visual.ts - update function

It's now a case or binding everything from the view model correctly to your chart logic.

The first fix is your maxValueY assignment - we just get this to match the highest value now that you have two measures:

 

let maxValueY: number = d3.max( viewModel.DataPoints, (dataPoint:BarchartDataPoint) => /** Get the higher of either measure per group */ +Math.max(dataPoint.actual, dataPoint.budget) );

 

Now, the actual shape! barSelectionMerged gets updated to now use the actual property from the data point:

 

barSelectionMerged .attr("x", (dataPoint: BarchartDataPoint) => xScale(dataPoint.category)) .attr("y", (dataPoint: BarchartDataPoint) => yScale(Number(dataPoint.actual))) .attr("width", xScale.bandwidth()) .attr("height", (dataPoint: BarchartDataPoint) => (plotArea.height - yScale(Number(dataPoint.actual)))) .style("fill",(dataPoint:BarchartDataPoint) => viewModel.BarColor);

 

...and barSelectionMerged2 gets the budget property as part of its yScale:

 

barSelectionMerged2 .attr("x", (dataPoint: BarchartDataPoint) => xScale(dataPoint.category) + xScale.bandwidth() / 4) .attr("y", (dataPoint: BarchartDataPoint) => yScale(Number(dataPoint.budget))) .attr("width", xScale.bandwidth() / 2) .attr("height", (dataPoint: BarchartDataPoint) => (plotArea.height - yScale(Number(dataPoint.budget)))) .style("fill", (dataPoint: BarchartDataPoint) => 'yellow') .style("fill-opacity", (dataPoint: BarchartDataPoint) => 1);

 

Verifying

We can now test in the developer visual! Here's how it looks for my data above:

image.png

And hopefully, this is where you need to be 🙂

Good luck!

Daniel


If my post solves your challenge, then please consider accepting as a solution to help other forum members find the answer more quickly 🙂


Re: Custom Visual update options.updateId

$
0
0

Hi ,

Hmmm... this might be getting to the point where I'm trying to punch above my weight 😉

You can get Power BI to provide a higher-level dataView if you've enabled drilling on the specified dataRole, but this is via the conventional buttons on the visual header, and/or through the context menu, if you've enabled it. I have this working very nicely on a visual using the categorical data view mapping. Doing this programmatically with elements in the visual is where I might get stuck.

If I were to approach the challenge of a matrix with expandable/collapsible groups, I'd start with the approach of ensuring that my dataView contained all the data I needed for all levels and would then map my view model to show/hide each node/level accordingly, and providing a toggle control in the approrpiate state to expand/collapse as appropriate. This comes with the disclaimer that I haven't really thought about this before today...

There's not so many people on the forums who chime in on custom visuals development, so I'm not sure (but hoping) that we might get something more timely for you from someone else. Have you tried asking the custom visuals team about this one? The can be contacted at pbicvsupport@microsoft.com if you haven't tried them. They do frequent the Custom Visuals Development Discussion forum on this board as well, but this might be a specific enough challenge that a direct question could be warranted.

Regards,

Daniel


If my post solves your challenge, then please consider accepting as a solution to help other forum members find the answer more quickly 🙂

Re: How to calculate count of IDs per Average Number of Hours bucket

$
0
0

Hi , and it's great that you found an approach that works for you! It might be worth marking your previous post as the solution, just to close-off the thread and allow any followers to see where the resolution ended up.

Glad you're finding the community a nice place to be - it's a great place to learn and network 🙂

All the best,

Daniel

Re: Adding a Second Measure to Custom Visual

$
0
0

Wow, this is incredible.  This is exactly what I need.  And thank you for your detailed explanation.  I have learned a tremendous amount from all this.  Thanks.

Re: Adding a Second Measure to Custom Visual

$
0
0

You're very welcome! Glad you found it useful 🙂

Re: Export selected data to an API from Power BI

$
0
0

Hi ,

The only way to programmatically export data from a Power BI dataset is using the XMLA endpoint - this is currently in preview (expected to be in GA by September) and is a premium feature, so won't be viable if you're using Pro.

To mimic exporting from a report, you'd need to apply the necessary filters and/or context via a suitable command over XMLA to get the desired result set before sending on to an external service.

There's also an idea for the feature you're specifically asking for, if you want to vote on it.

Not the answer you're hoping for, I know, but hopefully provides some further insight.

Regards,

Daniel


If my post solves your challenge, then please consider accepting as a solution to help other forum members find the answer more quickly 🙂

Re: Acquired token gives 401 Unauthorized when trying to create DataSet

$
0
0

Solved it myself 

 

App registration (service principal) needs to be a member of a Security group

Q&A Embedded shows the whole pages if you type the name of this page

$
0
0

Q&A shows results if I type the name of the page. How can I switch off this feature?

Q&A shows pages.JPGQ&A shows results for page namePowerBIDesktop.JPGPowerBI desktop

 


Re: Strange prefix on USERNAME() and USERPRINCIPALNAME() for external users

$
0
0

Thanks Greg. Will the email always be preceded by a #? In other words, can I build a statement into RLS that removes anything before #?

Refresh embed token using javascript

$
0
0

Hi All,

 

I am generating embed token if expired using javascript using below is complete code:

 

var url = "https://app.powerbi.com/groups/5036f206-3d0e-46b8-8e3e-a54a8b35dd19/reports/447a6395-0ab8-46aa-9afa-aa67e11633eb/ReportSection9fdfd7dd0814177238c5" var embedurl = "https://app.powerbi.com/reportEmbed?reportId=b4fc3b95-a1c4-462c-8ce4-3d6c0de928af&autoAuth=true&groupId=02a09d2e-96ea-480a-a402-a68d42f51e49"; var urlParams = url.split('/'); var groupId = urlParams[4]; var reportID = urlParams[6]; console.log(groupId,"&&", reportID); $(document).ready(function () { embedReportAndSetTokenListener(false, reportID, groupId, this.datasetId, "Create", "https://api.powerbi.com/beta/myorg/", embedurl); }); function generateEmbedToken(reportID, groupID) { var apiUrl = "https://api.powerbi.com/v1.0/myorg/groups/" + groupID + "/reports/" + reportID + "/GenerateToken" var bearerToken = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkhsQzBSMTJza3hOWjFXUXdtak9GXzZ0X3RERSIsImtpZCI6IkhsQzBSMTJza3hOWjFXUXdtak9GXzZ0X3RERSJ9.eyJhdWQiOiJodHRwczovL2FuYWx5c2lzLndpbmRvd3MubmV0L3Bvd2VyYmkvYXBpIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvYzQyNjExM2UtZDNjZS00YWRiLWJiOTEtYzUzYjM3YzU4ZjE0LyIsImlhdCI6MTU4MDQ2NTQwNywibmJmIjoxNTgwNDY1NDA3LCJleHAiOjE1ODA0NjkzMDcsImFjY3QiOjAsImFjciI6IjEiLCJhaW8iOiI0Mk5nWU5pemJ5MmpnM2pSMWRmdVd1NFhjM0o3TjBXMzcvajZPU01xdkdwYVdGbmxjMGtBIiwiYW1yIjpbInB3ZCJdLCJhcHBpZCI6IjdmNTlhNzczLTJlYWYtNDI5Yy1hMDU5LTUwZmM1YmIyOGI0NCIsImFwcGlkYWNyIjoiMiIsImZhbWlseV9uYW1lIjoiUmVwb3J0cyIsImdpdmVuX25hbWUiOiJQSE0iLCJpcGFkZHIiOiIxMDYuMTk3LjE5OS4xMDMiLCJuYW1lIjoiUEhNIFJlcG9ydHMiLCJvaWQiOiJhMjBkNTZkMi05MjlkLTQ2NTctOTY2Ny05NjRhNzU0YTM0ODQiLCJvbnByZW1fc2lkIjoiUy0xLTUtMjEtMjA0MTg1ODIxNS0yODY0NjY5MTUwLTM2ODc0NzYyNS0xNjYyOSIsInB1aWQiOiIxMDAzN0ZGRUE5M0ZFOTI2Iiwic2NwIjoidXNlcl9pbXBlcnNvbmF0aW9uIiwic3ViIjoiY29mOFFBcFJWTEpVWnowZnozTHhvbUFVWmdqM2FMWWxINFdUbnFJMVdyayIsInRpZCI6ImM0MjYxMTNlLWQzY2UtNGFkYi1iYjkxLWM1M2IzN2M1OGYxNCIsInVuaXF1ZV9uYW1lIjoiUEhNLlJlcG9ydHNASEVBTFRIRUMuQ09NIiwidXBuIjoiUEhNLlJlcG9ydHNASEVBTFRIRUMuQ09NIiwidXRpIjoid2xmZE5aakVoMGFhVlpKMkFlaHVBQSIsInZlciI6IjEuMCJ9.vb0qae_Dkzw_oT4_1YsxHc-UCBGhGoYTMPN5BX0t8f8skLMZUiSupqiQkh-oms2Rg-14qk_jIEvpq9mhZt4HJEJ0w94Tn-RD8GToDlJ0bjRLEkskkUrnLUgudlT-Qa0etD3fyINMgaUVRiN2uNfP2Umipi1gBpVu6-B7HMtRVz2gDIHQBS6_gu8FNYzZMe7Ktc6DAfMPgYW3RUWIeMoiwMFr-u_eTHQheh70xnidkFrw-3L1CGqvh4tZyd3TbiURp3UlXVtM5awg75b4zKLth1_Y0Z-ROOmYh9geo24ytG8i3RxfgnlNgwBfHvrL1jvuysTDNHotbZgQgjNslS7Y4Q"; $.ajax({ type: "POST", url: apiUrl, headers: { "Content-Type": "application/json", "Authorization": 'Bearer ${this.bearerToken}' }, body: { "accessLevel": "View", "datasetId": this.datasetId }, success: function (response) { console.log('Token accessed'); }, failure: function(xhr, status, error){ console.log(error); }, error: function (response) { console.log(response); } }); return false; } function embedReportAndSetTokenListener(setAccessToken, reportId, groupId, datasetId, accessLevel, baseUri, embedUrl){ // Generate embed token generateEmbedToken(reportId, groupId).then(function(Token) { var embedToken = Token.token; // set config for embedding report var config = createConfig(embedToken,embedUrl,reportId); // Get a reference to the embedded report HTML element var embedContainer = $('#embedContainer')[0]; // Embed the report and display it within the div container. var report = powerbi.embed(embedContainer, config); // Report.off removes a given event handler if it exists. report.off("loaded"); // Report.on will add an event handler which prints to Log window. report.on("loaded", function() { // Set token expiration listener setTokenExpirationListener(Token.expiration, 2 /*minutes before expiration*/, reportId, groupId); }); }); } function setTokenExpirationListener(tokenExpiration, minutesToRefresh, reportId, groupId){ // get current time var currentTime = Date.now(); var expiration = Date.parse(tokenExpiration); var safetyInterval = minutesToRefresh* 60 * 1000; // time until token refresh in milliseconds var timeout = expiration - currentTime - safetyInterval; // if token already expired, generate new token and set the access token if (timeout<=0) { console.log("Updating Report Embed Token"); updateToken(reportId, groupId); } // set timeout so minutesToRefresh minutes before token expires, token will be updated else { console.log("Report Embed Token will be updated in " + timeout + " milliseconds."); setTimeout(function() { updateToken(reportId, groupId); }, timeout); } } function updateToken(reportId, groupId) { // Generate new EmbedToken generateEmbedToken(reportId, groupId) .then(function( Token ) { // Get a reference to the embedded report HTML element var embedContainer = $('#embedContainer')[0]; // Get a reference to the embedded report. var report = powerbi.get(embedContainer); // Set AccessToken report.setAccessToken(Token.token) .then(function() { // Set token expiration listener // result.expiration is in ISO format setTokenExpirationListener(Token.expiration,2 /*minutes before expiration*/); }); }); }

  When i run application getting error like ypeError: generateEmbedToken(...).then is not a function
at embedReportAndSetTokenListener.

 

How could i fix this issue, Please suggest.

 

Re: Custom Visual update options.updateId

$
0
0

Thanks for your response.

 

I had already implemented your suggest approach to 'get all data' for rows, and that is working great.  But using this approach on columns, seems to me to be a performance nightmare.  I would like to start with fewer columns and make the user choose to drill for more.  Since it appears there is only the drill down (selectionManager.select) method and none of the other drilling options, with directly callable methods by the code, I have no other choice then to use the builtin drilling interface and/ context menu when it comes to columns.   

 

I new to power bi,  so I don't completely understand why the actions on the context menu cannot be callable from the custom visual directly.  It would also be great if you could add and restrict items on the menu. 

 

I'll try to reaching out to pbicvsupport@microsoft.com as well.

Thanks again for your response.  

Regards,

Bruce

 

 

 

Duplicate Workspaces

$
0
0

I seem to have duplicate workspaces in the service, just the name not the content. Did I forget to read something? Very confusing.Capture.PNG

Re: Duplicate Workspaces

Power bi embed with RLS

$
0
0

Hello guys! I'm still a newbie in power bi been developing a dashboard for a client for a year now and its going well. Recently we have talked about the possible deployment of our dashboard which will include RLS since we expect to introduce our solution to external users.

 

Currently the expected flow is SQL -> Power Bi -> RLS.   Now I have explored on secure embed with RLS i have already tested it via the microsoft github playground to use the parameter username() to trigger the RLS. 

But my question is for me to test this further would i need to develop a web app then generate the token within the app and pass the user variable for the RLS?  Do you have any sample practical applications? What if the current website of my client is not coded in C#,  python (could be wordpress) is there a way to use this for our current site without me having to dissect the website? Any practical applications?

Lastly, i understand there are possible charges that we might be paying specially if we opt for the dedicated capacity but is there a way around this? 

 

What we have right now is a Powerbi Pro license. 

Thanks so much guys for the replies

Re: Duplicate Workspaces

$
0
0

 Thank you, I'll have a quick word with myself about creating duplicates


Save User Filters in Power BI Embedded

$
0
0

Hello,

We have a customer who embeds his reports in a web application (written in java) using embed code (not publish to web).

They ask about implementing persistent filters (in the user level) in this scenario, same as when the user logs to the Power BI service.

 They also ask about the Reset to Default ability.

 

I guess that in this scenario, this should be part of the application. It should read (with REST) the current filters and store it in the application DB. The application should also contain its own Reset to Default tool.

 

Am I going in the correct direction? Is there some kind of tutorial to get a reference how it's done?

 

Thanks,

Barak  

Dynamic Row Level Security - Bridge Table

$
0
0

Hello,

 

I have been watching videos and reading all the blog posts I can about Dynamic Row Level Security in Power BI but I cannot get an answer to my problem.

 

Problem:

I have the following bridge table in my data model. It is joined to the Orders table on City. It is also joined to a UserList table (which contains the users email address) on UserID.

 

UserIDContinentCountryCity
1EuropeNULLNULL
1AsiaThailandBangkok
2AsiaChinaBeijing
2AsiaThailandBangkok
2AfricaSouth AfricaCape Town

 

User 1 should see data from the Orders table for Bangkok and all cities in Europe.

User 2 should only see data from the Orders table for Beijing, Bangkok and Cape Town.

 

The following DAX script works correctly for filtering the Orders table for User 2 but only shows Bangkok for User 1. I have entered it under the Orders table in the Manage Roles window.

[SalesCity] =
LOOKUPVALUE(
'UserOrderBridge'[City],
'UserList'[UserName],UserPrincipalName(),
'UserOrderBridge'[City],
Orders[SalesCity]
)

 

So my issue is how do I change this script to pull back all of Europe and Bangkok for User 1?

I tried to use a IF(ISBLANK() statement but that didn't work. 

If i create another Role for Continent, itll bring back all the data for Asia and africa for User2.

 

I am pretty new to DAX so any help is greatly appreciated.

Thanks

Flow and Push Datasets. My data set only brings in 5 tweets an hour

$
0
0

really odd one

 

I set up Microsoft Flow to capture tweets into a push data set. However I only ever get 5 an hour when I check my report. I do a visual on count by hour and it only ever goes up to 5. So I checked Flow and there are just 5 an hour. Im 100% there isnt 5 an hour every hour coming through.

 

Has any one got any ideas on this. Im a bit stumped?

Access to Keboola system through REST API

$
0
0

Hi all ... I have credentials to access Keboola but facing issues to translate the commands into Power Query. I tested the options in browser (by the documentation) and it is working (response=200 from API) but facing problems in Power BI.

Anyone with experience in this system who could share some knowledge?

Re: Access to Keboola system through REST API

$
0
0

Hi  , 

You said that you have problem  when  connect to powerbi, so if possible, could you please inform me more detailed error information? Then I will help you more correctly. In addition, you also could post this problem in Keboola  forum for more suggestions.

Best Regards,
Zoe Zhi

If this post helps, then please consider Accept it as the solution to help the other members find it more quickly.

 

Viewing all 48175 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>