Skip to content

Instantly share code, notes, and snippets.

@AlexKorovyansky
Last active August 29, 2015 14:07
Show Gist options
  • Save AlexKorovyansky/fad23755d9244b986988 to your computer and use it in GitHub Desktop.
Save AlexKorovyansky/fad23755d9244b986988 to your computer and use it in GitHub Desktop.
GAS Stuff for transformation Google Form response to Mailchimp subscriber
String.prototype.startsWith=function(str){return this.indexOf(str) == 0;}
String.prototype.trim=function(){return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');};
var tag = "devfest14_registration";
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var metaSheetResponses = makeMetaSheet(spreadSheet.getSheetByName("Responses"), 2, 0);
var metaRowFlatRange = metaSheetResponses.getMetaRowFlatRange(1);
var emailPositionInDataRange = metaRowFlatRange.positionOf("EMAIL^");
var invitationCodePositionInRange = metaRowFlatRange.positionOf("MERGE_INVCODE^");
var invitationCodeStatusPositionInRange = metaRowFlatRange.positionOf("MERGE_INVSTAT");
var responseStatusPositionInRange = metaRowFlatRange.positionOf(":STATUS");
function onOpen() {
var menu = [
{name: "Enable", functionName: "enableAutoProcessing"},
{name: "Disable", functionName: "disableAutoProcessing"},
{name: "Force", functionName: "autoProcessLastResponse"}
];
SpreadsheetApp.getActive().addMenu("Auto Processing", menu);
var menu = [
{name: "Delete All Triggers", functionName: "deleteAllTriggers"},
{name: "Clear Logs", functionName: "clearLogs"}
];
SpreadsheetApp.getActive().addMenu("Debug", menu);
}
function enableAutoProcessing() {
ScriptApp.newTrigger("autoProcessLastResponse")
.forSpreadsheet(spreadSheet)
.onFormSubmit()
.create();
}
function disableAutoProcessing() {
var allTriggers = ScriptApp.getScriptTriggers();
for(var i = 0; i < allTriggers.length; ++i) {
if (allTriggers[i].getHandlerFunction().equals("autoProcessLastResponse")) {
ScriptApp.deleteTrigger(allTriggers[i]);
}
}
}
function deleteAllTriggers() {
var allTriggers = ScriptApp.getScriptTriggers();
for(var i = 0; i < allTriggers.length; ++i) {
ScriptApp.deleteTrigger(allTriggers[i]);
}
}
function autoProcessLastResponse() {
var timestamp = new Date().getTime();
debug(tag, "[" + timestamp + "]: Starting of autoProcessLastResponse...");
try {
validate();
processResponse(-1);
} catch(e) {
metaSheetResponses.getDataRowFlatRange(-1).getRange().setBackground("#777777");
metaSheetResponses.getDataRowFlatRange(-1).getCell(responseStatusPositionInRange).setValue(JSON.stringify(e));
debug(tag, "EXCEPTION" + JSON.stringify(e));
// TODO: send exception stacktrace to admin
}
debug(tag, "[" + timestamp + "]: Finished of autoProcessJustAddedResponse.");
}
function validate() {
debug(tag, "mcFieldsRange = " + metaRowFlatRange.toPrettyString());
debug(tag, "emailPositionInRowsRange = " + emailPositionInDataRange);
debug(tag, "invitationCodePositionInRange = " + invitationCodePositionInRange);
debug(tag, "invitationCodeStatusPositionInRange = " + invitationCodeStatusPositionInRange);
debug(tag, "responseStatusPositionInRange = " + responseStatusPositionInRange);
// TODO: add exception if something == -1
}
function processResponse(dataRowPosition) {
var newResponseRowFlatRange = metaSheetResponses.getDataRowFlatRange(dataRowPosition);
debug(tag, "newResponseRowFlatRange = " + newResponseRowFlatRange.toPrettyString());
var newResponseEmailAsString = String(newResponseRowFlatRange.getValue(emailPositionInDataRange));
debug(tag, "newResponseEmailAsString = " + newResponseEmailAsString);
var newResponseInvitationCodeAsString = String(newResponseRowFlatRange.getValue(invitationCodePositionInRange));
debug(tag, "newResponseInvitationCodeAsString = " + newResponseInvitationCodeAsString);
// Trim spaces in response
for (var i = 0; i < metaRowFlatRange.length(); ++i) {
var metaRowItemAsString = String(metaRowFlatRange.getValue(i));
if (metaRowItemAsString.indexOf('^') > -1) {
newResponseRowFlatRange.getCell(i).setValue(String(newResponseRowFlatRange.getValue(i)).trim());
}
}
// Process Invitation Code
var newResponseInvitationCodeStatus = getStatusForInvitationCode(newResponseInvitationCodeAsString);
debug(tag, "newResponseInvitationCodeStatus = " + newResponseInvitationCodeStatus);
var newResponseInvitationCodeStatusCell = newResponseRowFlatRange.getCell(invitationCodeStatusPositionInRange);
if (newResponseInvitationCodeStatus == InvitationCodeStatus.NOT_PROVIDED) {
newResponseInvitationCodeStatusCell.setValue("");
} else if (newResponseInvitationCodeStatus == InvitationCodeStatus.CORRECT) {
newResponseInvitationCodeStatusCell.setValue("correct");
} else {
newResponseInvitationCodeStatusCell.setValue("wrong");
}
// Send request to MailChimp API
var payload = {
"apikey": "apikey-us3",
"id": "95d64c3075",
"email": {
"email": newResponseEmailAsString
},
"merge_vars": {
},
"double_optin": false,
"send_welcome": true
}
for (var i = 0; i < metaRowFlatRange.length(); ++i) {
var metaRowItemAsString = String(metaRowFlatRange.getValue(i));
if (metaRowItemAsString.startsWith("MERGE_")) {
var key = metaRowItemAsString.replace("MERGE_", "").replace("^", "");
payload.merge_vars[key] = String(newResponseRowFlatRange.getValue(i));
}
}
debug(tag, "request = " + JSON.stringify(payload));
var options = {
"method" : "post",
"muteHttpExceptions": true,
"payload" : JSON.stringify(payload)
};
var rawResponse = UrlFetchApp.fetch("https://us3.api.mailchimp.com/2.0/lists/subscribe.json", options);
debug(tag, "response = " + rawResponse.getResponseCode() + ": " + rawResponse.getContentText());
if (rawResponse.getResponseCode() == 200 || rawResponse.getResponseCode() == 500) {
var response = JSON.parse(rawResponse.getContentText());
if (rawResponse.getResponseCode() == 200) {
// success
newResponseRowFlatRange.getCell(responseStatusPositionInRange).setValue("SUCCESS");
newResponseRowFlatRange.getRange().setBackground("#E0FFF5");
if (newResponseInvitationCodeStatus == InvitationCodeStatus.CORRECT) {
redeemInvitationCode(newResponseInvitationCodeAsString);
}
debug(tag, "SUCCESS");
} else if (rawResponse.getResponseCode() == 500) {
// mailchimp reject
newResponseRowFlatRange.getCell(responseStatusPositionInRange).setValue(response.error);
newResponseRowFlatRange.getRange().setBackground("#FFD6CC");
debug(tag, "MailChimp Reject");
}
} else {
// http error
newResponseRowFlatRange.getCell(responseStatusPositionInRange).setValue("HTTP ERROR " + rawResponse.getResponseCode());
newResponseRowFlatRange.getRange().setBackground("#FAA5A5");
debug(tag, "HTTP Error");
}
}
String.prototype.trim=function(){return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');};
var InvitationCodeStatus = {
CORRECT: 200,
NOT_PROVIDED: 204,
NOT_FOUND: 403,
GONE: 410,
};
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var metaSheetInvites = makeMetaSheet(spreadSheet.getSheetByName("Invites"), 1, 0);
var invitationCodePositionInDataRange = 0;
var inviteBalancePositionInDataRange = 1;
function getStatusForInvitationCode(invitationCode) {
if (!invitationCode) {
return InvitationCodeStatus.NOT_PROVIDED;
}
var invitationCodeAsString = String(invitationCode).trim();
if (invitationCodeAsString.length == 0) {
return InvitationCodeStatus.NOT_PROVIDED;
}
var matchedInvitesFlatRows = metaSheetInvites.filterDataRows(function(flatDataRow) {
var flatDataRowInvitationCodeAsString = String(flatDataRow.getValue(invitationCodePositionInDataRange));
return invitationCodeAsString.equals(flatDataRowInvitationCodeAsString);
});
if (matchedInvitesFlatRows.length == 0) {
return InvitationCodeStatus.NOT_FOUND;
} else {
for (var i = 0; i < matchedInvitesFlatRows.length; ++i) {
var matchedInvitationCodeCellFlatRow = matchedInvitesFlatRows[i].getCell(inviteBalancePositionInDataRange);
var matchedInvitationCodeBalance = matchedInvitationCodeCellFlatRow.getValue();
if (matchedInvitationCodeBalance > 0) {
return InvitationCodeStatus.CORRECT;
}
}
return InvitationCodeStatus.GONE;
}
}
function redeemInvitationCode(invitationCode) {
if (!invitationCode) {
return;
}
var invitationCodeAsString = String(invitationCode).trim();
var matchedInvitesFlatRows = metaSheetInvites.filterDataRows(function(flatDataRow) {
var flatDataRowInvitationCodeAsString = String(flatDataRow.getValue(invitationCodePositionInDataRange));
return invitationCodeAsString.equals(flatDataRowInvitationCodeAsString);
});
if (matchedInvitesFlatRows.length > 0) {
for (var i = 0; i < matchedInvitesFlatRows.length; ++i) {
var matchedInvitationCodeBalanceCell = matchedInvitesFlatRows[i].getCell(inviteBalancePositionInDataRange);
var matchedInvitationCodeBalance = matchedInvitationCodeBalanceCell.getValue();
if (matchedInvitationCodeBalance > 0) {
matchedInvitationCodeBalanceCell.setValue(matchedInvitationCodeBalance - 1);
}
}
}
}
function fooGetStatusForInvitationCode() {
debug("foo", getStatusForInvitationCode("c"));
}
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var metaSheetLogs = makeMetaSheet(spreadSheet.getSheetByName("Logs"), 1, 0);
function debug(tag, message) {
log(tag, message, "debug");
}
function info(tag, message) {
log(tag, message, "info");
}
function warning(tag, message) {
log(tag, message, "warning");
}
function error(tag, message) {
log(tag, message, "error");
}
function log(tag, message, level){
metaSheetLogs.getSheet().appendRow([new Date(), level, tag, String(message)]);
}
function clearLogs() {
if (metaSheetLogs.hasData()) {
metaSheetLogs.getDataRange(0, 0, metaSheetLogs.getDataRowsNumber(), metaSheetLogs.getDataColumnsNumber()).clear();
}
}
function makeMetaSheet(sheet, metaRowsNum, metaColumnsNum) {
var metaSheet = {
sheet: sheet,
metaRowsNum: metaRowsNum,
metaColumnsNum: metaColumnsNum,
metaRowsRanges: [],
metaColumnsRanges: [],
getSheet: function() {
return this.sheet;
},
getMetaRowsNumber: function() {
return this.metaNumRows;
},
getMetaColumnsNumber: function() {
return this.metaNumColumns;
},
getDataRowsNumber: function() {
return this.sheet.getLastRow() - this.metaRowsNum;
},
getDataColumnsNumber: function() {
return this.sheet.getLastColumn() - this.metaColumnsNum;
},
hasData: function() {
return this.getDataRowsNumber() > 0 && this.getDataColumnsNumber() > 0;
},
getMetaRowFlatRange: function(index) {
return makeFlatRange(this.sheet.getRange(index + 1, this.metaColumnsNum + 1, 1, this.sheet.getLastColumn() - this.metaColumnsNum));
},
getMetaColumnFlatRange: function(index) {
return makeFlatRange(this.sheet.getRange(this.metaRowsNum + 1, index + 1, this.sheet.getLastRow() - this.metaRowsNum, 1));
},
getDataRange: function(rowOffset, columnOffset, numRows, numColumns) {
var startRow = this.translateRowPositionDataToAbsolute(rowOffset);
var startColumn = this.translateColumnPositionDataToAbsolute(columnOffset);
return this.sheet.getRange(startRow, startColumn, numRows, numColumns);
},
getDataRowFlatRange: function(position) {
return makeFlatRange(this.getDataRange(position, 0, 1, this.getDataColumnsNumber()));
},
getDataColumnFlatRange: function(position) {
return makeFlatRange(this.getDataRange(0, position, this.getDataRowsNumber(), 1));
},
filterDataRows: function(filter) {
var ranges = [];
for (i = 0; i < this.getDataRowsNumber(); ++i) {
var flatDataRow = this.getDataRowFlatRange(i);
if (filter(flatDataRow)) {
ranges.push(flatDataRow);
}
}
return ranges;
},
translateRowPositionDataToAbsolute: function(position) {
return position >= 0 ? (this.metaRowsNum + position + 1) : (this.sheet.getLastRow() + position + 1);
},
translateColumnPositionDataToAbsolute: function(position) {
return position >= 0 ? (this.metaColumnsNum + position + 1) : (this.sheet.getLastColumn() + position + 1);
},
// TODO:
toPrettyString: function() {
}
}
return metaSheet;
}
function makeFlatRange(range) {
var isRow = false;
if (range.getWidth() == 1 && range.getHeight() >= 1) {
isRow = false;
} else if (range.getHeight() == 1 && range.getWidth() >= 1) {
isRow = true;
} else {
throw "Impossible to flattern data range, width:" + range.getWidth() + ", height:" + range.getHeight();
}
return {
getRange: function() {
return range;
},
getValue: function(position) {
if (isRow) {
return range.getValues()[0][position]
} else {
return range.getValues()[position][0];
}
},
getCell: function(position) {
if (isRow) {
return range.offset(0, position, 1, 1);
} else {
return range.offset(position, 0, 1, 1);
}
},
positionOf: function(data) {
for (var i = 0; i < this.length(); ++i) {
var item = this.getValue(i);
if (item.equals(data)) {
return i;
}
}
return -1;
},
toPrettyString: function() {
var result = [];
for (var i = 0; i < this.length(); ++i) {
if (i > 0) {
result.push(", ");
}
result.push(this.getValue(i));
}
return result.join("");
},
length: function() {
if (isRow) {
return range.getWidth();
} else {
return range.getHeight();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment