I started with the basics and learned how to insert a fixed bit of rows to a Google Sheet from my program now I've been trying to figure out how to append output from a SQL. I have a C# program that calls a stored procedure or it can do a select * from table. Simply trying to insert the results/output of that select into a Google Sheet. Every example I find is dealing with hard coded fixed rows and columns. My select produces less than 2000 lines of output. Appreciate the help!
namespace CS_Gsheet1
{
class Program
{
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/sheets.googleapis.com-dotnet-quickstart.json
//static string[] Scopes = { SheetsService.Scope.SpreadsheetsReadonly };
//static string ApplicationName = "Google Sheets API .NET Quickstart";
static string[] Scopes = { SheetsService.Scope.Spreadsheets };
static string ApplicationName = "Test3";
static void Main(string[] args)
{
var service = AuthorizeGoogleApp();
String spreadsheetId = "sheetIDstring";
// Write to specified sheet
String writeRange = "Sheet1!A1:K";
ValueRange valueRange = new ValueRange { MajorDimension = "ROWS" };
IList<object> dataList = new List<object>();
using(SqlConnection myConnection = new SqlConnection("connectionstring"))
{
myConnection.Open();
using(SqlCommand cmd = new SqlCommand("storedproc-selectsmultiplecolumnsandrows", myConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
using(SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
// dataList.Add(reader.GetString(0));
// dataList.Add(reader.GetValue(0));
IList<IList<Object>> values = new List<IList<Object>>();
values.Add(dataList);
}
}
}
}
// Data is accessible through the DataReader object here.
ValueRange valueDataRange = new ValueRange() { MajorDimension = "ROWS" };
valueDataRange.Values = new List<IList<object>> { dataList };
int c = dataList.Count;
Console.WriteLine("dataList Count {0}", c);
for (int i = 0; i < c; i++)
{
// API to append data to sheet
SpreadsheetsResource.ValuesResource.AppendRequest appendRequest = service.Spreadsheets.Values.Append(valueDataRange, spreadsheetId, writeRange);
appendRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.AppendRequest.ValueInputOptionEnum.RAW;
appendRequest.InsertDataOption = SpreadsheetsResource.ValuesResource.AppendRequest.InsertDataOptionEnum.INSERTROWS;
AppendValuesResponse appendValueResponse = appendRequest.Execute();
}
}
private static SheetsService AuthorizeGoogleApp()
{
UserCredential credential;
using (var stream = new FileStream("client_secret.json", FileMode.Open, FileAccess.ReadWrite))
{
string credPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/sheets.googleapis.com-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
// Create Google Sheets API service.
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
return service;
}
}
}
}
I was able to tweak this solution to get the process to work without requesting a quota increase from Google. It does not run very fast, but ok for ~1000 records.
https://github.com/jeffcrowder/GoogleSheetsAPIExample/blob/master/GoogleSheetsAPIExample/Program.cs
Related
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;
}
}
}
I am trying to read google sheets data in my xamarin forms ios project. I followed this tutorial: https://www.youtube.com/watch?v=afTiNU6EoA8&t=325s
To no avail.
My program says it can't find the client_secret.json file, and I can't find a solution to my problem.
it gives me this exception error
Here is my code:
using System;
using Google.Apis.Sheets.v4;
using Google.Apis.Auth.OAuth2;
using System.IO;
using Google.Apis.Services;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace CUapp
{
public class GoogleSheets
{
static readonly string[] Scopes = { SheetsService.Scope.Spreadsheets };
static readonly string SpreadsheetId = "1Me8q2o54xphoO_L1l-aK6_qzwIX2AAmmZzCCxvmGbl8";
static readonly string ApplicationName = "CUapp";
static readonly string sheet = "Sheet1";
static SheetsService service;
public GoogleSheets()
{
GoogleCredential credential;
using (
var stream = new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
//var stream = Assembly.GetManifestResourceStream("client_secret.json"))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(Scopes);
}
// Create Google Sheets API service.
service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential, ApplicationName = ApplicationName
});
ReadEntries();
}
static public void ReadEntries()
{
var range = $"{sheet}!B1:B30";
SpreadsheetsResource.ValuesResource.GetRequest request = service.Spreadsheets.Values.Get(SpreadsheetId, range);
request.MajorDimension = SpreadsheetsResource.ValuesResource.GetRequest.MajorDimensionEnum.COLUMNS;
var response = request.Execute();
var values = response.Values;
if (values != null && values.Count > 0)
{
foreach (var column in values)
{
string title = (string)column[0];
int i = (int)column[1];
//string buttonImg = $"{title}.jpeg";
//labels
char[] seperator = new char[] { ' ', ',' };
string labels_ = (string)column[3];
List<string> labels = labels_.Split(seperator, StringSplitOptions.RemoveEmptyEntries).ToList();
//_nums
int ingredients_num = (int)column[4];
int howto_num = (int)column[5];
int nutritionTips_num = (int)column[6];
int cookingTips_num = (int)column[7];
//ingredients
List<string> ingredients_raw = (List<string>)column.Skip(8).Take(ingredients_num);
List<ingredientBlock> ingredients = new List<ingredientBlock>();
foreach (var value_raw in ingredients_raw)
{
ingredientBlock ingredient = new ingredientBlock { text = value_raw };
ingredients.Add(ingredient);
}
//howto
List<string> howto_raw = (List<string>)column.Skip(8 + 1 + ingredients_num).Take(howto_num);
List<ingredientBlock> howto = new List<ingredientBlock>();
char howtoSeperator = '#';
foreach (string value_raw in howto_raw)
{
var value = value_raw.Split(howtoSeperator).ToList();
ingredientBlock ingredient = new ingredientBlock { step = value[0], text = value[1] };
howto.Add(ingredient);
}
recipeList.list.Add(new recipeModel { title = title, howto = howto, i = i, ingredients = ingredients, labels = labels });
//nutritionTips
//cookingTips_
// create new recipemodel
// title = 0, index = 1, buttonImg = 2, labels = 3
// ingredients_num = 4, methods_num = 5, nutritionTips_num = 6
// cookingTips_num = 7, ingredients = 8:8+ingredients_num-1,
}
}
}
}
}
Any response is appreciated :D
My program says it can't find the client_secret.json file, and I can't find a solution to my problem.
Firstly, put your client_secret.json file in shared code, make sure to set Build Action to EmbeddedResource
Then using the following code to parse our local json file.
var assembly = typeof(listviewsample.Page24).GetTypeInfo().Assembly;
Stream stream = assembly.GetManifestResourceStream("FormsSample.user.json");
using (var reader = new System.IO.StreamReader(stream))
{
var jsonString = reader.ReadToEnd();
//........
}
Update:
I test the code in simple sample, and you can take a look the following screenshot.
public partial class Page2 : ContentPage
{
public Page2()
{
InitializeComponent();
var assembly = typeof(Page2).GetTypeInfo().Assembly;
Stream stream = assembly.GetManifestResourceStream("mediasample.user.json");
using (var reader = new System.IO.StreamReader(stream))
{
var jsonString = reader.ReadToEnd();
//Converting JSON Array Objects into generic list
}
}
}
screenshot:
Thanks to Cherry BU, I overcame this hurdle. Here is my code:
public GoogleSheets()
{
GoogleCredential credential;
var assembly = typeof(GoogleSheets).GetTypeInfo().Assembly;
Stream stream = assembly.GetManifestResourceStream("CUapp.client_secret.json");
using (var reader = new StreamReader(stream))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(Scopes);
}
// Create Google Sheets API service.
service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential, ApplicationName = ApplicationName
});
ReadEntries();
}
I am trying to add a new event to a calendar, but I am getting the error that I don't have permission to do that. However, I can get the calendars and the events, because I have read permissions.
My code is this:
static string[] ScopesParaModificar = { CalendarService.Scope.Calendar };
private void SetCredentialParaModificar()
{
using (FileStream stream = new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
string credPath = "token.json";
_credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.FromStream(stream).Secrets,
ScopesParaModificar,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
}
private CalendarService GetCalendarService()
{
return new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = _credential,
ApplicationName = ApplicationName,
});
}
private void ucBtnCrearEvento_Click(object sender, RoutedEventArgs e)
{
CalendarService miCalendarService = GetCalendarService();
IEnumerable<CalendarListEntry> misCalendarios = miCalendarService.CalendarList.List().Execute().Items;
CalendarListEntry miCalendario = misCalendarios.FirstOrDefault(x => string.CompareOrdinal(x.Summary, "TestAGarcia") == 0);
Event miEventoNuevo = new Event();
miEventoNuevo.Id = "1";
miEventoNuevo.Summary = "Esto es Summary 01";
miEventoNuevo.Description = "Esto es el description";
Event.CreatorData miCreador = new Event.CreatorData();
miCreador.DisplayName = "DisplayName of creator";
miCreador.Email = "email del creador";
miCreador.Id = "ID creador";
miEventoNuevo.Creator = miCreador;
miCalendarService.Events.Insert(miEventoNuevo, miCalendario.Id).Execute();
}
Notice that when I set _credential, I am using the array that it uses the scope calendar, that it says that it is what I have to use if I want to modify events.
Thanks.
I am trying to write a console app for our elementary school which will read courses, students, coursework (etc) from Google Classrooms and write it to our SQL Server database. This is the first time I have ever used an API and I have very little experience with C# - but school starts in two days, so I'm using Google searches to figure out how to do it.
I pasted my code below that successfully reads the courses from Google Classroom and writes them to our SQL Server. I thought I could use the same logic for the rest of the tables (students, Coursework, etc). However, when I tried to add students, there is a problem with this line (list is underlined in red):
CoursesResource.ListRequest RequestStudents = service.Courses.Students.List();
I can't figure out what is wrong and can't seem to find an example online.
Any help would be very appreciated.
Thanks,
JMC
namespace ClassroomQuickstart
{
class Program
{
static string ApplicationName = "Google Classroom ETL Process";
static string[] Scopes =
{
ClassroomService.Scope.ClassroomAnnouncementsReadonly,
ClassroomService.Scope.ClassroomCoursesReadonly,
ClassroomService.Scope.ClassroomCourseworkStudentsReadonly,
ClassroomService.Scope.ClassroomGuardianlinksStudentsReadonly,
ClassroomService.Scope.ClassroomRostersReadonly,
ClassroomService.Scope.ClassroomStudentSubmissionsStudentsReadonly,
ClassroomService.Scope.ClassroomTopicsReadonly
};
static void Main(string[] args)
{
UserCredential credential;
using (var stream = new FileStream("MyCredentials.json", FileMode.Open, FileAccess.Read))
{
string credPath = "TokenGoogleClassroomETLClient";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create the service.
var service = new ClassroomService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
//==============================================================================================
// Transfer courses
// Define request parameters
CoursesResource.ListRequest RequestCourses = service.Courses.List();
RequestCourses.PageSize = 1000;
ListCoursesResponse ResponseCourses = RequestCourses.Execute();
if (ResponseCourses.Courses != null && ResponseCourses.Courses.Count > 0)
{
Console.WriteLine("\n=========================================");
Console.WriteLine("\nTransferring Google Classrooms Courses:");
// prepare the parameterized SQL statement
StringBuilder sb = new StringBuilder();
sb.Append("INSERT INTO GoogleClassroomsCourse ");
sb.Append("(CourseID, CourseName, CourseSection, DescriptionHeading, Description, Room, OwnerID, CreationTime, UpdateTime, EnrollmentCode, CourseState, AlternateLink, TeacherGroupEmail, CourseGroupEmail, GuardianEnabled, CalendarID) ");
sb.Append("VALUES (#Id , #Name, #Section, #DescriptionHeading, #Description, #Room, #OwnerId, #CreationTime, #UpdateTime, #EnrollmentCode, #CourseState, #AlternateLink, #TeacherGroupEmail, #CourseGroupEmail, #GuardiansEnabled, #CalendarId)");
String sql = sb.ToString();
// establish connecion to the SQL Server
using (SqlConnection connection = new SqlConnection("MyConnectionString"))
{
connection.Open();
// process each course record in Google Classrom
foreach (var course in ResponseCourses.Courses)
{
// ouput the course name and id to the console
Console.WriteLine("{0} ({1})", course.Name, course.Id);
// populate the variables from the current course record
string Id = course.Id ?? "";
string Name = course.Name ?? "";
string Section = course.Section ?? "";
string DescriptionHeading = course.DescriptionHeading ?? "";
string Description = course.Description ?? "";
string Room = course.Room ?? "";
string OwnerId = course.OwnerId ?? "";
string CreationTime = course.CreationTime.ToString() ?? "";
string UpdateTime = course.UpdateTime.ToString() ?? "";
string EnrollmentCode = course.EnrollmentCode ?? "";
string CourseState = course.CourseState ?? "";
string AlternateLink = course.AlternateLink ?? "";
string TeacherGroupEmail = course.TeacherGroupEmail ?? "";
string CourseGroupEmail = course.CourseGroupEmail ?? "";
string GuardiansEnabled = course.GuardiansEnabled.ToString() ?? "";
string CalendarId = course.CalendarId ?? "";
// write the record to sql database
try
{
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.Parameters.AddWithValue("#Id", Id);
command.Parameters.AddWithValue("#Name", Name);
command.Parameters.AddWithValue("#Section", Section);
command.Parameters.AddWithValue("#DescriptionHeading", DescriptionHeading);
command.Parameters.AddWithValue("#Description", Description);
command.Parameters.AddWithValue("#Room", Room);
command.Parameters.AddWithValue("#OwnerId", OwnerId);
command.Parameters.AddWithValue("#CreationTime", CreationTime);
command.Parameters.AddWithValue("#UpdateTime", UpdateTime);
command.Parameters.AddWithValue("#EnrollmentCode", EnrollmentCode);
command.Parameters.AddWithValue("#CourseState", CourseState);
command.Parameters.AddWithValue("#AlternateLink", AlternateLink);
command.Parameters.AddWithValue("#TeacherGroupEmail", TeacherGroupEmail);
command.Parameters.AddWithValue("#CourseGroupEmail", CourseGroupEmail);
command.Parameters.AddWithValue("#GuardiansEnabled", GuardiansEnabled);
command.Parameters.AddWithValue("#CalendarId", CalendarId);
int rowsAffected = command.ExecuteNonQuery();
}
}
catch (SqlException e)
{
Console.WriteLine(e.ToString());
}
//==============================================================================================
// Transfer students in the current course
// Define request parameters.
CoursesResource.ListRequest RequestStudents = service.Courses.Students.List();
RequestStudents.PageSize = 1000;
ListStudentsResponse ResponseStudents = RequestStudents.Execute();
if (ResponseStudents.Students != null && ResponseStudents.Students.Count > 0)
{
Console.WriteLine("\n=========================================");
Console.WriteLine("\nTransferring Google Classrooms Students:");
Console.WriteLine(ResponseStudents.Students.Count.ToString());
}
}
connection.Close();
}
}
else
{
Console.WriteLine("No courses found.");
}
Console.Read();
}
}
}
Looking further at the signature, Students.List requires a course ID as a parameter, which is currently not provided. On top of this, the return type for Students.List is CoursesResource.StudentsResource.ListRequest. To fix, replace the line with:
CoursesResource.StudentsResource.ListRequest RequestStudents = service.Courses.Students.List(course.Id);
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