Skip to content
This repository was archived by the owner on Feb 2, 2021. It is now read-only.
This repository was archived by the owner on Feb 2, 2021. It is now read-only.

Google Script to Sync with Calendar #98

@fstab

Description

@fstab

I wrote a little Google script that creates a cfp calendar from your list. To try it, go to drive.google.com, click New -> Google Apps Script, copy the code into the editor, select syncCfpCalendar as the function to be executed and click the play button. Reload calendar.google.com and you should see a new cfp calendar. Google also provides triggers so you can run this nightly (click Edit -> Current Project's Triggers).

If you think this is useful, feel free to publish it, put it in a readme, provide a public calendar, whatever you want.

var sources = [
  {
    rawUrl: 'https://raw.githubusercontent.com/softwaremill/it-cfp-list/master/README.md',
    link: 'https://github.com/softwaremill/it-cfp-list'
  }
]

// name of the calendar to be updated / created.
var calendarName = 'cfp';

// All calendar events are tagged, so we don't accidentally delete unrelated events.
var tag = {
  key: 'generated-by',
  value: 'cfp-calendar-sync'
};

// main function
function syncCfpCalendar() {
  var cal = openCalendar();
  for each (source in sources) {
    var markdownContent = loadMarkdownContent(source.rawUrl);
    processMarkdownContent(markdownContent, source.link, cal);
  }
}

function loadMarkdownContent(url) {
  var response = UrlFetchApp.fetch(sources[0].rawUrl);
  if (response.getResponseCode() != 200) {
    throw new Error("Got HTTP " + response.getResponseCode() + " when fetching " + cfpListUrl);
  }
  return response.getContentText();
}

function processMarkdownContent(markdownContent, link, cal) {
  var tableStart = false
  for each (line in markdownContent.split(/\r?\n/)) {
    if (!tableStart) {
      if (line.match(/\|-+\|-+\|-+\|-+\|-+\|-+\|/)) {
        tableStart = true;
      }
    } else {
      if (!startsWith(line, "|")) {
        return; // end of table
      }
      processTableLine(line, link, cal)
    }
  }
}

function processTableLine(line, link, cal) {
  if (!line.match(/\|.*\|.*\|.*\|.*\|.*\|.*\|/)) {
    Logger.log("skipping line " + line)
  }
  line = line.substring(1, line.length-1); // strip "|" from the beginning and end.
  fields = line.split("|")
  var cfpStart = startDate(fields[0].trim());
  var cfpEnd = endDate(fields[0].trim());
  var confStart = startDate(fields[1].trim());
  var confEnd = endDate(fields[1].trim());
  var location = fields[2].trim();
  var name = stripLink(fields[3].trim());
  var keywords = fields[5].trim();
  if (cfpStart && cfpEnd && cfpStart <= cfpEnd) {
    createEvent('CFP ' + name, cfpStart, cfpEnd, keywords, location, link, cal)
  }
  if (confStart && confEnd && confStart <= confEnd) {
    createEvent(name, confStart, confEnd, keywords, location, link, cal)
  }
}

function createEvent(name, start, endInclusive, keywords, location, link, cal) {
  // end date is exclusive, so add 1 day
  var evt = cal.createAllDayEvent(name, start, addDays(endInclusive, 1), {description: keywords + ', see ' + link, location: location});
  evt.setTag(tag.key, tag.value);
}

function startDate(dateString) {
  // Date formats used in the CFP List:
  // 2018.12.08
  // 2018.11.27-29
  // 2018.11.30-12.01
  // For the start date, we can ignore stuff following the '-' character.
  match = dateString.match(/^([0-9]{4})\.([0-9]{2})\.([0-9]{2})(-.*)?$/)
  if (match) {
    return dateFromCaptureGroups(match, 1, 2, 3);
  }
}

function endDate(dateString) {
  // Date formats used in the CFP List:
  // 2018.12.08
  // 2018.11.27-29
  // 2018.11.30-12.01
  match = dateString.match(/^([0-9]{4})\.([0-9]{2})\.([0-9]{2})$/)
  if (match) {
    return dateFromCaptureGroups(match, 1, 2, 3);
  }
  match = dateString.match(/^([0-9]{4})\.([0-9]{2})\.([0-9]{2})-([0-9]{2})$/)
  if (match) {
    return dateFromCaptureGroups(match, 1, 2, 4);
  }
  match = dateString.match(/^([0-9]{4})\.([0-9]{2})\.([0-9]{2})-([0-9]{2})\.([0-9]{2})$/)
  if (match) {
    return dateFromCaptureGroups(match, 1, 4, 5);
  }
}

function dateFromCaptureGroups(match, yearIndex, monthIndex, dayIndex) {
  return new Date(parseInt(match[yearIndex], 10), parseInt(match[monthIndex], 10) - 1, dayInt = parseInt(match[dayIndex], 10));
}

function stripLink(markdownString) {
  // "[devoxx](https://devoxx.be)" -> "devoxx" 
  return markdownString.replace(/\[([^\]]+)\]\([^)]+\)/, '$1')
}

function startsWith(s, c) {
  return s.indexOf(c) == 0;
}

function addYears(date, years) {
  var result = new Date(date);
  result.setFullYear(date.getFullYear()+years);
  return result;
}

function subtractYears(date, years) {
  return addYears(date, 0-years);
}

function addDays(date, days) {
  var result = new Date(date);
  result.setDate(result.getDate() + days);
  return result;
}

function openCalendar() {
  var calendars = CalendarApp.getCalendarsByName(calendarName);
  switch (calendars.length) {
    case 0:
      // not found. create a new calendar.
      return CalendarApp.createCalendar(calendarName, {summary: 'CFP calendar generated from ...'});
    case 1:
      // found. clear existing events, because events will be re-created
      var today = new Date(),
          twoYearsAgo = subtractYears(today, 2), // API requires a time range for getEvents. take a 4 year time range so we get all events.
          twoYearsFromNow = addYears(today, 2),
          events = calendars[0].getEvents(twoYearsAgo, twoYearsFromNow);
      for (var i = 0; i < events.length; i++) {
        if (events[i].getTag(tag.key) === tag.value) {
          events[i].deleteEvent()
        }
      }
      return calendars[0]
    default:
      throw new Error('found ' + calendars.length + ' calendars with name ' + calendarName + '.');
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions