-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvehicle_dispatch.py
More file actions
310 lines (266 loc) · 10.3 KB
/
vehicle_dispatch.py
File metadata and controls
310 lines (266 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
케어브이 차량 배차 알고리즘
- 주/부운전자 규칙에 따른 차량 배정
- 운전자는 항상 본인 차량으로 운행
- 어르신 결석 시 탑승 순서에서 제외
"""
# ============================================
# [1] 기본 데이터 설정
# ============================================
# 노선(라인)별 운전자 배정 (인덱스 0 = 주운전자, 1 = 부1, 2 = 부2 ...)
ROUTE_DRIVERS = {
"카니발": ["박정현", "조순옥"],
"스타리아1": ["방현주", "이연숙"],
"스타리아2": ["이경숙", "홍은영", "최형순"],
"스타렉스": ["최형순", "홍은영"],
"레이": ["차미숙"],
"다마스": ["정선례"],
"쏠라티": ["정재훈"],
}
# 운전자별 본인 소유 차량 (운전자는 항상 본인 차량으로 운행!)
DRIVER_OWN_CAR = {
"박정현": "카니발",
"조순옥": "조순옥차",
"방현주": "스타리아1",
"이연숙": "이연숙차",
"이경숙": "스타리아2",
"홍은영": "홍은영차",
"최형순": "스타렉스",
"차미숙": "레이",
"정선례": "다마스",
"정재훈": "쏠라티",
}
# 운전자별 출근 현황 (날짜: 출근여부) - 1 = 출근, 0 = 휴무
# 날짜 목록은 여기서 자동 추출됨
DRIVER_ATTENDANCE = {
"박정현": { # 주5일 (1/7만 휴무 테스트)
2:1, 3:1, 6:1, 7:0, 8:1, 9:1, 10:1,
13:1, 14:1, 15:1, 16:1, 17:1,
20:1, 21:1, 22:1, 23:1, 24:1,
27:1, 28:1, 29:1, 30:1, 31:1,
},
"조순옥": { # 화수금만 출근
2:0, 3:1, 6:0, 7:1, 8:1, 9:0, 10:1,
13:0, 14:1, 15:1, 16:0, 17:1,
20:0, 21:1, 22:1, 23:0, 24:1,
27:0, 28:1, 29:1, 30:0, 31:1,
},
"방현주": { # 주5일
2:1, 3:1, 6:1, 7:1, 8:1, 9:1, 10:1,
13:1, 14:1, 15:1, 16:1, 17:1,
20:1, 21:1, 22:1, 23:1, 24:1,
27:1, 28:1, 29:1, 30:1, 31:1,
},
"이연숙": { # 화수금만 출근
2:0, 3:1, 6:0, 7:1, 8:1, 9:0, 10:1,
13:0, 14:1, 15:1, 16:0, 17:1,
20:0, 21:1, 22:1, 23:0, 24:1,
27:0, 28:1, 29:1, 30:0, 31:1,
},
"이경숙": { # 주5일
2:1, 3:1, 6:1, 7:1, 8:1, 9:1, 10:1,
13:1, 14:1, 15:1, 16:1, 17:1,
20:1, 21:1, 22:1, 23:1, 24:1,
27:1, 28:1, 29:1, 30:1, 31:1,
},
"홍은영": { # 화수금만 출근
2:0, 3:1, 6:0, 7:1, 8:1, 9:0, 10:1,
13:0, 14:1, 15:1, 16:0, 17:1,
20:0, 21:1, 22:1, 23:0, 24:1,
27:0, 28:1, 29:1, 30:0, 31:1,
},
"최형순": { # 주5일
2:1, 3:1, 6:1, 7:1, 8:1, 9:1, 10:1,
13:1, 14:1, 15:1, 16:1, 17:1,
20:1, 21:1, 22:1, 23:1, 24:1,
27:1, 28:1, 29:1, 30:1, 31:1,
},
"차미숙": { # 주5일
2:1, 3:1, 6:1, 7:1, 8:1, 9:1, 10:1,
13:1, 14:1, 15:1, 16:1, 17:1,
20:1, 21:1, 22:1, 23:1, 24:1,
27:1, 28:1, 29:1, 30:1, 31:1,
},
"정선례": { # 주5일
2:1, 3:1, 6:1, 7:1, 8:1, 9:1, 10:1,
13:1, 14:1, 15:1, 16:1, 17:1,
20:1, 21:1, 22:1, 23:1, 24:1,
27:1, 28:1, 29:1, 30:1, 31:1,
},
"정재훈": { # 주5일
2:1, 3:1, 6:1, 7:1, 8:1, 9:1, 10:1,
13:1, 14:1, 15:1, 16:1, 17:1,
20:1, 21:1, 22:1, 23:1, 24:1,
27:1, 28:1, 29:1, 30:1, 31:1,
},
}
# 운행 날짜 자동 추출 (출근 현황에서)
WORK_DATES = sorted(list(DRIVER_ATTENDANCE["박정현"].keys()))
# 노선(라인)별 어르신 탑승 순서 (테스트 데이터)
# 노선에 배정된 어르신들 - 운행 차량은 출근한 운전자에 따라 달라짐
ROUTE_BOARDING_ORDER = {
"카니발": ["김순자", "이영희", "박말순", "최복순", "정금자", "한순덕", "오복임", "강점례"],
"스타리아1": ["윤춘자", "이옥자", "박천자", "박학선", "이정자", "전기순"],
"스타리아2": ["최삼순", "김민자", "신애숙", "한정숙", "정금순", "이춘숙"],
"스타렉스": ["박수근", "박명선", "이지산", "임춘화"],
"레이": ["권숙인", "서오순", "김말자", "이복순", "최정희", "박영자", "한순자"], # 7인
"다마스": ["배신화", "이춘자"],
"쏠라티": ["이재순", "조혜련", "이순자", "김영태", "김장수", "최순자", "차재광", "이병선", "안점덕", "김길자", "이남석"],
}
# 어르신별 결석 현황 (날짜 리스트)
SENIOR_ABSENCE = {
"박재명": [2],
"손은자": [2],
"김명환": [2],
# 테스트용 추가 결석 데이터
"김순자": [8, 15], # 1/8, 1/15 결석
"이영희": [10], # 1/10 결석
"박수근": [6, 7, 8], # 1/6~8 결석
}
# ============================================
# [2] 핵심 알고리즘
# ============================================
def is_driver_working(driver: str, date: int) -> bool:
"""해당 날짜에 운전자가 출근하는지 확인"""
attendance = DRIVER_ATTENDANCE.get(driver, {})
return attendance.get(date, 0) == 1
def is_senior_absent(senior: str, date: int) -> bool:
"""해당 날짜에 어르신이 결석인지 확인"""
absent_dates = SENIOR_ABSENCE.get(senior, [])
return date in absent_dates
def get_route_assignment_for_date(route: str, date: int) -> dict:
"""
특정 날짜에 노선을 운행할 운전자 및 차량 결정
핵심: 운전자는 항상 본인 차량으로 운행!
- 주운전자 출근 → 주운전자가 본인 차량으로 운행
- 주운전자 휴무 → 부운전자가 본인 차량으로 운행
- 모두 휴무 → 운행 없음
반환값:
{
"route": 노선명,
"driver": 운전자명 또는 None,
"vehicle": 실제 운행 차량 (운전자 본인 차량),
"driver_role": "주운전자" / "부1운전자" / None,
"status": "정상" / "대체" / "운행없음",
}
"""
drivers = ROUTE_DRIVERS.get(route, [])
if not drivers:
return {
"route": route,
"driver": None,
"vehicle": None,
"driver_role": None,
"status": "운행없음",
}
# 주운전자 확인
main_driver = drivers[0]
if is_driver_working(main_driver, date):
return {
"route": route,
"driver": main_driver,
"vehicle": DRIVER_OWN_CAR.get(main_driver),
"driver_role": "주운전자",
"status": "정상",
}
# 주운전자 휴무 → 부운전자 순서대로 확인
for i, sub_driver in enumerate(drivers[1:], start=1):
if is_driver_working(sub_driver, date):
return {
"route": route,
"driver": sub_driver,
"vehicle": DRIVER_OWN_CAR.get(sub_driver), # 부운전자 본인 차량!
"driver_role": f"부{i}운전자",
"status": "대체",
}
# 모든 운전자 휴무
return {
"route": route,
"driver": None,
"vehicle": None,
"driver_role": None,
"status": "운행없음",
}
def get_boarding_list_for_date(route: str, date: int) -> list:
"""
특정 날짜, 특정 노선의 탑승 어르신 목록 (결석자 제외)
"""
all_seniors = ROUTE_BOARDING_ORDER.get(route, [])
return [s for s in all_seniors if not is_senior_absent(s, date)]
def get_daily_dispatch(date: int) -> list:
"""
특정 날짜의 전체 배차 결과 반환
"""
results = []
for route in ROUTE_DRIVERS.keys():
assignment = get_route_assignment_for_date(route, date)
seniors = get_boarding_list_for_date(route, date) if assignment["status"] != "운행없음" else []
results.append({
"route": route,
"driver": assignment["driver"],
"vehicle": assignment["vehicle"],
"role": assignment["driver_role"],
"status": assignment["status"],
"seniors": seniors,
})
return results
# ============================================
# [3] 출력 함수
# ============================================
def print_daily_dispatch(date: int):
"""특정 날짜의 배차 결과 출력 - 깔끔한 형식"""
results = get_daily_dispatch(date)
print(f"\n{'='*70}")
print(f" 📅 1월 {date}일 배차표")
print(f"{'='*70}")
for item in results:
route = item["route"]
driver = item["driver"]
vehicle = item["vehicle"]
status = item["status"]
role = item["role"]
seniors = item["seniors"]
if status == "운행없음":
print(f"\n❌ [{route} 노선] 운행 없음 (모든 운전자 휴무)")
else:
# 상태 아이콘
icon = "✅" if status == "정상" else "🔄"
status_text = "" if status == "정상" else f" (대체: {role})"
print(f"\n{icon} [{route} 노선]{status_text}")
print(f" 운전자: {driver}")
print(f" 차 량: {vehicle}")
print(f" 탑승자: {' → '.join(seniors) if seniors else '없음'}")
print(f"\n{'='*70}")
def print_compact_dispatch(date: int):
"""특정 날짜의 배차 결과 - 한 줄 컴팩트 형식"""
results = get_daily_dispatch(date)
print(f"\n📅 1월 {date}일")
print("-" * 70)
for item in results:
if item["status"] == "운행없음":
print(f"❌ {item['route']} - 운행없음")
else:
icon = "✅" if item["status"] == "정상" else "🔄"
seniors_str = ", ".join(item["seniors"][:3])
if len(item["seniors"]) > 3:
seniors_str += f" 외 {len(item['seniors'])-3}명"
print(f"{icon} {item['vehicle']} - {item['driver']} | {seniors_str}")
# ============================================
# [4] 메인 실행
# ============================================
if __name__ == "__main__":
print("\n" + "="*70)
print(" 🚐 케어브이 차량 배차 시스템")
print("="*70)
# 전체 평일 배차표 출력
for date in WORK_DATES:
print_daily_dispatch(date)
print("\n\n✅ 테스트 완료!")
print("="*70)
print("테스트 케이스:")
print("- 1/6(월): 주운전자들 정상 출근 → 본인 차량으로 운행")
print("- 1/7(화): 박정현(주) 휴무 → 조순옥이 '조순옥차'로 카니발 노선 대체 운행")
print("- 1/8(수): 김순자 결석 → 카니발 노선 탑승순서에서 제외")
print("="*70)