from datetime import datetime, timedelta
import random

# Your predefined variables…
duty_date = "2024-09-11"  # Just an update to reflect September
date = datetime.strptime(duty_date, "%Y-%m-%d")

day_of_week = date.strftime("%A")

# Finding weekends for September
weekends = [date + timedelta(days=i) for i in range((5 - date.weekday()) % 7, 30, 7)] + \
           [date + timedelta(days=i) for i in range((6 - date.weekday()) % 7, 30, 7)]

# Ensure weekends include the 30th if they fall on a weekend
for i in range(30, 31):
    potential_weekend = datetime(date.year, date.month, i)
    if potential_weekend.weekday() in [5, 6]:  # Saturday or Sunday
        weekends.append(potential_weekend)

print(day_of_week)
print(weekends)

# Define the list of persons for all three groups
primary_persons = ["NTALAMPEKOU", "MAFOUNI", "MEMIS", "RAISAKIS", "ENECHELIDOU", "DIMADIS"]
secondary_persons = ["SACHANIDOU", "VASILIADIS", "PAPAGEORGIOU", "KARAISKOS", "TSIPLAKIS", "SPANTIDAKIS", "PAPADOPOULOS"]
tertiary_persons = ["LAZARIDOU", "KIKINIS", "GRIGORIADIS", "KOMNINOS", "KITSIS"]

# Define the unavailable days for September 
unavailable_days = {
    "NTALAMPEKOU": ["2024-09-12", "2024-09-13", "2024-09-14", "2024-09-15", "2024-09-16", "2024-09-17", "2024-09-18", "2024-09-19"],
    "MAFOUNI": ["2024-09-20", "2024-09-21", "2024-09-22", "2024-09-23", "2024-09-24", "2024-09-25"],
    "MEMIS": ["2024-09-01", "2024-09-02", "2024-09-03", "2024-09-04", "2024-09-05", "2024-09-06", "2024-09-07"],
    "RAISAKIS": ["2024-09-10", "2024-09-11", "2024-09-12", "2024-09-13", "2024-09-15", "2024-09-16", "2024-09-17"],
    "ENECHELIDOU": ["2024-09-12", "2024-09-13", "2024-09-14", "2024-09-15", "2024-09-16", "2024-09-17"],
    "DIMADIS": ["2024-09-05", "2024-09-06", "2024-09-07"],
    "SACHANIDOU": ["2024-09-21", "2024-09-22", "2024-09-23", "2024-09-24", "2024-09-25"],
    "VASILIADIS": ["2024-09-10", "2024-09-11", "2024-09-12", "2024-09-13", "2024-09-14", "2024-09-15"],
    "PAPAGEORGIOU": ["2024-09-01", "2024-09-02", "2024-09-03", "2024-09-04", "2024-09-05", "2024-09-06"],
    "KARAISKOS": ["2024-09-11", "2024-09-12", "2024-09-13", "2024-09-14", "2024-09-15"],
    "TSIPLAKIS": ["2024-09-15"],
    "SPANTIDAKIS": ["2024-09-14", "2024-09-15", "2024-09-16"],
    "PAPADOPOULOS": ["2024-09-12", "2024-09-13", "2024-09-14", "2024-09-15"],
    # New group
    "LAZARIDOU": ["2024-09-01"],
    "KIKINIS": ["2024-09-02"],
    "GRIGORIADIS": ["2024-09-03"],
    "KOMNINOS": ["2024-09-04"],
    "KITSIS": ["2024-09-05"]
}

# Define the previous month’s duties (history remains the same)
previous_duties = {
    "NTALAMPEKOU": 24,
    "MAFOUNI": 22,
    "MEMIS": 22,
    "RAISAKIS": 24,
    "ENECHELIDOU": 0,
    "DIMADIS": 27,
    "SACHANIDOU": 9,
    "VASILIADIS": 12,
    "PAPAGEORGIOU": 11,
    "KARAISKOS": 11,
    "TSIPLAKIS": 8,
    "SPANTIDAKIS": 11,
    "PAPADOPOULOS": 22,
    # New group
    "LAZARIDOU": 0,
    "KIKINIS": 0,
    "GRIGORIADIS": 0,
    "KOMNINOS": 0,
    "KITSIS": 0
}

# Define the duties on specific days of the week (history remains the same)
weekly_duties = {
    "NTALAMPEKOU": {"Monday": 3, "Tuesday": 4, "Wednesday": 5, "Thursday": 5, "Friday": 1, "Saturday": 3, "Sunday": 3},
    "MAFOUNI": {"Monday": 2, "Tuesday": 5, "Wednesday": 5, "Thursday": 4, "Friday": 2, "Saturday": 2, "Sunday": 2},
    "MEMIS": {"Monday": 5, "Tuesday": 1, "Wednesday": 2, "Thursday": 2, "Friday": 4, "Saturday": 4, "Sunday": 4},
    "RAISAKIS": {"Monday": 2, "Tuesday": 3, "Wednesday": 6, "Thursday": 4, "Friday": 3, "Saturday": 4, "Sunday": 2},
    "ENECHELIDOU": {"Monday": 0, "Tuesday": 0, "Wednesday": 0, "Thursday": 0, "Friday": 0, "Saturday": 0, "Sunday": 0},
    "DIMADIS": {"Monday": 5, "Tuesday": 3, "Wednesday": 1, "Thursday": 3, "Friday": 5, "Saturday": 3, "Sunday": 7},
    "SACHANIDOU": {"Monday": 1, "Tuesday": 2, "Wednesday": 3, "Thursday": 0, "Friday": 1, "Saturday": 1, "Sunday": 1},
    "VASILIADIS": {"Monday": 1, "Tuesday": 2, "Wednesday": 0, "Thursday": 4, "Friday": 2, "Saturday": 1, "Sunday": 2},
    "PAPAGEORGIOU": {"Monday": 1, "Tuesday": 1, "Wednesday": 1, "Thursday": 2, "Friday": 2, "Saturday": 3, "Sunday": 1},
    "KARAISKOS": {"Monday": 1, "Tuesday": 3, "Wednesday": 2, "Thursday": 0, "Friday": 2, "Saturday": 2, "Sunday": 1},
    "TSIPLAKIS": {"Monday": 2, "Tuesday": 0, "Wednesday": 0, "Thursday": 1, "Friday": 2, "Saturday": 1, "Sunday": 2},
    "SPANTIDAKIS": {"Monday": 1, "Tuesday": 2, "Wednesday": 0, "Thursday": 2, "Friday": 3, "Saturday": 2, "Sunday": 1},
    "PAPADOPOULOS": {"Monday": 3, "Tuesday": 4, "Wednesday": 4, "Thursday": 3, "Friday": 2, "Saturday": 3, "Sunday": 3},
    # New group
    "LAZARIDOU": {"Monday": 0, "Tuesday": 0, "Wednesday": 0, "Thursday": 1, "Friday": 0, "Saturday": 1, "Sunday": 0},
    "KIKINIS": {"Monday": 0, "Tuesday": 0, "Wednesday": 0, "Thursday": 0, "Friday": 5, "Saturday": 0, "Sunday": 0},
    "GRIGORIADIS": {"Monday": 1, "Tuesday": 0, "Wednesday": 1, "Thursday": 1, "Friday": 1, "Saturday": 0, "Sunday": 0},
    "KOMNINOS": {"Monday": 0, "Tuesday": 0, "Wednesday": 1, "Thursday": 2, "Friday": 0, "Saturday": 2, "Sunday": 0},
    "KITSIS": {"Monday": 0, "Tuesday": 0, "Wednesday": 0, "Thursday": 0, "Friday": 0, "Saturday": 0, "Sunday": 0}
}

# Define the predefined duties shifted to September
predefined_duties = {
    "2024-09-10": "RAISAKIS",
    "2024-09-11": "KARAISKOS"  # Adjust as needed
}

# Function to check availability
def is_available(person, date, predefined_duties, unavailable_days):
    return date not in unavailable_days.get(person, []) and date not in predefined_duties

# Generate Schedule Function
import random
from datetime import datetime, timedelta

def generate_schedule(month, year, primary_persons, secondary_persons, tertiary_persons, predefined_duties, unavailable_days, weekends):
    schedule = {}
    date = datetime(year, month, 1)
    end_date = (datetime(year, month + 1, 1) - timedelta(days=1)).day  # Last day of the month
    primary_duties = {p: 0 for p in primary_persons}
    secondary_duties = {p: 0 for p in secondary_persons}
    tertiary_duties = {p: 0 for p in tertiary_persons}
    previous_duties = {p: 0 for p in primary_persons + secondary_persons + tertiary_persons}
    weekly_duties = {p: {day: 0 for day in ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]} for p in primary_persons + secondary_persons + tertiary_persons}

    # Assign predefined duties
    for duty_date, person in predefined_duties.items():
        schedule[duty_date] = person
        if person in primary_duties:
            primary_duties[person] += 1
        elif person in secondary_duties:
            secondary_duties[person] += 1
        elif person in tertiary_duties:
            tertiary_duties[person] += 1
        previous_duties[person] += 1
        day_of_week = datetime.strptime(duty_date, "%Y-%m-%d").strftime("%A")
        weekly_duties[person][day_of_week] += 1

    # Shuffle weekends
    random.shuffle(weekends)

    # Additional logic to assign duties can be added here

    # Assign duties on weekends
    for person in primary_persons:
        for weekend in weekends:
            weekend_date = weekend.strftime("%Y-%m-%d")
            if is_available(person, weekend_date, predefined_duties, unavailable_days):
                schedule[weekend_date] = person
                primary_duties[person] += 1
                previous_duties[person] += 1
                day_of_week = weekend.strftime("%A")
                weekly_duties[person][day_of_week] += 1
                weekends.remove(weekend)  # Remove the weekend once 

                break

    # Main scheduling loop
    while date.month == month:
    	date_str = date.strftime("%Y-%m-%d")
    	if date_str not in schedule:
            available_primary = [p for p in primary_persons if is_available(p, date_str, predefined_duties, unavailable_days) and primary_duties[p] < 3]
            available_secondary = [p for p in secondary_persons if is_available(p, date_str, predefined_duties, unavailable_days) and secondary_duties[p] < 2]
            available_tertiary = [p for p in tertiary_persons if is_available(p, date_str, predefined_duties, unavailable_days) and tertiary_duties[p] < 1]

    # Prioritize primary duties
        if available_primary:  
            available_primary.sort(key=lambda p: (previous_duties.get(p, 0), weekly_duties[p][date.strftime("%A")]))
            person = available_primary[0]
            primary_duties[person] += 1
        elif available_secondary:
            available_secondary.sort(key=lambda p: (previous_duties.get(p, 0), weekly_duties[p][date.strftime("%A")]))
            person = available_secondary[0]
            secondary_duties[person] += 1
        elif available_tertiary:
            available_tertiary.sort(key=lambda p: (previous_duties.get(p, 0), weekly_duties[p][date.strftime("%A")]))
            person = available_tertiary[0]
            tertiary_duties[person] += 1
        else:
            print(f"No available duty person on {date_str}")  # Logging
            person = None  # Handle the case where nobody is available

        if person:
            schedule[date_str] = person

    date += timedelta(days=1)  # Move to the next day

	# Ensure primary persons have exactly 3 days of duty
    	for person in primary_persons:
            while primary_duties[person] < 3:
            	next_available_date = datetime(year, month, 1) + timedelta(days=len(schedule))  # Base date

	# Ensure primary persons have exactly 3 days of duty
    	for person in primary_persons:
            while primary_duties[person] < 3:
            	next_available_date = datetime(year, month, 1) + timedelta(days=len(schedule))  # Base date 

            # Increment next_available_date until found
            while next_available_date.month == month:
                date_str = next_available_date.strftime("%Y-%m-%d")
                if is_available(person, date_str, predefined_duties, unavailable_days):
                    # Assign duty
                    schedule[date_str] = person
                    primary_duties[person] += 1
                    previous_duties[person] += 1
                    day_of_week = next_available_date.strftime("%A")
                    weekly_duties[person][day_of_week] += 1
                    break  # Break inner loop if duty is assigned 
                next_available_date += timedelta(days=1)  # Move to next day

            # If next_available_date moves out of the month, break the outer loop
            if next_available_date.month != month:
                break

    return schedule, primary_duties, secondary_duties, tertiary_duties  # Ensure return statement at the end

# Ensure primary persons have exactly 3 days of duty
for person in primary_persons:
    while primary_duties[person] < 3:
        next_available_date = datetime(year, month, 1) + timedelta(days=len(schedule))  # Starts off at the base date

        while next_available_date.month == month:
            date_str = next_available_date.strftime("%Y-%m-%d")
            if is_available(person, date_str, predefined_duties, unavailable_days):
                # Assign duty
                schedule[date_str] = person
                primary_duties[person] += 1
                previous_duties[person] += 1
                day_of_week = next_available_date.strftime("%A")
                weekly_duties[person][day_of_week] += 1
                break  # Break to try and check if more duties are needed

            next_available_date += timedelta(days=1)  # Move to the next day
        
        # If next_available_date moves out of the month, break the outer loop
        if next_available_date.month != month:
            break

return schedule, primary_duties, secondary_duties, tertiary_duties  # Ensure return statement at the end
        
        # If we exit the inner loop due to the next date being outside this month
        if next_available_date.month != month:
            break  # Break the outer loop, we cannot assign more duties to this person
        
        # Now we check again at the outer level
        if next_available_date.month == month and is_available(person, date_str, predefined_duties, unavailable_days):
            schedule[date_str] = person
            primary_duties[person] += 1
            previous_duties[person] += 1
            day_of_week = next_available_date.strftime("%A")
            weekly_duties[person][day_of_week] += 1
        
        # Check if this date is in the month and the person is available
        if next_available_date.month == month and is_available(person, date_str, predefined_duties, unavailable_days):
            schedule[date_str] = person
            primary_duties[person] += 1
            previous_duties[person] += 1
            day_of_week = next_available_date.strftime("%A")
            weekly_duties[person][day_of_week] += 1

            
            # Adjust the number of days used in the month
            schedule[date_str] = person
        
       # Prevent going beyond the end of September
if next_available_date.month != month:  # If we have transitioned to October, break
    break

return schedule, primary_duties, secondary_duties, tertiary_duties  # Ensure return statement is within the function.

# Call the scheduling function to generate the schedule for September 2024
schedule, primary_duties, secondary_duties, tertiary_duties = generate_schedule(
    month=9,
    year=2024,
    primary_persons=primary_persons,
    secondary_persons=secondary_persons,
    tertiary_persons=tertiary_persons,
    predefined_duties=predefined_duties,
    unavailable_days=unavailable_days,
    weekends=weekends
)

# Further processing of the schedule, e.g., printing the final schedule, availability checks, etc.

# Generate the schedule for September 2024
schedule, primary_duties, secondary_duties, tertiary_duties = generate_schedule(
    month=9,
    year=2024,
    primary_persons=primary_persons,
    secondary_persons=secondary_persons,
    tertiary_persons=tertiary_persons,
    predefined_duties=predefined_duties,
    unavailable_days=unavailable_days,
    weekends=weekends
)

# Check availability for specific dates in September
dates_to_check = ["2024-09-29", "2024-09-30"]  # Example dates
for date_str in dates_to_check:
    available_primary = [p for p in primary_persons if is_available(p, date_str, predefined_duties, unavailable_days) and primary_duties[p] < 3]
    available_secondary = [p for p in secondary_persons if is_available(p, date_str, predefined_duties, unavailable_days) and secondary_duties[p] < 2]
    available_tertiary = [p for p in tertiary_persons if is_available(p, date_str, predefined_duties, unavailable_days) and tertiary_duties[p] < 1]

    print(f"Date: {date_str}")
    print(f"Available Primary: {available_primary}")
    print(f"Available Secondary: {available_secondary}")
    print(f"Available Tertiary: {available_tertiary}")

# Print the final schedule for September
for day, person in sorted(schedule.items()):
    print(f"{day}: {person}")
can you purpose any changes to make the code more efficient and functional  and give the whole code again with all the necessary changes? please use only straight quotes.