From an Excel schedule to Outlook Calendar with PowerShell

(it was actually quite pleasant!)

I am starting studies at Georgia Tech (yay!) and while the semester didn’t even start yet, it already feels a bit overwhelming. Tons of emails, activities, and lots of calculus. More than I was expecting, actually!

Now, one of the ways I deal with such stress is by getting organized. Sometimes I wonder if that’s just a form of procrastination (organizing stuff vs. actually dealing with it), but it helps. When my email inbox is under control, my calendar is up to date, I just feel better. period.

So it probably won’t be surprising that when an email arrived announcing some additional “TA sessions”, with 3 sessions every week until the end of the semester, I had an immediate urge to *organize it*. Except that the schedule came in a form of an Excel spreadsheet…

Not exactly “Outlook-ready”…

Now, I’ve spent the past few days refreshing Python skills, and setting up a new laptop (WSL2 is great, it looks like I won’t be dual-booting Windows/Ubuntu anymore.. feels slightly morally wrong, though). In all that process, I had a chance to play around with PowerShell, and I was wondering how hard would it be to create .ics files/events in Outlook calendar for all these sessions. For someone who wasn’t really familiar with PowerShell control structures, equality operators (“-ne”, that’s basically Lithuanian..), the answer is ~1 hour hard. That was surprisingly fast.

Most of the credit goes to the author of the New-IcsEvent PowerShell function which generates .ics files based on supplied parameters. In my case, I did not bother with reading the Excel file either – turned out that working with lists/strings in PowerShell is very intuitive.

$weeks = "08-23-21
08-30-21
09-06-21
09-13-21
09-20-21
09-27-21
10-04-21
10-11-21
10-18-21
10-25-21
11-01-21
11-08-21
11-15-21
11-22-21
11-29-21
12-06-21".split("`n") 
#weeks is now an array - just like in Python 
#(except, of course, unlike the rest of the world, we don't use \n for line breaks..)

After some fiddling (and extracting locations and session titles to similar arrays as above), all my code had to do was pretty much this:

for ($i = 0; $i -lt $weeks.Count; $i++) {
    $start_week_date = [datetime]::parseexact($weeks[$i], 'MM-dd-yy', $null)
    if ($tuesdays[$i] -ne "") {
        $day = $start_week_date.AddDays(1).ToString("yyyy-MM-dd") + " 09:15"
        $topic = "MSA TA: " + $tuesdays[$i]
        Write-Output $day
        New-IcsEvent -Path C:\users\aurim\Desktop -Subject $topic -Location 'location' -StartDate $day -ShowAs Free -Duration '01:00:00'
    }
    if ($thursdays[$i] -ne "") {
        $day = $start_week_date.AddDays(3).ToString("yyyy-MM-dd") + " 09:15"
        $topic = "MSA TA: " + $thursdays[$i]
        Write-Output $day
        New-IcsEvent -Path C:\users\aurim\Desktop -Subject $topic -Location 'location' -StartDate $day -ShowAs Free -Duration '01:00:00'
    }
    if ($fridays[$i] -ne "") {
        $day = $start_week_date.AddDays(4).ToString("yyyy-MM-dd") + " 13:00"
        $topic = "MSA TA: " + $fridays[$i]
        Write-Output $day
        New-IcsEvent -Path C:\users\aurim\Desktop -Subject $topic -Location 'location' -StartDate $day -ShowAs Free -Duration '01:00:00'
    }    
}

Now, this created a bunch of .ics files on my desktop. 44 to be precise. Turns out that .ics can contain multiple events, however. So all I needed to do was to concatenate all the files together, skipping the first 4 and the last line. That would be quite simple on Unix: ls *.ics " | tail -n +2 | head -n -1 | cat > my_master_schedule.ics. I have a feeling that with some fiddling with awk/sed, it’s very likely possible to even ensure that the first 4 lines of the first file and the last line of the very last file are not skipped, giving us a fully valid .ics file in one go.

BEGIN:VCALENDAR
VERSION:2.0
METHOD:PUBLISH
PRODID:-//JHP//We love PowerShell!//EN
BEGIN:VEVENT
UID: ab4d0283-885b-433f-b269-7ce6b841df46
CREATED: 20210820T005313Z
DTSTAMP: 20210820T005313Z
LAST-MODIFIED: 20210820T005313Z
CLASS:Public
CATEGORIES:
SEQUENCE:0
DTSTART: 20210826T131500Z
DTEND: 20210826T141500Z
DESCRIPTION: 
SUMMARY: MSA TA: R Intro part I
LOCATION: IC215
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR

Nevertheless, I was curious how difficult would it be to do the same in PowerShell. It took some searching, but it turned out it is pretty straightforward, too. By the way, that’s one thing I did not like about PowerShell – there are tons of answers on the web, but quite a few of them are clearly low quality, even on StackOverflow. Reminds me a bit of looking for good VBA guidance. Curse of the masses, I suppose?

ls *.ics | get-content | where {$_.readcount -gt 4 -and $_ -ne "END:VCALENDAR"} > MSA_TA_SESSIONS.icsx                   

As you can see, I cheated a bit as I think get-content reads line-by-line, and so I could not refer to total line count (there definitely are ways to do that, though, it just seemed to require more steps). Either way, I had all I needed.

Now it was time to finish up the exercise by opening Notepad and adding those missing first 4 and the very last line manually. No point in writing code for that.