Can't get Google Sheet Data, but other attributes are avelible - c#

I'm just try to fetch my test spreadsheet's(API v4) sheets' data by this way:
var service = new SheetsService(
new BaseClientService.Initializer()
{
HttpClientInitializer = new Credentials().Data,
ApplicationName = Config.AppName
});
var request = service.Spreadsheets.Get(sheet_id);
var response = request.Execute();
var sheets = response.Sheets;
foreach (var sheet in sheets)
{
try
{
Console.WriteLine(sheet.Data.GetType());
}
catch (NullReferenceException err)
{
Console.WriteLine(err.Message);
}
}
It throws NullReferenceException, but if I replace Data by another attribute(e.g. Properties, Merges) it works correctly.
In a nutshell it fails only on Data attribute.
How can I solve this?

According to the documentation, you need to add includeGridData=true to the URL parameters if you want to include all data.
The C# wrapper you are using exposes this property on the SpreadsheetsResource.GetRequest:
var request = service.Spreadsheets.Get(sheet_id);
request.IncludeGridData = true; // <-- This will include the grid data
var response = request.Execute();

Related

How to copy styling while converting excel to Google sheets using c#?

I have a code that would take an excel sheet and convert it to google sheet.
It is working but only copying the values.
How can I copy the styling and range (merged cells) also to google Sheets?
Is there also a more simple way to just simulate as I am opening the file as is on google sheets?
foreach (var sheet in wb.Worksheets)
{
if (sheet.Index == 0)
{
// First sheet is created by default, so only set range
range = $"{defaultWorksheetName}!A:Y";
}
else
{
// Add a new sheet
AddSheet(sheetService, spreadhsheet.SpreadsheetId, sheet.Name);
range = $"{sheet.Name}!A:Y";
}
// Get number of rows and columns
int rows = sheet.Cells.MaxDataRow;
int cols = sheet.Cells.MaxDataColumn;
IList<IList<Object>> list = new List<IList<Object>>() { };
// Loop through rows
for (int i = 0; i < rows; i++)
{
List<object> lists = new List<object>();
// Loop through each column in selected row
for (int j = 0; j < cols; j++)
{
lists.Add(sheet.Cells[i, j].Value);
}
list.Add(lists);
}
// Define range
ValueRange VRange = new ValueRange();
VRange.Range = range;
// Set values
VRange.Values = list;
// Create request
SpreadsheetsResource.ValuesResource.UpdateRequest upd = sheetService.Spreadsheets.Values.Update(VRange, spreadhsheet.SpreadsheetId, range);
upd.ValueInputOption = SpreadsheetsResource.ValuesResource.UpdateRequest.ValueInputOptionEnum.USERENTERED;
// Execute request
UpdateValuesResponse response = upd.Execute();
// Get response
string responseString = JsonConvert.SerializeObject(response);
}
It appears that you are looping through the Excel rows to create an object to build the Google Sheet manually. Looking at the Microsoft documentation, you probably could also read the formatting for these cells and use the Sheets API to apply it, however, the process seems like it could be too time-consuming and error-prone.
My recommendation would be to just have Google do the work for you. The Drive API is capable of uploading Excel files and converting them to Google Sheets by setting the MimeType to application/vnd.google-apps.spreadsheet. There are some limitations, but generally it does a good job of keeping the cell formatting the same.
Google's documentation doesn't include .NET samples and I'm no expert in it, but you can check out their .NET API which does have the Files.create methods, or you could use their REST API instead.
Sources:
Importing files to Google Docs types
Drive API .NET documentation
Drive REST documentation
I found that it is much better and easier to use the Drive API instead of the Sheets API and upload the file as is, then get the link and it will open it in Google sheets.
public static string UploadFile(Stream file, string fileName, string fileMime, string fileDescription)
{
DriveService service = GetService();
var driveFile = new Google.Apis.Drive.v3.Data.File();
driveFile.Name = fileName;
driveFile.Description = fileDescription;
driveFile.MimeType = "application/vnd.google-apps.spreadsheet";
var request = service.Files.Create(driveFile, file, driveFile.MimeType);
request.Fields = "id, webViewLink";
var response = request.Upload();
if (response.Status != Google.Apis.Upload.UploadStatus.Completed)
throw response.Exception;
return request.ResponseBody.WebViewLink;
}
private static DriveService GetService()
{
string[] Scopes = { SheetsService.Scope.Drive };
string ApplicationName = "Excel to Google Sheet";
UserCredential credential = null;
using (var stream =
new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
string credPath = "token.json";
var thread = new Thread(() =>
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.FromStream(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result)
{ IsBackground = false };
thread.Start();
if (!thread.Join(20000))
{
throw new Exception("Timeout exception..!!!");
}
else
{
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
return service;
}
}
}

How to use dimensions in C# Google Analytics Real Time Reporting API?

I'm trying to get realtime data from Google Analytics Real Time Reporting API (Google.Apis.Analytics.v3), the following code works fine and gets the active users.
What I can't wrap my head around is how to get data from dimensions?
Fx. this dimension: rt:deviceCategory, it doesn't have a metric according to this: https://developers.google.com/analytics/devguides/reporting/realtime/dimsmets/
If anyone could point me in the right direction on this it would be greatly appreciated.
var credential = GetCredential().Result;
using (var svc = new AnalyticsService(
new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "Google Analytics API Console"
})
)
{
GetRequest request;
RealtimeData response;
request = svc.Data.Realtime.Get("ga:XXXXX", "rt:activeUsers");
response = request.Execute();
foreach (var row in response.Rows)
{
foreach (string col in row)
{
Console.Write(col + " "); // writes the value of the column
}
Console.Write("\r\n");
}
I found the answer here: https://github.com/LindaLawton/Google-Dotnet-Samples/tree/Genreated-samples1.0/Google%20Analytics%20API/v3
The RealtimeSample.cs can add dimensions to the Real Time API, just have to change line no. 127 to:
if (piShared != null && property.GetValue(optional, null) != null) // TODO Test that we do not add values for items that are null
And capitalize the A of analyticsService in a couple of places. Then use as:
RealtimeSample.RealtimeGetOptionalParms param = new RealtimeSample.RealtimeGetOptionalParms();
param.Dimensions = "rt:deviceCategory";
response = RealtimeSample.Get(svc, "ga:XXXXX", "rt:activeUsers", param);
foreach (var row in response.Rows)
{
foreach (string col in row)
{
Console.Write(col + " "); // writes the value of the column
}
Console.Write("\r\n");
}

Http post .csv file Angular 2 and then reading it on the server .NET

I'm trying to send .csv file from my client app (angular 2) to my web api (ASP.NET), and I have done the following:
Tried to make FormData from my .csv file the following way:
public sendData() {
let formData = new FormData();
formData.append('file', this.file, this.file.name);
this.myService.postMyData(formData, this.name)
.subscribe(data => this.postData = JSON.stringify(data),
error => this.error = error,
() => console.log('Sent'));
}
Created a service on the client app where I'm sending this .csv file from.
postMyData(formData: any, name: string) {
this.s = <string><any>name;
const headers = new Headers();
headers.append('Content-Disposition', 'form-data');
const url: string = 'myUrl?methodName=' + name;
return this.http.post(url, formData, {headers: headers})
.map((res: Response) => res.json());
}
What's the problem now is that I don't know how to get that .csv file on the server. I tried it with the code found below, but I can't get the real content, I can only see the name, content type, length and stuff like that.
[HttpPost("GetMyCsvFile")]
public async Task<IActionResult> GetMyCsvFile(string name) {
var rawMessage = await Request.ReadFormAsync();
var msg = rawMessage.Files[0];
....
}
And then whatever I do with rawMessage, I can't get the content which I could read and do the stuff needed.
Is this possible to do?
You need to get the file and not the file name. Try this code, I'm getting a CSV file from my angular app.
public async Task<bool> GetFileFromAngular(IFormFile file) {
using (var reader = new StreamReader(file.OpenReadStream())) {
var config = new CsvConfiguration(CultureInfo.InvariantCulture) {
HasHeaderRecord = true,
MissingFieldFound = null,
BadDataFound = null,
TrimOptions = TrimOptions.Trim
};
using (var csv = new CsvReader(reader, config)) {
try {
var records = csv.GetRecords<DrugFormulary>().ToList();
var csvProcessor = new CsvProcessor(_dbContext, _configuration);
await csvProcessor.ProcessPlan(records);
} catch (System.Exception ex) {
throw ex;
}
}
}
return true;
}

Google Cloud Datastore request returns Error 400 in my browser

After many trials and errors I managed to compile a piece of code which should return Entities' values from Google Datastore (it's SQL-like db). Code I used:
static async void Run()
{
UserCredential credential;
using (var stream = new FileStream(#"c:/fakepath/client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { DatastoreService.Scope.Datastore }, "user", CancellationToken.None);
}
// Create the service.
var service = new DatastoreService(new BaseClientService.Initializer
{
ApplicationName = "My Project",
HttpClientInitializer = credential
});
// Run the request.
Console.WriteLine("Executing a list request...");
var request = new LookupRequest();
var key = new Google.Apis.Datastore.v1beta2.Data.Key();
key.Path = new List<KeyPathElement> { new KeyPathElement() { Kind = "book", Name = "title42" } };
request.Keys = new List<Key>() { key };
var lookup = service.Datasets.Lookup(request, "project-name-192"); //yea
var response = lookup.Execute();
// Display the results.
if (response.Found != null)
{
foreach (var x in response.Found)
{
foreach (var y in x.Entity.Properties)
{
Console.WriteLine(y.Key.FirstOrDefault() + " " + y.Value);
}
}
}
}
Error I get:
So, what am I missing? I did just like in example on docs.
You need to go to:
https://console.developers.google.com/project/apps~{your-app-name}/apiui/credential
and use http://localhost:63324/authorize/ as the redirect url.
Remember to change it to your production url when you deploy

Retrieving data from Google Analytics API using .NET/C#

I'm trying to retrieve data from google analytics with a local console app. Im able to extract some data without having to log in to google account and only using the API.
The problem is i'm not getting the right values and im not sure how to format the code to extract the correct values. I wan't to retrieve all visitors within a certain time frame, in this case "2012-01-01" - "2014-02-20". The real number of visitors is like 10 times larger when looking in the Google Analytics dashboard. I'm getting a number of 15000 when debugging the code. I'm displaying d.TotalResults in the console wich might be wrong, the variable "d" contains lots of different properties.
This is the code i'm running:
public static void Main(string[] args)
{
var serviceAccountEmail = "MY#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"C:\Users\User\Desktop\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { AnalyticsService.Scope.Analytics }
}.FromCertificate(certificate));
// Create the service.
//Twistandtango
var gas = new AnalyticsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "TestGoogleAnalytics",
});
var r = gas.Data.Ga.Get("ga:MYProfileID", "2012-01-01", "2014-02-20", "ga:visitors");
//Specify some addition query parameters
r.Dimensions = "ga:pagePath";
r.Sort = "-ga:visitors";
r.MaxResults = 5;
//Execute and fetch the results of our query
Google.Apis.Analytics.v3.Data.GaData d = r.Execute();
Console.WriteLine(d.TotalResults);
Console.ReadLine();
}
I'm trying to query the results I want with this tool https://ga-dev-tools.appspot.com/explorer/. When I implement this in my code it looks like this:
public static void Main(string[] args)
{
var serviceAccountEmail = "MY#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"C:\Users\User\Desktop\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { AnalyticsService.Scope.Analytics }
}.FromCertificate(certificate));
// Create the service.
//Twistandtango
var gas = new AnalyticsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "TestGoogleAnalytics",
});
var r = gas.Data.Ga.Get("ga:MYProfileID", "2012-01-01", "2014-02-20", "ga:visits");
//Specify some addition query parameters
r.Dimensions = "ga:visitCount";
r.Metrics = "ga:visits";
r.Segment = "gaid::-1";
//r.Sort = "-ga:visitors";
r.MaxResults = 10000;
//Execute and fetch the results of our query
Google.Apis.Analytics.v3.Data.GaData d = r.Execute();
Console.WriteLine(d.TotalResults);
Console.ReadLine();
}
}
However after adding the metric (r.Metrics = "ga:visits";) to the project I get this error:
Error 7 Property or indexer 'Google.Apis.Analytics.v3.DataResource.GaResource.GetRequest.Metrics' cannot be assigned to -- it is read only
Also here's a index of classes in analytics v3:
https://developers.google.com/resources/api-libraries/documentation/analytics/v3/csharp/latest/annotated.html
Does anyone have knowledge of how this works? How do i retrieve total amount of visitors from the time frame i specified??
Thx
Your problem is that you supply metrics in the data.ga.get method, you dont use r.metrics metrics add more them one separate them with a ,. In this case ga:visits is the metrics you are requesting.
var r = gas.Data.Ga.Get("ga:MYProfileID", "2012-01-01", "2014-02-20", "ga:visits");
Dimensions aren't a required filed so you could and remove r.Dimensions = "ga:visitCount"; see what that returns.
Remember that simply doing Google.Apis.Analytics.v3.Data.GaData d = r.Execute(); isn't going to return all the rows if there are more then what you have max result set to. You need to check your next link to make sure you don't have more rows.
Update in response to question below about next link.
You currently have max-results set to 10000 which is the max number of rows that can be returned in a chunk. If there are more then 10000 rows then you will get a nextlink you need to request the the next chunk of rows. TotalResult will give you the total number of rows that you should be returning. Below I keep requesting more data using .execute until there isn't anymore Next links.
List result = new List();
do {
try
{
GaData DataList = request.Execute(); // Make the request
result.AddRange(DataList.Rows); // store the Data to return later
// hack to get next link stuff
totalResults = (!DataList.TotalResults.HasValue) ? 0 : Int32.Parse(DataList.TotalResults.ToString());
rowcnt = rowcnt + DataList.Rows.Count;
NextLink = DataList.NextLink;
request.StartIndex = rowcnt + 1 ;
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
totalResults = 0;
}
} while (request.StartIndex <= totalResults);

Categories

Resources