I have a google apps script like the following:
// Initialize DB variables
var address = 'my-address'
var user = 'my-user-name'
var password = 'my-password'
var db = 'my-db'
var dbUrl = 'jdbc:mysql://' + address + '/' + db
function testUtf8() {
// Generate an SQL query
var conn = Jdbc.getConnection(dbUrl, user, password)
var stmt = conn.createStatement();
var results = stmt.executeQuery('SELECT * FROM my_table WHERE col_name=\'中文\';')
// Display query result
var colCount = results.getMetaData().getColumnCount()
while (results.next()) {
var colCount = results.getMetaData().getColumnCount() // No result is retured even though there is a value of `中文` in col_name.
for (var col = 0; col < colCount; col++) {
Logger.log(results.getString(col + 1))
}
}
// Clean up
stmt.close();
conn.close();
}
The SQL query fails to find the data even if I have a value of 中文 in col_name in the table.
When I change the query from SELECT * FROM my_table WHERE col_name='中文'; to SELECT * FROM my_table WHERE col_name='abc';, it successfully returns the data.
So this must be a problem of encoding.
How can I successfully execute an SQL query which contains Chinese character?
Changing
var dbUrl = 'jdbc:mysql://' + address + '/' + db to var dbUrl = 'jdbc:mysql://' + address + '/' + db
to
var dbUrl = 'jdbc:mysql://' + address + '/' + db + '?useUnicode=true&characterEncoding=UTF-8'
solves the problem.
It tells jdbc to use utf-8 encoding.
Related
Given that I have a Google sheet similar to this:
ID;username,email,date_created;
hdJNGDyd;user;me#example.com;12/03/2020 4:20:22
...
Is there a way to determine the formats of the Sheet columns and list them?
eg.
ID - string(8)
username - varchar(34)
email - varchar(255)
date_created - datetime
or map the sheet structure to a SQL query similar to:
CREATE table MyTable
`ID` varchar(8) NOT NULL
...
The length could be for example maximum length of the values in the particular column.
I would create a tab with a table listing the data dictionary
However, sheets has a TYPE() function you can use. If you also wanted to get the max length that would require some additional calculations.
So as you can see there is no date type and only a number type. Much different than what SQL offers.
In some of my projects I use google app script to get the schema from the MySQL database and then act accordingly
function getColTypes(conn, table) {
var stmt = conn.createStatement();
var query = "SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='" + DATABASE + "' AND TABLE_NAME='" + table + "'";
var rs = stmt.executeQuery(query);
var colTypes = [];
while (rs.next()) {
colTypes.push(rs.getString(1));
}
rs.close();
stmt.close();
return colTypes;
}
And this one for the names
function getColNames(conn, table) {
var stmt = conn.createStatement();
var query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='" + DATABASE + "' AND TABLE_NAME='" + table + "'";
var rs = stmt.executeQuery(query);
var colNames = [];
while (rs.next()) {
colNames.push(rs.getString(1));
}
rs.close();
stmt.close();
return colNames;
}
Hey this is my first question on StackOverflow.
I've been struggling with this code for two days and tried to move the loops up and down but nothing happened. The code doesn't show any error but i don't get a mail.
What I'm trying to do is to send multiple messages (sheet: buysignals) to multiple users (sheet: Sheet1). It should appear that everyone gets each email as the only receiver. f.ex. you get a new mail if there is a text in the Buysignals sheet.
Anyone here who can help me how to fix it?
function sendBuySignal() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Buysignals");
const dataRange = sheet.getRange("A2:R17");
const data = dataRange.getValues();
data.forEach(function (rowData) {
const ticker = rowData[0];
// Stock info
const kurtosis = rowData[1];
const sigma = rowData[2];
const mCap = rowData[17];
const std = rowData[4];
const timeStamp = rowData[6];
const buyPrice = rowData[8];
const buyIndicator = rowData[16];
// Send Signal Mail
if (buyIndicator > 0) {
const greeting = 'The stock ' + ticker + ',\n'
const buyMessage = 'Is at ' + timeStamp + ' in the buy zone at ' + buyPrice + '. With a kurtosis of ' + kurtosis + ', sigma ' + sigma + ', mcap in bill ' + mCap + '.';
const greatJobMessage = 'Hurry up! Go buy it :)';
const message = [greeting, buyMessage, greatJobMessage].join('\n');
const subject = 'Test: New Buy ' + ticker;
// Fetch the email address
const emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
const n=emailRange.getLastRow();
for (var i = 2; i > n+1 ; i++ ) {
const emailAddress = emailRange.getRange(i,1).getValue();
}
MailApp.sendEmail(emailAddress, subject, message);
}
return
})
}
The issue is related to looping
The syntax for (var i = 2; i > n+1 ; i++ ) { cannot work
Assuming that the last row is bigger than the start row (that is bigger than 2) the condition i > n+1 will never be fulfilled, so you will not enter the loop - instead you should specify i < n
It is n and not n+1 because you start with the second row instead of the first (i = 2)
MailApp.sendEmail(emailAddress, subject, message); is located outside of the for loop
This means that in each loop the function overwrites the emailAddress and after exiting the loop you will send a single email - to the last saved email address
This issue can be solved by inserting MailApp.sendEmail(emailAddress, subject, message); inside the for loop
Sample:
function sendBuySignal() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Buysignals");
const dataRange = sheet.getRange("A2:R17");
const data = dataRange.getValues();
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var n = sheet1.getLastRow();
const emails = sheet1.getRange("A2:A" + n).getValues();
data.forEach(function (rowData) {
const ticker = rowData[0];
// Stock info
const kurtosis = rowData[1];
const sigma = rowData[2];
const mCap = rowData[17];
const std = rowData[4];
const timeStamp = rowData[6];
const buyPrice = rowData[8];
const buyIndicator = rowData[16];
// Send Signal Mail
if (buyIndicator > 0) {
Logger.log("buyIndicator > 0");
const greeting = 'The stock ' + ticker + ',\n'
const buyMessage = 'Is at ' + timeStamp + ' in the buy zone at ' + buyPrice + '. With a kurtosis of ' + kurtosis + ', sigma ' + sigma + ', mcap in bill ' + mCap + '.';
const greatJobMessage = 'Hurry up! Go buy it :)';
const message = [greeting, buyMessage, greatJobMessage].join('\n');
const subject = 'Test: New Buy ' + ticker;
// Fetch the email address
for (var i = 0; i < emails.length ; i++ ) {
var emailAddress = emails[i][0];
Logger.log("email: " + emailAddress);
MailApp.sendEmail(emailAddress, subject, message);
}
}
})
}
Note that for this sample I allowed myself to retrieve all emails before entering the loop. This is more efficient than multiple calls of getRange() within each loop iteration.
I'm using GAS to create a contact lookup, where the function parameters build a query that is used in AdminDirectory.Users.List . This problem only happens when the orgTitle and orgDepartment parameters are used. When I copy and paste the Logger output in place of the q variable, the query works. If I used the q variable, it fails.
function listAllUsersBase(name = null, familyName = null, givenName = null, orgTitle = 'Help Desk', orgDepartment = null) {
var aQuery = 'query:\''
if (name !== null){
Logger.log('Name: ' + name);
var aQuery = aQuery + 'name:' + name + ' ';
} if (familyName !== null){
Logger.log('familyName: ' + familyName);
var aQuery = aQuery + 'familyName:' + familyName + ' ';
} if (givenName !== null){
Logger.log('givenName: ' + givenName);
var aQuery = aQuery + 'givenName:' + givenName + ' ';
} if (orgTitle !== null){
Logger.log('orgTitle: ' + orgTitle);
var aQuery = aQuery + 'orgTitle:\\\'' + orgTitle + '\\\' ';
} if (orgDepartment !== null){
Logger.log('orgDepartment: ' + orgDepartment)
var aQuery = aQuery + 'orgDepartment:\\\'' + orgDepartment + '\\\' ';
}
var aQuery = aQuery + '\''
var q = aQuery.toString();
Logger.log(q);
var pageToken;
var page;
do {
page = AdminDirectory.Users.list({
domain: 'yourdomain.com',
query: q,
orderBy: 'givenName',
maxResults: 100,
pageToken: pageToken
});
var users = page.users;
if (users) {
for (var i = 0; i < users.length; i++) {
var user = users[i];
Logger.log('%s (%s)', user.name.fullName, user.primaryEmail);
}
} else {
Logger.log('No users found.');
}
pageToken = page.nextPageToken;
} while (pageToken);
}
There are a number of issues with your script.
First, you do not need to re-declare the aQuery variable. The first declaration of var aQuery = ... is sufficient for your use-case.
Second, query is not a valid field for a search clause so it is not necessary (see documentation with list of valid fields available to search clauses). To clarify, when you first declare your aQuery variable you initialized it as var aQuery = 'query:\'';. Here, query is being used as a field in a search clause. However, it is not a valid option in this context.
Third, the position of the opening single-quotes on the field values for orgTitle and orgDepartment are incorrect, the opening escaped single-quote should precede the escaped backslash.
So, instead of:
var aQuery = aQuery + 'orgTitle:\\\'' + orgTitle + '\\\' ';
you should have:
var aQuery = aQuery + 'orgTitle:\'\\' + orgTitle + '\\\' ';
I’m trying to automate work routines with google forms, php and mysql. I did the following software architecture.
There is the form named as S, it is the static one, since I created it manually using GUI. Form Sform has the form submit trigger which puts data into the my mysql database.
After that data is processed by the php script which finally calls google script, which is published as a web application.
This script gets get parameters and parses them, gets new processed data from my database and programmatically builds forms depending on the selected data, finally it adds a trigger to dynamically (programmatically) created form or forms (let's name them as Dform).
The problem is that the function FormApp.getActiveForm() which is coded in the trigger of programmatically created form returns form id of the static form (which is manually created).
Here the code of static form Sform submit trigger:
function SaveData() {
var form = FormApp.getActiveForm();
var formResponses = form.getResponses();
var formResponse = formResponses[formResponses.length-1];
var email = formResponse.getRespondentEmail();
var itemResponses = formResponse.getItemResponses();
var workDay = null;
var workFiles = null;
for (var j = 0; j < itemResponses.length; j++) {
var itemResponse = itemResponses[j];
if (itemResponse.getItem().getTitle() == 'Pick the date of files you uploading') {
workDay = itemResponse.getResponse().toString();
}
if (itemResponse.getItem().getTitle() == 'Choose log files to be uploaded') {
workFilesIds = itemResponse.getResponse();
}
}
var dbUrl = '...';
var user = '...';
var userPwd = '...';
var conn = Jdbc.getConnection(dbUrl, user, userPwd);
var stmt = conn.prepareStatement('select worker_id from in_workers where worker_name = ?');
stmt.setString(1, email);
var results = stmt.executeQuery();
var worker_id = -1;
while (results.next()) {
worker_id = results.getString(1);
}
results.close();
if (worker_id == -1) {
stmt = conn.prepareStatement('INSERT INTO in_workers '
+ '(worker_name) values (?)', 1);
stmt.setString(1, email);
stmt.executeUpdate();
var res = stmt.getGeneratedKeys();
res.beforeFirst();
res.next();
worker_id = res.getInt(1);
}
var fName = '';
var fBlob = new Array(workFilesIds.length);
for (var j = 0; j < workFilesIds.length; j++) {
var file = DriveApp.getFileById(workFilesIds[j]);
fName = fName + '\n\r' + file.getName();
fBlob[j] = file;
stmt = conn.prepareStatement('INSERT INTO test_dep_files '
+ '(worker_id, file_name, file_date, file_body) values (?, ?, ?, ?)');
stmt.setString(1, worker_id);
stmt.setString(2, file.getName());
stmt.setDate(3, Jdbc.parseDate(workDay));
stmt.setBytes(4, file.getBlob().getBytes());
stmt.executeUpdate();
}
conn.close();
GmailApp.sendEmail('...', 'Control workplace form completed'
, 'User ' + email + ' uploaded data on ' + workDay
+ ' file list: ' + fName,
{attachments: fBlob, name: fName});
}
Here the code of script published as web application:
function doGet(e) {
var worker_id = e.parameter['worker_id'];
var form_trig_func_name = e.parameter['form_trig_func_name'];
var dbUrl = '...';
var user = '...';
var userPwd = '...';
var conn = Jdbc.getConnection(dbUrl, user, userPwd);
stmt = conn.prepareStatement('select form_id from in_workers where worker_id = ?');
stmt.setString(1, worker_id);
var results = stmt.executeQuery();
var form_id;
results.beforeFirst();
while (results.next()) {
form_id = results.getString(1);
}
results.close();
var form;
try {
form = FormApp.openById(form_id);
var items = form.getItems();
var l = items.length;
for (var j = 0; j < l; j++) {
form.deleteItem(0);
}
} catch (e) {
form = FormApp.create('Enter additional products data');
}
stmt = conn.prepareStatement('select fc_id, fc_name_sys, fc_date from test_dep_fc t1'
+ ', test_dep_files t2 where t1.file_id = t2.file_id and fc_name_real is null '
+ 'and worker_id = ?');
stmt.setString(1, worker_id);
var results = stmt.executeQuery();
results.beforeFirst();
while (results.next()) {
form.addTextItem()
.setTitle(results.getString(2) + ' от ' + results.getString(3))
.setHelpText(results.getString(1));
}
results.close();
form_id = form.getId();
var allTriggers = ScriptApp.getUserTriggers(form);
var l = allTriggers.length;
for (var i = 0; i < l; i++)
ScriptApp.deleteTrigger(allTriggers[i]);
allTriggers = ScriptApp.getProjectTriggers();
l = allTriggers.length;
for (var i = 0; i < l; i++) {
if (allTriggers[i].getHandlerFunction() === 'saveData' + form_trig_func_name) {
ScriptApp.deleteTrigger(allTriggers[i]);
}
}
ScriptApp.newTrigger('saveData'+form_trig_func_name)
.forForm(form)
.onFormSubmit()
.create();
var form_url = form.getPublishedUrl();
stmt = conn.prepareStatement('update in_workers set form_id = ?, form_url = ? where worker_id = ?');
stmt.setString(1, form_id);
stmt.setString(2, form_url);
stmt.setString(3, worker_id);
stmt.execute();
conn.close();
return HtmlService.createHtmlOutput(form_id + '\n\r' + form_trig_func_name);
}
This is the code of dynamically (programmatically) created forms Dforms:
function saveData2() {
var form = FormApp.getActiveForm();
var formResponses = form.getResponses();
var formResponse = formResponses[formResponses.length-1];
var itemResponses = formResponse.getItemResponses();
var dbUrl = '...';
var user = '...';
var userPwd = '...';
var conn = Jdbc.getConnection(dbUrl, user, userPwd);
var fc_names_real = '';
var fc_ids = '';
var fc_names_sys = '';
for (var j = 0; j < itemResponses.length; j++) {
var itemResponse = itemResponses[j];
var fc_id = itemResponse.getItem().getHelpText();
var fc_name_sys = itemResponse.getItem().getTitle();
var fc_name_real = itemResponse.getResponse().toString();
fc_names_real = fc_names_real + ' ' + fc_name_real;
fc_ids = fc_ids + ' ' + fc_id;
fc_names_sys = fc_names_sys + ' ' + fc_name_sys;
stmt = conn.prepareStatement('update test_dep_fc '
+ 'set fc_name_real = ? where '
+ 'fc_id = ?');
stmt.setString(1, fc_name_real);
stmt.setString(2, fc_id);
stmt.executeUpdate();
}
conn.close();
GmailApp.sendEmail('...', 'Control workplace form2 completed'
, 'Product serial nums: ' + fc_names_real
+ '\n\rProducts sys nums: ' + fc_names_sys
+ '\n\rProducts ids: ' + fc_ids
);
}
function saveData3() {
the same code as for saveData2
}
function saveData4() {
the same code as for saveData2
}
function saveData5() {
the same code as for saveData2
}
...
So the problem is that in saveData2 first code instruction returns wrong form id. It always returns form id of Sform.
Please help me with this, I'm ready to think that I have found a google forms bug.
Thanks a lot, Yaroslav
EDIT 1
hi #tehhowch, thank you, I did try, but, unfortunately, following code
function saveData2(e) {
//var form_id = e.source.getId();
...
returns wrong form id as well, it returns Sform id.
Finally this issue was solved next day it was written here. The solution is as simple as it is stupid one.
In the script published as web application code I add following instructions after form creation code. I save trigger function name to its properties.
PropertiesService.getDocumentProperties().setProperty(
'saveData'+form_trig_func_name, form_id);
Code to dynamically (programmatically) create forms Dforms was rewritten. When corresponding trigger fires I just take a formId in earlier saved property value.
function saveData2(e) {
//var form_id = e.source.getId();
var form_id = PropertiesService.getDocumentProperties().getProperty('saveData2');
saveDataAll (form_id, 2);
}
function saveData3() {
var form_id = PropertiesService.getDocumentProperties().getProperty('saveData3');
saveDataAll (form_id, 3);
}
function saveData4() {
var form_id = PropertiesService.getDocumentProperties().getProperty('saveData4');
saveDataAll (form_id, 4);
}
function saveData5() {
var form_id = PropertiesService.getDocumentProperties().getProperty('saveData5');
saveDataAll (form_id, 5);
}
function saveData6() {
var form_id = PropertiesService.getDocumentProperties().getProperty('saveData6');
saveDataAll (form_id, 6);
}
function saveData7() {
var form_id = PropertiesService.getDocumentProperties().getProperty('saveData7');
saveDataAll (form_id, 7);
}
function saveData8() {
var form_id = PropertiesService.getDocumentProperties().getProperty('saveData8');
saveDataAll (form_id, 8);
}
function saveData9() {
var form_id = PropertiesService.getDocumentProperties().getProperty('saveData9');
saveDataAll (form_id, 9);
}
function saveData10() {
var form_id = PropertiesService.getDocumentProperties().getProperty('saveData10');
saveDataAll (form_id, 10);
}
function saveData11() {
var form_id = PropertiesService.getDocumentProperties().getProperty('saveData11');
saveDataAll (form_id, 11);
}
function saveDataAll (form_id, saveData)
{
var form = FormApp.openById(form_id);
...
I have not more then ten users of this functional and such workaround works for me. So the problem is closed.
I'm trying to create a function that takes a data range/specified range in my Google spreadsheet and inserts every row that isn't blank into a MySQL table.
I want to take column range A:V in "Sheet One" and insert them to the "mysql_table" table. I'm new to using JDBC google scripts and below is what I've come up with so far, but I can't get it to work with my current knowledge.
function putData() {
var conn = Jdbc.getConnection(dbUrl, user, userPwd);
conn.setAutoCommit(false);
var ss = SpreadsheetApp.getActiveSpreadsheet(),
sheet = ss.getSheetByName("Sheet One"),
range = sheet.getDataRange(),
values = range.getValues(),
row;
var start = new Date();
function writeManyRecords() {
for (var r=1; r<values.length; r++) {
if( values[r].join('') === '' ) return 'Blank row encountered';
row = values[r];
var i = 0;
var stmt = conn.prepareStatement('INSERT INTO mysql_table '
+ '(field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13, field14, field15, field16, field17, field18, field19, field20, field21, field22) values (' + start + row[i] + ')');
stmt.addBatch();
var batch = stmt.executeBatch();
conn.commit();
conn.close();
var end = new Date();
}
}
}
Anyone have any ideas on how I can achieve this?
I came up with a solution and I'm posting it here for anyone else looking for a way of writing to MySQL from Google Sheets. Here's a simplified version:
function putData() {
var conn = Jdbc.getConnection(dbUrl, user, userPwd);
var stmt = conn.createStatement();
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 0; i < data.length; i++) {
var sql = "INSERT INTO test (test, num) VALUES ('" + data[i][0] + "'," + data[i][1] + ")";
var count = stmt.executeUpdate(sql,1)
}
stmt.close();
conn.close();
}