Skip to content

Instantly share code, notes, and snippets.

@sliminality
Created February 17, 2018 21:51
Show Gist options
  • Save sliminality/a2e467f9770a53572c8334e203cb998c to your computer and use it in GitHub Desktop.
Save sliminality/a2e467f9770a53572c8334e203cb998c to your computer and use it in GitHub Desktop.
Google Sheets script for my ops keystone, inspired by http://sebastianmarshall.com/key-points-week-one
var config = {
templateSheetName: 'TEMPLATE',
dateFormat: 'ddd M/d',
sheetTitlePrefix: 'Week ',
daysPerSheet: 7,
showLatestOnLeft: true,
dateHeaderRow: 1,
};
// Get the first sheet from the given spreadsheet.
// If no spreadsheet is passed, defaults to active spreadsheet.
function getFirstSheet(ss) {
var ss = ss || SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
return sheet;
}
// Get the sheet labeled 'TEMPLATE'.
function getTemplateSheet(ss) {
var ss = ss || SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(config.templateSheetName);
return sheet;
}
// Return a record {row, column, value, cell} of the first non-frozen cell.
function getFirstNonFrozenCol(sheet) {
var sheet = sheet || SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var frozenCols = sheet.getFrozenColumns();
var totalCols = sheet.getLastColumn();
if (totalCols === frozenCols) {
throw new Error('getFirstNonFrozenCol: sheet ' + sheet.getSheetName() +
' has no non-frozen columns');
}
var row = config.dateHeaderRow;
var column = frozenCols + 1;
var cell = sheet.getRange(row, column, 1, 1);
var value = cell.getValue();
return {row: row, column: column, value: value, cell: cell};
}
// Get the most recent date column from the given sheet.
// Returns a Date object.
function getLatestDateCell(sheet) {
// If the most recent date is on the left, then we can just return the
// first non-frozen cell.
if (config.showLatestOnLeft) {
return getFirstNonFrozenCol(sheet);
}
var lastCol = sheet.getLastColumn();
var cell = sheet.getRange(config.dateHeaderRow, lastCol, 1, 1);
var value = cell.getValue();
return {row: config.dateHeaderRow, column: lastCol, cell: cell, value: value};
}
// Parse the week number from a sheet title.
// e.g. "Week 15" -> 15
function parseWeekNumber(str) {
var re = new RegExp('^' + config.sheetTitlePrefix + '([0-9]+)');
var match = str.match(re);
if (!match || match.length < 2) {
throw new Error('parseWeekNumber: could not parse week from title '.concat(str));
}
var week = parseInt(match[1], 10);
return week;
}
// Create a new weekly sheet, and set it to the active sheet.
function addWeeklySheet(ss) {
var ss = ss || SpreadsheetApp.getActiveSpreadsheet();
var sheet = getFirstSheet(ss);
// Get the week number from the latest sheet's title.
var lastWeek = parseWeekNumber(sheet.getSheetName());
var week = lastWeek + 1;
var name = config.sheetTitlePrefix.concat(week);
// Get the date range from the latest sheet's leftmost column.
var lastDate = getLatestDateCell(sheet);
if (!(lastDate.value instanceof Date)) {
throw new Error('addWeeklySheet: expected Date object, got ' + lastDate.value);
}
var dates = generateNextDates(lastDate.value, config.daysPerSheet);
// Show dates from left to right.
if (config.showLatestOnLeft) {
dates.reverse();
}
ss.insertSheet(name, 0, {template: getTemplateSheet()});
var newSheet = getFirstSheet(ss);
return addDates(newSheet, dates);
}
// Takes a Date object and a number of days to generate, and returns
// a list of Date objects representing that many days afterwards.
function generateNextDates(start, n) {
var startMs = start.getTime();
var MS_PER_DAY = 1000 * 60 * 60 * 24;
var result = [];
for (var i = 1; i < n + 1; i += 1) {
var delta = i * MS_PER_DAY;
var date = new Date(startMs + delta);
result.push(date);
}
return result;
}
// Insert the given range of dates in the non-frozen header cells of the
// given sheet.
function addDates(sheet, dates) {
var lastCol = sheet.getLastColumn();
var available = lastCol - sheet.getFrozenColumns();
if (available < dates.length) {
sheet.insertColumnsAfter(lastCol, dates.length - available);
}
var range = sheet.getRange(1, sheet.getFrozenColumns() + 1, 1, dates.length);
range.setValues([dates]).setNumberFormat(config.dateFormat);
return sheet;
}
// Create menu.
function onOpen(e) {
SpreadsheetApp.getUi()
.createMenu('Keystone')
.addItem('Add weekly sheet', 'addWeeklySheet')
.addToUi();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment