The number of consecutive day-offs with any length

2022-06-04
3 min read

This project is an example project from a user request.
Simple counting of two consecutive day-off is easy using GUI; however, it is not trivial to model the number of consecutive day-offs. We implemented the spec. on the following project. Maximum-length-series-of-shifts slow down solving speed

Specifications

I want to constrain the number of consecutive day-offs for two or more days.
I want each consecutive day-offs to count as one, regardless of the length of the day-offs.

Problem of simple counting

The constraint below makes at least two times for 2days consecutive day-offs.

Let’s assume we have three successive day-offs.
Note the constraint counts as two for the consecutive day-offs. Therefore, we must consider another method to achieve the spec.

Implementation

The table below shows our idea. We have two and three successive patterns in the three consecutive day-offs problems. With the patterns we scanned all the day this month, considering the first day and the end of the month.

In the same manner, we can do four consecutive day-off as follows.

Python Code

First, we consider the specific case of two consecutive day-offs.

import sc3
def get_working_var(person,day):
    v1A=sc3.GetShiftVar(person,day,'rest');#

    return ~(v1A)


def get_2consecutive_day_off(person,day): 
#xoox 
#3210   

    if day <= FinishDate-3:
        v3=get_working_var(person,day)
        v2=get_working_var(person,day+1)
        v1=get_working_var(person,day+2)
        v0=get_working_var(person,day+3)
        if day ==StartDate:
            return (v3 & ~v2 &  ~v1 & v0) | (~v3 & ~v2 & v1) #xoox
        else:
            return v3 & ~v2 &  ~v1 & v0
    elif day <=FinishDate-2:
        v3=get_working_var(person,day)
        v2=get_working_var(person,day+1)
        v1=get_working_var(person,day+2)
        return v3 & ~v2 & ~v1
    else :
        v=get_working_var(person,day)#return false
        return v& ~v

Second, we consider the specific case of three consecutive day-offs.

def get_3consecutive_day_off(person,day): 
#xooox 
#43210 

    if day<=FinishDate-4:
        v4=get_working_var(person,day)
        v3=get_working_var(person,day+1)
        v2=get_working_var(person,day+2)
        v1=get_working_var(person,day+3)
        v0=get_working_var(person,day+4)
        if day==StartDay:
            return (v4 & ~v3 & ~v2 &  ~v1 & v0) | (~v4 & ~v3 & ~v2 & v1)
        else:
            return v4 & ~v3 & ~v2 &  ~v1 & v0 #xooox

    elif day <= FinishDate-3:
        v3=get_working_var(person,day)
        v2=get_working_var(person,day+1)
        v1=get_working_var(person,day+2)
        v0=get_working_var(person,day+3)
        return v3 & ~v2 &  ~v1 & ~v0 #xooo
    else:
        v=get_working_var(person,day)#return false
        return v& ~v

After debugging the routine, we generalize it as follows. (Actually, we do not use the codes above.)


def get_Nconsecutive_day_off(N,person,day):
    
    vlist=[]
    vlist2=[]
 
    if day<=FinishDate-N-1:#xooo...oox
        for n in range(N+2):
            v=get_working_var(person,day+n)
            if n==0 or n==N+1:
                vlist2.append(v)
            else:
                vlist.append(v)
    elif day<=FinishDate-N:
        for n in range(N+1):
            v=get_working_var(person,day+n)
            if n==0:
                vlist2.append(v)
            else:
                vlist.append(v)
    else:
        v=get_working_var(person,day)#return false
        return v& ~v
    v=~sc3.Or(vlist) & sc3.And(vlist2)
    if day ==StartDate:#oooo..ooox
        vlist=[]
        vlist2=[]
        for n in range(N+1):
            v1=get_working_var(person,day+n)
            if n==N:
                vlist2.append(v1)
            else:
                vlist.append(v1)
        return v | (~sc3.Or(vlist) & sc3.And(vlist2))
    return v

We now have N consecutive detection routines. Note that each routine will not be active at the same time. So, instead of addition, we can use OR.


def get_N_Number_of_consecutive_dayoffs_of_2_or_more_days_requested():
    for person in A_Member_in_All:
        if person not in h_Number_of_consecutive_dayoffs_of_2_or_more_days_requested:
            continue
        day_off_days=h_Number_of_consecutive_dayoffs_of_2_or_more_days_requested[person]
        st=staffdef[person]+' consecutive_dayoffs_of_2_or_more_days_requested '+str(day_off_days)
        sc3.print(st+' is constrainted.\n')
        vlist=[]
        for day in ThisMonth:
            v1=get_Nconsecutive_day_off(2,person,day)
            v2=get_Nconsecutive_day_off(3,person,day)
            v3=get_Nconsecutive_day_off(4,person,day)
            v4=get_Nconsecutive_day_off(5,person,day)
            v5=get_Nconsecutive_day_off(6,person,day)
            v6=get_Nconsecutive_day_off(7,person,day)
            vlist.append(v1|v2|v3|v4|v5 |v6)
        sc3.AddSoft(sc3.SeqError(day_off_days,day_off_days,5,vlist),st,7)

Finally, we call it as main routine.


get_N_Number_of_consecutive_dayoffs_of_2_or_more_days_requested():

Row Constraints

The code above assumes that maximum consecutive day-off is seven. We must care there are no more than seven consecutive day-offs.

Group Constraints

We make choice table items.

Staff Property

Solve it!

Before solving process, you should see following messages from python script.

Solution

Note that the numbers on the blue background in the left pane address the sum of consecutive day-offs in the right pane, the same as the requested number in staff property.

Project File

File → Open Project File from Github