Is Universal Analytics C# SDK compatible with GA4? - c#

I am using Google.Apis.AnalyticsReporting.v4 library for old google analytics views. How do I convert this code to GA4? I can't find a line about switching View Id to something else in code.
I have checked this post "How do I get view id in GA4", but my properties already exist and I don't see option to modify them after creation.
using (var svc = new AnalyticsReportingService(authInitializer.CreateInitializer()))
{
var dateRange = new DateRange
{
StartDate = analyticsParams.From.ToString("yyyy-MM-dd"),
EndDate = analyticsParams.To.ToString("yyyy-MM-dd")
};
var sessions = new Metric
{
Expression = "ga:sessions",
Alias = "Sessions"
};
var date = new Dimension { Name = "ga:date" };
var reportRequest = new ReportRequest
{
DateRanges = new List<DateRange> { dateRange },
Dimensions = new List<Dimension> { date },
Metrics = new List<Metric> { sessions },
ViewId = analyticsParams.ViewId, // <------------------------- My view id
};
var getReportsRequest = new GetReportsRequest
{
ReportRequests = new List<ReportRequest> { reportRequest }
};
var batchRequest = svc.Reports.BatchGet(getReportsRequest);
var response = batchRequest.Execute();
var reports = response.Reports.First();
return reports.Data.Rows.Select(x => new DataEntry()
{
Date = DateTime.ParseExact(x.Dimensions[0], "yyyyMMdd", CultureInfo.InvariantCulture),
Value = int.Parse(x.Metrics[0].Values[0]),
}).ToList();
}

You need to use the Google Analytics Data API V1 (currently in alpha) in order to access your GA4 properties. Here is a quick start sample for .NET which seems similar to what you are trying to do.
using Google.Analytics.Data.V1Alpha;
using System;
namespace AnalyticsSamples
{
class QuickStart
{
static void SampleRunReport(string propertyId)
{
// Using a default constructor instructs the client to use the credentials
// specified in GOOGLE_APPLICATION_CREDENTIALS environment variable.
AlphaAnalyticsDataClient client = AlphaAnalyticsDataClient.Create();
// Initialize request argument(s)
RunReportRequest request = new RunReportRequest
{
Entity = new Entity{ PropertyId = propertyId },
Dimensions = { new Dimension{ Name="city"}, },
Metrics = { new Metric{ Name="activeUsers"}, },
DateRanges = { new DateRange{ StartDate="2020-03-31", EndDate="today"}, },
};
// Make the request
RunReportResponse response = client.RunReport(request);
Console.WriteLine("Report result:");
foreach( Row row in response.Rows )
{
Console.WriteLine("{0}, {1}", row.DimensionValues[0].Value, row.MetricValues[0].Value);
}
}
static int Main(string[] args)
{
if (args.Length == 0 || args.Length > 2)
{
Console.WriteLine("Arguments: <GA4 property ID>");
Console.WriteLine("A GA4 property id parameter is required to make a query to the Google Analytics Data API.");
return 1;
}
string propertyId = args[0];
SampleRunReport(propertyId);
return 0;
}
}
}

Currently there aren't API available for GA4 Property. Furthermore GA4 does not provide Views, you have to use BigQuery to get data programmatically.

Related

Google Analytics get daily data from last 30 days

I'm trying to create a report showing daily conversions from the last 30 days with google analytics data api. This is using the dotnet client library Google.Analytics.Data.V1Beta.
However I'm only getting back about 5 conversion values, the last value seems to be reflecting total conversions of all time, ideally I'd like a running update of how many conversions per day so I can plot it to a line chart.
I'm planning to change this to weekly over the course of the past X months once I have the previous issue sorted out.
I'm essentially trying to recreate the chart on the google analytics dashboard:
What am I missing?
public async Task<RunReportResponse> RunReport(string propertyId = "xxxx")
{
BetaAnalyticsDataClient client = BetaAnalyticsDataClient.Create();
FilterExpression hostnameFilter = new();
hostnameFilter.Filter = new();
hostnameFilter.Filter.InListFilter = new();
hostnameFilter.Filter.InListFilter.Values.Add("xxxx");
hostnameFilter.Filter.FieldName = "hostName";
CohortSpec cohort = new();
cohort.Cohorts.Add(new Cohort
{
Dimension = "firstSessionDate",
DateRange = new DateRange()
{
StartDate = DateTime.Now.AddDays(-30).ToString("yyyy-MM-dd"),
EndDate = DateTime.Now.ToString("yyyy-MM-dd")
},
});
cohort.CohortsRange = new CohortsRange
{
EndOffset = 5,
Granularity = CohortsRange.Types.Granularity.Daily
};
// Initialize request argument(s)
RunReportRequest request = new()
{
Property = "properties/" + propertyId,
Dimensions = {new Dimension { Name = "cohort" }, new Dimension { Name = "cohortNthDay" } },
Metrics = { new Metric { Name = "conversions" }, },
DimensionFilter = hostnameFilter,
CohortSpec = cohort,
};
// Make the request
var response = await client.RunReportAsync(request);
return response;
}
}

How to check if my response contains sampled data

I am using Google.Apis.AnalyticsReporting.v4 to download a view between two days. I would like to know where in my response can I see whether the data contains sampled data?
This is the code for my request and we catch the data in response object below:
using (var analytics = new AnalyticsReportingService(new BaseClientService.Initializer { HttpClientInitializer = credential }))
{
while (nextPageToken != null)
{
var reportRequest = new ReportRequest
{
ViewId = "123456",
DateRanges = new[] { new DateRange { StartDate = "2021-06-01", EndDate = "2021-11-30" } },
Dimensions = new List<Dimension>() {
new Dimension { Name = "ga:transactionId" },
new Dimension { Name = "ga:campaign" },
new Dimension { Name = "ga:sourceMedium" }
},
Metrics = new[] { new Metric { Expression = "ga:users" }, new Metric { Expression = "ga:sessions" } },
};
var requests = new List<ReportRequest>();
requests.Add(reportRequest);
// Create the GetReportsRequest object.
var getReport = new GetReportsRequest() { ReportRequests = requests };
// Call the batchGet method.
var response = analytics.Reports.BatchGet(getReport).Execute();
}
}
It does not sample the data requested through Google.Apis.AnalyticsReporting.v4. However, if your query is too complex, it will return a 5xx error. Looks like it prevents its backend from executing queries for too long to avoid high loads. In that case, you'd need to chop your request into a few.

How to query the Google Analytics Data API (GA4) using an event_category and an event_label?

I'm using .NET 5 and the nuget package Google.Analytics.Data.V1Beta to query Google Analytics. But I need to query events using the event_category and the event_label and I get this error: "Field customEvent:event_category is not a valid dimension". This is my code:
var click = new FilterExpression
{
Filter = new Filter
{
FieldName = "ga:event_category",
StringFilter = new Filter.Types.StringFilter
{
CaseSensitive = false,
MatchType = Filter.Types.StringFilter.Types.MatchType.Exact,
Value = "click"
}
},
};
var request = new RunReportRequest
{
Property = $"properties/{_settings.GoogleAnalytics.PropertyId}",
Dimensions = {new Dimension {Name = "eventName"}, new Dimension {Name = "customEvent:event_category" } },
DimensionFilter = click,
Metrics = {new Metric {Name = "eventCount"}},
DateRanges =
{
new DateRange
{StartDate = startDate.ToString("yyyy-MM-dd"), EndDate = endDate.ToString("yyyy-MM-dd")}
}
};
var response = await _analyticsClient.GetClient().RunReportAsync(request);
How do I pass event_category and event_label to the query?

Quickbooks Online API - Payment 'Unapplied' when Invoice ID provided

I've managed to create a payment using the C# .NET SDK, however it keeps showing up as 'unapplied' when I check in QBO.
I am providing the Invoice ID and tried to follow their developer API documentation but I been at this so long now that maybe I am missing something?
The following code creates the payment but doesn't 'receive' the payment towards the invoice, is there something I missed or need to do in order for the two to be linked together?
Payment payment = new Payment
{
ProcessPayment = false,
CustomerRef = new ReferenceType { name = customer.DisplayName, Value = customer.Id },
CurrencyRef = new ReferenceType { type = "Currency", Value = "CAD" },
TotalAmt = amount,
TotalAmtSpecified = true
};
if (method == PaymentTypes.Cash)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "1");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{name = paymentType.Name, Value = paymentType.Id};
}
}
if (method == PaymentTypes.DebitCard)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "9");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{ name = paymentType.Name, Value = paymentType.Id };
}
}
if (method == PaymentTypes.CreditCard)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "8");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{ name = paymentType.Name, Value = paymentType.Id };
}
}
List<LinkedTxn> linkedTxns = new List<LinkedTxn>
{
new LinkedTxn()
{
TxnId = invoice.Id,
TxnType = TxnTypeEnum.Invoice.ToString()
},
};
foreach (Line line in invoice.Line)
{
//line.Amount = amount;
//line.AmountSpecified = true;
line.Received = amount;
line.ReceivedSpecified = true;
line.DetailType = LineDetailTypeEnum.PaymentLineDetail;
line.DetailTypeSpecified = true;
line.LinkedTxn = linkedTxns.ToArray();
}
payment.DepositToAccountRef = new ReferenceType() { Value = "5" };
payment.Line = invoice.Line;
payment.PaymentRefNum = reference;
DataService dataService = new DataService(serviceContext);
dataService.Add<Payment>(payment);
This is not an answer. However there's too much to add to the comments. I'm hoping this will be a helpful starting point (if not I'll remove it later).
Firstly I'd suggest refactoring your code and parameterise your variables. Doing so you should be able to preform repeatable testing in isolation.
When you preform the dataService.Add<Payment>(payment), store the response object as it may offer clues on how the request was processed. Alternatively if this is suppressing error messages, you might want to try an use Postman to send HTTP requests. This may help determine what's parameters are missing/ incorrect.
Avoid creating objects that are partially assigned it makes it a lot easier to read 7 work out which properties need to be assigned.
Also if you have a look at Full update a payment section on https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/payment
the json payload has an additional Line with the property TxnType set to CreditMemo. I would assume you'll need to add something like ReceivePayment or CreditCardPayment?
To refactor your code consider:
// TODO - Set variables for testing purposes.
// This can be added as params to a method.
decimal amount = 100;
string reference = "";
string invoiceId = ""; // invoice.Id
string customerDisplayName = ""; //customer.DisplayName
string customerId = ""; //customer.Id
string paymentName = "Cash"; // paymentType.Name
string paymentId = "1"; // paymentType.Id
List<Line> lines = new List<Line>(); // invoice.Line
if(lines.Count() == 0)
{
// TODO: You might want to check there are lines?
throw new ArgumentException("No invoice.");
}
Line[] invoiceLines = lines.Select(m => new Line()
{
AnyIntuitObject = m.AnyIntuitObject,
Amount = m.Amount,
AmountSpecified = m.AmountSpecified,
CustomField = m.CustomField,
Id = m.Id,
LineNum = m.LineNum,
Description = m.Description,
DetailType = LineDetailTypeEnum.PaymentLineDetail, //m.DetailType,
DetailTypeSpecified = true, //m.DetailTypeSpecified,
LinkedTxn = new List<LinkedTxn>
{
new LinkedTxn()
{
TxnId = invoiceId,
TxnType = TxnTypeEnum.Invoice.ToString() // TODO: Should this be ReceivePayment?
},
}.ToArray(), //m.LinkedTxn,
LineEx = m.LineEx,
Received = amount, //m.Received,
ReceivedSpecified = true // m.ReceivedSpecified
}).ToArray();
Payment payment = new Payment
{
ProcessPayment = false,
CustomerRef = new ReferenceType { name = customerDisplayName, Value = customerId },
CurrencyRef = new ReferenceType { type = "Currency", Value = "CAD" },
TotalAmt = amount,
TotalAmtSpecified = true,
DepositToAccountRef = new ReferenceType() { Value = "5" },
Line = invoiceLines, // Variable is for debugging purposes - it should be inline or call a method.
PaymentRefNum = reference,
PaymentMethodRef = new ReferenceType()
{
name = paymentName,
Value = paymentId,
}
};
DataService dataService = new DataService(serviceContext);
Payment results = dataService.Add<Payment>(payment);
var json = JsonConvert.SerializeObject(results);
Console.Write(json); // TODO - Use break point/ or write response somewhere for clues.

Google Analytics Reporting API v4 returning separate data for the same URL with different capitalization (C#)

I'm using the Google Anayltics Reporting API v4 to query page views on my site, ordered by most viewed. I discovered that I'm getting duplicate entries, where the path is the same but the capitalization is different
DateRange dateRange = new DateRange()
{
StartDate = startDate.ToString("yyyy-MM-dd"),
EndDate = DateTime.Now.ToString("yyyy-MM-dd")
};
// Create the Metrics object.
Metric pageviews = new Metric {Expression = "ga:pageviews", Alias = "Pageviews"};
//Create the Dimensions object.
Dimension path = new Dimension { Name = "ga:pagePath" };
OrderBy orderby = new OrderBy {FieldName = "ga:pageviews", SortOrder = "DESCENDING"};
// Create the ReportRequest object.
ReportRequest reportRequest = new ReportRequest
{
ViewId = viewId,
DateRanges = new List<DateRange>() { dateRange },
Dimensions = new List<Dimension>() { path },
Metrics = new List<Metric>() { pageviews },
OrderBys = new List<OrderBy>() { orderby }
};
List<ReportRequest> requests = new List<ReportRequest>();
requests.Add(reportRequest);
// Create the GetReportsRequest object.
GetReportsRequest getReport = new GetReportsRequest() { ReportRequests = requests };
// Call the batchGet method.
GetReportsResponse response = svc.Reports.BatchGet(getReport).Execute();
foreach (var report in response.Reports)
{
var data = report.Data.Rows.Select(x => new AnalyticsPage
{
Path = x.Dimensions[0],
Views = Convert.ToInt32(x.Metrics[0].Values[0])
});
pages.AddRange(data);
}
When I view my results, pages[41] is "/events/tech-knowledge" (3150 page views) and pages[429] is "/Events/Tech-Knowledge" (200 page views)
Is there something I can add to my query to make the API ignore case in the path field? I know I could modify my code to combine entries where the path.ToLower() is the same, but it would be better to get the API to return the data correctly
Checked the Analytics API reference, no such ready made filter. I suggest proceeding with your workaround.

Categories