I have a workflow that copies items from a standard Calendar list to a second standard Calendar list. But the copies contain less details about any given calendar item than the original. This works just fine except that the Start Time and End Time fields get copied over in UTC time and we need those times displayed in the user's local time zone (we have employees all over the world, so it can't be a static adjustment). I am aware that SharePoint stores all Dates/Times as UTC and I get why.
My solution to this problem is to (in the main calendar) create two hidden Date/Time columns that I would populate with the dates/times from the EventDate and EndDate Calendar columns (the UTC values), but these new values would be corrected for the time zone offset using the SharePoint Client Side Object Model (using JS). Then my workflow would copy these corrected dates/times over to the second calendar.
The following code appears to work. My success handlers are getting called for each iteration of the list items and my logged output shows all the correct values. However, after the code runs, the two Date fields that are supposed to have been updated/set on each item by the code remain blank.
<script src="/_layouts/15/sp.runtime.js"></script>
<script src="/_layouts/15/sp.js"></script>
<script>
var siteUrl = "https://duckcreek.sharepoint.com/sites/DCU";
function loadListItems(siteUrl) {
var ctx = new SP.ClientContext(siteUrl);
var oList = ctx.get_web().get_lists().getByTitle("Master Calendar");
var camlQuery = new SP.CamlQuery();
this.collListItem = oList.getItems(camlQuery);
// EventDate and EndDate are the pre-defined fields (from the Event
// content type) that represent the event start and end date / times
// CorrectedStartDateTime and CorrectedEndDateTime are pre-existing
// Date / Time fields in the list.
ctx.load(collListItem,
"Include(Id, DisplayName, EventDate, EndDate, CorrectedStartDateTime, CorrectedEndDateTime)");
ctx.executeQueryAsync(
Function.createDelegate(this, this.onLoadListItemsSucceeded),
Function.createDelegate(this, this.onLoadListItemsFailed));
}
function onLoadListItemsSucceeded(sender, args) {
var ctx = new SP.ClientContext(siteUrl);
var oList = ctx.get_web().get_lists().getByTitle("Master Calendar");
var listItemInfo = "";
var listItemEnumerator = collListItem.getEnumerator();
while (listItemEnumerator.moveNext()) {
// List is loaded. Enumerate the list items
let oListItem = listItemEnumerator.get_current();
// Get the Calendar item Start and End dates as stored in SharePoint (UTC)
let startDateString = oListItem.get_item("EventDate").toString();
let endDateString = oListItem.get_item("EndDate").toString();
// Get the GMT time zone offset
let startDateOffsetHours = parseInt(startDateString.split("GMT-")[1]) / 100;
let endDateOffsetHours = parseInt(endDateString.split("GMT-")[1]) / 100;
// Create new dates that are the same as the originals
let resultStartDate = new Date(startDateString);
let resultEndDate = new Date(endDateString);
// Adjust the new dates to be correct for the local time zone based on the UTC offset
resultStartDate.setHours(resultStartDate.getHours() + startDateOffsetHours);
resultEndDate.setHours(resultEndDate.getHours() + endDateOffsetHours);
// Update the two placeholder fields in the current list item with the corrected dates
oListItem.set_item("CorrectedStartDateTime", resultStartDate);
oListItem.set_item("CorrectedEndDateTime", resultEndDate);
// Update SharePoint
oListItem.update();
ctx.executeQueryAsync(
Function.createDelegate(this, this.onSetCorrectDateTimesSucceeded),
Function.createDelegate(this, this.onSetCorrectDateTimesFailed)
);
// This is just for diagnostics, but it does show the correct data.
// And since we are using .get_item() here, it would seem that we
// are pulling the correct data out of SharePoint, but the two "Corrected"
// fields remain empty after this function completes successfully.
listItemInfo += "\nDisplay name: " + oListItem.get_displayName() +
"\n\tEventDate: " + oListItem.get_item("EventDate") +
"\n\tEndDate: " + oListItem.get_item("EndDate") +
"\n\t\tCorrectedStartDateTime: " + oListItem.get_item("CorrectedStartDateTime") +
"\n\t\tCorrectedEndDateTime: " + oListItem.get_item("CorrectedEndDateTime") +
"\n--------------------------------------------------------------------------------";
}
console.log(listItemInfo.toString());
}
function onLoadListItemsFailed(sender, args) {
console.log("Request failed!" + args.get_message() + "\n" + args.get_stackTrace());
}
function onSetCorrectDateTimesSucceeded() {
console.log("Item updated!");
}
function onSetCorrectDateTimesFailed(sender, args) {
console.log("Request failed!" + args.get_message() + "\n" + args.get_stackTrace());
}
loadListItems(siteUrl);
</script>
And, here is one of the records that the code produces (it actually produces one of the following for each of the items on the first calendar as it should):
Display name: NEW TEST
EventDate: Mon Dec 31 2018 19:00:00 GMT-0500 (Eastern Standard Time)
EndDate: Tue Jan 01 2019 18:59:00 GMT-0500 (Eastern Standard Time)
CorrectedStartDateTime: Tue Jan 01 2019 00:00:00 GMT-0500 (Eastern Standard Time)
CorrectedEndDateTime: Tue Jan 01 2019 23:59:00 GMT-0500 (Eastern Standard Time)
--------------------------------------------------------------------------------
However, the calendar items don't get the two "corrected" fields updated:
Simple answer
You recreate the SP.ClientContext in the onLoadListItemsSucceeded function, so you don't actually call update for the original list item. Save it in a variable and reuse it instead.
Complete code
<script src="/_layouts/15/sp.runtime.js"></script>
<script src="/_layouts/15/sp.js"></script>
<script>
var siteUrl = "https://duckcreek.sharepoint.com/sites/DCU";
var ctx;
function loadListItems(siteUrl) {
ctx = new SP.ClientContext(siteUrl);
var oList = ctx.get_web().get_lists().getByTitle("Master Calendar");
var camlQuery = new SP.CamlQuery();
this.collListItem = oList.getItems(camlQuery);
// EventDate and EndDate are the pre-defined fields (from the Event
// content type) that represent the event start and end date / times
// CorrectedStartDateTime and CorrectedEndDateTime are pre-existing
// Date / Time fields in the list.
ctx.load(collListItem,
"Include(Id, DisplayName, EventDate, EndDate, CorrectedStartDateTime, CorrectedEndDateTime)");
ctx.executeQueryAsync(
Function.createDelegate(this, this.onLoadListItemsSucceeded),
Function.createDelegate(this, this.onLoadListItemsFailed));
}
function onLoadListItemsSucceeded(sender, args) {
var oList = ctx.get_web().get_lists().getByTitle("Master Calendar");
var listItemInfo = "";
var listItemEnumerator = collListItem.getEnumerator();
while (listItemEnumerator.moveNext()) {
// List is loaded. Enumerate the list items
let oListItem = listItemEnumerator.get_current();
// Get the Calendar item Start and End dates as stored in SharePoint (UTC)
let startDateString = oListItem.get_item("EventDate").toString();
let endDateString = oListItem.get_item("EndDate").toString();
// Get the GMT time zone offset
let startDateOffsetHours = parseInt(startDateString.split("GMT-")[1]) / 100;
let endDateOffsetHours = parseInt(endDateString.split("GMT-")[1]) / 100;
// Create new dates that are the same as the originals
let resultStartDate = new Date(startDateString);
let resultEndDate = new Date(endDateString);
// Adjust the new dates to be correct for the local time zone based on the UTC offset
resultStartDate.setHours(resultStartDate.getHours() + startDateOffsetHours);
resultEndDate.setHours(resultEndDate.getHours() + endDateOffsetHours);
// Update the two placeholder fields in the current list item with the corrected dates
oListItem.set_item("CorrectedStartDateTime", resultStartDate);
oListItem.set_item("CorrectedEndDateTime", resultEndDate);
// Update SharePoint
oListItem.update();
ctx.executeQueryAsync(
Function.createDelegate(this, this.onSetCorrectDateTimesSucceeded),
Function.createDelegate(this, this.onSetCorrectDateTimesFailed)
);
// This is just for diagnostics, but it does show the correct data.
// And since we are using .get_item() here, it would seem that we
// are pulling the correct data out of SharePoint, but the two "Corrected"
// fields remain empty after this function completes successfully.
listItemInfo += "\nDisplay name: " + oListItem.get_displayName() +
"\n\tEventDate: " + oListItem.get_item("EventDate") +
"\n\tEndDate: " + oListItem.get_item("EndDate") +
"\n\t\tCorrectedStartDateTime: " + oListItem.get_item("CorrectedStartDateTime") +
"\n\t\tCorrectedEndDateTime: " + oListItem.get_item("CorrectedEndDateTime") +
"\n--------------------------------------------------------------------------------";
}
console.log(listItemInfo.toString());
}
function onLoadListItemsFailed(sender, args) {
console.log("Request failed!" + args.get_message() + "\n" + args.get_stackTrace());
}
function onSetCorrectDateTimesSucceeded() {
console.log("Item updated!");
}
function onSetCorrectDateTimesFailed(sender, args) {
console.log("Request failed!" + args.get_message() + "\n" + args.get_stackTrace());
}
loadListItems(siteUrl);
</script>
Additional notes
Time zones offsets can be negative(-) or positive (+), so these lines are probably incomplete
// Get the GMT time zone offset
let startDateOffsetHours = parseInt(startDateString.split("GMT-")[1]) / 100;
let endDateOffsetHours = parseInt(endDateString.split("GMT-")[1]) / 100;
Related
I'm creating a POC for a Gantt project for my company. I'm using the Resource Gantt from Anychart version 8.2.0. I'm trying to determine in which period am I, in order to set a custom tooltip. If I'm not in edit mode my code works, but if in edit mode, it doesn't! I can't determine in which period we are, because the periodIndex is undefined. Can anyone help me?
My custom tooltip code:
//custom timeLine Tooltip
timeLine.tooltip().useHtml(true);
timeLine.tooltip().format(function (e) {
var tooltip = '';
var startDate;
var endDate;
var item = e.item;
var parentElement = item.getParent();
if (parentElement !== null ) {
tooltip = 'Project: ' + parentElement.get('name') + '<br>';
} else {
tooltip = '';
}
if (e.periodIndex !== undefined){
startDate = e.period.start;
endDate = e.period.end;
tooltip = tooltip + 'Period Id: ' + e.period.id + '<br>';
tooltip = tooltip + 'Start Date: ' + startDate + '<br>' + 'End Date: ' + endDate;
} else {
var periodsArr = item.get('periods');
startDate = periodsArr[0].start;
endDate = periodsArr[periodsArr.length-1].end;
tooltip = tooltip + 'From ' + startDate + ' to ' + endDate;
}
return tooltip;
});
I have a sample in here: https://jsfiddle.net/migrafik/yobu0t6y/6/
Thanks.
Unfortunately, when the LiveEdit mode is enabled, you hover the period along with editing thumbs and other elements which implement this feature. That's why the period info is not available. So, with the current version of library 8.2.1 this cannot be done. I will suggest this improvement to our dev team and probably in future releases, the period info will become available. I will notify you about the ETA and when the improvement gets released.
I have this code which works to transform a json object into a CSV file
HTML:
<div class='mydiv'>
<textarea id="txt" class='txtarea'>[{"Vehicle":"BMW","Date":"30, Jul 2013 09:24 AM","Location":"Hauz Khas, Enclave, New Delhi, Delhi, India","Speed":42},{"Vehicle":"Honda CBR","Date":"30, Jul 2013 12:00 AM","Location":"Military Road, West Bengal 734013, India","Speed":0},{"Vehicle":"Supra","Date":"30, Jul 2013 07:53 AM","Location":"Sec-45, St. Angel's School, Gurgaon, Haryana, India","Speed":58},{"Vehicle":"Land Cruiser","Date":"30, Jul 2013 09:35 AM","Location":"DLF Phase I, Marble Market, Gurgaon, Haryana, India","Speed":83},{"Vehicle":"Suzuki Swift","Date":"30, Jul 2013 12:02 AM","Location":"Behind Central Bank RO, Ram Krishna Rd by-lane, Siliguri, West Bengal, India","Speed":0},{"Vehicle":"Honda Civic","Date":"30, Jul 2013 12:00 AM","Location":"Behind Central Bank RO, Ram Krishna Rd by-lane, Siliguri, West Bengal, India","Speed":0},{"Vehicle":"Honda Accord","Date":"30, Jul 2013 11:05 AM","Location":"DLF Phase IV, Super Mart 1, Gurgaon, Haryana, India","Speed":71}]</textarea>
<button class='gen_btn'>Generate File</button>
</div>
JS:
$(document).ready(function(){
$('button').click(function(){
var data = $('#txt').val();
if(data == '')
return;
JSONToCSVConvertor(data, "Vehicle Report", true);
});
});
function JSONToCSVConvertor(JSONData, ReportTitle, ShowLabel) {
//If JSONData is not an object then JSON.parse will parse the JSON string in an Object
var arrData = typeof JSONData != 'object' ? JSON.parse(JSONData) : JSONData;
var CSV = '';
//Set Report title in first row or line
CSV += ReportTitle + '\r\n\n';
//This condition will generate the Label/Header
if (ShowLabel) {
var row = "";
//This loop will extract the label from 1st index of on array
for (var index in arrData[0]) {
//Now convert each value to string and comma-seprated
row += index + ',';
}
row = row.slice(0, -1);
//append Label row with line break
CSV += row + '\r\n';
}
//1st loop is to extract each row
for (var i = 0; i < arrData.length; i++) {
var row = "";
//2nd loop will extract each column and convert it in string comma-seprated
for (var index in arrData[i]) {
row += '"' + arrData[i][index] + '",';
}
row.slice(0, row.length - 1);
//add a line break after each row
CSV += row + '\r\n';
}
if (CSV == '') {
alert("Invalid data");
return;
}
//Generate a file name
var fileName = "MyReport_";
//this will remove the blank-spaces from the title and replace it with an underscore
fileName += ReportTitle.replace(/ /g,"_");
//Initialize file format you want csv or xls
var uri = 'data:text/csv;charset=utf-8,' + escape(CSV);
// Now the little tricky part.
// you can use either>> window.open(uri);
// but this will not work in some browsers
// or you will not get the correct file extension
//this trick will generate a temp <a /> tag
var link = document.createElement("a");
link.href = uri;
//set the visibility hidden so it will not effect on your web-layout
link.style = "visibility:hidden";
link.download = fileName + ".csv";
//this part will append the anchor tag and remove it after automatic click
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
here is the code working: http://jsfiddle.net/hybrid13i/JXrwM/
the problem is that my json object is stored in a collection, anybody knows how to get the json format object printed in the client to then use the script I mentioned above?
so I want my meteor collection object to be printed in the client as a normal json example like this :
[{"Vehicle":"BMW","Date":"30, Jul 2013 09:24 AM","Location":"Hauz Khas, Enclave, New Delhi, Delhi, India","Speed":42},
If you want to convert a collection to an array (which is JSON) you can just use .fetch()
MyCollection.find({}).fetch();
There's also a pre-built collection-to-csv export package lfergson:exportcsv
I am new to Netsuite and I was asked to perform a script that was launched from an application programmed in java. The script with a function to generate a Purchase Order in Netsuite and other function to list the Purchase Order created earlier. It turns out that for this I am using the api SuiteScript but when creating the Purchase Order run the java application and launches the script but it gives the following error:
Aug 03, 2015 2:49:00 PM com.gargoylesoftware.htmlunit.WebClient printContentIfNecessary
INFO: {"error": {"code": "user_error", "message": "Please enter value (s) for: Vendor"}}
Javascript function to create is:
function CreatePurchase_Orders(datain){
var output = '';
nlapiLogExecution('DEBUG','createRecord','ingreso la consulta' ) ;
nlapiLogExecution('DEBUG','createRecord', 'Ingresa: '+ datain);
//var msg = validateTimeBills(datain);
var msg = null;
if (msg){
var err = new Object();
err.status = "failed";
err.message= msg;
return err;
}
var Purchase_Orders = datain.Purchase_Order;
nlapiLogExecution('DEBUG','createRecord', 'obtuvo el objeto: '+ Purchase_Orders);
for (var Purchase_Orderobject in Purchase_Orders){
var Purchase_Order = Purchase_Orders[Purchase_Orderobject];
var transdate = Purchase_Order.Transdate;
var Form = Purchase_Order.Form;
var Vendor = Purchase_Order.Vendor;
var Currency = Purchase_Order.Currency;
var Item = Purchase_Order.Item;
nlapiLogExecution('DEBUG','campos','transdate: '+ transdate+'/Form: '+Form + ' /Vendor: ' + Vendor + ' /Currency: ' + Currency
+ ' /Item: ' + Item);
var Purchase_Order = nlapiCreateRecord('purchaseorder');
var nlobjAssistant = nlapiCreateAssistant ( 'asistente' , false ) ;
var Purchase_Orderid = 1;//nlapiSubmitRecord( Purchase_Order , true, true);
if(Purchase_Order){
nlapiLogExecution('DEBUG', 'Purchase_Order ' + Purchase_Orderid + ' successfully created', '');
nlapiLogExecution('DEBUG', 'createRecord', 'creo el record');
}
Purchase_Order.setFieldValue('transdate', transdate);
Purchase_Order.setFieldValue('inpt_customform1', Form);
Purchase_Order.setFieldValue('vendor', Vendor);
Purchase_Order.setFieldValue('inpt_currency7', Currency);
Purchase_Order.setFieldValue('inpt_item', Item);
Purchase_Order.setFieldText('quantity_formattedValue', '1');
Purchase_Order.setFieldText('rate_formattedValue', '1');
Purchase_Order.setFieldText('amount_formattedValue', '1');
Purchase_Order.setFieldText('inpt_taxcode', 'VAT_MX:UNDEF_MX');
Purchase_Order.setFieldText('grossamt_formattedValue', '1');
Purchase_Order.setFieldText('tax1amt_formattedValue', '0');
Purchase_Order.setFieldText('expectedreceiptdate', '24/6/2015');
//var Purchase_Orderid = 1;//nlapiSubmitRecord( Purchase_Order , true, true);
var submitRecord = nlapiSubmitRecord(Purchase_Order);//,true);
nlapiLogExecution('DEBUG', 'submirRecord ' + submitRecord);
}
var mesg = new Object();
mesg.status = "OK";
mesg.message= nlobjAssistant.getAllFields();
return mesg;
}
And the function code in Java is:
WebClient client = new WebClient(BrowserVersion.FIREFOX_31);
client.getOptions().setJavaScriptEnabled(false);
client.getOptions().setThrowExceptionOnScriptError(false);
WebRequest requestSettings = new WebRequest(new URL(url),HttpMethod.POST);
requestSettings.setAdditionalHeader("Host", "rest.na1.netsuite.com");
requestSettings.setAdditionalHeader("User-Agent", "SuiteScript-Call");
requestSettings.setAdditionalHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
requestSettings.setAdditionalHeader("Accept-Language", " es-cl,es;q=0.8,en-us;q=0.5,en;q=0.3");
requestSettings.setAdditionalHeader("Accept-Encoding", "gzip, deflate");
requestSettings.setAdditionalHeader("Content-Type", "application/json");
requestSettings.setAdditionalHeader("Pragma", "no-cache");
requestSettings.setAdditionalHeader("Cache-Control", "no-cache");
requestSettings.setAdditionalHeader("Referer", "http://localhost:8084");
requestSettings.setAdditionalHeader("Cookie", "");
requestSettings.setAdditionalHeader("Connection", "keep-alive");
requestSettings.setAdditionalHeader("Authorization", "NLAuth nlauth_account=" + account + ", nlauth_email=" + mail + ", nlauth_signature=" + pass + ", nlauth_role=" + role + "");
Gson gson = new Gson();
//objeto llenado estaticamente de forma momentanea, se debe leer desde archivo externo
Purchase_Order purchaseOrder = new Purchase_Order("25/06/2015","formTest","vendorTest","CurrencyTest","itemTest");
String cuerpo = gson.toJson(purchaseOrder);
System.out.println(cuerpo);
// Set the request parameters
requestSettings.setRequestBody(cuerpo);
Page page = client.getPage(requestSettings);
WebResponse response = page.getWebResponse();
String json = response.getContentAsString();
System.out.println(json);
With this javascript function you should create me a record Purchase Order but I can not find the error and the solution if someone could please help me I would appreciate it a lot.
PS: if you have to create a customized form could tell me how?
thanks!
Here are some points :
As your error message says Please enter value (s) for: Vendor, you're missing the value for vendor field, which is mandatory. In your piece of code you're passing wrong internalid value for vendor. You should use entity instead of vendor
Purchase_Order.setFieldValue('entity', Vendor); // where vendor is the internal id of the vendor record
For setting custom form you can use
Purchase_Order.setFieldValue('customform', Form); // where Form is the id of the custom form
I also noticed that you're setting some values in purchase order which I suspect are to be a kind of custom one. If that is the case, then your custom field internal id should be prefixed with custbody.
For all the standard fields internal id you can refer to the Suite script Records Browser.
I've been trying to create a code that takes info from a Google Spreadsheet, and creates Google Calendar events. I'm new to this, so bear with my lack of in-depth coding knowledge!
I initially used this post to create a code:
Create Google Calendar Events from Spreadsheet but prevent duplicates
I then worked out that it was timing out due to the number of rows on the spreadsheet, and wasn't creating eventIDs to avoid the duplicates. I got an answer here to work that out!
Google Script that creates Google Calendar events from a Google Spreadsheet - "Exceeded maximum execution time"
And now I've realised that it's over-writing the formulas, I have in the spreadsheet, auto-completing into each row, as follows:
Row 12 - =if(E4="","",E4+1) // Row 13 - =if(C4="","",C4+1) // Row 18 - =if(B4="","","WHC - "&B4) // Row 19 - =if(B4="","","Docs - "&B4)
Does anyone have any idea how I can stop it doing this?
/**
* Adds a custom menu to the active spreadsheet, containing a single menu item
* for invoking the exportEvents() function.
* The onOpen() function, when defined, is automatically invoked whenever the
* spreadsheet is opened.
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Export WHCs",
functionName : "exportWHCs"
},
{
name : "Export Docs",
functionName : "exportDocs"
}];
sheet.addMenu("Calendar Actions", entries);
};
/**
* Export events from spreadsheet to calendar
*/
function exportWHCs() {
// check if the script runs for the first time or not,
// if so, create the trigger and PropertiesService.getScriptProperties() the script will use
// a start index and a total counter for processed items
// else continue the task
if(PropertiesService.getScriptProperties().getKeys().length==0){
PropertiesService.getScriptProperties().setProperties({'itemsprocessed':0});
ScriptApp.newTrigger('exportWHCs').timeBased().everyMinutes(5).create();
}
// initialize all variables when we start a new task, "notFinished" is the main loop condition
var itemsProcessed = Number(PropertiesService.getScriptProperties().getProperty('itemsprocessed'));
var startTime = new Date().getTime();
var sheet = SpreadsheetApp.getActiveSheet();
var headerRows = 4; // Number of rows of header info (to skip)
var range = sheet.getDataRange();
var data = range.getValues();
var calId = "flightcentre.com.au_pma5g2rd5cft4lird345j7pke8#group.calendar.google.com";
var cal = CalendarApp.getCalendarById(calId);
for (i in data) {
if (i < headerRows) continue; // Skip header row(s)
var row = data[i];
var date = new Date(row[12]); // First column
var title = row[18]; // Second column
var tstart = new Date(row[15]);
tstart.setDate(date.getDate());
tstart.setMonth(date.getMonth());
tstart.setYear(date.getYear());
var tstop = new Date(row[16]);
tstop.setDate(date.getDate());
tstop.setMonth(date.getMonth());
tstop.setYear(date.getYear());
var id = row[17]; // Sixth column == eventId
// Check if event already exists, update it if it does
try {
var event = cal.getEventSeriesById(id);
}
catch (e) {
// do nothing - we just want to avoid the exception when event doesn't exist
}
if (!event) {
//cal.createEvent(title, new Date("March 3, 2010 08:00:00"), new Date("March 3, 2010 09:00:00"));
var newEvent = cal.createEvent(title, tstart, tstop).addEmailReminder(5).getId();
row[17] = newEvent; // Update the data array with event ID
}
else {
event.setTitle(title);
}
if(new Date().getTime()-startTime > 240000){ // if > 4 minutes
var processed = i+1;// save usefull variable
PropertiesService.getScriptProperties().setProperties({'itemsprocessed':processed});
range.setValues(data);
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),'progress sheet to cal','item processed : '+processed);
return;
}
debugger;
}
// Record all event IDs to spreadsheet
range.setValues(data);
}
/**
* Export events from spreadsheet to calendar
*/
function exportDocs() {
// check if the script runs for the first time or not,
// if so, create the trigger and PropertiesService.getScriptProperties() the script will use
// a start index and a total counter for processed items
// else continue the task
if(PropertiesService.getScriptProperties().getKeys().length==0){
PropertiesService.getScriptProperties().setProperties({'itemsprocessed':0});
ScriptApp.newTrigger('exportDocs').timeBased().everyMinutes(5).create();
}
// initialize all variables when we start a new task, "notFinished" is the main loop condition
var itemsProcessed = Number(PropertiesService.getScriptProperties().getProperty('itemsprocessed'));
var startTime = new Date().getTime();
var sheet = SpreadsheetApp.getActiveSheet();
var headerRows = 4; // Number of rows of header info (to skip)
var range = sheet.getDataRange();
var data = range.getValues();
var calId = "flightcentre.com.au_pma5g2rd5cft4lird345j7pke8#group.calendar.google.com";
var cal = CalendarApp.getCalendarById(calId);
for (i in data) {
if (i < headerRows) continue; // Skip header row(s)
var row = data[i];
var date = new Date(row[13]); // First column
var title = row[19]; // Second column
var tstart = new Date(row[15]);
tstart.setDate(date.getDate());
tstart.setMonth(date.getMonth());
tstart.setYear(date.getYear());
var tstop = new Date(row[16]);
tstop.setDate(date.getDate());
tstop.setMonth(date.getMonth());
tstop.setYear(date.getYear());
var id = row[20]; // Sixth column == eventId
// Check if event already exists, update it if it does
try {
var event = cal.getEventSeriesById(id);
}
catch (e) {
// do nothing - we just want to avoid the exception when event doesn't exist
}
if (!event) {
//cal.createEvent(title, new Date("March 3, 2010 08:00:00"), new Date("March 3, 2010 09:00:00"));
var newEvent = cal.createEvent(title, tstart, tstop).addEmailReminder(5).getId();
row[20] = newEvent; // Update the data array with event ID
}
else {
event.setTitle(title);
}
if(new Date().getTime()-startTime > 240000){ // if > 4 minutes
var processed = i+1;// save usefull variable
PropertiesService.getScriptProperties().setProperties({'itemsprocessed':processed});
range.setValues(data);
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),'progress sheet to cal','item processed : '+processed);
return;
}
debugger;
}
// Record all event IDs to spreadsheet
range.setValues(data);
}
You have to ways to solve that problem.
First possibility : update your sheet with array data only on columns that have no formulas, proceeding as in this other post but in your case (with multiple columns to skip) it will rapidly become tricky
Second possibility : (the one I would personally choose because I 'm not a "formula fan") is to do what your formulas do in the script itself, ie translate the formulas into array level operations.
following your example =if(E4="","",E4+1) would become something like data[n][4]=data[n][4]==''?'':data[n+1][4]; if I understood the logic (but I'm not so sure...).
EDIT
There is actually a third solution that is even simpler (go figure why I didn't think about it in the first place...) You could save the ranges that have formulas, for example if col M has formulas you want to keep use :
var formulM = sheet.getRange('G1:G').getFormulas();
and then, at the end of the function (after the global setValues()) rewrite the formulas using :
sheet.getRange('G1:G').setFormulas(formulM);
to restore all the previous formulas... as simple as that, repeat for every column where you need to keep the formulas.
I want to be able to pass multiple data sets to my view. Here is how I am currently doing it in my controller:
transactions: function (req, res) {
var queryexpenses = 'select * from expense order by name';
Expense.query(queryexpenses, function (err, expense) {
this.expenses = expense;
});
if (req.param('filter')) {
var where = 'where fk_expense = ' + req.param('expensefilter');
where += ' and datePosted > "' + req.param('yearfilter') + '-01-01" ';
where += ' and datePosted < "' + req.param('yearfilter') + '-12-31" ';
} else {
var where = 'where fk_expense IS NULL';
}
var query = 'select * from accounting ' + where + ' order by description';
Accounting.query(query, function (err, trans) {
this.transactions = trans;
});
var total = 0;
_.each(this.transactions, function (element, index, list) {
// format dates
element.datePosted = dateFormat(element.datePosted, 'dd/mm/yyyy');
var tmp0 = element.amount
var tmp1 = tmp0.replace(/ /g, '');
var tmp2 = parseFloat(tmp1);
total += tmp2;
});
this.total = total.toFixed(2);
return res.view();
}
This is the only way I am able to accomplish what Im trying to do but there are problems which I believe are caused by me putting the query objects in the "this" scope. The first problem is the page will crash after server restart on first reload. The second problem is everything seems to happen one step behind. What I mean is if I issue commands on the UI (eg submit a form) nothing will happen unless I take the same action twice.
So how do I pass multiple sets of data to my views without scoping them in "this"?
res.view({
corndogs: [{name: 'Hank the Corndog'}, {name: 'Lenny the Corndog'}]
});
Here is the relevant docs page: http://sailsjs.org/#!documentation/views
Also, it looks like you're not taking full advantage of Waterline for making SQL queries.