Unable to write columns using Google Sheets C# API - c#

I am trying to use the c# Google Sheets API for inputting information into my Google Sheets via the code below, but it is only inputting as rows and I would like to input it as columns instead. Is there a way to do this?
valueRange.Values = new List<IList<object>> { objectList };
appendRequest = service.Spreadsheets.Values.Append(valueRange, SpreadsheetId, range);
appendRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.AppendRequest.ValueInputOptionEnum.USERENTERED;
appendResponse = appendRequest.Execute();

I fixed your code in the below. Seems your valueRange.Values convert is incorrect.
public static AppendValuesResponse InsertColumnLine(this SheetsService service, string spreadsheetId, string range, params object[] columnValues)
{
// convert columnValues to columList
var columList = columnValues.Select(v => new List<object> { v });
// Add columList to values and input to valueRange
var values = new List<IList<object>>();
values.AddRange(columList.ToList());
var valueRange = new ValueRange()
{
Values = values
};
// Create request and execute
var appendRequest = service.Spreadsheets.Values.Append(valueRange, spreadsheetId, range);
appendRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.AppendRequest.ValueInputOptionEnum.USERENTERED;
return appendRequest.Execute();
}
Use the extension InsertColumnLine method.
// Sample!A1 => Sample is workseet name, A1 cell feild
_sheetsService.InsertColumnLine(_chickenOptions.Debug.SpreadSheetId, "Sample!A1", 1, 2, 3, 4, 5);
See the result. I hope it will help you.

Related

C# Google Sheets API - Protecting a range

I'm trying to protect a range in a google sheet using the sheets API (v4) in a .NET project.
I've been able to create a sheet and move it to the desired folder using the Drive API, but now I need to protect a certain range. I'm unsure how to form the request. I'm thinking I may need to use the batchUpdateRequest - but not entirely sure.
I have this so far:
var credential = GoogleCredential.FromFile(PathToServiceAccountKeyFile).CreateScoped(Google.Apis.Sheets.v4.SheetsService.ScopeConstants.Spreadsheets);
var service = new Google.Apis.Sheets.v4.SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential
});
var body = new Google.Apis.Sheets.v4.Data.Request();
body.AddProtectedRange.ProtectedRange.ProtectedRangeId(){
};
var request = service.Spreadsheets.BatchUpdate(body, fileId);
Any help would be greatly appreciated. Thanks!
You can use this code to protect a range:
var spreadsheetId = "spreadsheet id"; //can get from sheet url
var sheetId = "sheet id";
var range = "A1:B10";
var request = new BatchUpdateSpreadsheetRequest()
{
Requests = new List<Request>
{
new()
{
AddProtectedRange = new AddProtectedRangeRequest
{
ProtectedRange = new ProtectedRange
{
Range = new GridRange
{
SheetId = sheetId,
StartRowIndex = 0,
EndRowIndex = 9,
StartColumnIndex = 0,
EndColumnIndex = 1
},
Description = "Protected Range",
WarningOnly = false
}
}
}
}
};
service.Spreadsheets.BatchUpdate(request, spreadsheetId).Execute();
AddProtectedRangeRequest docs

Send data google sheets with c#

Read the data from the google sheet, it works fine but I don't get to send data to said sheet
HttpClient client = new HttpClient();
string url = "https://docs.google.com/.....";
var response = await client.GetAsync(string.Format(url));
string result = await response.Content.ReadAsStringAsync();
string cadena = stringBetween(result, "\n\n", "\"");
cadena = Regex.Replace(cadena, #"\n", ",");
string[] words = cadena.Split(',');
int x = 4;
List<User> listOfUsers = new List<User>();
for(x=4;x<28;x=x+4)
{
listOfUsers.Add(new User() { Nombre = words[x], Correo = words[x + 1], Telefono = words[x + 2], Comentario = words[x + 3] });
};
Your code is sending data to listOfUsers, which is a collection not tied to the spreadsheet in any way. Here is a good article from C-Sharp Corner showcasing how to create/update a Google Sheets document:
https://www.c-sharpcorner.com/article/create-and-update-google-spreadsheet-via-google-api-net-library/
I would encourage you to read the full article so you can understand what nuget packages you need to communicate with Google sheets. With that said, the most relevant part of it is towards the bottom, when the author writes a method to update an existing sheet:
private static void UpdatGoogleSheetinBatch(IList<IList<Object>> values, string spreadsheetId, string newRange, SheetsService service)
{
SpreadsheetsResource.ValuesResource.AppendRequest request =
service.Spreadsheets.Values.Append(new ValueRange() { Values = values }, spreadsheetId, newRange);
request.InsertDataOption =
SpreadsheetsResource.ValuesResource.AppendRequest.InsertDataOptionEnum.INSERTROWS;
request.ValueInputOption =
SpreadsheetsResource.ValuesResource.AppendRequest.ValueInputOptionEnum.RAW;
var response = request.Execute();
}
Notice how the method is taking a list of lists that contain values as an argument. It is then appended to the spreadsheet in question, and the insert option is then configured to add the new data as rows. The ValueInputOption is then set as RAW, meaning all values will be inserted without being parsed, and then the sheet is finally updated on the last line.
You will want to take note of how the author is generating their values, as they have a List containing as list of objects, whereas you have a list of users.
private static IList<IList<Object>> GenerateData()
{
List<IList<Object>> objNewRecords = new List<IList<Object>>();
int maxrows = 5;
for (var i = 1; i <= maxrows; i++)
{
IList<Object> obj = new List<Object>();
obj.Add("Data row value - " + i + "A");
obj.Add("Data row value - " + i + "B");
obj.Add("Data row value - " + i + "C");
objNewRecords.Add(obj);
}
return objNewRecords;
}
For what you are trying to do, I would modify it to do something like this
private static IList<IList<Object>> GenerateData(string[] words)
{
List<IList<Object>> objNewRecords = new List<IList<Object>>();
for (int x = 4; x < 28; x =x + 4)
{
IList<Object> obj = new List<Object>();
//nombre
obj.Add(words[x]);
// Correo
obj.Add(words[x+1]);
// Telefono
obj.Add(words[x+2]);
// Comentario
obj.Add(words[x+3]);
objNewRecords.Add(obj);
};
return objNewRecords;
}

How to rename sheet using google sheets api v4?

I have a code that its purpose is to rename a specific sheet, but when executing the BatchUpdate and the code is crached, does anyone have any ideas?
public void UpdateSheetName(string sheetName,string newSheetName)
{
//get sheet id by sheet name
Spreadsheet spr = service.Spreadsheets.Get(SpreadsheetId).Execute();
Sheet sh = spr.Sheets.Where(s => s.Properties.Title == sheetName).FirstOrDefault();
int sheetId = (int)sh.Properties.SheetId;
BatchUpdateSpreadsheetRequest bussr = new BatchUpdateSpreadsheetRequest();
var request = new Request()
{
UpdateSpreadsheetProperties= new UpdateSpreadsheetPropertiesRequest(){
Properties=new SpreadsheetProperties()
{
Title= newSheetName,
},
Fields ="title"
}
};
bussr.Requests = new List<Request>();
bussr.Requests.Add(request);
var bur = service.Spreadsheets.BatchUpdate(bussr, SpreadsheetId);
bur.Execute();
}
Error Message:
Invalid value at 'requests[0]' (oneof), oneof field 'kind' is already set. Cannot set 'updateSpreadsheetProperties' [400]
I found the problem, the problem was that I had used the wrong class, use UpdateSpreadsheetProperties instead of UpdateSheetPropertiesRequest
var request = new Request()
{
UpdateSheetProperties =new UpdateSheetPropertiesRequest {
Properties=new SheetProperties()
{
Title=newName,
SheetId=sheetId
},
Fields = "Title"
}
};

Xamarin iOS Reading Step Counts from HealthKit

I am trying to read the stepcount from 365 days back in time from the user and then upload this to a server. But I'm currently stuck at extracting the data, I get the permission from the iOS healthkit correctly, but the return type of my data is just get "[0:] HealthKit.HKSample[]"
public void GetSteps()
{
var healthKitStore = new HKHealthStore();
var stepRateType = HKQuantityType.Create(HKQuantityTypeIdentifier.StepCount);
var sort = new NSSortDescriptor(HKSample.SortIdentifierStartDate, true);
var q = new HKSampleQuery(stepRateType, HKQuery.GetPredicateForSamples(NSDate.Now.AddSeconds(TimeSpan.FromDays(-365).TotalSeconds), NSDate.Now.AddSeconds(TimeSpan.FromDays(1).TotalSeconds), HKQueryOptions.None), 0, new NSSortDescriptor[] { },
new HKSampleQueryResultsHandler((HKSampleQuery query2,HKSample[] results, NSError error2) =>
{
var query = results; //property created within the model to expose later.
Debug.WriteLine(query);
Debug.WriteLine(results);
}));
healthKitStore.ExecuteQuery(q);
}
I think I know why you are getting "[0:] HealthKit.HKSample[]", you are trying to Debug.WriteLine an array of objects. The results variable is an array. Loop through the array instead and extract out the "Quantity", "StartDate", and "EndDate" among other fields that are available:
foreach (var item in results)
{
var sample = (HKQuantitySample) item;
var hkUnit = HKUnit.Count;
var quantity = sample.Quantity.GetDoubleValue(hkUnit);
var startDateTime = sample.StartDate.ToDateTime().ToLocalTime();
var endDateTime = sample.EndDate.ToDateTime().ToLocalTime();
Debug.WriteLine(quantity);
Debug.WriteLine(startDateTime);
Debug.WriteLine(endDateTime);
}

epplus using LoadFromCollection with anonymous types

I have a IEnumerable<object> dataSource which contains a collection anonymous types. The actual structure of the anonymous type won't be known at design time, so I'm trying to find a generic solution that can handle any anonymous type.
How can I load them into epplus to create a spreadsheet? I have a worksheet called ws and I tried:
ws.Cells["A1"].LoadFromCollection(dataSource, true);
However when that runs it outputs all of the anonymous type's properties into a single cell:
{ Id = 10000, Title = This is a test }
I've tried passing in MemberInfo using:
var members = dataSource.First().GetType().GetMembers();
ws.Cells["A1"].LoadFromCollection(this._dataSource, true,
TableStyles.Medium1, BindingFlags.Public, members);
But that throws an exception:
Supplied properties in parameter Properties must be of the same type as T
Any suggestions on how I can create a spreadsheet using anonymous types in c#?
I have tested
using (var excel = new OfficeOpenXml.ExcelPackage())
{
var sheet = excel.Workbook.Worksheets.Add("Test");
sheet.Cells["A1"].LoadFromCollection(dataSource, true);
excel.SaveAs(new FileInfo(#"C:\Temp\Test.xlsx"));
}
with this sample data:
var dataSource = Enumerable.Range(1, 100).Select(i => new{ ID=i, Title="Title " + i });
It works fine. It creates two columns with the correct headers and 100 rows.
But you should use anonymous types only if you know the structure at compile time.
You could use a DataTable and LoadFromDataTable instead. Since i don't know how you create the anonymous type i show you just a small sample:
DataTable dataSource = new DataTable();
dataSource.Columns.Add("Id"); // default type is string
dataSource.Columns.Add("Title");
// add other columns
dataSource.Rows.Add("1", "Title1");
// add other rows
using (var excel = new OfficeOpenXml.ExcelPackage())
{
var sheet = excel.Workbook.Worksheets.Add("Test");
sheet.Cells["A1"].LoadFromDataTable(dataSource, true);
excel.SaveAs(new FileInfo(#"C:\Temp\Test.xlsx"));
}
You could group the anonymous types to make it easier for exporting with dataTables. The bug "Supplied properties in parameter Properties must be of the same type as T" is still there and a workaround is using DataTables.
// Imagine list is your main datasource
IEnumerable<object> list = Enumerable.Empty<object>(); // Data Source of <object>
// Added anon types at runtime added to the object list
var anonTypesOne = new object[]
{
new { GuidID = Guid.NewGuid(), StringProperty = "the string property" },
new { IntegerID = 1, IntegerProperty = 99 }
};
var anonTypesTwo = new object[]
{
new { StringID = "1", BooleanProperty = true, NumberProperty = 3, StringProperty = "Four" },
new { GuidID = Guid.NewGuid(), NumberThree = 3 },
new { GuidID = Guid.NewGuid(), NumberThree = 3 },
new { GuidID = Guid.NewGuid(), NumberThree = 3 }
};
list = list.Concat(anonTypesOne).Concat(anonTypesTwo);
// Grouping works on anon types so we can group the export into their own tables
var groupings = list.GroupBy(i => i.GetType());
using(var package = new ExcelPackage(new FileInfo("C:\\Temp\\Anon.xlsx")))
{
var ws = package.Workbook.Worksheets.Add("Anonymous Types");
// add each "anon type matched grouping"
foreach(var grouping in groupings)
{
var isNew = ws.Dimension == null; // the sheet is empty if Dimension is null.
var row = 0;
if(isNew)
{
row = 1; // start from the first row
}
else
{
// otherwise there are tables already, start from the bottom
row = ws.Dimension.End.Row;
}
// because of EPP inheritance bug of T, we can just use dataTable
DataTable dt = new DataTable(grouping.Key.Name);
var properties = grouping.Key.GetProperties(); // Get anon type Properties
foreach(var property in properties)
{
dt.Columns.Add(property.Name);
}
foreach(var item in grouping.ToList())
{
var dataRow = dt.NewRow();
foreach(var p in properties) // populate a single row
{
dataRow[p.Name] = p.GetValue(item); // item is anon object instance
}
dt.Rows.Add(dataRow);
}
if(isNew) // load into the top most left cell of the worksheet
ws.Cells[1, 1].LoadFromDataTable(dt, PrintHeaders: true);
else // load from the dimension of current items + 1 row for spacing
ws.Cells[ws.Dimension.End.Row + 1, 1].LoadFromDataTable(dt, PrintHeaders: true);
ws.InsertRow(ws.Dimension.End.Row + 2, 5); // Insert some padding between each group
}
package.Save();
}
I was, this thread is older, but I'm looking for the same problem.
With the following code (VB) I have success.
Carsten
Dim targetFile = New IO.FileInfo(sFN)
Dim dataSource = Enumerable.Range(0, 1).Select(Function(i) New With {.ID = 1000, .Titel = "This is a test "}).ToList
Using epp = New OfficeOpenXml.ExcelPackage(targetFile)
Dim ws = epp.Workbook.Worksheets.Add("lst_Anonymous")
ws.Cells(1, 1).LoadFromCollection(dataSource, True,
OfficeOpenXml.Table.TableStyles.Medium1,
Reflection.BindingFlags.Public,
dataSource.GetType.GetGenericArguments()(0).GetProperties)
epp.Save()
End Using

Categories

Resources