Составлен план работ + выполнен этап 1
This commit is contained in:
159
main.py
159
main.py
@@ -1,101 +1,82 @@
|
||||
from ortools.sat.python import cp_model
|
||||
"""
|
||||
main.py - Основ
|
||||
|
||||
ный скрипт для создания графика смен
|
||||
Использует модуль models.py для создания и решения модели
|
||||
"""
|
||||
from models import create_model, solve_model, get_solution, SHIFTS, OPERATORS
|
||||
import csv
|
||||
|
||||
# Определяем даты апреля 2026 года
|
||||
days = [
|
||||
"2026-04-{:02d}".format(i) for i in range(1, 31)
|
||||
]
|
||||
|
||||
# Смены
|
||||
shifts = [
|
||||
(6, 14.5), # с 06:00 до 14:30
|
||||
(8, 16.5), # с 08:00 до 16:30
|
||||
(11.5, 20), # с 11:30 до 20:00
|
||||
(12.5, 21) # с 12:30 до 21:00
|
||||
]
|
||||
|
||||
# Операторы и их пожелания
|
||||
operators = [
|
||||
"Иванов",
|
||||
"Алексеев",
|
||||
"Сидоров",
|
||||
"Козобородов",
|
||||
"Трудяшкина",
|
||||
"Петрова"
|
||||
]
|
||||
|
||||
operator_preferences = {
|
||||
"Иванов": ["2026-04-10", "2026-04-13"],
|
||||
"Алексеев": [(i, 0) for i in days if (int(i[-2:]) - 1) % 7 == 0], # Понедельники
|
||||
"Сидоров": ["2026-04-25", "2026-04-26"],
|
||||
"Козобородов": [(i, 2) for i in days], # Предпочитает смену 11:30–20:00
|
||||
"Трудяшкина": ["2026-04-04"],
|
||||
"Петрова": ["2026-04-08", "2026-04-29"]
|
||||
}
|
||||
|
||||
# Создаем модель
|
||||
model = cp_model.CpModel()
|
||||
|
||||
# Переменные: определяем, работает ли оператор на смене в день
|
||||
works = {}
|
||||
for day in days:
|
||||
for shift_idx in range(len(shifts)):
|
||||
for op in operators:
|
||||
works[(day, shift_idx, op)] = model.NewBoolVar(f"{op}_{day}_shift_{shift_idx}")
|
||||
|
||||
# # Пожелания операторов
|
||||
for op, prefs in operator_preferences.items():
|
||||
if isinstance(prefs, list):
|
||||
for pref in prefs:
|
||||
if isinstance(pref, tuple):
|
||||
day, shift = pref
|
||||
model.AddHint(works[day, shift, op], 1)
|
||||
else:
|
||||
day = pref
|
||||
model.Add(sum(works[day, shift_idx, op] for shift_idx in range(len(shifts))) == 0)
|
||||
|
||||
# Условие 1: На каждую смену должен быть назначен оператор
|
||||
for day in days:
|
||||
for shift_idx in range(len(shifts)):
|
||||
model.Add(sum(works[day, shift_idx, op] for op in operators) == 1)
|
||||
|
||||
# Условие 2: У каждого оператора должно быть 8 выходных
|
||||
for op in operators:
|
||||
model.Add(sum(works[day, shift_idx, op] for day in days for shift_idx in range(len(shifts))) == 20 )
|
||||
|
||||
# Условие 3: Оператор не должен работать больше 5 дней подряд
|
||||
for op in operators:
|
||||
for day_idx in range(len(days) - 4):
|
||||
model.Add(sum(works[days[day_idx + i], shift_idx, op] for i in range(5) for shift_idx in range(len(shifts))) <= 4)
|
||||
|
||||
# Условие 4: В день оператор может работать только одну смену
|
||||
for day in days:
|
||||
for op in operators:
|
||||
model.Add(sum(works[day, shift_idx, op] for shift_idx in range(len(shifts))) <= 1)
|
||||
|
||||
# Решаем модель
|
||||
solver = cp_model.CpSolver()
|
||||
status = solver.Solve(model)
|
||||
|
||||
if status != cp_model.OPTIMAL and status != cp_model.FEASIBLE:
|
||||
print("Невозможно составить график")
|
||||
else:
|
||||
# Создаем CSV файл с результатами
|
||||
with open('shift_schedule.csv', 'w', newline='', encoding='utf-8') as file:
|
||||
def generate_csv_report(schedule: dict, filename: str = 'shift_schedule.csv'):
|
||||
"""
|
||||
Генерирует CSV-отчет на основе расписания.
|
||||
|
||||
Аргументы:
|
||||
schedule: словарь расписания из get_solution()
|
||||
filename: имя файла для вывода CSV
|
||||
|
||||
Возвращает:
|
||||
True если файл создан успешно
|
||||
"""
|
||||
with open(filename, 'w', newline='', encoding='utf-8') as file:
|
||||
writer = csv.writer(file)
|
||||
writer.writerow(['Day'] + [f'{i+1} shift' for i in range(len(shifts))])
|
||||
# Заголовок
|
||||
writer.writerow(['День'] + [f'Смена {i+1}' for i in range(len(SHIFTS))])
|
||||
|
||||
for day in days:
|
||||
for day, day_shifts in schedule.items():
|
||||
row = [day]
|
||||
for shift_idx in range(len(shifts)):
|
||||
for shift_idx in range(len(SHIFTS)):
|
||||
op = None
|
||||
for op_name in operators:
|
||||
if solver.Value(works[day, shift_idx, op_name]) == 1:
|
||||
for op_name in OPERATORS:
|
||||
if schedule[day][shift_idx].get(op_name, False):
|
||||
op = op_name
|
||||
break
|
||||
row.append(op)
|
||||
writer.writerow(row)
|
||||
|
||||
print(f"График успешно составлен и сохранен в '{filename}'")
|
||||
return True
|
||||
|
||||
print("График успешно составлен и сохранен в 'shift_schedule.csv'")
|
||||
|
||||
def main():
|
||||
"""
|
||||
Главная функция: создает и решает модель, затем генерирует CSV-отчет.
|
||||
"""
|
||||
print("=" * 60)
|
||||
print("Создание графика смен")
|
||||
print("=" * 60)
|
||||
|
||||
# Шаг 1: Создаем модель
|
||||
print("\n[1] Создание модели...")
|
||||
model, variables, operators_set = create_model()
|
||||
print(f" Модель создана с {len(variables)} переменными")
|
||||
print(f" Операторы: {', '.join(operators_set)}")
|
||||
|
||||
# Шаг 2: Решаем модель
|
||||
print("\n[2] Решение модели...")
|
||||
success = solve_model()
|
||||
|
||||
if success:
|
||||
print(" ✅ Модель решена успешно!")
|
||||
|
||||
# Шаг 3: Получаем решение
|
||||
print("\n[3] Получение решения...")
|
||||
schedule = get_solution()
|
||||
print(f" Решение получено для {len(schedule)} дней")
|
||||
|
||||
# Шаг 4: Генерируем CSV-отчет
|
||||
print("\n[4] Генерация CSV-отчета...")
|
||||
generate_csv_report(schedule, 'shift_schedule.csv')
|
||||
|
||||
else:
|
||||
print(" ❌ Не удалось составить график!")
|
||||
print(" Проверьте ограничения и предпочтения операторов.")
|
||||
return False
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Готово!")
|
||||
print("=" * 60)
|
||||
|
||||
return True
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user