How to retrieve column names from a excel sheet? - linq

Using EPPlus I'm writing data to multiple sheets. If a sheet is not created I'm adding a sheet else I'm retrieving the used rows and adding data from that row and saving it
FileInfo newFile = new FileInfo("Excel.xlsx");
using (ExcelPackage xlPackage = new ExcelPackage(newFile))
{
var ws = xlPackage.Workbook.Worksheets.FirstOrDefault(x => x.Name == language.Culture);
if (ws == null)
{
worksheet = xlPackage.Workbook.Worksheets.Add(language.Culture);
//writing data
}
else
{
worksheet = xlPackage.Workbook.Worksheets[language.Culture];
colCount = worksheet.Dimension.End.Column;
rowCount = worksheet.Dimension.End.Row;
//write data
}
worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
xlPackage.Save();
And it is working great.
Now I want to retrieve the column names of each sheet in the excel using LinqToExcel and this is my code
string sheetName = language.Culture;
var excelFile = new ExcelQueryFactory(excelPath);
IQueryable<Row> excelSheetValues = from workingSheet in excelFile.Worksheet(sheetName) select workingSheet;
string[] headerRow = excelFile.GetColumnNames(sheetName).ToArray();
At header row it is throwing me an exception
An OleDbException exception was caught
External table is not in the expected format.
But I don't want to use Oledb and want to work with Linq To Excel.
Note: When I'm working with single sheet rather than multiple sheets
it is working fine and retrieving all columns. Where am I going wrong.

(Based on OP's Comments)
The AutoFitColumn function has always been a little touchy. The important thing to remember is to call it AFTER you load the cell data.
But if you want a use a minimum width (when columns are very narrow and you want to use a minimum) I find EPP to be unreliable. It seems to always use DefualtColWidth of the worksheet even if you pass in a minimumWidth to one of the function overloads.
Here is how I get around it:
[TestMethod]
public void Autofit_Column_Range_Test()
{
//http://stackoverflow.com/questions/31165959/how-to-retrieve-column-names-from-a-excel-sheet
//Throw in some data
var datatable = new DataTable("tblData");
datatable.Columns.Add(new DataColumn("Nar", typeof(int))); //This would not be autofitted without the workaround since the default width of a new ws, usually 8.43
datatable.Columns.Add(new DataColumn("Wide Column", typeof(int)));
datatable.Columns.Add(new DataColumn("Really Wide Column", typeof(int)));
for (var i = 0; i < 20; i++)
{
var row = datatable.NewRow();
row[0] = i;
row[1] = i * 10;
row[2] = i * 100;
datatable.Rows.Add(row);
}
var existingFile2 = new FileInfo(#"c:\temp\temp.xlsx");
if (existingFile2.Exists)
existingFile2.Delete();
using (var package = new ExcelPackage(existingFile2))
{
//Add the data
var ws = package.Workbook.Worksheets.Add("Sheet1");
ws.Cells.LoadFromDataTable(datatable, true);
//Keep track of the original default of 8.43 (excel default unless the user has changed it in their local Excel install)
var orginaldefault = ws.DefaultColWidth;
ws.DefaultColWidth = 15;
//Even if you pass in a miniumWidth as the first parameter like '.AutoFitColumns(15)' EPPlus usually ignores it and goes with DefaultColWidth
ws.Cells[ws.Dimension.Address].AutoFitColumns();
//Set it back to what it was so it respects the user's local setting
ws.DefaultColWidth = orginaldefault;
package.Save();
}
}

Related

Google Apps Script that loops through a filter and sends an e-mail with a PDF?

I have data from a questionnaire (20K rows) that I need to share with the store managers (report) of our shops (400 shops). I managed to write a script that sends a pdf of my sheet to a list of e-mail addresses. But I'm stuck on writing the loop for the filter, since I can't get the setVisibleValues(values) function to work for FilterCriteriaBuilder. The setHiddenValues(values) function works, but I can't figure out how to combine that with the loop.
Sample of my Google Sheet
See below for my current code:
/**
* Filtersheet by location
*/
function FilterSheet() {
var spreadsheet = SpreadsheetApp.getActive().getSheetByName('Data')
spreadsheet.getRange('F1').activate();
var criteria = SpreadsheetApp.newFilterCriteria()
.setHiddenValues(['Amsterdam, Rotterdam'])
.build();
spreadsheet.getFilter().setColumnFilterCriteria(6, criteria);
};
/**
* Send pdf of currentspreadsheet
*/
function SendPdf() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheet = SpreadsheetApp.getActive().getSheetByName('Adres');
var blob = DriveApp.getFileById(ss.getId()).getAs("application/pdf");
blob.setName(ss.getName() + ".pdf");
var startRow = 2; // First row of data to process
var numRows = 2; // Number of rows to process
// Fetch the range of cells A2:B3
var dataRange = spreadsheet.getRange(startRow, 1, numRows, 2);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i in data) {
var row = data[i];
var emailAddress = row[0]; // First column
var message = 'I hearby send you the overview of your data'
var subject = 'Overview of data';
MailApp.sendEmail(emailAddress, subject, message,{
attachments:[blob]});
}
}
getValues() returns the values of all range's cells no matter if they are shown or hidden.
Use a loop and isRowHiddenByFilter(rowPosition) to reap out all the filtered values. You could use Array.prototype.push to add the values to a new array or use Array.prototype.splice to modify the array holdin the values returned by getValues()
Related
How to use in Google Sheets setValue only for range of filtered rows (getRange for not hidden cells)?
I managemed to solve the problem.
This script takes a google spreadsheet with 2 sheets,one with Data and one with a combination EmailAdresses.
It sends a filtered list (filter column F) of sheet Data to the corresponding salon (location) in sheet Emailadresses (var mode email). Additionally, it has the option to "store" the pdf's in your google drive (var mode store)
*/
function construct() {
// settings:
//var mode = "store";
var mode = "email";
// get list of all salons and email
var salonList = SpreadsheetApp.getActive().getSheetByName('EmailAdressen');
// set endvar for loop
var endRow = salonList.getLastRow();
// loop trough the rows to get the Salon name and the corresponding email
for(i=1;i<=endRow;i++){
var salonName = salonList.getRange(i,2).getValue();
var email = salonList.getRange(i,1).getValue();
// create an array with all salons that should be hidden (we cant pick which one to show, so we have to go the other way around...)
var filterArray = [];
// create array with all salons to hide
for(c=1;c<=endRow;c++){
// get value from email list, check if it is not the current selected one and if so add it to the list to filter out
salonFilterName = salonList.getRange(c,2).getValue();
if(salonFilterName != salonName) {
filterArray.push(salonFilterName);
}
} // end for c
// filter the list with the array we just created
var spreadsheet = filterList(filterArray);
if(mode == "email"){
// export to PDF
var pdf = exportToPdf(spreadsheet);
// email to email address belonging to this salon
emailToAddress(email, pdf);
} // end if
if(mode == "store"){
StorePdf(spreadsheet, salonName);
}
} // end for i
return;
}
function filterList(salonNameArray) {
// select data sheet
var spreadsheet = SpreadsheetApp.getActive().getSheetByName('Data');
// first remove all existing filters to make sure we are on a clean sheet
if(spreadsheet.getFilter()){
spreadsheet.getFilter().remove();
}
// create the filter
spreadsheet.getRange('F:F').createFilter();
// set criteria for filter with array passed from construct
var criteria = SpreadsheetApp.newFilterCriteria().setHiddenValues(salonNameArray).build();
// apply filter
spreadsheet.getFilter().setColumnFilterCriteria(6, criteria);
return spreadsheet;
}
function exportToPdf(ss) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheet = SpreadsheetApp.getActive().getSheetByName('Data');
var blob = DriveApp.getFileById(ss.getId()).getAs("application/pdf");
blob.setName(ss.getName() + ".pdf");
return blob;
}
function StorePdf(ss, salonName) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheet = SpreadsheetApp.getActive().getSheetByName('Data');
var blob = DriveApp.getFileById(ss.getId()).getBlob();
blob.setName(salonName + "_" + Utilities.formatDate(new Date(), "GMT+1", "ddMMyyyy")+".pdf");
DriveApp.createFile(blob);
return;
}
function emailToAddress(email, pdf) {
MailApp.sendEmail(email, 'Type here the subject', 'Type here the body',{
attachments:[pdf]});
return;
}

Script editor, automatic end time

Full disclosure, I am in no way a programmer of any kind. My library was looking for an easier way for multiple locations to add events to the public calendar.
Eventually I stumbled upon this script which I was able to adapt for our needs. However, the one change they would like is to have the end time default to 2 hours later. For example, if an event starts at 1 then the end time automatically defaults to 3.
Can anyone show me what change in the script I need to make for that to happen? Here is the test form that we use to enter the dates. Right now the end time is entered manually but I'd imagine that would have to be removed, correct?
Any help in figuring this out would be greatly appreciated.
Thanks!
//insert your google calendar ID
var calendarId = "ID-FOR-TEST-CALENDAR";
//index (starting from 1) of each column in the sheet
var titleIndex = 2;
var descriptionIndex = 3;
var startDateIndex = 4;
var endDateIndex = 5;
var googleCalendarIndex = 6;
/*
find the row where the Google Calendar Event ID is blank or null
The data of this row will be used to create a new calendar event
*/
function findRow(sheet) {
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
if(values[i][googleCalendarIndex-1]=="" || values[i][googleCalendarIndex-1]==null)
newEvent(i+1);
}
};
/*
get the data of the new row by calling getSheetData() and
create a new Calendar event by calling submitToGoogleCalendar()
*/
function newEvent(row){
var sheet = SpreadsheetApp.getActiveSheet();
var eventId = submitToGoogleCalendar(getSheetData(sheet,row),null)
if(eventId!=null)
sheet.getRange(row,googleCalendarIndex,1,1).setValue(eventId);
};
/*
Store the data of a row in an Array
*/
function getSheetData(sheet,row)
{
var data = new Array();
data.title=sheet.getRange(row,titleIndex,1,1).getValue();
data.description=sheet.getRange(row,descriptionIndex,1,1).getValue();
data.startDate = sheet.getRange(row,startDateIndex,1,1).getValue();
data.endDate = sheet.getRange(row,endDateIndex,1,1).getValue();
return data;
};
/*
if a cell is edited in the sheet, get all the data of the corresponding row and
create a new calendar event (after deleting the old event) by calling submitToGoogleCalendar()
*/
function dataChanged(event){
var sheet = SpreadsheetApp.getActiveSheet();
var row = event.range.getRow();
var eventId = sheet.getRange(row,googleCalendarIndex,1,1).getValue();
var eventId = submitToGoogleCalendar(getSheetData(sheet,row),eventId)
if(eventId!=null)
sheet.getRange(row,googleCalendarIndex,1,1).setValue(eventId);
};
/*
This function creates an event in the Google Calendar and returns the calendar event ID
which is stored in the last column of the sheet
*/
function submitToGoogleCalendar(sheetData,eventId) {
// some simple validations ;-)
if(sheetData.title == "" || sheetData.startDate == "" || sheetData.startDate == null)
return null;
var cal = CalendarApp.getCalendarById(calendarId);
var start = new Date(sheetData.startDate);
var end = new Date(sheetData.endDate);
// some simple date validations
if(start > end)
return null;
var event = null;
//if eventId is null (when called by newEvent()) create a new calendar event
if(eventId==null)
{
event = cal.createEvent(sheetData.title, start, end, {
description : sheetData.description,
});
return event.getId();
}
/*
else if the eventid is not null (when called by dataChanged()), delete the calendar event
and create a new event with the modified data by calling this function again
*/
else
{
event = cal.getEventSeriesById(eventId);
event.deleteEventSeries();
return submitToGoogleCalendar(sheetData,null);
}
return event.getId();
};
Without having tried it myself, I believe you could do it like this:
function submitToGoogleCalendar(sheetData,eventId) {
var cal = CalendarApp.getCalendarById(calendarId);
var start = new Date(sheetData.startDate);
var end = new Date(sheetData.endDate);
end.setHours(start.getHours() + 2);
...where the last line is the new line added to your original script.
To your question wether the end time should be removed, I would say yes, it should be removed.

Master details batch crud with entity framework only saving single detail record

I'm building a master details page with batch editing, that is multiple details records with single master record. but only one detail record is being saved into the data base. I tried to debug & found that detail loop is executing multiple time accurately but not the saving multiple data. Here is my Code for save method:
public ActionResult CMN_VAL_FORM(HRM_CMN_VLU_MST_ViewModel model)
{
//var ctx=new Entities1();
var CMN_VLU_MST_OBJ = new HRM_CMN_VLU_MST();
var CMN_VLU_DTL_OBJ = new HRM_CMN_VLU_DTL();
//using (TransactionScope transaction = new TransactionScope())
//{
using (var ctx = new Entities1())
{
var type_code = ctx.ExecuteStoreQuery<string>("select get_pk_code('hrm_cmn_vlu_mst','CMN_VLU_TYPE_CODE') from dual").SingleOrDefault(); //A scalar function to generate the code in the format yymmdd0001
var value_code = ctx.ExecuteStoreQuery<string>("select get_pk_code('hrm_cmn_vlu_dtl','CMN_VLU_CODE') from dual").SingleOrDefault();
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_CODE = type_code;
CMN_VLU_MST_OBJ.CMN_VLU_REM = model.CMN_VLU_REM;
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_FOR = model.CMN_VLU_TYPE_FOR;
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_SRTNM = model.CMN_VLU_TYPE_SRTNM;
CMN_VLU_MST_OBJ.ENTRY_DATE = DateTime.Now;
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_NAME = model.CMN_VLU_TYPE_NAME;
foreach (var item in model.HRM_CMN_VLU_DTL)
{
CMN_VLU_DTL_OBJ.CMN_VLU_LEVL = item.CMN_VAL_LEVL;
CMN_VLU_DTL_OBJ.CMN_VLU_REM = item.CMN_VLU_REM;
CMN_VLU_DTL_OBJ.CMN_VLU_SLNO = item.CMN_VLU_SLNO;
CMN_VLU_DTL_OBJ.CMN_VLU_TITL = item.CMN_VAL_TITL;
CMN_VLU_DTL_OBJ.CMN_VLU_CNTN = item.CMN_VAL_CNTN;
CMN_VLU_DTL_OBJ.CMN_VLU_CODE = value_code;
CMN_VLU_DTL_OBJ.CMN_VLU_TYPE_CODE = type_code;
CMN_VLU_DTL_OBJ.ENTRY_DATE = DateTime.Now;
CMN_VLU_DTL_OBJ.MAIL_ADDR_INT = item.MAIL_ADDR_INT;
CMN_VLU_DTL_OBJ.MAIL_ADDR_EXT = item.MAIL_ADDR_EXT;
CMN_VLU_DTL_OBJ.MAIL_AUTO_SEND_INT = item.MAIL_AUTO_SEND_INT;
CMN_VLU_DTL_OBJ.MAIL_AUTO_SEND_EXT = item.MAIL_AUTO_SEND_EXT;
CMN_VLU_DTL_OBJ.ACTIVE_STATUS = item.ACTIVE_STATUS;
CMN_VLU_MST_OBJ.HRM_CMN_VLU_DTL.Add(CMN_VLU_DTL_OBJ);
ctx.SaveChanges();
var temp_value_code = Int32.Parse(value_code);
temp_value_code++;
value_code = temp_value_code.ToString();
ctx.HRM_CMN_VLU_MST.AddObject(CMN_VLU_MST_OBJ);
}
ctx.SaveChanges();
// transaction.Complete();
//}
}
return View();
}
No Error message for the code, but not saving multiple detail records. What I'm doing wrong?
What I was doing wrong, I created the object just once & updated that same object every time while executing the foreach loop! I just moved the object declaration into the foreach loop & it works like a charm!

JQGrid: Export Grid to PDF

Is there any way of exporting JQGrid data to Excel/PDF. I am using SQL server 2008 R2 as database and WCF service for HTTP Request/response. Client is written using JavaScript and AJAX calls are made to interact with SQL database through WCF service.
Will 'excelExport' function of jqgrid work?
Here is the code to collect Grid Data and store:
enter code here
function ExportExcel() {
var mya=new Array();
mya = $("#PrjBudgetGrid").getDataIDs(); // Get All IDs
var data = $("#PrjBudgetGrid").getRowData(mya[0]); // Get First row to get the labels
var colNames=new Array();
var ii=0;
for (var i in data) {
colNames[ii++] = i;
} // capture col names
var html = "";
for (i = 0; i < mya.length; i++) {
data = $("#PrjBudgetGrid").getRowData(mya[i]); // get each row
for (j = 0; j < colNames.length; j++) {
html = html + data[colNames[j]] + "\t"; // output each column as tab delimited
}
html = html + "\n"; // output each row with end of line
}
html=html+"\n"; // end of line at the end
}
You can use the code from the answer or even better from another more recent answer. The part of the code which export data to Excel you can easy change to WCF code. See here an example how to use Stream as the output of WCF method.

how do you loop through all rows in kendoUI grid with filter

here's my code. it works, if you want to loop through all the rows. now, QA told me I have to make it to support filter. so, when user use filter, only a subset of the rows will show on the grid. I need to only loop through only those rows.
var entityGrid = $("#EntitesGrid").data("kendoGrid");
var data = entityGrid.dataSource.data();
var totalNumber = data.length;
for(var i = 0; i<totalNumber; i++) {
var currentDataItem = data[i];
VersionIdArray[i] = currentDataItem.VersionId;
}
I tried.
var data = entityGrid.dataSource.data().fetch();
and
var data = entityGrid.dataSource.data().filter();
couldn't get it working.
For future reference and for those who are interested, I found the the solution at:
http://colinmackay.scot/2012/07/23/kendo-ui-paging-and-accessing-the-filtered-results-in-javascript/
It works by first getting hold of the grid's data source, getting the filter and the data, creating a new query with the data and applying the filter to it. While this does result in getting the results of the filter it does have the distinct disadvantage of processing the filter operation twice.
function displayFilterResults() {
// Gets the data source from the grid.
var dataSource = $("#MyGrid").data("kendoGrid").dataSource;
// Gets the filter from the dataSource
var filters = dataSource.filter();
// Gets the full set of data from the data source
var allData = dataSource.data();
// Applies the filter to the data
var query = new kendo.data.Query(allData);
var filteredData = query.filter(filters).data;
// Output the results
$('#FilterCount').html(filteredData.length);
$('#TotalCount').html(allData.length);
$('#FilterResults').html('');
$.each(filteredData, function(index, item){
$('#FilterResults').append('<li>'+item.Site+' : '+item.Visitors+'</li>')
});
}
Many thanks!!! With this help now I did this...
kendo.data.DataSource.prototype.dataFiltered = function () {
// Gets the filter from the dataSource
var filters = this.filter();
// Gets the full set of data from the data source
var allData = this.data();
// Applies the filter to the data
var query = new kendo.data.Query(allData);
// Returns the filtered data
return query.filter(filters).data;
}
So now I can get my filtered data very easy!!! Awesome!!!
Example:
var dataFiltered = $("#MyGrid").data("kendoGrid").dataSource.dataFiltered();

Resources