Power BI report is not loading in MVC application - c#

Few days back i learnt making reports in power BI and it was great experience learning power BI. As i am creating a dashboard for my MVC based web application, i wanted to make look and fill of my dashboard attractive. I am thinking of embedding power BI report with that for that i have used following code inside view :-
<body>
<script type="text/javascript" src="~/Scripts/PowerBI/powerbi.js"></script>
<script type="text/javascript">
window.onload = function () {
var iframe = document.getElementById("iFrameEmbedReport");
iframe.src = "https://app.powerbi.com/reportEmbed?reportId=" + embedReportId;
iframe.onload = postActionLoadReport;
}
function postActionLoadReport() {
var m = {
action: "loadReport",
accessToken: accessToken
};
message = JSON.stringify(m);
iframe = document.getElementById("iFrameEmbedReport");
iframe.contentWindow.postMessage(message, "*");;
}
</script>
<style>
#iFrameEmbedReport {
width: 95%;
height: 95%;
}
</style>
<iframe ID="iFrameEmbedReport"></iframe>
</body>
And code for controller is given below :-
public class DashBoardController : Controller
{
string baseUri = WebConfigurationManager.AppSettings["PowerBiDataset"];
string AccessToken = string.Empty;
// GET: DashBoard
public ActionResult DashBoard()
{
if (Request.Params.Get("code") != null)
{
Session["AccessToken"] = GetAccessToken(
Request.Params.GetValues("code")[0],
WebConfigurationManager.AppSettings["ClientID"],
WebConfigurationManager.AppSettings["ClientSecret"],
WebConfigurationManager.AppSettings["RedirectUrl"]);
Response.Redirect("~/DashBoard/DashBoard");
}
if (Session["AccessToken"] != null)
{
AccessToken = Session["AccessToken"].ToString();
GetReport(0);
}
return View();
}
protected void GetReport(int index)
{
System.Net.WebRequest request = System.Net.WebRequest.Create(
String.Format("{0}/Reports",
baseUri)) as System.Net.HttpWebRequest;
request.Method = "GET";
request.ContentLength = 0;
request.Headers.Add("Authorization", String.Format("Bearer {0}", AccessToken));
using (var response = request.GetResponse() as System.Net.HttpWebResponse)
{
using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
{
PBIReports Reports = JsonConvert.DeserializeObject<PBIReports>(reader.ReadToEnd());
if (Reports.value.Length > 0)
{
var report = Reports.value[index];
ViewData["AccessToken"] = Session["AccessToken"].ToString();
ViewData["EmbedURL"] = report.embedUrl;
ViewData["ReportID"] = report.id;
}
}
}
}
public void GetAuthorizationCode()
{
var #params = new NameValueCollection
{
{"response_type", "code"},
{"client_id", WebConfigurationManager.AppSettings["ClientID"]},
{"resource", WebConfigurationManager.AppSettings["PowerBiAPI"]},
{ "redirect_uri", WebConfigurationManager.AppSettings["RedirectUrl"]}
};
var queryString = HttpUtility.ParseQueryString(string.Empty);
queryString.Add(#params);
Response.Redirect(String.Format(WebConfigurationManager.AppSettings["AADAuthorityUri"] + "?{0}", queryString));
}
public string GetAccessToken(string authorizationCode, string clientID, string clientSecret, string redirectUri)
{
TokenCache TC = new TokenCache();
string authority = WebConfigurationManager.AppSettings["AADAuthorityUri"];
AuthenticationContext AC = new AuthenticationContext(authority, TC);
ClientCredential cc = new ClientCredential(clientID, clientSecret);
return AC.AcquireTokenByAuthorizationCode(
authorizationCode,
new Uri(redirectUri), cc).AccessToken;
}
}
public class PBIReports
{
public PBIReport[] value { get; set; }
}
public class PBIReport
{
public string id { get; set; }
public string name { get; set; }
public string webUrl { get; set; }
public string embedUrl { get; set; }
}
As i think, I am doing everything right but i don't know why it's not able to display report. Please suggest me if i did any mistake in the above given code.

It's not clear where the error is, as you provided lots of code and no specfics on the error itself. Here are few things to note:
To embed Power BI content in your HTML you just need an empty div element
Too much code overall.
You need to perform following steps:
Acquire authentication token by using AuthenticationContext.AcquireTokenAsync with credentials provisioned on Power BI side
Instantiate PowerBIClient with the token you just obtained. That's the token for your application. Never pass it to users. Don't store in Session as it'll expire. PowerBIClient(new Uri(_Context.ApiUrl), new TokenCredentials(authResult.AccessToken, "Bearer"))
Obtain ID(s) of content that's available in Power BI. There are different APIs for different types (dashboards, reports, tiles) as well as for content that's in Groups(workspaces) or not, e.g. client.Dashboards.GetDashboardsInGroupAsync(GroupId). This step could be skipped if you already know what type of content you're getting and its ID. Keep in mind that if EmbedUrl property is empty on returned object(s) you won't be able to render, even if you manually construct such url.
Obtain Embed Token for particular content. There are different methods available, e.g. client.Reports.GenerateTokenInGroupAsync(GroupId, Id-of-content, new GenerateTokenRequest(accessLevel: "view"))
The final step is to apply Embed Token and EmbedUrl on the client-side. Something among following lines:
.
var embedToken = $('#embedToken').val();
var txtEmbedUrl = $('#txtReportEmbed').val();
var txtEmbedReportId = $('#txtEmbedReportId').val();
var models = window['powerbi-client'].models;
var permissions = models.Permissions.All;
var config= {
type: 'report',
tokenType: models.TokenType.Embed,
accessToken: embedToken,
embedUrl: txtEmbedUrl,
id: txtEmbedReportId,
permissions: permissions,
settings: {
filterPaneEnabled: true,
navContentPaneEnabled: true
}
};
var embedContainer = $('#embedContainer')[0];
var report = powerbi.embed(embedContainer, config);
You should be able to test your stuff here. Just plugin your values.
You can also observe sample app here]2. Flow provided above is for "app owns data" case.

Related

Lsing API to list results in different method

The aim of the program below is to get a list of Reports built in our database and find out how many of these reports use the field NameFirst within them.
I'm able to make an API call and, at GetReports, get a list of the ReportIDs.
However, I'm unable to move forward with calling the list I created at GetReports in the next method, GetNameFirst. I was wondering if someone could please help me out with this.
For the script below, I get a red underline for the variable values. This is understandable because I didn't know where and how to tell my code to bind the list output for GetReports to the variable values in GetNameFirst.
Also, if I could get some help in finding out which reports have the field NameFirst in them once I accomplish calling the list from the first method to the second, I'd appreciate that also. I'm currently heading in the direction of using a foreach, but I'm unsure if that's the best path to take.
Main Program
namespace NameFirstSearch
{
class Program
{
static void Main(string[] args)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
const string username = "Username";
const string password = "Password";
const string baseUrl = "https://example.com/rest/services/";
const string queryString = "query?q=Select * From Report Where LastRanDate is not null";
const string queryNameFirst = "getreport/";
var client = new HttpClient();
client.BaseAddress = new Uri(baseUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var auth = Convert.ToBase64String(Encoding.Default.GetBytes(username + ":" + password));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", auth);
GetReports(client, queryString).Wait();
GetNameFirst(client, queryNameFirst).Wait();
Console.ReadLine();
}
static async Task<List<Properties>> GetReports(HttpClient client, string queryString)
{
List<Properties> result = new List<Properties>();
var response = await client.GetAsync(queryString);
// Check for a successfull result
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<List<Properties>>(json);
Console.WriteLine(result.Count());
}
else
{
// Error code returned
Console.WriteLine("No records found on first method.");
}
return result;
}
static async Task GetNameFirst(HttpClient client, string queryNameFirst)
{
string reportType = "JSON";
foreach (var item in values)
{
var output = await client.GetAsync(queryNameFirst + item.ReportID + reportType);
if (output.IsSuccessStatusCode)
{
var allText = await output.Content.ReadAsStringAsync();
var fields = JsonConvert.DeserializeObject<List<NameFirst>>(allText);
}
else
{
// Error code returned
Console.WriteLine("No records found on second method.");
}
}
}
}
Class for report list
class Properties
{
public int ReportID { get; set; }
}
Class for reports' NameFirst property
class NameFirst
{
public string FirstName { get; set; }
}
I thought this was a partial code, but since you've cleared things out.
you'll need to change your code a bit
this :
GetReports(client, queryString).Wait();
do it like this :
var reportsList = GetReports(client, queryString).Result;
now, you'll need to pass the reportsList to the second method GetNameFirst which would be adjusted to this :
static async Task GetNameFirst(HttpClient client, string queryNameFirst, List<Properties> results)
{
string reportType = "JSON";
foreach (var item in results)
{
var output = await client.GetAsync(queryNameFirst + item.ReportID + reportType);
if (output.IsSuccessStatusCode)
{
var allText = await output.Content.ReadAsStringAsync();
var fields = JsonConvert.DeserializeObject<List<NameFirst>>(allText);
}
else
{
// Error code returned
Console.WriteLine("No records found on second method.");
}
}
}
with this adjustment, you'll need to adjust the call as well :
GetNameFirst(client, queryNameFirst, reportsList).Wait();

C# Response = "WaitingForActivation"

I have an asp.net website which sends a tweet on a button click event.
I am using the TwitterApi for this and have an authenticated developer account.
This function was working from September 2018 until last month, but all of a sudden it won't send the tweets on request.
The response I get now is - "Id = 1, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}""
After searching around, this doesn't seem like a twitter error, but an async error of some kind. I have made some minor alterations to my website in this time, but I cant see anything I have changed that would cause this issue.
The code is below.
Can any one see why this error would occur?
protected void Publish_Click(object sender, EventArgs e)
{
DataAccess.Publish();
SendEmails();
SendTweet();
Response.Redirect("OtherPage.aspx");
}
public static void SendTweet()
{
string text = DataAccess.GetText();
var twitter = new TwitterApi();
var response = twitter.Tweet(text);
}
public TwitterApi()
{
this.consumerKey = "XXX";
this.consumerKeySecret = "XXX";
this.accessToken = "XXX";
this.accessTokenSecret = "XXX";
sigHasher = new HMACSHA1(new ASCIIEncoding().GetBytes(string.Format("{0}&{1}", consumerKeySecret, accessTokenSecret)));
}
public Task<string> Tweet(string text)
{
var data = new Dictionary<string, string> {
{ "status", text },
{ "trim_user", "1" }
};
return SendRequest("statuses/update.json", data);
}
Task<string> SendRequest(string url, Dictionary<string, string> data)
{
var fullUrl = TwitterApiBaseUrl + url;
// Timestamps are in seconds since 1/1/1970.
var timestamp = (int)((DateTime.UtcNow - epochUtc).TotalSeconds);
// Add all the OAuth headers we'll need to use when constructing the hash.
data.Add("oauth_consumer_key", consumerKey);
data.Add("oauth_signature_method", "HMAC-SHA1");
data.Add("oauth_timestamp", timestamp.ToString());
data.Add("oauth_nonce", "a"); // Required, but Twitter doesn't appear to use it, so "a" will do.
data.Add("oauth_token", accessToken);
data.Add("oauth_version", "1.0");
// Generate the OAuth signature and add it to our payload.
data.Add("oauth_signature", GenerateSignature(fullUrl, data));
// Build the OAuth HTTP Header from the data.
string oAuthHeader = GenerateOAuthHeader(data);
// Build the form data (exclude OAuth stuff that's already in the header).
var formData = new FormUrlEncodedContent(data.Where(kvp => !kvp.Key.StartsWith("oauth_")));
return SendRequest(fullUrl, oAuthHeader, formData);
}
async Task<string> SendRequest(string fullUrl, string oAuthHeader, FormUrlEncodedContent formData)
{
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Add("Authorization", oAuthHeader);
var httpResp = await http.PostAsync(fullUrl, formData);
var respBody = httpResp.ToString();
return respBody;
}
}

.NET Core 2.0 Is the below code thread safe

Here's my code and I have doubt on thread safe implementation. My questions are below
The return value from GetHtmlPageAsync is object. Is it thread safe? I will use this object and add into the collection and finally upload into database.
The main method logic is below (implementation in-progress). I have set of domains, I have list of 10000 domains in the collection, the idea is, I will put it in the queue and call the GetHtmlPageAsync to get the HTML of the page. Based on the HTML, I will get the necessary hyperlinks. Once I get the hyper links, I will check certain word is available in the link. If the word is available in the link, I will call the same method GetHTMLPageAsync to get the HTML of that page. So the same thread may call the GetHtmlPageAsync to process another link. I am trying to reuse the same method for multiple calls in thread safe way. Please help.
#edit1 . I have added the main method. Instead of Queue. I have used ForEach
public static async Task<int> ProcessDomainAsync(List<string> domains)
{
Parallel.ForEach(domains, async (currentDomain) =>
{
var domainBody = await GetHtmlPageAsync(currentDomain);
var language = string.Empty;
var country = string.Empty;
var createdOn = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
var updatedOn = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
var machine = Environment.MachineName;
var message = "[" + domainBody.ErrorCode + "] - " + domainBody.ErrorMessage;
var active = false;
var stage = "End";
var url = currentDomain;
if (domainBody.ErrorCode == 0)
{
var html = domainBody.Body;
language = Common.GetLanguageIdentification(html);
country = Common.GetCountryIdentification(currentDomain);
message = string.Empty;
active = true;
stage = "Stage1";
var hyperLinks = Common.GetAllAHrefTags(html);
//Process Hyper Links
}
_domainList.Add(new Domain
{
Url = url,
Language = language,
Country = country,
MachineName = machine,
Message = message,
Active = active,
Stage = stage,
CreatedOn = createdOn,
UpdatedOn = updatedOn
});
domainCount++;
});
return domainCount;
}
public class DomainBody
{
public string Body;
public string ErrorMessage;
public int ErrorCode;
}
public static class DomainProcessing {
static async Task<DomainBody> GetHtmlPageAsync(string url)
{
#region Initialize Proxy
var sessionId = new Random().Next().ToString();
var proxy = new WebProxy(Constant.ProxyUrl, Constant.ProxyPort);
var login = Constant.ProxyUserName + "-session-" + sessionId;
proxy.Credentials = new NetworkCredential(login,Constant.ProxyPassword);
#endregion
#region Initialize Variables
var user_agent = Common.GenerateRandomUserAgent();
var body = string.Empty;
var errorCode = 0;
var errorMessage = string.Empty;
#endregion
try
{
#region Format URL with Http Protocol
var domainSB = new StringBuilder();
domainSB.Append("http://");
domainSB.Append(url);
#endregion
#region Process Domain
var request = (HttpWebRequest) WebRequest.Create(new Uri(url));
request.Proxy = proxy;
request.UserAgent = user_agent;
request.Timeout = Constant.TimeOut;
using (var response = await request.GetResponseAsync().ConfigureAwait(true))
using (var content = new MemoryStream())
using (var responseStream = response.GetResponseStream())
{
await responseStream.CopyToAsync(content);
var bodyArray = content.ToArray();
body = Encoding.UTF8.GetString(bodyArray, 0, bodyArray.Length);
}
errorCode = 0;
errorMessage = string.Empty;
#endregion
}
catch (HttpRequestException ex)
{
body = string.Empty;
errorCode = ex.InnerException.HResult;
errorMessage = ex.InnerException.Message;
}
catch (Exception ex)
{
body = string.Empty;
errorCode = ex.HResult;
errorMessage = ex.Message;
}
var domainBody = new DomainBody
{
Body = body,
ErrorCode = errorCode,
ErrorMessage = errorMessage
};
return domainBody;
}
}enter code here
Generally speaking, local variables should be thread safe (simply because they have no idea there even is another thread and other threads have no way to access them).
Anything that can be accessed by multiple threads should be looked at. _domainList for example. Make sure the Add method is thread-safe because you are calling it potentially in parallel.

mvc paypal payment details amount dont show

In my project include paypal express check out. I send all details in below class. And My code below;
public class PayPal
{
public static PayPalRedirect ExpressCheckout(PayPalOrder order)
{
var values = new NameValueCollection();
values["USER"] = PayPalSettings.Username;
values["PWD"] = PayPalSettings.Password;
values["SIGNATURE"] = PayPalSettings.Signature;
values["METHOD"] = "SetExpressCheckout";
values["VERSION"] = "63.0";
values["RETURNURL"] = PayPalSettings.ReturnUrl;
values["CANCELURL"] = PayPalSettings.CancelUrl;
values["PAYMENTREQUEST_0_PAYMENTACTION"] = "SALE";
values["PAYMENTREQUEST_0_CURRENCYCODE"] = "USD";
values["PAYMENTREQUEST_0_AMT"] = order.Amount.ToString("0.00", CultureInfo.InvariantCulture);
values["PAYMENTREQUEST_0_DESC"] = "Apart Name";
values = Submit(values);
string ack = values["ACK"].ToLower();
if (ack == "success" || ack == "successwithwarning")
{
return new PayPalRedirect
{
Token = values["TOKEN"],
Url = String.Format("https://{0}/cgi-bin/webscr?cmd=_express-checkout&token={1}",
PayPalSettings.CgiDomain, values["TOKEN"])
};
}
throw new Exception(values["L_LONGMESSAGE0"]);
}
private static NameValueCollection Submit(NameValueCollection values)
{
string data = String.Join("&", values.Cast<string>()
.Select(key => String.Format("{0}={1}", key, HttpUtility.UrlEncode(values[key]))));
var request = (HttpWebRequest)WebRequest.Create(
String.Format("https://{0}/nvp", PayPalSettings.ApiDomain));
request.Method = "POST";
request.ContentLength = data.Length;
using (var writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(data);
}
using (var reader = new StreamReader(request.GetResponse().GetResponseStream()))
{
return HttpUtility.ParseQueryString(reader.ReadToEnd());
}
}
}
and my controller ;
public ActionResult Pay(FormCollection form)
{
var redirect = PayPal.ExpressCheckout(new PayPalOrder { Amount = 50 });
Session["token"] = redirect.Token;
return new RedirectResult(redirect.Url);
}
But I cant show amount on paypal page????? I show desc but I dont show amount??? what is wrong? thanks for reply.
Have you passing Amount or not, I think you are not passing Amount Value If not then add
public class CartController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Pay()
{
PayPalRedirect redirect = PayPal.ExpressCheckout(new PayPalOrder { Amount = 50 });
Session["token"] = redirect.Token;
return new RedirectResult(redirect.Url);
}
}
For More Details Check PayPal with ASP.NET MVC
Hope it helps you.
Try passing over a line item name and amount and see if it shows up in that case. Also, can you provide the actual string of data you are sending over to PayPal, minus your API credentials so that I can test it with my API credentials.
Example:
https://api-3t.sandbox.paypal.com/nvp?USER=paypal_api1.x.com&PWD=NAEWP67N2BMRSD234P2&SIGNATURE=Ae0iZ4smtdchhBLFKKdS8s8OSA220f033rNWM4EYTk1J-tsdbDOFq0JpNi&METHOD=SetExpressCheckout&VERSION=92.0&RETURNURL=https://www.ccaples.com/mts/pp_nvp_quick_test.php&CANCELURL=https://www.ccaples.com/mts/pp_nvp_quick_test.php&PAYMENTREQUEST_0_PAYMENTACTION=Sale&PAYMENTREQUEST_0_AMT=200&PAYMENTREQUEST_0_ITEMAMT=200&PAYMENTREQUEST_0_SHIPPINGAMT=0.00&PAYMENTREQUEST_0_TAXAMT=0.0&PAYMENTREQUEST_0_CURRENCYCODE=USD&PAYMENTREQUEST_0_DESC=test EC payment

Use Google Analytics API to show information in C#

I have been looking for a good solution all day, but Google evolves so fast that I can't find something working. What I want to do is that, I have a Web app that has an admin section where user need to be logged in to see the information. In this section I want to show some data from GA, like pageviews for some specific urls. Since it's not the user information that I'm showing but the google analytics'user I want to connect passing information (username/password or APIKey) but I can't find out how. All the sample I found use OAuth2 (witch, if I understand, will ask the visitor to log in using google).
What I found so far :
Google official Client Library for .Net : http://code.google.com/p/google-api-dotnet-client/, no sample for GA
official developers help : https://developers.google.com/analytics/
an other question with code on SO : Google Analytics API - Programmatically fetch page views in server side but I get a 403 when I try to authenticate
some source that access the API : http://www.reimers.dk/jacob-reimers-blog/added-google-analytics-reader-for-net downloaded the source but I can't figure out how it works
this other question on SO : Google Analytics Access with C# but it does not help
while writing this they suggest me this 09 old post Google Analytics API and .Net.
Maybe I'm just tired and that tomorrow it will be easy to find a solution but right now I need help!
Thanks
It requires a bit of setup on the google side but it's actually quite simple. I will list step by step.
First you will need to create an application in the Google cloud console and enable the Analytics API.
Go to http://code.google.com/apis/console
Select the drop down and create a project if you do not already have one
Once the project is created click on services
From here enable the Analytics API
Now that the Analytics API is enabled the next step will be to enable a service account to access your desired analytics profiles/sites. The service account will allow you to log in without having to prompt a user for credentials.
Go to http://code.google.com/apis/console and choose the project you
created from the drop down.
Next go to the "API Access" section and click the "Create another client id" button.
In the Create Client ID window choose service account and click
create client id.
Download the public key for this account if it doesn't start the
download automatically.You will need this later on when you code for
authorization.
Before exiting copy the service accounts auto generated email address as you will need this in the next step. The client email looks like #developer.gserviceaccount.com
Now that we have a service account you will need to allow this service account to access to your profiles/sites in Google Analytics.
Log into Google Analytics.
Once logged in click on the Admin button to the bottem left on the
screen.
In Admin click the account drop down and select the account/site you would like your service account to be able to access then click on "User Management" under the account section.
Enter the email address that was generated for your service account and give it read and analyze permission.
Repeat these steps for any other account/site you would like your service to have access to.
Now that the setup is done for the service account to access Google Analytics through the API we can start to code.
Get this package from NuGet:
Google.Apis.Analytics.v3 Client Library
Add these usings:
using Google.Apis.Analytics.v3;
using Google.Apis.Analytics.v3.Data;
using Google.Apis.Services;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Auth.OAuth2;
using System.Collections.Generic;
using System.Linq;
Some things to note are.
The keyPath is the path to the key file you downloaded with a .p12 file extention.
The accountEmailAddress is the api email we got earlier.
Scope is an Enum in the Google.Apis.Analytics.v3.AnalyticService class that dictates the url to use in order to authorize (ex: AnalyticsService.Scope.AnalyticsReadonly ).
Application name is a name of your choosing that tells the google api what is accessing it (aka: it can be what ever you choose).
Then the code to do some basic calls is as follows.
public class GoogleAnalyticsAPI
{
public AnalyticsService Service { get; set; }
public GoogleAnalyticsAPI(string keyPath, string accountEmailAddress)
{
var certificate = new X509Certificate2(keyPath, "notasecret", X509KeyStorageFlags.Exportable);
var credentials = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(accountEmailAddress)
{
Scopes = new[] { AnalyticsService.Scope.AnalyticsReadonly }
}.FromCertificate(certificate));
Service = new AnalyticsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials,
ApplicationName = "WorthlessVariable"
});
}
public AnalyticDataPoint GetAnalyticsData(string profileId, string[] dimensions, string[] metrics, DateTime startDate, DateTime endDate)
{
AnalyticDataPoint data = new AnalyticDataPoint();
if (!profileId.Contains("ga:"))
profileId = string.Format("ga:{0}", profileId);
//Make initial call to service.
//Then check if a next link exists in the response,
//if so parse and call again using start index param.
GaData response = null;
do
{
int startIndex = 1;
if (response != null && !string.IsNullOrEmpty(response.NextLink))
{
Uri uri = new Uri(response.NextLink);
var paramerters = uri.Query.Split('&');
string s = paramerters.First(i => i.Contains("start-index")).Split('=')[1];
startIndex = int.Parse(s);
}
var request = BuildAnalyticRequest(profileId, dimensions, metrics, startDate, endDate, startIndex);
response = request.Execute();
data.ColumnHeaders = response.ColumnHeaders;
data.Rows.AddRange(response.Rows);
} while (!string.IsNullOrEmpty(response.NextLink));
return data;
}
private DataResource.GaResource.GetRequest BuildAnalyticRequest(string profileId, string[] dimensions, string[] metrics,
DateTime startDate, DateTime endDate, int startIndex)
{
DataResource.GaResource.GetRequest request = Service.Data.Ga.Get(profileId, startDate.ToString("yyyy-MM-dd"),
endDate.ToString("yyyy-MM-dd"), string.Join(",", metrics));
request.Dimensions = string.Join(",", dimensions);
request.StartIndex = startIndex;
return request;
}
public IList<Profile> GetAvailableProfiles()
{
var response = Service.Management.Profiles.List("~all", "~all").Execute();
return response.Items;
}
public class AnalyticDataPoint
{
public AnalyticDataPoint()
{
Rows = new List<IList<string>>();
}
public IList<GaData.ColumnHeadersData> ColumnHeaders { get; set; }
public List<IList<string>> Rows { get; set; }
}
}
Other Links that will prove helpful:
Analytic API Explorer - Query API From The Web
Analytic API Explorer version 2 - Query API From The Web
Dimensions and Metrics Reference
Hopefully this helps someone trying to do this in the future.
I did a lot of search and finally either looking up code from multiple places and then wrapping my own interface around it i came up with the following solution. Not sure if people paste their whole code here, but i guess why not save everyone else time :)
Pre-requisites, you will need to install Google.GData.Client and google.gdata.analytics package/dll.
This is the main class that does the work.
namespace Utilities.Google
{
public class Analytics
{
private readonly String ClientUserName;
private readonly String ClientPassword;
private readonly String TableID;
private AnalyticsService analyticsService;
public Analytics(string user, string password, string table)
{
this.ClientUserName = user;
this.ClientPassword = password;
this.TableID = table;
// Configure GA API.
analyticsService = new AnalyticsService("gaExportAPI_acctSample_v2.0");
// Client Login Authorization.
analyticsService.setUserCredentials(ClientUserName, ClientPassword);
}
/// <summary>
/// Get the page views for a particular page path
/// </summary>
/// <param name="pagePath"></param>
/// <param name="startDate"></param>
/// <param name="endDate"></param>
/// <param name="isPathAbsolute">make this false if the pagePath is a regular expression</param>
/// <returns></returns>
public int GetPageViewsForPagePath(string pagePath, DateTime startDate, DateTime endDate, bool isPathAbsolute = true)
{
int output = 0;
// GA Data Feed query uri.
String baseUrl = "https://www.google.com/analytics/feeds/data";
DataQuery query = new DataQuery(baseUrl);
query.Ids = TableID;
//query.Dimensions = "ga:source,ga:medium";
query.Metrics = "ga:pageviews";
//query.Segment = "gaid::-11";
var filterPrefix = isPathAbsolute ? "ga:pagepath==" : "ga:pagepath=~";
query.Filters = filterPrefix + pagePath;
//query.Sort = "-ga:visits";
//query.NumberToRetrieve = 5;
query.GAStartDate = startDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
query.GAEndDate = endDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
Uri url = query.Uri;
DataFeed feed = analyticsService.Query(query);
output = Int32.Parse(feed.Aggregates.Metrics[0].Value);
return output;
}
public Dictionary<string, int> PageViewCounts(string pagePathRegEx, DateTime startDate, DateTime endDate)
{
// GA Data Feed query uri.
String baseUrl = "https://www.google.com/analytics/feeds/data";
DataQuery query = new DataQuery(baseUrl);
query.Ids = TableID;
query.Dimensions = "ga:pagePath";
query.Metrics = "ga:pageviews";
//query.Segment = "gaid::-11";
var filterPrefix = "ga:pagepath=~";
query.Filters = filterPrefix + pagePathRegEx;
//query.Sort = "-ga:visits";
//query.NumberToRetrieve = 5;
query.GAStartDate = startDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
query.GAEndDate = endDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
Uri url = query.Uri;
DataFeed feed = analyticsService.Query(query);
var returnDictionary = new Dictionary<string, int>();
foreach (var entry in feed.Entries)
returnDictionary.Add(((DataEntry)entry).Dimensions[0].Value, Int32.Parse(((DataEntry)entry).Metrics[0].Value));
return returnDictionary;
}
}
}
And this is the interface and implementation that i use to wrap it up with.
namespace Utilities
{
public interface IPageViewCounter
{
int GetPageViewCount(string relativeUrl, DateTime startDate, DateTime endDate, bool isPathAbsolute = true);
Dictionary<string, int> PageViewCounts(string pagePathRegEx, DateTime startDate, DateTime endDate);
}
public class GooglePageViewCounter : IPageViewCounter
{
private string GoogleUserName
{
get
{
return ConfigurationManager.AppSettings["googleUserName"];
}
}
private string GooglePassword
{
get
{
return ConfigurationManager.AppSettings["googlePassword"];
}
}
private string GoogleAnalyticsTableName
{
get
{
return ConfigurationManager.AppSettings["googleAnalyticsTableName"];
}
}
private Analytics analytics;
public GooglePageViewCounter()
{
analytics = new Analytics(GoogleUserName, GooglePassword, GoogleAnalyticsTableName);
}
#region IPageViewCounter Members
public int GetPageViewCount(string relativeUrl, DateTime startDate, DateTime endDate, bool isPathAbsolute = true)
{
int output = 0;
try
{
output = analytics.GetPageViewsForPagePath(relativeUrl, startDate, endDate, isPathAbsolute);
}
catch (Exception ex)
{
Logger.Error(ex);
}
return output;
}
public Dictionary<string, int> PageViewCounts(string pagePathRegEx, DateTime startDate, DateTime endDate)
{
var input = analytics.PageViewCounts(pagePathRegEx, startDate, endDate);
var output = new Dictionary<string, int>();
foreach (var item in input)
{
if (item.Key.Contains('&'))
{
string[] key = item.Key.Split(new char[] { '?', '&' });
string newKey = key[0] + "?" + key.FirstOrDefault(k => k.StartsWith("p="));
if (output.ContainsKey(newKey))
output[newKey] += item.Value;
else
output[newKey] = item.Value;
}
else
output.Add(item.Key, item.Value);
}
return output;
}
#endregion
}
}
And now the rest is the obvious stuff - you will have to add the web.config values to your application config or webconfig and call IPageViewCounter.GetPageViewCount
This answer is for those of you who want access to your own Analytics account and want to use the new Analytics Reporting API v4.
I recently wrote a blog post about how to get Google Analytics data using C#. Read there for all the details.
You first need to choose between connecting with OAuth2 or a Service Account. I'll assume you own the Analytics account, so you need to create a "Service account key" from the Google APIs Credentials page.
Once you create that, download the JSON file and put it in your project (I put mine in my App_Data folder).
Next, install the Google.Apis.AnalyticsReporting.v4 Nuget package. Also install Newtonsoft's Json.NET.
Include this class somewhere in your project:
public class PersonalServiceAccountCred
{
public string type { get; set; }
public string project_id { get; set; }
public string private_key_id { get; set; }
public string private_key { get; set; }
public string client_email { get; set; }
public string client_id { get; set; }
public string auth_uri { get; set; }
public string token_uri { get; set; }
public string auth_provider_x509_cert_url { get; set; }
public string client_x509_cert_url { get; set; }
}
And here's what you've been waiting for: a full example!
string keyFilePath = Server.MapPath("~/App_Data/Your-API-Key-Filename.json");
string json = System.IO.File.ReadAllText(keyFilePath);
var cr = JsonConvert.DeserializeObject(json);
var xCred = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(cr.client_email)
{
Scopes = new[] {
AnalyticsReportingService.Scope.Analytics
}
}.FromPrivateKey(cr.private_key));
using (var svc = new AnalyticsReportingService(
new BaseClientService.Initializer
{
HttpClientInitializer = xCred,
ApplicationName = "[Your Application Name]"
})
)
{
// Create the DateRange object.
DateRange dateRange = new DateRange() { StartDate = "2017-05-01", EndDate = "2017-05-31" };
// Create the Metrics object.
Metric sessions = new Metric { Expression = "ga:sessions", Alias = "Sessions" };
//Create the Dimensions object.
Dimension browser = new Dimension { Name = "ga:browser" };
// Create the ReportRequest object.
ReportRequest reportRequest = new ReportRequest
{
ViewId = "[A ViewId in your account]",
DateRanges = new List() { dateRange },
Dimensions = new List() { browser },
Metrics = new List() { sessions }
};
List requests = new List();
requests.Add(reportRequest);
// Create the GetReportsRequest object.
GetReportsRequest getReport = new GetReportsRequest() { ReportRequests = requests };
// Call the batchGet method.
GetReportsResponse response = svc.Reports.BatchGet(getReport).Execute();
}
We start by deserializing the service account key information from the JSON file and convert it to a PersonalServiceAccountCred object. Then, we create the ServiceAccountCredential and connect to Google via the AnalyticsReportingService. Using that service, we then prepare some basic filters to pass to the API and send off the request.
It's probably best to set a breakpoint on the line where the response variable is declared, press F10 once, then hover over the variable, so you can see what data is available for you to use in the response.
I was hoping just to add a comment to the answer for v3 Beta, but rep points prevent that. However, I thought it would be nice for others to have this information so here it is:
using Google.Apis.Authentication.OAuth2;
using Google.Apis.Authentication.OAuth2.DotNetOpenAuth;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Services;
These name spaces are used throughout the code in that post. I always wish people would post name spaces more often, I seem to spend a good bit of time looking for them. I hope this saves some people a few minutes of work.
I've setup something pretty similar to the above answer in a nuGet package. It does the following:
- connects to a "Service Account" you set up in the API Console
- Pulls any Google Analytics data you would like
- Displays that data using Google's Charts API
and it does all of this in a very easy to modify way. You can see more here: https://www.nuget.org/packages/GoogleAnalytics.GoogleCharts.NET/.
Hope google will provide proper documentation someday. Here I am listing all the steps to integrate google analytics server side authentication in ASP.NET C#.
Step 1: Create a project in google console
Goto the link https://console.developers.google.com/iam-admin/projects and create a project by clicking on "Create Project" button and provide project name in the pop up window and submit it.
Step 2: Create credentials and service account
After creation of the project, you will be redirected to "API Manager" page.
Click on credentials and press "Create Credentials" button. select "service account key" from dropdown you will be redirected to next page.
In the service account drop down, select "New service account". Fill in the service account name and download the p12 key. It will have p12 extension. you will get a popup having a password "notasecret" which is default and your private key will be downloaded.
Step 3: Create 0auth client ID
click on the "create credentials" dropdown and select "0auth client ID" you will be redirected to "0auth consent screen" tab. provide a random name in the project name textbox. select application type as "Web application" and click create button. Copy the generated client ID in a notepad.
Step 4: Enable the APIs
On the left side click on the "Overview" tab and select "Enabled APIs" from the horizontal tab. In the search bar search for "Analytics API" click on the dropdown and press "Enable" button. Now again search for "Analytics Reporting V4" and enable it.
Step 5: Install nuget packages
In visual studio, Go to Tools > Nuget Package Manager > Package Manager Console.
Copy paste the below code in the console to install the nuget packages.
Install-Package Google.Apis.Analytics.v3
Install-Package DotNetOpenAuth.Core -Version 4.3.4.13329
The above two packages are Google analytics and DotNetOpenAuth nuget packages.
Step 6: Provide 'View and Analyze' permission to service account
Go to google analytics account and click on "Admin" tab and select "User Management" from left menus,select a domain of which you want to access analytics data and insert the service account email id under it and select "Read and Analyze" permission from the dropdown. Service account email id looks like ex: googleanalytics#googleanalytics.iam.gserviceaccount.com.
Working Code
FRONT END CODE:
Copy and paste the below analytics embed script in your front end or else you can get this code from google analytics documentation page also.
<script>
(function (w, d, s, g, js, fs) {
g = w.gapi || (w.gapi = {}); g.analytics = { q: [], ready: function (f) { this.q.push(f); } };
js = d.createElement(s); fs = d.getElementsByTagName(s)[0];
js.src = 'https://apis.google.com/js/platform.js';
fs.parentNode.insertBefore(js, fs); js.onload = function () { g.load('analytics'); };
}(window, document, 'script'));</script>
Paste the below code in the body tag of your front end page.
<asp:HiddenField ID="accessToken" runat="server" />
<div id="chart-1-container" style="width:600px;border:1px solid #ccc;"></div>
<script>
var access_token = document.getElementById('<%= accessToken.ClientID%>').value;
gapi.analytics.ready(function () {
/**
* Authorize the user with an access token obtained server side.
*/
gapi.analytics.auth.authorize({
'serverAuth': {
'access_token': access_token
}
});
/**
* Creates a new DataChart instance showing sessions.
* It will be rendered inside an element with the id "chart-1-container".
*/
var dataChart1 = new gapi.analytics.googleCharts.DataChart({
query: {
'ids': 'ga:53861036', // VIEW ID <-- Goto your google analytics account and select the domain whose analytics data you want to display on your webpage. From the URL ex: a507598w53044903p53861036. Copy the digits after "p". It is your view ID
'start-date': '2016-04-01',
'end-date': '2016-04-30',
'metrics': 'ga:sessions',
'dimensions': 'ga:date'
},
chart: {
'container': 'chart-1-container',
'type': 'LINE',
'options': {
'width': '100%'
}
}
});
dataChart1.execute();
/**
* Creates a new DataChart instance showing top 5 most popular demos/tools
* amongst returning users only.
* It will be rendered inside an element with the id "chart-3-container".
*/
});
</script>
You can also get your View ID from https://ga-dev-tools.appspot.com/account-explorer/
BACK END CODE:
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Web.Script.Serialization;
using System.Net;
using System.Text;
using Google.Apis.Analytics.v3;
using Google.Apis.Analytics.v3.Data;
using Google.Apis.Services;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Util;
using DotNetOpenAuth.OAuth2;
using System.Security.Cryptography;
namespace googleAnalytics
{
public partial class api : System.Web.UI.Page
{
public const string SCOPE_ANALYTICS_READONLY = "https://www.googleapis.com/auth/analytics.readonly";
string ServiceAccountUser = "googleanalytics#googleanalytics.iam.gserviceaccount.com"; //service account email ID
string keyFile = #"D:\key.p12"; //file link to downloaded key with p12 extension
protected void Page_Load(object sender, EventArgs e)
{
string Token = Convert.ToString(GetAccessToken(ServiceAccountUser, keyFile, SCOPE_ANALYTICS_READONLY));
accessToken.Value = Token;
var certificate = new X509Certificate2(keyFile, "notasecret", X509KeyStorageFlags.Exportable);
var credentials = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(ServiceAccountUser)
{
Scopes = new[] { AnalyticsService.Scope.AnalyticsReadonly }
}.FromCertificate(certificate));
var service = new AnalyticsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials,
ApplicationName = "Google Analytics API"
});
string profileId = "ga:53861036";
string startDate = "2016-04-01";
string endDate = "2016-04-30";
string metrics = "ga:sessions,ga:users,ga:pageviews,ga:bounceRate,ga:visits";
DataResource.GaResource.GetRequest request = service.Data.Ga.Get(profileId, startDate, endDate, metrics);
GaData data = request.Execute();
List<string> ColumnName = new List<string>();
foreach (var h in data.ColumnHeaders)
{
ColumnName.Add(h.Name);
}
List<double> values = new List<double>();
foreach (var row in data.Rows)
{
foreach (var item in row)
{
values.Add(Convert.ToDouble(item));
}
}
values[3] = Math.Truncate(100 * values[3]) / 100;
txtSession.Text = values[0].ToString();
txtUsers.Text = values[1].ToString();
txtPageViews.Text = values[2].ToString();
txtBounceRate.Text = values[3].ToString();
txtVisits.Text = values[4].ToString();
}
public static dynamic GetAccessToken(string clientIdEMail, string keyFilePath, string scope)
{
// certificate
var certificate = new X509Certificate2(keyFilePath, "notasecret");
// header
var header = new { typ = "JWT", alg = "RS256" };
// claimset
var times = GetExpiryAndIssueDate();
var claimset = new
{
iss = clientIdEMail,
scope = scope,
aud = "https://accounts.google.com/o/oauth2/token",
iat = times[0],
exp = times[1],
};
JavaScriptSerializer ser = new JavaScriptSerializer();
// encoded header
var headerSerialized = ser.Serialize(header);
var headerBytes = Encoding.UTF8.GetBytes(headerSerialized);
var headerEncoded = Convert.ToBase64String(headerBytes);
// encoded claimset
var claimsetSerialized = ser.Serialize(claimset);
var claimsetBytes = Encoding.UTF8.GetBytes(claimsetSerialized);
var claimsetEncoded = Convert.ToBase64String(claimsetBytes);
// input
var input = headerEncoded + "." + claimsetEncoded;
var inputBytes = Encoding.UTF8.GetBytes(input);
// signature
var rsa = certificate.PrivateKey as RSACryptoServiceProvider;
var cspParam = new CspParameters
{
KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName,
KeyNumber = rsa.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2
};
var aescsp = new RSACryptoServiceProvider(cspParam) { PersistKeyInCsp = false };
var signatureBytes = aescsp.SignData(inputBytes, "SHA256");
var signatureEncoded = Convert.ToBase64String(signatureBytes);
// jwt
var jwt = headerEncoded + "." + claimsetEncoded + "." + signatureEncoded;
var client = new WebClient();
client.Encoding = Encoding.UTF8;
var uri = "https://accounts.google.com/o/oauth2/token";
var content = new NameValueCollection();
content["assertion"] = jwt;
content["grant_type"] = "urn:ietf:params:oauth:grant-type:jwt-bearer";
string response = Encoding.UTF8.GetString(client.UploadValues(uri, "POST", content));
var result = ser.Deserialize<dynamic>(response);
object pulledObject = null;
string token = "access_token";
if (result.ContainsKey(token))
{
pulledObject = result[token];
}
//return result;
return pulledObject;
}
private static int[] GetExpiryAndIssueDate()
{
var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var issueTime = DateTime.UtcNow;
var iat = (int)issueTime.Subtract(utc0).TotalSeconds;
var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds;
return new[] { iat, exp };
}
}
}
Another Working Approach
Add below code in the ConfigAuth
var googleApiOptions = new GoogleOAuth2AuthenticationOptions()
{
AccessType = "offline", // can use only if require
ClientId = ClientId,
ClientSecret = ClientSecret,
Provider = new GoogleOAuth2AuthenticationProvider()
{
OnAuthenticated = context =>
{
context.Identity.AddClaim(new Claim("Google_AccessToken", context.AccessToken));
if (context.RefreshToken != null)
{
context.Identity.AddClaim(new Claim("GoogleRefreshToken", context.RefreshToken));
}
context.Identity.AddClaim(new Claim("GoogleUserId", context.Id));
context.Identity.AddClaim(new Claim("GoogleTokenIssuedAt", DateTime.Now.ToBinary().ToString()));
var expiresInSec = 10000;
context.Identity.AddClaim(new Claim("GoogleTokenExpiresIn", expiresInSec.ToString()));
return Task.FromResult(0);
}
},
SignInAsAuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
};
googleApiOptions.Scope.Add("openid"); // Need to add for google+
googleApiOptions.Scope.Add("profile");// Need to add for google+
googleApiOptions.Scope.Add("email");// Need to add for google+
googleApiOptions.Scope.Add("https://www.googleapis.com/auth/analytics.readonly");
app.UseGoogleAuthentication(googleApiOptions);
Add below code, name spaces and relative references
using Google.Apis.Analytics.v3;
using Google.Apis.Analytics.v3.Data;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Responses;
using Google.Apis.Services;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using System;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
public class HomeController : Controller
{
AnalyticsService service;
public IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
public async Task<ActionResult> AccountList()
{
service = new AnalyticsService(new BaseClientService.Initializer()
{
HttpClientInitializer = await GetCredentialForApiAsync(),
ApplicationName = "Analytics API sample",
});
//Account List
ManagementResource.AccountsResource.ListRequest AccountListRequest = service.Management.Accounts.List();
//service.QuotaUser = "MyApplicationProductKey";
Accounts AccountList = AccountListRequest.Execute();
return View();
}
private async Task<UserCredential> GetCredentialForApiAsync()
{
var initializer = new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = ClientId,
ClientSecret = ClientSecret,
},
Scopes = new[] { "https://www.googleapis.com/auth/analytics.readonly" }
};
var flow = new GoogleAuthorizationCodeFlow(initializer);
var identity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ApplicationCookie);
if (identity == null)
{
Redirect("/Account/Login");
}
var userId = identity.FindFirstValue("GoogleUserId");
var token = new TokenResponse()
{
AccessToken = identity.FindFirstValue("Google_AccessToken"),
RefreshToken = identity.FindFirstValue("GoogleRefreshToken"),
Issued = DateTime.FromBinary(long.Parse(identity.FindFirstValue("GoogleTokenIssuedAt"))),
ExpiresInSeconds = long.Parse(identity.FindFirstValue("GoogleTokenExpiresIn")),
};
return new UserCredential(flow, userId, token);
}
}
Add this in the Application_Start() in Global.asax
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

Categories

Resources