Sometimes this is my life. But it’s so satisfying when you write a program that saves you time! Here is an example.
The Problem:
For several years at Hopkins I have been involved in teaching a large (500+ person) introductory Biostatistics class. This class usually has a team of 12-15 teaching assistants, who together staff the twice-daily office hours. TAs are generally assigned to “random” office-hours, with the intention that students in the class get a variety of view-points if, for example, they can only come to Tuesday afternoon office hours.
When I started the course, the random office-hours were assigned by our very dedicated administrative coordinator, who sent them out via email on a spreadsheet. Naturally, there were several individual TA conflicts, which would result in more emails, which would lead to version control problems and — ultimately — angry students at empty office hours. Additionally, assigning TAs to random time-slots for an 8-week class was a nightmare for the administrative assistant.
The Solution:
I really wanted us to start using Google Calendar, so that students could easily load the calendars into their own personal calendars, and that changes on the master calendar would automatically be pushed to students’ calendars. So, in order to achieve this goal, I wrote a function in R that would automatically generate a random calendar for the TAs, write these calendars to CSV documents that I could then upload to a master Google Calendar. It has worked wonderfully!
My general philosophy with this project was that it is much easier to create a draft and then modify it, than it is to create something from scratch that fits every single parameter. So, I controlled for as many factors as I reasonably could (holidays, weekends, days when we need double the TAs, etc.). Then, as TAs have conflicts or other problems arise, I can just go back and modify the calendar to fit individual needs. You’d be amazed — even with each TA having several constraints on his or her time, I generally only need to make a few modifications to the calendar before the final draft. Randomness works!
There are a few tricks that made this function work seamlessly. My code below depends on the chron package. To assign TAs, it’s best to think about the dates for your class as a sequence of slots that need to be filled, omitting weekend days, holidays and other days off, and accommodating for days when you might need twice as many TAs (for example right before exams when office hours are flooded).
dts <- seq.dates(start_date,end_date) weekdts <- weekdays(dts) dates <- dts[weekdts!="Sat"&weekdts!="Sun"&!as.character(dts)%in%no_TA_dates] dates <- c(dates,double_days) dates <- sort(c(dates,dates))
Now that you have the sequence of dates, you need to randomly fill them. I accomplish this by dividing the sequence of dates into bins that are equal to the number of TAs, and then randomly sampling the TA names without replacement for each of the bins. This might leave a remainder of un-filled days at the end, so you have to make up for that as well.
len_dates <- length(dates) len_tas <- length(ta_names) mult <- floor(len_dates/len_tas) temp <- rep(NA,len_tas) ta_sched <- 0 for(i in 1:mult){ temp<-sample(ta_names,len_tas,replace=FALSE) ta_sched<-c(ta_sched,temp) } ta_sched <- ta_sched[-1] rem <- length(dates)-length(ta_sched) temp <- sample(ta_names,rem,replace=FALSE) ta_sched<-c(ta_sched,temp)
nms<-c('Subject','Start Date','Start Time','End Date','End Time','All Day Event','Description','Location','Private') len_names <- length(nms) mat <- matrix(nrow=len_dates,ncol=len_names) mat <- data.frame(mat) colnames(mat)<-nms mat$Subject <- ta_sched mat$"Start Date" <- dates mat$"End Date" <- dates mat$"All Day Event" <- "False" mat$Description <- "Biostat 621 TA Schedule" mat$Private <- "False" start_times <- c("12:15:00 PM","2:30:00 PM") end_times <- c("1:15:00 PM","3:20:00 PM") mat$"Start Time" <- start_times mat$"End Time" <- end_times mat$Location <- ta_location for(i in 1:len_tas){ filename<-paste(ta_names[i],".csv",sep="") temp<-mat[mat$Subject==ta_names[i],] write.csv(temp,file=filename,quote=FALSE,row.names=FALSE) }
The Workflow:
- Using this function (github — use the example template which calls the gen_cal.R function), create a random calendar for each of the TAs. These calendars will be saved as CSV documents which can then be uploaded to Google Calendar. There are options for the starting day of class, ending day of class, days off from class, days when you want twice as many TAs (for example, right before an exam), and some extra info like the location of the office hours.
- On Google Calendar, create a calendar for each of the TAs (I might name mine “Hilary Biostat 621” for example). Make the calendar public (this helps with sharing it to TAs and posting it online)
- For each TA, import the CSV document created in step 1 by clicking “import calendar” under the “other calendars” tab in Google Calendar. For the options, select the CSV document for a specific TA, and for the “calendar” option, select the calendar you created in step 2 for that specific TA. (Life will be easiest if you only perform this step once, because if you try to import a calendar twice, you’ll get error messages that you’ve already imported these events before.)
- Bonus step: Once all the calendars are imported, embed the calendars onto a website (keep secret from students in the class if you don’t want them to know when certain TAs are working!). This serves two purposes. First, it serves as the master calendar for all of the TAs and instructors. Second, by distributing it to TAs, they can add their calendar to their personal Google Calendar by clicking the plus icon on the bottom right corner. Your final beautiful product will look something like this! (Note that I also created a calendar that has important class dates like the quizzes and exams).
The Result:
Time spent developing and implementing: ~3 hours. Time spent each semester re-creating for new TAs: ~30 minutes. Time saved by myself, the administrative coordinator, and all of the TAs — and instead spent feeling awesome: infinity.