Scheduling tasks on a rotating semester basis

| categories: python | tags:

Let us say we have a list of tasks labeled task a through k. We want to schedule these tasks on a rotating basis, so that some tasks are done in even years and some tasks are done in odd years. Within those years, some tasks are done in the Fall, and some are done in the spring. This post explores how to code those tasks so we can figure out which tasks should be done in some part of some year.

We break the problem down like this. A year is an even year if mod(year,2)=0, and it is odd if mod(year,2)=1. So for a year, we have a bit of information. Now, since there are two times of the year we will do the tasks, we can assign this as another bit, e.g. FALL=0, and SPRING=1. Now, we have the following possibilities:

year time period binary code decimal number
2013 Fall 10 2
2014 Spring 01 1
2014 Fall 00 0
2015 Spring 11 3

And then the cycle will repeat. So, if we code each task with an integer of 0, 1, 2 or 3, we can say in a given year and time period whether a task should be completed. If 2 * mod(year, 2) + period_code is equal to the code on the task, then it should be executed.

Now, we need to start the task sequence. Let us say we start in the Fall of 2013. That is an odd year, so year % 2 = 1, and we use a tag of 0 to represent the Fall semester, giving an overall binary code of 10 which is equal to 2, so all tasks labeled 2 should be executed.

We will assign the codes to each task by enumerating a string of letters, and giving the task a code of mod(letter index, 4). That will loop through the tasks assigning codes of 0, 1, 2 or 3 to each task.

So to schedule these we will loop through a list of years, calculate the code for each year and time perid, and then filter the list of tasks with that code.

tasks = [(letter, i % 4) for i,letter in enumerate('abcdefghijk')]

print 'tasks = ',tasks

SEMESTERS = (('FALL',0), ('SPRING',1))

for year in [2013, 2014, 2015, 2016, 2017, 2018]:
    for semester,i in SEMESTERS:
        N = 2 * (year % 2) + i
        print '{0} {1:8s}: {2}'.format(year, semester,
                                    [x[0] for x in 
                                     filter(lambda x: x[1]==N,
                                            tasks)])
tasks =  [('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 0), ('f', 1), ('g', 2), ('h', 3), ('i', 0), ('j', 1), ('k', 2)]
2013 FALL    : ['c', 'g', 'k']
2013 SPRING  : ['d', 'h']
2014 FALL    : ['a', 'e', 'i']
2014 SPRING  : ['b', 'f', 'j']
2015 FALL    : ['c', 'g', 'k']
2015 SPRING  : ['d', 'h']
2016 FALL    : ['a', 'e', 'i']
2016 SPRING  : ['b', 'f', 'j']
2017 FALL    : ['c', 'g', 'k']
2017 SPRING  : ['d', 'h']
2018 FALL    : ['a', 'e', 'i']
2018 SPRING  : ['b', 'f', 'j']

This leads to each task being completed every other year. We could also write a function and filter by list comprehension.

tasks = [(letter, i % 4) for i,letter in enumerate('abcdefghijk')]

FALL = 0
SPRING = 1

def execute_p(year, semester, task):
    'year is an integer, semester is 0 for fall, 1 for spring, task is a tuple of (label,code)'
    N = 2 * (year % 2) + semester
    return task[1] == N

YEAR, SEMESTER = 2018, FALL
print '{0} {1:8s}: {2}'.format(YEAR, 
                               'FALL' if SEMESTER==0 else 'SPRING',
                               [task[0]  for task in tasks
                                if execute_p(2018, FALL, task)])
2018 FALL    : ['a', 'e', 'i']

Now, at any point in the future you can tell what tasks should be done!

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.5h

Discuss on Twitter