Skip to content

Instantly share code, notes, and snippets.

@justinsbarrett
Last active January 25, 2022 07:51
Show Gist options
  • Save justinsbarrett/2ebae3bd1c0a1e3956bfb3338ac6aeda to your computer and use it in GitHub Desktop.
Save justinsbarrett/2ebae3bd1c0a1e3956bfb3338ac6aeda to your computer and use it in GitHub Desktop.
An Airtable script that will organize sequential events, where one starts when the previous one ends. See the Airtable community forum thread for screenshots and other notes: https://community.airtable.com/t/set-sequential-event-times/43722
// Allow user to select the table and view to use, and which fields will be affected
const config = input.config({
title: "Set Sequential Event Times",
description: `Update a collection of events that occur in sequence so that each new event starts when the previous event ends.
* Use the fixed start marker field (a checkbox field) to mark all records that have fixed start times.
* The first record in the selected view **must** have a fixed start time.
* Any record without a fixed start time will have its start time calculated based on the end time of the previous record.`,
items: [
input.config.table("eventsTable", {
label: "Events table",
description: "The table containing the event records"
}),
input.config.view("eventsOrderedView", {
label: "Event order view",
description: "The view where you have set the order for the event records",
parentTable: "eventsTable"
}),
input.config.field("startTimeField", {
label: "Start time field",
description: "The field to contain the event start time",
parentTable: "eventsTable"
}),
input.config.field("durationField", {
label: "Duration field",
description: "The field containing the event duration",
parentTable: "eventsTable"
}),
input.config.field("endTimeField", {
label: "End time field",
description: "The field to contain the event end time",
parentTable: "eventsTable"
}),
input.config.field("fixedCheckboxField", {
label: "Fixed start marker field",
description: "A checkbox field used to mark records that have fixed start times",
parentTable: "eventsTable"
})
]
})
const {eventsTable, eventsOrderedView, startTimeField, durationField, endTimeField, fixedCheckboxField} = config
/**
* Main function
* @param {readonly AirtableRecord[]} records
*/
async function main(records) {
let updates = []
let endTime
// Cycle through the records
for (let record of records) {
let update = {
id: record.id,
fields: {}
}
let start
// If this record has a fixed time, use it
if (record.getCellValue(fixedCheckboxField)) {
start = new Date(record.getCellValue(startTimeField))
// Otherwise use the previous end time
} else {
start = endTime
update.fields[startTimeField.name] = endTime
}
// Calculate the end time
if (start) {
endTime = new Date(start.getTime() + (record.getCellValue(durationField) * 1000))
update.fields[endTimeField.name] = endTime
updates.push(update)
}
}
// Update all records
while (updates.length > 0) {
await eventsTable.updateRecordsAsync(updates.slice(0, 50))
updates = updates.slice(50)
}
}
// Check for errors
let errors = [];
let query = await eventsOrderedView.selectRecordsAsync({
fields: [startTimeField, endTimeField, durationField, fixedCheckboxField]
});
// If no time is set for the first record, alert the user
if (!query.records[0].getCellValue(fixedCheckboxField) || !query.records[0].getCellValue(startTimeField)) {
errors.push(`The first record must be marked as fixed and have a set start time.`);
}
// Are any records are missing durations?
const missingDurations = query.records.filter(record => !record.getCellValue(durationField))
if (missingDurations.length) {
errors.push(`${missingDurations.length} record${missingDurations.length === 1 ? " does" : "s do"} not have a duration value set.`)
}
// Check that records marked as having fixed start times actually do
const missingFixedTimes = query.records.filter(record => record.getCellValue(fixedCheckboxField) && !record.getCellValue(startTimeField))
if (missingFixedTimes.length) {
errors.push(`${missingFixedTimes.length} fixed start time record${missingFixedTimes.length === 1 ? " does" : "s do"} not have a start time set.`)
}
// Check for correct field types
if (startTimeField.type !== "dateTime") {
errors.push(`Incorrect field type for the start time field. It should be a dateTime field.`);
}
if (endTimeField.type !== "dateTime") {
errors.push(`Incorrect field type for the end time field. It should be a dateTime field.`);
}
if (durationField.type !== "duration") {
errors.push(`Incorrect field type for the duration field.`);
}
if (fixedCheckboxField.type !== "checkbox") {
errors.push("Incorrect field type for fixed start marker field.")
}
// Display errors (if any); otherwise kick off the main function
if (errors.length) {
output.markdown("# ❌ ERROR ❌");
output.markdown("Please address the following issue(s) and re-run the script.")
output.markdown("* " + errors.join("\n* "));
} else {
await main(query.records);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment