Skip to content

Instantly share code, notes, and snippets.

@vcaraulean
Last active June 26, 2017 15:11
Show Gist options
  • Save vcaraulean/6173831 to your computer and use it in GitHub Desktop.
Save vcaraulean/6173831 to your computer and use it in GitHub Desktop.
This is a Google Apps Script Engine that imports data from Google Spreadsheet to Trello. This script was used to import SoftShake'13 conference submissions from the spreadsheets to Trello. So, it's highly specific for SoftShake and it's one-off thing. Still, may be used as a base for more complex things. The conference had 2 spreadsheets, for Fr…
/**
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function uploadTracksToTrello() {
// Security settings
// Requesting token: https://trello.com/1/authorize?key=your app key&name=softshake+upload&expiration=never&response_type=token&scope=read,write
ScriptProperties.setProperty("appKey", "your app key");
ScriptProperties.setProperty("token", "your token");
// EN/FR specific parameters
var commonBoardId,
language,
titleColumnHeader,
firstNameColumnHeader,
lastNameColumnHeader,
emailColumnHeader;
var spreadSheetName = SpreadsheetApp.getActiveSpreadsheet().getName();
if (spreadSheetName == "Soft-shake 2013 - Call for paper"){
ScriptProperties.setProperty("cardColor", "blue");
commonBoardId = "51e1d47947e06e3654003631";
language = "EN";
titleColumnHeader = "Title";
firstNameColumnHeader = "First Name";
lastNameColumnHeader = "Last Name";
emailColumnHeader = "Email";
}
else if (spreadSheetName == "Soft-shake 2013 - Appel à contribution"){
ScriptProperties.setProperty("cardColor", "green");
commonBoardId = "51e1d461f6480cff2e002418";
language = "FR";
titleColumnHeader = "Titre";
firstNameColumnHeader = "Nom";
lastNameColumnHeader = "Prénom";
emailColumnHeader = "Mail";
}
else{
Logger.log("Unknown document name");
}
// spreadsheet
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var startRow = 2;
var endRow = sheet.getLastRow();
var totalColumns = sheet.getLastColumn();
// Finding column indexes
var firstRowData = sheet.getRange(1, 1, 1, totalColumns).getValues()[0];
var trackColumnIndex = firstRowData.indexOf("Track");
var titleColumnIndex = firstRowData.indexOf(titleColumnHeader);
var descriptionColumnIndex = firstRowData.indexOf("Description");
var firstNameIndex = firstRowData.indexOf(firstNameColumnHeader);
var lastNameIndex = firstRowData.indexOf(lastNameColumnHeader);
var emailIndex = firstRowData.indexOf(emailColumnHeader);
// Loading main board (lists and tracks) and all track boards
var commonBoard = getCompleteBoard(commonBoardId);
var trackBoards = [];
var boardsAndTracksLookup = getTrackToBoardLookup();
for (var i = 0; i < boardsAndTracksLookup.length; i++){
var board = getCompleteBoard(boardsAndTracksLookup[i].board);
trackBoards.push(board);
}
// storing in one array all the cards from all track boards
var cardsFromAllTracks = [];
for (var i = 0; i < trackBoards.length; i++){
cardsFromAllTracks = cardsFromAllTracks.concat(trackBoards[i].cards);
}
var rows=sheet.getRange(startRow, 1, endRow-startRow+1, totalColumns).getValues();
for (var i = 0; i < rows.length; i++){
var rowData = rows[i];
var title = rowData[titleColumnIndex].trim();
Logger.log("Processing talk: " + title);
var description = rowData[descriptionColumnIndex];
var track = rowData[trackColumnIndex];
var author = "**" + rowData[firstNameIndex] + " " + rowData[lastNameIndex] + " (" + rowData[emailIndex] + ")**\n\n";
description = author + description;
// find the card in submissions board. if missing, creating it.
var cardFound = findCard(commonBoard.cards, title);
if (cardFound === false){
createCard(commonBoard, track, title, description);
}
// finding the board for track
var trackBoardId = getBoardForTrack(track);
var trackBoard;
for (var j = 0; j < trackBoards.length; j++) {
if (trackBoards[j].id === trackBoardId){
trackBoard = trackBoards[j];
break;
}
}
// check that a talk with specific title exists in any of track boards
// When not found we'll create the card in respective track board
var cardFoundInOneOfTrackBoards = findCard(cardsFromAllTracks, title);
if (cardFoundInOneOfTrackBoards === false){
var listName = "Submitted talks (" + language + ")";
createCard(trackBoard, listName, title, description);
}
}
};
function createCard(board, listName, title, description){
var listId = findList(board.lists, listName);
if (listId == "undefined"){
var newList = createNewListInBoard(board.id, listName);
board.lists.push(newList);
listId = newList.id;
}
createTrelloCard(title, description, listId);
}
function createTrelloCard(cardName, cardDesc, listID){
// limiting the length of description as it throws an error
// Error is caused by the Google's App Script engine not accepting URLs longer than 2048 chars
var cutDescriptionAt = 1000;
var description = cardDesc;
if (cardDesc.length > cutDescriptionAt){
description = cardDesc.slice(0, cutDescriptionAt) + "...";
Logger.log("Description too long. Title: " + cardName);
}
var url = constructTrelloURL("cards") + "&name=" + encodeURIComponent(cardName) + "&desc=" + encodeURIComponent(description) + "&idList=" + listID + "&labels=" + ScriptProperties.getProperty("cardColor");
try{
var resp = UrlFetchApp.fetch(url, {"method": "post"});
}
catch(e){
Logger.log("Failed to create card: " + cardName);
Logger.log(e);
return;
}
// whole description has been posted?
if (description.length == cardDesc.length){
return;
}
// If description is too long will add the rest of it as a comment to the card.
var response = Utilities.jsonParse(resp.getContentText());
var cardId = response.id;
var comment = "*(descripton continued)*\n\n..." + cardDesc.slice(cutDescriptionAt, cardDesc.length);
createNewComment(cardId, comment);
}
function createNewComment(cardId, comment){
var url = constructTrelloURL("cards/"+ cardId + "/actions/comments") + "&text=" + encodeURIComponent(comment);
var resp = UrlFetchApp.fetch(url, {"method": "post"});
}
function findCard(cardList, title){
for(var i = 0; i < cardList.length; i++){
if (cardList[i].name === title)
return true;
}
return false;
}
function findList(listList, listName){
for(var i = 0; i < listList.length; i++){
if (listList[i].name === listName)
return listList[i].id;
}
return;
}
function getTrackToBoardLookup(){
// Getting track's board from track's name.
// The boards should be created before running the script and IDs copied in this structure
var trackToBoardLookup = [
{ board : "51e1dd4a7c544a24540019e3", names : ["Agilité (Scrum, XP, Lean)", "Agile (Scrum, XP, Lean)"] },
{ board : "51e1ea27d4ba1d1a6f0019a0", names : ["Incubateur (NoSQL, cloud, ...)", "Incubator (NoSQL, cloud, ...)"] },
{ board : "51e1dd5e0a2f2ebf59003fb5", names : ["Microsoft", "Microsoft"] },
{ board : "51e1ea45f5d09728050061a9", names : ["FunProg", "FunProg"] },
{ board : "51e1df400e9aaaea37002480", names : ["Java (Scala, Groovy, ...)", "Java (Scala, Groovy, ...)"] },
{ board : "51e1df684056f8b80a001a66", names : ["BigData", "BigData"] },
{ board : "51e1ea07015991931e005130", names : ["Javascript", "Javascript"] },
{ board : "51e1e9ed42f0718954006041", names : ["Ruby", "Ruby"] },
{ board : "51e1ea6364c4575a1f003b64", names : ["Mobilité (iPhone, iPad, Android, ...)", "Mobility (iPhone, iPad, Android)"]},
{ board : "51ed07c42935727036000ad2", names : ["Gamification", "Gamification"]},
{ board : "51e1e97267dddcc033000d1a", names : [""]}
];
return trackToBoardLookup;
}
function getBoardForTrack(trackName){
var trackToBoardLookup = getTrackToBoardLookup();
for (var i = 0; i < trackToBoardLookup.length; i++){
if (trackToBoardLookup[i].names.indexOf(trackName) != -1)
return trackToBoardLookup[i].board;
}
// when not found - return fallback board
return "51e1e97267dddcc033000d1a";
}
function createNewListInBoard(boardId, name){
var url = constructTrelloURL("boards/"+ boardId + "/lists") + "&name=" + encodeURIComponent(name);
var resp = UrlFetchApp.fetch(url, {"method": "post"});
return Utilities.jsonParse(resp.getContentText());
}
function getCompleteBoard(boardId)
{
var url = constructTrelloURL("boards/" + boardId) + "&lists=open&cards=visible";
var resp = UrlFetchApp.fetch(url, {"method": "get"});
var values = Utilities.jsonParse(resp.getContentText())
return values;
}
function constructTrelloURL(baseURL){
return "https://api.trello.com/1/"+ baseURL +"?key="+ScriptProperties.getProperty("appKey")+"&token="+ScriptProperties.getProperty("token");
}
// Helper method, used to manually discover the IDs of boards
function listOrganizationBoards(){
var url = constructTrelloURL("organizations/softshake13/boards");
var resp = UrlFetchApp.fetch(url, {"method": "get"});
var values = Utilities.jsonParse(resp.getContentText())
Logger.log(values);
}
/**
* Adds a custom menu to the active spreadsheet, containing a single menu item
* for invoking the readRows() function specified above.
* 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 : "Upload to Trello", functionName : "uploadTracksToTrello" },
];
sheet.addMenu("Trello", entries);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment