How do you implement 44 consecutive hours of rest in 7 days? In particular, there are multiple shifts with the same work hours and start time.

Shifts

Describe working hours and the start time as follows.
Set 0:00 for work hours and 0:00 for the work start time for Days Off shifts.



Python Source

import sc3
import datetime
def get_start_time_from_word(s):
    start_time = datetime.datetime.strptime(s, '%H:%M')
    return start_time
def get_start_time(tuple):
    start_time = datetime.datetime.strptime(tuple[1], '%H:%M')
    return start_time

def get_end_time(tuple):#(duration,work_start_time)
    #print(tuple)
    start_time = tuple[1]
    end_time=start_time+datetime.timedelta(minutes=tuple[0])
    return end_time

def get_intruded_offtime(Dic):
    
    oneday_duration=datetime.timedelta(hours=24)
 
    i=0
    for tuple in Dic:
        end_time=get_end_time(tuple)
        if i==0:
            most_intruded_time=end_time
        else:
            if end_time> most_intruded_time:
                most_intruded_time=end_time
        i+=1
    work_end_duration=datetime.timedelta(days=most_intruded_time.day,hours=most_intruded_time.hour,minutes=most_intruded_time.minute)
    #print(work_end_duration)
    if work_end_duration>2*oneday_duration:
        return (work_end_duration-2*oneday_duration)
    else:
        return 0

def get_rest_duration_after_work(tuple,intruded_offtime):
    oneday_duration=datetime.timedelta(hours=24)
    if tuple[0]==0:#Day Off
        return oneday_duration-intruded_offtime#Assume previous day is most time consuming night shift
    end_time=get_end_time(tuple)
    work_end_duration=datetime.timedelta(days=end_time.day,hours=end_time.hour,minutes=end_time.minute)
    if work_end_duration>2*oneday_duration:#Night Shift
        return 2*oneday_duration-work_end_duration#works over night. return minus rest time
    return 2*oneday_duration-work_end_duration#Regular Shift

def get_rest_duration_before_work(tuple):
    oneday_duration=datetime.timedelta(hours=24)
    if tuple[0]==0:#Day Off
        return oneday_duration
    ws=tuple[1]
    work_duration=datetime.timedelta(days=ws.day,hours=ws.hour,minutes=ws.minute)
    return work_duration-oneday_duration

def get_weekly_rest_pattern(Dic,rest_time):
    rest_duration=datetime.timedelta(hours=rest_time)
    oneday_duration=datetime.timedelta(hours=24)
    intruded_offtime=get_intruded_offtime(Dic)
    #print(intruded_offtime)
    for i in Dic:#Get Days Off List
        if i[0]==0:
            days_off_list=Dic[i]

    li=[]
    for i in Dic:
        #print("i",Dic[i])
        res=get_rest_duration_after_work(i,intruded_offtime)#First Day
        res+=oneday_duration#Second Day Off
        full=True
        l=[]
        for m in Dic:
            res1=get_rest_duration_before_work(m)
            #print("m=",m,res1,"i=",i,res)
            if (res1+res) >=rest_duration:
                print(Dic[i],'→',days_off_list,'→',Dic[m],(res1+res).total_seconds()/3600,'hours')
                l.append((Dic[i],days_off_list,Dic[m]))
            else:
                full=False
                #print("False",Dic[i],Dic[m])
        if full:
            
            for a in l:
                li.append((a[0],a[1],['*']))
                #print((a[0],a[1],'*'))
                break#enough
        else:
            for a in l:
                li.append((a[0],a[1],a[2]))
    return li


def check_pat3(list44,person,day,patterns):
    
    #print(list44)
    list=[]
    
    S=set()
    
    for tuple in list44:
        and_list=[]
        #rint("tuple",tuple)
        
        if patterns<=2:
            name_str=""
            for i in range(patterns):
                for name in tuple[i]:
                    name_str+=name
            if name_str in S:
                #print("name_str",name_str)
                continue
            S.add(name_str)
        
        
        for i in range(patterns):
            lx=[]
            li=tuple[i]
            break_true=False
            for name in li:
                if name=='*':
                    #print("*detected")
                    break_true=True
                    break
                #print(i,day+i,name)
                lx.append(sc3.GetShiftVar(person,day+i,name))
            
            if break_true:
                break
            if len(lx)==1:
                and_list.append(lx[0])
            else:
                and_list.append(sc3.Or(lx))
        #print("andlist",len(and_list))
        list.append(sc3.And(and_list))
        
    return sc3.Or(list)


def check_44h_sub(list44,person):
    
    for day in FromStartDateMinus6:#-6 -5 -3 -2 -1 0(StartDate)
        
        if day >=FinishDate-3:# 6 5 4 3 2 1 0 |   :patterns 3
            break             #   6 5 4 3 2 1 |0  :patterns 2
                              #     6 5 4 3 2 |1 0:patterns 1
        list=[]
        #print(daydef[day],end=' ')
        for i in range(0,7,1):
           #print(daydef[day+i],end=' ')
           if i>=3 and i<=4 and day+i==FinishDate-1:
                #print("FinishDate-1 ",person,daydef[day+i])
                v=check_pat3(list44,person,day+i,2)
                list.append(v)
           elif i==4 and day+i==FinishDate:
                #print("FinishDate ",person,daydef[day+i])
                v=check_pat3(list44,person,day+i,1)
                list.append(v)
           elif day+i<=FinishDate-2: 
                v=check_pat3(list44,person,day+i,3)
                list.append(v)
        s='Consecutive Rest'+staffdef[person]+' '+daydef[day]
        #sc3.AddSoft(sc3.Or(list),s,7)
        
        if Use_weekly_rest44hours_as_hard_constraint:
            sc3.AddHard(sc3.Or(list),s)
        else:
            sc3.AddSoft(sc3.Or(list),s,7)
        
def check_44h(list):
    for person in A_Member_in_All:
        check_44h_sub(list,person)



def get_shift_dic():
    S={}
    for s in shiftdef:
        #print(s,shiftdef[s][2],shiftdef[s][3])
        work_duration=int(shiftdef[s][2])
        start_time=get_start_time_from_word(shiftdef[s][3])
        if work_duration>=0 and shiftdef[s][3]!="":
            st=(work_duration,start_time)
            if st not in S:
                l=[]
                l.append(s)
                S[st]=l
            else:
                S[st].append(s)
    return S

S=get_shift_dic()
print("\n*** Weekly Consecutive Rest =",weekly_rest_hours," hours ***\n")
weekly_rest_pattern=get_weekly_rest_pattern(S,weekly_rest_hours)
print(weekly_rest_pattern)
#Use_weekly_rest=0
if Use_weekly_rest:
    check_44h(weekly_rest_pattern)
print()



Macro

Describe macro as follows.



Reference

How do you implement 44 hours of rest consecutively within 7 days period?

Solution



Load the Example Project File

File → Open Project File from GitHub