How do I get jobs to rotate timeslots in a scheduling script keeping a 4-day on/off cycle?

  Kiến thức lập trình

I’m trying to fix the rotation logic in the job scheduling script in Python. There are four timeslots with jobs signed up for them. This is the list of allowed timeslot/job combination. Each job needs to run for 4 days in a row in one timeslot and then take a rest of 4 days during which the next job takes its place. After the rest period of 4 days, a job should be rotated into the next timeslot for the next cycle.

timeslot_jobs = {
    'North_6AM': ['Job A', 'Job B', 'Job C', 'Job D'],
    'North_6PM': ['Job A', 'Job B', 'Job C', 'Job D'],
    'South_3AM': ['Job E', 'Job F', 'Job G', 'Job H'],
    'South_3PM': ['Job E', 'Job F', 'Job G', 'Job H']
}

The expected output needed is like this:

Date Timeslot Job
2024-04-15 North_6AM Job A
2024-04-15 North_6PM Job B
2024-04-15 South_3AM Job E
2024-04-15 South_3PM Job F
2024-04-16 North_6AM Job A
2024-04-16 North_6PM Job B
2024-04-16 South_3AM Job E
2024-04-16 South_3PM Job F
2024-04-17 North_6AM Job A
2024-04-17 North_6PM Job B
2024-04-17 South_3AM Job E
2024-04-17 South_3PM Job F
2024-04-18 North_6AM Job A
2024-04-18 North_6PM Job B
2024-04-18 South_3AM Job E
2024-04-18 South_3PM Job F
2024-04-19 North_6AM Job C
2024-04-19 North_6PM Job D
2024-04-19 South_3AM Job G
2024-04-19 South_3PM Job H
2024-04-20 North_6AM Job C
2024-04-20 North_6PM Job D
2024-04-20 South_3AM Job G
2024-04-20 South_3PM Job H
2024-04-21 North_6AM Job C
2024-04-21 North_6PM Job D
2024-04-21 South_3AM Job G
2024-04-21 South_3PM Job H
2024-04-22 North_6AM Job C
2024-04-22 North_6PM Job D
2024-04-22 South_3AM Job G
2024-04-22 South_3PM Job H
2024-04-23 North_6AM Job B
2024-04-23 North_6PM Job A
2024-04-23 South_3AM Job F
2024-04-23 South_3PM Job E
2024-04-24 North_6AM Job B
2024-04-24 North_6PM Job A
2024-04-24 South_3AM Job F
2024-04-24 South_3PM Job E
2024-04-25 North_6AM Job B
2024-04-25 North_6PM Job A
2024-04-25 South_3AM Job F
2024-04-25 South_3PM Job E
2024-04-26 North_6AM Job B
2024-04-26 North_6PM Job A
2024-04-26 South_3AM Job F
2024-04-26 South_3PM Job E
2024-04-27 North_6AM Job D
2024-04-27 North_6PM Job C
2024-04-27 South_3AM Job H
2024-04-27 South_3PM Job G
2024-04-28 North_6AM Job D
2024-04-28 North_6PM Job C
2024-04-28 South_3AM Job H
2024-04-28 South_3PM Job G
2024-04-29 North_6AM Job D
2024-04-29 North_6PM Job C
2024-04-29 South_3AM Job H
2024-04-29 South_3PM Job G
2024-04-30 North_6AM Job D
2024-04-30 North_6PM Job C
2024-04-30 South_3AM Job H
2024-04-30 South_3PM Job G

I’m not able to get the rotation right. Some jobs are rotating timeslots before their 4-day work period and working past their 4-day slots because of which there are jobs that don’t get assigned timeslots.

The current output is:

Date Timeslot Job
2024-04-15 North_6AM Job A
2024-04-15 North_6PM Job B
2024-04-15 South_3AM Job E
2024-04-15 South_3PM Job F
2024-04-16 North_6AM Job A
2024-04-16 North_6PM Job B
2024-04-16 South_3AM Job E
2024-04-16 South_3PM Job F
2024-04-17 North_6AM Job B
2024-04-17 North_6PM Job A
2024-04-17 South_3AM Job F
2024-04-17 South_3PM Job E
2024-04-18 North_6AM Job B
2024-04-18 North_6PM Job A
2024-04-18 South_3AM Job F
2024-04-18 South_3PM Job E
2024-04-19 North_6AM Job B
2024-04-19 North_6PM Job A
2024-04-19 South_3AM Job F
2024-04-19 South_3PM Job E
2024-04-20 North_6AM Job B
2024-04-20 North_6PM Job A
2024-04-20 South_3AM Job F
2024-04-20 South_3PM Job E
2024-04-21 North_6AM Job A
2024-04-21 North_6PM Job B
2024-04-21 South_3AM Job E
2024-04-21 South_3PM Job F
2024-04-22 North_6AM Job A
2024-04-22 North_6PM Job B
2024-04-22 South_3AM Job E
2024-04-22 South_3PM Job F
2024-04-23 North_6AM Job A
2024-04-23 North_6PM Job B
2024-04-23 South_3AM Job E
2024-04-23 South_3PM Job F
2024-04-24 North_6AM Job A
2024-04-24 North_6PM Job B
2024-04-24 South_3AM Job E
2024-04-24 South_3PM Job F
2024-04-25 North_6AM Job A
2024-04-25 North_6PM Job C
2024-04-25 South_3AM Job E
2024-04-25 South_3PM Job G
2024-04-26 North_6AM Job B
2024-04-26 North_6PM Job C
2024-04-26 South_3AM Job F
2024-04-26 South_3PM Job G
2024-04-27 North_6AM Job B
2024-04-27 North_6PM Job C
2024-04-27 South_3AM Job F
2024-04-27 South_3PM Job G
2024-04-28 North_6AM Job B
2024-04-28 North_6PM Job A
2024-04-28 South_3AM Job F
2024-04-28 South_3PM Job E
2024-04-29 North_6AM Job B
2024-04-29 North_6PM Job A
2024-04-29 South_3AM Job F
2024-04-29 South_3PM Job E
2024-04-30 North_6AM Job B
2024-04-30 North_6PM Job A
2024-04-30 South_3AM Job F
2024-04-30 South_3PM Job E

The script is below:

import pandas as pd
from datetime import datetime, timedelta

# Define the timeslots and the jobs that can be assigned to each timeslot
timeslot_jobs = {
    'North_6AM': ['Job A', 'Job B', 'Job C', 'Job D'],
    'North_6PM': ['Job A', 'Job B', 'Job C', 'Job D'],
    'South_3AM': ['Job E', 'Job F', 'Job G', 'Job H'],
    'South_3PM': ['Job E', 'Job F', 'Job G', 'Job H']
}

# Initialize scheduling parameters
start_date = datetime(2024, 4, 15)
end_date = datetime(2024, 4, 30)
days_on = 4
days_off = 4

# Generate a list of all days within the given period
all_days = [start_date + timedelta(days=x) for x in range((end_date - start_date).days + 1)]

# Initialize job status and assignment structures
jobs = [job for sublist in timeslot_jobs.values() for job in sublist]
job_status = {
    job: {
        'current_timeslot_index': 0,
        'days_worked': 0,
        'days_rest': 0,
        'is_working': False
    }
    for job in jobs
}

# Function to get the next timeslot for a job
def get_next_timeslot(job, timeslot_jobs, current_timeslot_index):
    allowed_timeslots = [ts for ts, jobs in timeslot_jobs.items() if job in jobs]
    next_index = (current_timeslot_index + 1) % len(allowed_timeslots)
    return allowed_timeslots[next_index], next_index

# Function to generate the schedule
def generate_schedule(start_date, end_date, timeslot_jobs, job_status):
    schedule = []
    day = start_date

    while day <= end_date:
        daily_assignment = {ts: None for ts in timeslot_jobs.keys()}

        for job in jobs:
            job_info = job_status[job]
            allowed_timeslots = [ts for ts, jobs in timeslot_jobs.items() if job in jobs]
            current_timeslot = allowed_timeslots[job_info['current_timeslot_index']]

            if job_info['is_working']:
                if job_info['days_worked'] < days_on:
                    if daily_assignment[current_timeslot] is None:
                        schedule.append({'Date': day.strftime('%Y-%m-%d'), 'Timeslot': current_timeslot, 'Job': job})
                        daily_assignment[current_timeslot] = job
                        job_info['days_worked'] += 1

                    if job_info['days_worked'] == days_on:
                        job_info['is_working'] = False
                        job_info['days_rest'] = 0
                continue

            if not job_info['is_working']:
                if job_info['days_rest'] < days_off:
                    job_info['days_rest'] += 1
                if job_info['days_rest'] == days_off:
                    job_info['is_working'] = True
                    job_info['days_worked'] = 0
                    job_info['days_rest'] = 0
                    current_timeslot, current_timeslot_index = get_next_timeslot(job, timeslot_jobs, job_info['current_timeslot_index'])
                    job_info['current_timeslot_index'] = current_timeslot_index

        # Assign new jobs to timeslots where necessary
        for timeslot in daily_assignment:
            if daily_assignment[timeslot] is None:
                for job in timeslot_jobs[timeslot]:
                    job_info = job_status[job]
                    if job_info['days_worked'] == 0 and job_info['days_rest'] == 0 and not job_info['is_working']:
                        schedule.append({'Date': day.strftime('%Y-%m-%d'), 'Timeslot': timeslot, 'Job': job})
                        daily_assignment[timeslot] = job
                        job_info['days_worked'] += 1
                        job_info['is_working'] = True
                        break

        day += timedelta(days=1)

    return schedule

# Generate the initial schedule
initial_schedule = generate_schedule(start_date, end_date, timeslot_jobs, job_status)

# Ensure all timeslots are filled each day
final_schedule = []
for day in all_days:
    daily_assignment = {ts: None for ts in timeslot_jobs.keys()}
    assigned_jobs = set()
    for entry in initial_schedule:
        if entry['Date'] == day.strftime('%Y-%m-%d'):
            daily_assignment[entry['Timeslot']] = entry['Job']
            assigned_jobs.add(entry['Job'])
    for timeslot, allowed_jobs in timeslot_jobs.items():
        if daily_assignment[timeslot] is None:
            for job in allowed_jobs:
                if job not in assigned_jobs:
                    daily_assignment[timeslot] = job
                    assigned_jobs.add(job)
                    break
    for timeslot, job in daily_assignment.items():
        final_schedule.append({
            'Date': day.strftime('%Y-%m-%d'),
            'Timeslot': timeslot,
            'Job': job
        })

# Convert schedule to DataFrame and write to Excel
df = pd.DataFrame(final_schedule)
df.to_excel('job_schedule.xlsx', index=False)

print("Schedule created successfully and saved to 'job_schedule.xlsx'")

Any help fixing this would be much appreciated!

New contributor

Sanad is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

LEAVE A COMMENT