JDBC connection in Google Sheet - javascript

I am using the script made by Mike Seekwell [Link] for connecting a MySQL database to a Sheet, and it works.
My problem is that I need it to run always from the first cell of the first sheet, while actually it can run from every active cell of every active sheet.
How can I modify this thing?
Here is the script:
/**
* #OnlyCurrentDoc
*/
var MAXROWS = 1000
var SEEKWELL_J_SHORT_DATES = { day: "yyyy-MM-dd", month: "yyyy-MM", year: "yyyy", dayNum: "dd", monthNum: "MM", yearNum: "yyyy", week: "W" }
var SEEKWELL_J_TIMEZONE = "UTC"
var HOST = '//host'
var PORT = '//port'
var USERNAME = '//username'
var PASSWORD = '//password'
var DATABASE = '/database'
var DB_TYPE = 'mysql'
function goToSheet(sheetName) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName(sheetName));
};
function runSql(query, options) {
var doc = SpreadsheetApp.getActiveSpreadsheet();
var sheet = doc.getActiveSheet();
var sheetName = sheet.getName();
var cell = doc.getActiveSheet().getActiveCell();
var activeCellRow = cell.getRow();
var activeCellCol = cell.getColumn();
try {
var fullConnectionString = 'jdbc:' + DB_TYPE + '://' + HOST + ':' + PORT
var conn = Jdbc.getConnection(fullConnectionString, USERNAME, PASSWORD);
console.log('query :', query)
var stmt = conn.createStatement();
stmt.execute('USE ' + DATABASE);
var start = new Date();
var stmt = conn.createStatement();
stmt.setMaxRows(MAXROWS);
var rs = stmt.executeQuery(query);
} catch (e) {
console.log(e, e.lineNumber);
Browser.msgBox(e);
return false
}
var results = [];
cols = rs.getMetaData();
console.log("cols", cols)
var colNames = [];
var colTypes = {};
for (i = 1; i <= cols.getColumnCount(); i++) {
var colName = cols.getColumnLabel(i)
colTypes[colName] = { type: cols.getColumnTypeName(i), loc: i }
colNames.push(colName);
}
var rowCount = 1;
results.push(colNames);
while (rs.next()) {
curRow = rs.getMetaData();
rowData = [];
for (i = 1; i <= curRow.getColumnCount(); i++) {
rowData.push(rs.getString(i));
}
results.push(rowData);
rowCount++;
}
rs.close();
stmt.close();
conn.close();
console.log('results', results)
var colCount = results[0].length
var rowCount = results.length
var comment = "Updated on: " + (new Date()) + "\n" + "Query:\n" + query
if (options.omitColumnNames) {
results = results.slice(1)
rowCount -= 1
}
if (options.clearColumns && sheet.getLastRow() > 0) {
var startCellRange = sheet.getRange(startCell)
sheet.getRange(startCellRange.getRow(), startCellRange.getColumn(), sheet.getLastRow(), colCount).clearContent();
}
if (options.clearSheet) {
var startCellRange = sheet.getRange(startCell)
sheet.clear({ contentsOnly: true });
}
sheet.getRange(activeCellRow, activeCellCol, rowCount, colCount).clearContent();
sheet.getRange(activeCellRow, activeCellCol, rowCount, colCount).setValues(results);
var cell = sheet.getRange(activeCellRow, activeCellCol)
cell.clearNote()
cell.setNote(comment);
sheet.setActiveRange(sheet.getRange(activeCellRow + rowCount + 1, activeCellCol))
console.log('query success!, rows = ', rowCount - 1)
}
function runSqlFromSheet() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
var sql = doc.getRange('query!a2').getDisplayValue();
var options = {}
Logger.log('sql;', sql)
runSql(sql, options)
}
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('SeekWell Blog')
.addItem('Run SQL', 'runSqlFromSheet')
.addToUi();
}
function launch() {
var html = HtmlService.createHtmlOutputFromFile('sidebar')
.setTitle('SeekWell');
SpreadsheetApp.getUi()
.showSidebar(html);
}

I solved the problem. Not in the most elegant way, but it is a solution...
In order to run the script always on the same sheet, I substitute two variables.
The first variable to substitute is the following one:
var sheet = doc.getActiveSheet();
With this one (so it is always the first tab)
var sheet = doc.getSheets()[0];
Then the following one:
var cell = doc.getActiveSheet().getActiveCell();
With this one (so it is possible to define from which cell the script has to start, in this example from A1)
var cell = sheet.getRange('A1');

Related

Google Sheets App Script If Else not working

Test1 Sheet
Test2 Sheet
I want to compare News01 from Test1 A Column with Test2 A Column, and need to fetch the corresponding Test2 B column value
So the result should be Finance in Sheet1 B column
But If I use else, even when if statement is true it's going to else statement.
If I delete else statement, then if statement is working.
I don't know why when if statement is true, it's going to else statement by default
function test() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = sheet.getSheetByName("Test1");
var target_sheet = sheet.getSheetByName("Test2");
var lastRow = source_sheet.getLastRow();
var inputs = source_sheet.getRange('A' + lastRow).getValues().flat();
var days = target_sheet.getRange('A1:A').getValues().flat();
var codes = target_sheet.getRange('B1:B').getValues().flat();
inputs.forEach(function(input, count){
for(var i = 0; i < days.length; i++){
if(days[i].trim() == input.trim()){
source_sheet.getRange('B' + (count + source_sheet.getLastRow())).setValue(codes[i]);
Logger.log(codes[i]);
break;
}
else{
Logger.log("News not found")
}
}
});
}
You are logging News not found for each comparisons with all the rows of Test2, which is improper.
Instead, you should only log when the comparison is finished.
function test() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = sheet.getSheetByName("Test1");
var target_sheet = sheet.getSheetByName("Test2");
var lastRow = source_sheet.getLastRow();
var inputs = source_sheet.getRange('A' + lastRow).getValues().flat();
var days = target_sheet.getRange('A1:A').getValues().flat();
var codes = target_sheet.getRange('B1:B').getValues().flat();
inputs.forEach(function(input, count){
let found = false;
for(var i = 0; i < days.length; i++){
if(days[i].trim() == input.trim()){
source_sheet.getRange('B' + (count + source_sheet.getLastRow())).setValue(codes[i]);
Logger.log(codes[i]);
found = true;
break;
}
}
if (!found) { Logger.log("News not found"); }
});
}
Or simply,
function test() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = sheet.getSheetByName("Test1");
var target_sheet = sheet.getSheetByName("Test2");
var lastRow = source_sheet.getLastRow();
var inputs = source_sheet.getRange('A' + lastRow).getValues().flat();
var days = target_sheet.getRange('A1:A').getValues().flat();
var codes = target_sheet.getRange('B1:B').getValues().flat();
inputs.forEach(function(input, count){
for(var i = 0; i < days.length; i++){
if(days[i].trim() == input.trim()){
source_sheet.getRange('B' + (count + source_sheet.getLastRow())).setValue(codes[i]);
Logger.log(codes[i]);
return;
}
}
Logger.log("News not found");
});
}
function test() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss.getSheetByName("Sheet0");
var sh2 = ss.getSheetByName("Sheet1");
var lastRow = sh1.getLastRow();
var vs1 = sh1.getRange(1,1,sh1.getLastRow()).getValues().flat().map(e => e.trim());
var vs2 = sh2.getRange(1,1,sh2.getLastRow(),2).getValues();
vs2.forEach(r => {
if(~vs1.indexOf(r[0].trim())) {
Logger.log(r[1]);
}
});
}

Exception: Argument cannot be null: prompt Error in google Scripts

I have created a mail merge with a confirmation pop up in order to confirm the script has not been run recently, and also to double check the user intends to send emails. The script works perfectly, but annoyingly I receive the following error when run:
Stating; Exception: Argument cannot be null: prompt
I would like to get rid of this if possible. I attach my code.
//Creates a functional log of how many emails are sent and to how many branches
function confirmationWindow(){
var numberSent = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Validations').getRange('D3').getValue();
var numberOfVios = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Validations').getRange('D2').getValue();
var ui = SpreadsheetApp.getUi();
var ui = SpreadsheetApp.getUi();
ui.alert(
'You have succesfully sent ' + numberOfVios + ' violations, to ' + numberSent + ' branches.',)
}
function saveData() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Log');
var date = new Date()
var user = Session.getActiveUser().getEmail();
var range = ss.getRange("A:B")
var numberSent = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Validations').getRange('D2').getValue();
var branchNumber = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Validations').getRange('D3').getValue();
ss.appendRow([date,user,numberSent,branchNumber]);
}
var EMAIL_SENT = 'True';
function sendEmails() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Output');
var sheetI= SpreadsheetApp.getActive().getSheetByName('Input');
var sheetC = SpreadsheetApp.getActive().getSheetByName('Control');
var emailNum = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Validations').getRange('D3').getValue();
var startRow = 2;
var numRows = emailNum;
var dataRange = sheet.getRange(startRow, 1, numRows, 6);
sheetI.setTabColor("ff0000");
saveData()
var data = dataRange.getValues();
for (var i in data) {
var row = data[i];
var emailAddress = row[3];
var message = row[5] + "\n\nT\n\nT\n\nT";
var message = message.replace(/\n/g, '<br>');
var subject = row[4];
MailApp.sendEmail(emailAddress, subject, message,{htmlBody:message});
sheet.getRange(startRow + i - 18, 8).setValue(EMAIL_SENT);
sheetC.getRange(startRow + i - 18, 3).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
confirmationWindow()
}
function recentSend() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Log');
var lastR = sheet.getLastRow();
var recentRun = sheet.getRange('A' + lastR).getValue();
if(lastR=1){var recentlySent = 'False'}
var today = new Date().valueOf()
var recentDate = recentRun
var sec = 1000;
var min = 60 * sec;
var hour = 60 * min;
var day = 24 * hour;
var difference = today - recentDate
var hourDifference =Math.floor(difference%day/hour);
if (hourDifference > '12'){var recentlySent = 'False'}
else {var recentlySent = 'True'}
return(recentlySent)
}
function recentlySentTrue(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Log');
var lastR = sheet.getLastRow();
var recentUser = sheet.getRange('B' + lastR).getValue();
var recentQuant = sheet.getRange('C' + lastR).getValue();
var recentT = sheet.getRange('A' + lastR).getValue();
var recentSendLog = recentT.toString();
var recentTime = recentSendLog.slice(16,21)
var recentDate = recentSendLog.slice(0,11)
var ui = SpreadsheetApp.getUi();
var result = ui.alert(
'Are you sure you wish to send?',
'The user, ' + recentUser + ' ,recently sent ' + recentQuant + ' emails, at ' + recentTime + ' on ' + recentDate,
ui.ButtonSet.YES_NO);
if (result == ui.Button.YES) {
ui.alert(recentlySentFalse());
}
else {
}
}
function recentlySentFalse(){
var ui = SpreadsheetApp.getUi();
var ui = SpreadsheetApp.getUi();
var result = ui.alert(
'Are you Sure you wish to send?',
'Are you sure you want to send these violations?',
ui.ButtonSet.YES_NO);
if (result == ui.Button.YES) {
ui.alert(sendEmails());
} else {
}
}
function confirm(){
var recentlySent = recentSend();
if (recentlySent == 'True'){
recentlySentTrue()
}
else{recentlySentFalse()
}
}
Any help would be great.
Explanation:
Your ui.alert() calls that have function arguments such as recentlySentFalse() are not valid, since the functions do not have return statements, hence they return the default value of null. ui.alert() calls need at least a prompt message.
Solution 1:
Put the function call after the UI alert message:
if (result == ui.Button.YES) {
ui.alert('Sending message...');
recentlySentFalse();
}
Solution 2:
If you do not want excessive alerts, just remove ui.alert and call the function immediately.
if (result == ui.Button.YES) {
recentlySentFalse();
}
References:
Class UI | Apps Script

Increase my script perfomance and speed - Google apps script

I have data in "Raw data" sheet 45000 rows. So I created a function that whenever I run this function will group my data to pivot table may be in 500 rows and turn to new format but totally take more than 30 minutes.
function sumData() {
var spreadsheet = SpreadsheetApp.getActive();
var ss = SpreadsheetApp.getActive().getSheetByName('Raw data');
var sourceData = spreadsheet.getRange('A1:I100000');
var pivotTable = spreadsheet.getRange('A1').createPivotTable(sourceData);
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Pivot table'), true);
sourceData = spreadsheet.getRange('\'Raw data\'!A1:I100000');
pivotTable = spreadsheet.getRange('A1').createPivotTable(sourceData);
var pivotValue = pivotTable.addPivotValue(3, SpreadsheetApp.PivotTableSummarizeFunction.COUNTA);
pivotGroup = pivotTable.addRowGroup(6);
pivotGroup.showTotals(false).showRepeatedLabels();
pivotGroup = pivotTable.addRowGroup(2);
pivotGroup.showTotals(false).showRepeatedLabels();
pivotGroup = pivotTable.addRowGroup(3);
pivotGroup.showTotals(false);
var ss1 = SpreadsheetApp.getActive().getSheetByName('Pivot table');
var ss2 = SpreadsheetApp.getActive().getSheetByName('Summary data');
ss2.getRange('A:D').activate();
ss2.getActiveRangeList().clear({contentsOnly: true, skipFilteredRows: true});
ss2.setFrozenRows(1);
var value = [["Shop order", "Part number", "count Workstation", "Workstation"]];
var range = ss2.getRange("A1:D1");
range.setValues(value);
var data = ss1.getRange(1, 1).getValue()
for(var i = 1, j = 1; i < ss1.getLastRow()+1; i++) {
var normalCase = true
if(i!=1) {
if(ss1.getRange(i, 1).getValue() == data) {
normalCase = false
ss2.getRange(j, 3).setValue(ss1.getRange(i, 4).getValue()+ss2.getRange(j, 3).getValue()) // for update count workstation
ss2.getRange(j, 4).setValue(ss1.getRange(i, 3).getValue()+ " = " +ss1.getRange(i, 4).getValue()+ ", " +ss2.getRange(j, 4).getValue()) // for update value
} else {
j++}}
if(normalCase) {
ss2.getRange(j, 1).setValue(ss1.getRange(i, 1).getValue())
ss2.getRange(j, 2).setValue(ss1.getRange(i, 2).getValue())
ss2.getRange(j, 3).setValue(ss1.getRange(i, 4).getValue())
ss2.getRange(j, 4).setValue(ss1.getRange(i, 3).getValue()+ " = " +ss1.getRange(i, 4).getValue())}
data = ss1.getRange(i, 1).getValue()}
ss2.deleteRow(2);
var ui = SpreadsheetApp.getUi();
ui.alert('Summarize complete.');
};
Is there any way to make my code run faster or maybe just a way to make the script run in 10-15 minutes?

Google apps script to re-execute function using trigger

I am new to javascript or google apps script. I am using this function to import data into MySQL database from a google spreadsheet. This sheet has over 16000 records and I would want to breakdown the import into smaller batches of 2000 records. In the function createGASTrigger() how would I change my logic to grab first 2000 records from the spreadsheet in the first iteration and then the next 2000 records in second iteration and so on.
Function 1
var address = 'database_IP_address';
var rootPwd = 'root_password';
var user = 'user_name';
var userPwd = 'user_password';
var db = 'database_name';
var root = 'root';
var instanceUrl = 'jdbc:mysql://' + address;
var dbUrl = instanceUrl + '/' + db;
function myFunction() {
var stime = new Date();
var col1;
var col2;
var col3;
var dbconnection = Jdbc.getConnection(dbUrl, root, rootPwd);
var statement = dbconnection.createStatement();
var googlesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet1');
var data = googlesheet.getDataRange().getValues();
dbconnection.setAutoCommit(false)
for (var i = 1; i < data.length; i++) {
col1 = data[i][0];
col2 = data[i][1];
col3 = data[i][2];
var sql = "{call [dbo].[sp_googlesheetimport](?,?,?)}";
statement = dbconnection.prepareCall(sql);
statement.setString(1, col1);
statement.setString(2, col2);
statement.setString(3, col3);
statement.addBatch();
}
statement.executeBatch();
dbconnection.commit();
statement.close();
dbconnection.close();
var etime = new Date();
Logger.log('Exec time: ' + (etime.getTime() - stime.getTime()));
}
Function 2
function createGASTrigger() {
var varUserPropertiesService = PropertiesService.getUserProperties();
var varUserTriggerId = varUserPropertiesService.getProperty("myFunction");
var varUserTrigger = ScriptApp.getProjectTriggers();
for (var i in varUserTrigger) {
if (varUserTrigger[i].getUniqueId() == varUserTriggerId)
try
{
ScriptApp.deleteTrigger(varUserTrigger[i]);
}
catch(e)
{
Utilities.sleep(30000);
ScriptApp.deleteTrigger(varUserTrigger[i]);
}
}
myFunction();
var userProperties = PropertiesService.getUserProperties();
try
{
var nextTrigger = ScriptApp.newTrigger("myFunction").timeBased().after(1 * 120 * 1000).create();
}
catch(e)
{
Utilities.sleep(30000);
var nextTrigger = ScriptApp.newTrigger("myFunction").timeBased().after(1 * 120 * 1000).create();
}
}
I haven't tested this but I think this is a good starting point.
function myFunction() {
var stime = new Date();
var col1;
var col2;
var col3;
var dbconnection = Jdbc.getConnection(dbUrl, root, rootPwd);
var statement = dbconnection.createStatement();
var googlesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet1');
var data = googlesheet.getDataRange().getValues();
var itrows=1000;
var complete=false;
if(PropertiesService.getScriptProperties().getProperty('iteration'))
{
var iteration=Number(PropertiesService.getScriptProperties().getProperty('iteration') + 1);
}
else
{
var iteration=Number(PropertiesService.getScriptProperties().setProperty('iteration', 1));
}
var max=itrows * iteration;
var min=max - itrows + 1;
if(max>data.length-1)
{
max=data.length-1;
complete=true;
}
dbconnection.setAutoCommit(false)
for (var i=min;i<=max;i++)
{
col1 = data[i][0];
col2 = data[i][1];
col3 = data[i][2];
var sql = "{call [dbo].[sp_googlesheetimport](?,?,?)}";
statement = dbconnection.prepareCall(sql);
statement.setString(1, col1);
statement.setString(2, col2);
statement.setString(3, col3);
statement.addBatch();
}
statement.executeBatch();
dbconnection.commit();
statement.close();
dbconnection.close();
if(complete)
{
PropertiesService.getScriptProperties().deleteProperty('iteration');
}
var etime = new Date();
Logger.log('Exec time: ' + (etime.getTime() - stime.getTime()));
}
function createGASTrigger() {
var varUserPropertiesService = PropertiesService.getUserProperties();
var varUserTriggerId = varUserPropertiesService.getProperty("myFunction");
var varUserTrigger = ScriptApp.getProjectTriggers();
for (var i=0;i<varUserTrigger.length;i++)
{
if (varUserTrigger[i].getUniqueId() == varUserTriggerId)
{
ScriptApp.deleteTrigger(varUserTrigger[i]);
break;
}
}
var userProperties = PropertiesService.getUserProperties();
var nextTrigger = ScriptApp.newTrigger("myFunction").timeBased().after(1 * 120 * 1000).create();
}
Either store a page value in PropertiesService.getScriptProperties() or add a value to the spreadsheet for all rows as you add them

Handling timezones when extracting Calendar data to a Spreadsheet

I'm working on a little Google Apps Script that will export event data from a selected Google Calendar to a new Google Spreadsheet for a selected date range. One problem I'm having is that the times when copied over to the spreadsheet are off by three hours. Any suggestions as to how to handle this and display the event times correctly?
Here's my code so far: (It's a work-in-progress)
function doGet() {
var app = UiApp.createApplication();
var handler = app.createServerHandler("change");
var picker1 = app.createDatePicker().addValueChangeHandler(handler).setId("picker1");
var picker2 = app.createDatePicker().addValueChangeHandler(handler).setId("picker2");
var pickerpanel = app.createHorizontalPanel();
var panel = app.createVerticalPanel();
pickerpanel.add(picker1);
pickerpanel.add(picker2);
panel.add(pickerpanel);
var lb = app.createListBox(false).setId('lbCalSelId').setName('lbCalSelect');
lb.setVisibleItemCount(3);
var cals = CalendarApp.getAllCalendars();
for (var i=0; i<cals.length;i++) {
lb.addItem(cals[i].getName(),cals[i].getId());
}
panel.add(lb);
var button = app.createPushButton().setText("Export").setId("button");
var handler = app.createServerClickHandler('doExport').addCallbackElement(panel);
button.addClickHandler(handler);
panel.add(button);
app.add(panel);
return app;
}
function change(eventInfo) {
var app = UiApp.getActiveApplication();
if (eventInfo.parameter.picker1) {
UserProperties.setProperties({"DateRangeStart":eventInfo.parameter.picker1});
app.add(app.createLabel("Start date " + eventInfo.parameter.picker1));
}
else if (eventInfo.parameter.picker2) {
UserProperties.setProperties({"DateRangeEnd":eventInfo.parameter.picker2});
app.add(app.createLabel("End date" + eventInfo.parameter.picker2));
}
return app;
}
function doExport(eventInfo) {
var app = UiApp.getActiveApplication();
var calId = eventInfo.parameter.lbCalSelect;
var cal = CalendarApp.getCalendarById(calId);
var rangeStart = UserProperties.getProperty("DateRangeStart");
var rangeEnd = UserProperties.getProperty("DateRangeEnd");
app.add(app.createLabel("The button was clicked!"));
if (rangeStart && rangeEnd) {
app.add(app.createLabel("exporting..."));
var events = cal.getEvents(new Date(rangeStart), new Date(rangeEnd));
var eventsData = [];
var headerRow = ['Title','Start Time','End Time','Location','Description'];
for (var i=0; i < events.length; i++) {
var eventData = [];
eventData.push(events[i].getTitle());
eventData.push(events[i].getStartTime());
eventData.push(events[i].getEndTime());
eventData.push(events[i].getLocation());
eventData.push(events[i].getDescription());
eventsData.push(eventData);
}
var ss = SpreadsheetApp.create("Export of " + cal.getName() + " from " + rangeStart + " to " + rangeEnd);
var sheet = ss.getSheets()[0];
var destRange = sheet.getRange(1, 1, events.length, headerRow.length);
destRange.setValues(eventsData);
}
else {
app.add(app.createLabel("Range not specified."));
}
return app;
}
I figured it out.
I just set the timezone of the spreadsheet to the same timezone as the calendar and it worked like a charm.
ss.setSpreadsheetTimeZone(cal.getTimeZone());

Categories