haileyhalimj@gmail.com commited on
Commit
4d36152
·
1 Parent(s): 167999c

Solve default problem and none date issue

Browse files
src/config/optimization_config.py CHANGED
@@ -14,124 +14,71 @@ import importlib
14
 
15
 
16
  def get_date_span():
 
17
  try:
18
- # Try to get from streamlit session state (from config page)
19
  import streamlit as st
20
- if hasattr(st, 'session_state') and 'start_date' in st.session_state:
21
- from datetime import datetime, timedelta
22
- start_date = datetime.combine(st.session_state.start_date, datetime.min.time())
23
-
24
- # Check if we have calculated planning_days, otherwise determine from data
25
- if 'planning_days' in st.session_state and st.session_state.planning_days:
26
  planning_days = st.session_state.planning_days
27
  end_date = start_date + timedelta(days=planning_days - 1)
28
- else:
29
- # Determine date range from actual demand data for the exact start date
30
- try:
31
- demand_data = extract.read_orders_data(start_date=start_date)
32
- if not demand_data.empty:
33
- import pandas as pd
34
- # Get unique finish dates for this exact start date
35
- finish_dates = pd.to_datetime(demand_data["Basic finish date"]).dt.date.unique()
36
- finish_dates = sorted(finish_dates)
37
- if finish_dates:
38
- end_date = datetime.combine(max(finish_dates), datetime.min.time())
39
- planning_days = (end_date - start_date).days + 1
40
- else:
41
- end_date = start_date
42
- planning_days = 1
43
- else:
44
- end_date = start_date + timedelta(days=4) # Default 5 days
45
- planning_days = 5
46
- except Exception as e:
47
- print(f"Could not determine date range from data: {e}")
48
- end_date = start_date + timedelta(days=4) # Default 5 days
49
- planning_days = 5
50
-
51
- date_span = list(range(1, planning_days + 1))
52
- print(f"Using dates from config page: {start_date} to {end_date} ({planning_days} days)")
53
- print("date span", date_span)
54
- return date_span, start_date, end_date
55
- except Exception as e:
56
- print(f"Could not get dates from streamlit session: {e}")
57
 
58
- print(f"Loading default date values")
59
- # Default to match the user's data in COOIS_Released_Prod_Orders.csv
60
  from datetime import datetime
61
- return list(range(1, 6)), datetime(2025, 7, 7), datetime(2025, 7, 11) # Default 5 days
62
-
63
 
64
- #fetch date from streamlit or default value. The streamlit and default references the demand data (COOIS_Planned_and_Released.csv)
65
- DATE_SPAN, start_date, end_date = get_date_span()
66
 
67
- # Note: No need to set global dates - extract functions take start_date as parameter directly
68
- print(f"\n📅 DATE RANGE: {start_date} to {end_date}")
69
- print(f"📁 PRODUCT SOURCE: COOIS_Released_Prod_Orders.csv")
 
 
70
 
71
  def get_product_list():
72
- """
73
- Get filtered product list.
74
- IMPORTANT: This dynamically loads data to reflect current Streamlit configs/dates.
75
- """
76
  try:
77
- # Always get fresh filtered products to reflect current configs
78
  from src.demand_filtering import DemandFilter
79
  filter_instance = DemandFilter()
80
-
81
- # Force reload data to pick up new dates/configs
82
  filter_instance.load_data(force_reload=True)
83
-
84
- product_list = filter_instance.get_filtered_product_list()
85
- print(f"📦 FRESH FILTERED PRODUCTS: {len(product_list)} products ready for optimization")
86
- print(f"🎯 Products: {product_list}")
87
- return product_list
88
- except Exception as e:
89
- print(f"Error loading dynamic product list: {e}")
90
- # Fallback to unfiltered list
91
- product_list = transformed_data.get_released_product_list(start_date)
92
- print(f"📦 FALLBACK UNFILTERED PRODUCTS: {len(product_list)} products -> {product_list}")
93
- return product_list
94
-
95
- # DO NOT load at import time - always call get_product_list() dynamically
96
- # PRODUCT_LIST = get_product_list() # REMOVED - was causing stale data!
97
 
98
 
99
  def get_employee_type_list():
100
- """Get employee type list - try from streamlit session state first, then from data files"""
101
  try:
102
- # Try to get from streamlit session state (from Dataset Metadata page)
103
  import streamlit as st
104
  if hasattr(st, 'session_state') and 'selected_employee_types' in st.session_state:
105
- print(f"Using employee types from Dataset Metadata page: {st.session_state.selected_employee_types}")
106
  return st.session_state.selected_employee_types
107
- except Exception as e:
108
- print(f"Could not get employee types from streamlit session: {e}")
109
 
110
  # Default: load from data files
111
- print(f"Loading employee type list from data files")
112
  employee_type_list = extract.read_employee_data()
113
- emp_type_list = employee_type_list["employment_type"].unique()
114
- return emp_type_list
115
-
116
- # DO NOT load at import time - always call get_employee_type_list() dynamically
117
- # EMPLOYEE_TYPE_LIST = get_employee_type_list() # REMOVED - was causing stale data!
118
 
 
119
  def get_shift_list():
120
- """Get shift list - try from streamlit session state first, then from data files"""
121
  try:
122
- # Try to get from streamlit session state (from Dataset Metadata page)
123
  import streamlit as st
124
  if hasattr(st, 'session_state') and 'selected_shifts' in st.session_state:
125
- print(f"Using shifts from Dataset Metadata page: {st.session_state.selected_shifts}")
126
  return st.session_state.selected_shifts
127
- except Exception as e:
128
- print(f"Could not get shifts from streamlit session: {e}")
129
 
130
  # Default: load from data files
131
- print(f"Loading shift list from data files")
132
  shift_list = extract.get_shift_info()
133
- shift_list = shift_list["id"].unique()
134
- return shift_list
135
 
136
  # Evening shift activation mode - define early to avoid circular dependency
137
  # Options:
@@ -145,53 +92,39 @@ EVENING_SHIFT_MODE = "normal" # Default: only regular + overtime
145
  EVENING_SHIFT_DEMAND_THRESHOLD = 0.9 # Activate if regular+overtime capacity < 90% of demand
146
 
147
  def get_active_shift_list():
148
- """
149
- Get the list of active shifts based on EVENING_SHIFT_MODE setting.
150
- """
 
 
 
 
 
 
 
 
151
  all_shifts = get_shift_list()
152
 
153
- if EVENING_SHIFT_MODE == "normal":
154
- # Only regular and overtime shifts - NO evening shift
155
- active_shifts = [s for s in all_shifts if s in ShiftType.REGULAR_AND_OVERTIME]
156
- print(f"[SHIFT MODE] Normal mode: Using shifts {active_shifts} (Regular + Overtime only, NO evening)")
157
-
158
- elif EVENING_SHIFT_MODE == "activate_evening":
159
- # All shifts including evening (2)
160
- active_shifts = list(all_shifts)
161
- print(f"[SHIFT MODE] Evening activated: Using all shifts {active_shifts}")
162
-
163
- elif EVENING_SHIFT_MODE == "always_available":
164
- # All shifts always available
165
- active_shifts = list(all_shifts)
166
- print(f"[SHIFT MODE] Always available: Using all shifts {active_shifts}")
167
-
168
  else:
169
- # Default to normal mode
170
- active_shifts = [s for s in all_shifts if s in ShiftType.REGULAR_AND_OVERTIME]
171
- print(f"[SHIFT MODE] Unknown mode '{EVENING_SHIFT_MODE}', defaulting to normal: {active_shifts}")
172
-
173
- return active_shifts
174
-
175
- # DO NOT load at import time - always call get_active_shift_list() dynamically
176
- # SHIFT_LIST = get_active_shift_list() # REMOVED - was causing stale data!
177
 
178
 
179
  def get_line_list():
180
- """Get line list - try from streamlit session state first, then from data files"""
181
  try:
182
- # Try to get from streamlit session state (from Dataset Metadata page)
183
  import streamlit as st
184
  if hasattr(st, 'session_state') and 'selected_lines' in st.session_state:
185
- print(f"Using lines from Dataset Metadata page: {st.session_state.selected_lines}")
186
  return st.session_state.selected_lines
187
- except Exception as e:
188
- print(f"Could not get lines from streamlit session: {e}")
189
 
190
  # Default: load from data files
191
- print(f"Loading line list from data files")
192
  line_df = extract.read_packaging_line_data()
193
- line_list = line_df["id"].unique().tolist()
194
- return line_list
195
 
196
  # DO NOT load at import time - always call get_line_list() dynamically
197
  # LINE_LIST = get_line_list() # REMOVED - was causing stale data!
@@ -559,7 +492,6 @@ def get_payment_mode_config():
559
  return payment_mode_config
560
 
561
  # DO NOT load at import time - always call get_payment_mode_config() dynamically
562
- # PAYMENT_MODE_CONFIG = get_payment_mode_config() # REMOVED - was causing stale data!
563
 
564
  # ============================================================================
565
  # INITIALIZE MODULE-LEVEL VARIABLES
 
14
 
15
 
16
  def get_date_span():
17
+ """Get date span from streamlit session state, or return default"""
18
  try:
 
19
  import streamlit as st
20
+ if hasattr(st, 'session_state'):
21
+ # Get from session state without printing (avoid spam)
22
+ if 'start_date' in st.session_state and 'planning_days' in st.session_state:
23
+ from datetime import datetime, timedelta
24
+ start_date = datetime.combine(st.session_state.start_date, datetime.min.time())
 
25
  planning_days = st.session_state.planning_days
26
  end_date = start_date + timedelta(days=planning_days - 1)
27
+ date_span = list(range(1, planning_days + 1))
28
+ return date_span, start_date, end_date
29
+ except:
30
+ pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
+ # Default values - no printing to avoid spam
 
33
  from datetime import datetime
34
+ return list(range(1, 6)), datetime(2025, 7, 7), datetime(2025, 7, 11)
 
35
 
 
 
36
 
37
+ # Only call get_date_span() when explicitly needed - avoid module-level execution
38
+ # DATE_SPAN, start_date, end_date = get_date_span() # REMOVED - called dynamically instead
39
+ DATE_SPAN = None
40
+ start_date = None
41
+ end_date = None
42
 
43
  def get_product_list():
44
+ """Get filtered product list without printing spam"""
 
 
 
45
  try:
 
46
  from src.demand_filtering import DemandFilter
47
  filter_instance = DemandFilter()
 
 
48
  filter_instance.load_data(force_reload=True)
49
+ return filter_instance.get_filtered_product_list()
50
+ except:
51
+ # Fallback: get from session state start_date
52
+ date_span, start_date, end_date = get_date_span()
53
+ return transformed_data.get_released_product_list(start_date)
 
 
 
 
 
 
 
 
 
54
 
55
 
56
  def get_employee_type_list():
57
+ """Get employee type list from session state or default"""
58
  try:
 
59
  import streamlit as st
60
  if hasattr(st, 'session_state') and 'selected_employee_types' in st.session_state:
 
61
  return st.session_state.selected_employee_types
62
+ except:
63
+ pass
64
 
65
  # Default: load from data files
 
66
  employee_type_list = extract.read_employee_data()
67
+ return employee_type_list["employment_type"].unique().tolist()
 
 
 
 
68
 
69
+
70
  def get_shift_list():
71
+ """Get shift list from session state or default"""
72
  try:
 
73
  import streamlit as st
74
  if hasattr(st, 'session_state') and 'selected_shifts' in st.session_state:
 
75
  return st.session_state.selected_shifts
76
+ except:
77
+ pass
78
 
79
  # Default: load from data files
 
80
  shift_list = extract.get_shift_info()
81
+ return shift_list["id"].unique().tolist()
 
82
 
83
  # Evening shift activation mode - define early to avoid circular dependency
84
  # Options:
 
92
  EVENING_SHIFT_DEMAND_THRESHOLD = 0.9 # Activate if regular+overtime capacity < 90% of demand
93
 
94
  def get_active_shift_list():
95
+ """Get the list of active shifts based on EVENING_SHIFT_MODE or session state"""
96
+ # Check session state first
97
+ try:
98
+ import streamlit as st
99
+ if hasattr(st, 'session_state') and 'evening_shift_mode' in st.session_state:
100
+ mode = st.session_state.evening_shift_mode
101
+ else:
102
+ mode = EVENING_SHIFT_MODE
103
+ except:
104
+ mode = EVENING_SHIFT_MODE
105
+
106
  all_shifts = get_shift_list()
107
 
108
+ if mode == "normal":
109
+ return [s for s in all_shifts if s in ShiftType.REGULAR_AND_OVERTIME]
110
+ elif mode in ["activate_evening", "always_available"]:
111
+ return list(all_shifts)
 
 
 
 
 
 
 
 
 
 
 
112
  else:
113
+ return [s for s in all_shifts if s in ShiftType.REGULAR_AND_OVERTIME]
 
 
 
 
 
 
 
114
 
115
 
116
  def get_line_list():
117
+ """Get line list from session state or default"""
118
  try:
 
119
  import streamlit as st
120
  if hasattr(st, 'session_state') and 'selected_lines' in st.session_state:
 
121
  return st.session_state.selected_lines
122
+ except:
123
+ pass
124
 
125
  # Default: load from data files
 
126
  line_df = extract.read_packaging_line_data()
127
+ return line_df["id"].unique().tolist()
 
128
 
129
  # DO NOT load at import time - always call get_line_list() dynamically
130
  # LINE_LIST = get_line_list() # REMOVED - was causing stale data!
 
492
  return payment_mode_config
493
 
494
  # DO NOT load at import time - always call get_payment_mode_config() dynamically
 
495
 
496
  # ============================================================================
497
  # INITIALIZE MODULE-LEVEL VARIABLES
src/demand_filtering.py CHANGED
@@ -125,11 +125,12 @@ class DemandFilter:
125
  Returns:
126
  int: Total capacity in hours for this line type
127
  """
128
- from src.config.optimization_config import get_line_cnt_per_type, get_max_hour_per_shift_per_person, get_active_shift_list, DATE_SPAN
129
 
130
  line_cnt_per_type = get_line_cnt_per_type()
131
  max_hours_per_shift_dict = get_max_hour_per_shift_per_person()
132
  active_shifts = get_active_shift_list()
 
133
 
134
  # Get line count for this specific line type
135
  line_count = line_cnt_per_type.get(line_type, 0)
 
125
  Returns:
126
  int: Total capacity in hours for this line type
127
  """
128
+ from src.config.optimization_config import get_line_cnt_per_type, get_max_hour_per_shift_per_person, get_active_shift_list, get_date_span
129
 
130
  line_cnt_per_type = get_line_cnt_per_type()
131
  max_hours_per_shift_dict = get_max_hour_per_shift_per_person()
132
  active_shifts = get_active_shift_list()
133
+ date_span, _, _ = get_date_span() # Get date span dynamically
134
 
135
  # Get line count for this specific line type
136
  line_count = line_cnt_per_type.get(line_type, 0)
src/models/optimizer_real.py CHANGED
@@ -13,7 +13,7 @@ from src.config.constants import ShiftType, LineType, KitLevel
13
 
14
  # ---- config import (프로젝트 경로에 맞춰 조정) ----
15
  from src.config.optimization_config import (
16
- DATE_SPAN, # [1..N]
17
  get_product_list, # DYNAMIC: list of products (e.g., ['A','B',...])
18
  get_employee_type_list, # DYNAMIC: e.g., ['UNICEF Fixed term','Humanizer']
19
  get_active_shift_list, # DYNAMIC: e.g., [1,2,3]
@@ -41,9 +41,8 @@ from src.config.optimization_config import (
41
 
42
 
43
 
44
- # 2) kit_line_match
45
  KIT_LINE_MATCH_DICT
46
- print("KIT_LINE_MATCH_DICT",KIT_LINE_MATCH_DICT)
47
 
48
  # 3) If specific product is not produced on specific date, set it to 0
49
  # ACTIVE will be built dynamically in solve function based on fresh PRODUCT_LIST
@@ -182,6 +181,9 @@ def run_optimization_for_week():
182
  DEMAND_DICTIONARY = get_demand_dictionary()
183
  TEAM_REQ_PER_PRODUCT = get_team_requirements(PRODUCT_LIST)
184
 
 
 
 
185
  print(f"📦 LOADED PRODUCTS: {len(PRODUCT_LIST)} products")
186
  print(f"📈 LOADED DEMAND: {sum(DEMAND_DICTIONARY.values())} total units")
187
  print(f"👥 LOADED TEAM REQUIREMENTS: {len(TEAM_REQ_PER_PRODUCT)} employee types")
 
13
 
14
  # ---- config import (프로젝트 경로에 맞춰 조정) ----
15
  from src.config.optimization_config import (
16
+ get_date_span, # DYNAMIC: Get date span dynamically
17
  get_product_list, # DYNAMIC: list of products (e.g., ['A','B',...])
18
  get_employee_type_list, # DYNAMIC: e.g., ['UNICEF Fixed term','Humanizer']
19
  get_active_shift_list, # DYNAMIC: e.g., [1,2,3]
 
41
 
42
 
43
 
44
+ # 2) kit_line_match - no printing to avoid spam
45
  KIT_LINE_MATCH_DICT
 
46
 
47
  # 3) If specific product is not produced on specific date, set it to 0
48
  # ACTIVE will be built dynamically in solve function based on fresh PRODUCT_LIST
 
181
  DEMAND_DICTIONARY = get_demand_dictionary()
182
  TEAM_REQ_PER_PRODUCT = get_team_requirements(PRODUCT_LIST)
183
 
184
+ # Get date span dynamically
185
+ DATE_SPAN, start_date, end_date = get_date_span()
186
+
187
  print(f"📦 LOADED PRODUCTS: {len(PRODUCT_LIST)} products")
188
  print(f"📈 LOADED DEMAND: {sum(DEMAND_DICTIONARY.values())} total units")
189
  print(f"👥 LOADED TEAM REQUIREMENTS: {len(TEAM_REQ_PER_PRODUCT)} employee types")
ui/app.py CHANGED
@@ -6,11 +6,15 @@ Simplified version with configuration and optimization results
6
  import streamlit as st
7
  import sys
8
  import os
 
 
 
 
 
 
9
  from ui.pages.config_page import render_config_page
10
  from ui.pages.optimization_results import display_optimization_results
11
  from src.demand_validation_viz import display_demand_validation
12
- # Add src directory to path for imports
13
- sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
14
 
15
  # Set page config
16
  st.set_page_config(
 
6
  import streamlit as st
7
  import sys
8
  import os
9
+
10
+ # Add project root to path BEFORE imports
11
+ project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
12
+ if project_root not in sys.path:
13
+ sys.path.insert(0, project_root)
14
+
15
  from ui.pages.config_page import render_config_page
16
  from ui.pages.optimization_results import display_optimization_results
17
  from src.demand_validation_viz import display_demand_validation
 
 
18
 
19
  # Set page config
20
  st.set_page_config(
ui/pages/config_page.py CHANGED
@@ -248,91 +248,56 @@ def render_config_page():
248
  display_optimization_results(st.session_state.optimization_results)
249
 
250
  def initialize_session_state():
251
- """Initialize session state with values from optimization_config.py (single source of truth)"""
252
 
253
- # Load ALL values from optimization_config.py - NO hard-coded defaults here
254
- try:
255
- sys.path.append('src')
256
- from config.optimization_config import (
257
- # Import the actual computed values, not just constants
258
- EVENING_SHIFT_MODE, EVENING_SHIFT_DEMAND_THRESHOLD,
259
- FIXED_STAFF_CONSTRAINT_MODE,
260
- MAX_HOUR_PER_PERSON_PER_DAY, get_max_hour_per_shift_per_person,
261
- get_max_parallel_workers, get_cost_list_per_emp_shift,
262
- get_payment_mode_config, get_line_cnt_per_type,
263
- get_max_employee_per_type_on_day, start_date, end_date,
264
- shift_code_to_name, line_code_to_name, get_fixed_min_unicef_per_day
265
- )
266
-
267
- # Get current dynamic values
268
- MAX_HOUR_PER_SHIFT_PER_PERSON = get_max_hour_per_shift_per_person()
269
- MAX_PARALLEL_WORKERS = get_max_parallel_workers()
270
- COST_LIST_PER_EMP_SHIFT = get_cost_list_per_emp_shift()
271
- PAYMENT_MODE_CONFIG = get_payment_mode_config()
272
- LINE_CNT_PER_TYPE = get_line_cnt_per_type()
273
- MAX_EMPLOYEE_PER_TYPE_ON_DAY = get_max_employee_per_type_on_day()
274
- FIXED_MIN_UNICEF_PER_DAY = get_fixed_min_unicef_per_day()
275
-
276
- # Get the actual computed default values from optimization_config.py
277
- defaults = {
278
- # Schedule configuration - from optimization_config.py
279
- 'start_date': start_date.date() if hasattr(start_date, 'date') else start_date,
280
- 'schedule_type': 'weekly', # Fixed to weekly only
281
-
282
- # Shift configuration - from optimization_config.py
283
- 'evening_shift_mode': EVENING_SHIFT_MODE,
284
- 'evening_shift_threshold': EVENING_SHIFT_DEMAND_THRESHOLD,
285
-
286
- # Fixed staff configuration - from optimization_config.py
287
- 'fixed_staff_mode': FIXED_STAFF_CONSTRAINT_MODE,
288
- 'fixed_min_unicef_per_day': FIXED_MIN_UNICEF_PER_DAY,
289
-
290
- # Payment configuration - from optimization_config.py
291
- 'payment_mode_shift_1': PAYMENT_MODE_CONFIG.get(ShiftType.REGULAR),
292
- 'payment_mode_shift_2': PAYMENT_MODE_CONFIG.get(ShiftType.EVENING),
293
- 'payment_mode_shift_3': PAYMENT_MODE_CONFIG.get(ShiftType.OVERTIME),
294
-
295
- # Working hours - from optimization_config.py
296
- 'max_hour_per_person_per_day': MAX_HOUR_PER_PERSON_PER_DAY,
297
- 'max_hours_shift_1': MAX_HOUR_PER_SHIFT_PER_PERSON.get(ShiftType.REGULAR),
298
- 'max_hours_shift_2': MAX_HOUR_PER_SHIFT_PER_PERSON.get(ShiftType.EVENING),
299
- 'max_hours_shift_3': MAX_HOUR_PER_SHIFT_PER_PERSON.get(ShiftType.OVERTIME),
300
-
301
- # Operations - from optimization_config.py
302
- 'max_parallel_workers_long_line': MAX_PARALLEL_WORKERS.get(LineType.LONG_LINE),
303
- 'max_parallel_workers_mini_load': MAX_PARALLEL_WORKERS.get(LineType.MINI_LOAD),
304
-
305
- # Workforce limits - from optimization_config.py (computed values)
306
- 'max_unicef_per_day': list(MAX_EMPLOYEE_PER_TYPE_ON_DAY.get("UNICEF Fixed term", {}).values())[0] if MAX_EMPLOYEE_PER_TYPE_ON_DAY.get("UNICEF Fixed term") else 8,
307
- 'max_humanizer_per_day': list(MAX_EMPLOYEE_PER_TYPE_ON_DAY.get("Humanizer", {}).values())[0] if MAX_EMPLOYEE_PER_TYPE_ON_DAY.get("Humanizer") else 10,
308
-
309
- # Line counts - from optimization_config.py (data-driven)
310
- 'line_count_long_line': LINE_CNT_PER_TYPE.get(LineType.LONG_LINE),
311
- 'line_count_mini_load': LINE_CNT_PER_TYPE.get(LineType.MINI_LOAD),
312
-
313
- # Cost rates - from optimization_config.py (computed or default values)
314
- 'unicef_rate_shift_1': COST_LIST_PER_EMP_SHIFT.get("UNICEF Fixed term", {}).get(ShiftType.REGULAR),
315
- 'unicef_rate_shift_2': COST_LIST_PER_EMP_SHIFT.get("UNICEF Fixed term", {}).get(ShiftType.EVENING),
316
- 'unicef_rate_shift_3': COST_LIST_PER_EMP_SHIFT.get("UNICEF Fixed term", {}).get(ShiftType.OVERTIME),
317
- 'humanizer_rate_shift_1': COST_LIST_PER_EMP_SHIFT.get("Humanizer", {}).get(ShiftType.REGULAR),
318
- 'humanizer_rate_shift_2': COST_LIST_PER_EMP_SHIFT.get("Humanizer", {}).get(ShiftType.EVENING),
319
- 'humanizer_rate_shift_3': COST_LIST_PER_EMP_SHIFT.get("Humanizer", {}).get(ShiftType.OVERTIME),
320
-
321
- # Data Selection defaults - reasonable defaults, not ALL available
322
- 'selected_employee_types': ["UNICEF Fixed term", "Humanizer"],
323
- 'selected_shifts': [1, 3], # Regular and Overtime by default, not evening
324
- 'selected_lines': [6, 7], # Both Long Line and Mini Load
325
- }
326
-
327
- except Exception as e:
328
- st.error(f"❌ Could not load values from optimization_config.py: {e}")
329
- st.error("Please check that the optimization_config.py file is working correctly.")
330
- st.stop() # Stop execution - we shouldn't have fallback values here
331
-
332
- # Initialize session state with values from optimization_config.py
333
- for key, value in defaults.items():
334
- if key not in st.session_state and value is not None:
335
- st.session_state[key] = value
336
 
337
  def render_schedule_config():
338
  """Render schedule configuration section"""
@@ -343,7 +308,7 @@ def render_schedule_config():
343
  with col1:
344
  st.session_state.start_date = st.date_input(
345
  "Start Date",
346
- value=st.session_state.start_date,
347
  help="Exact start date to filter demand data - will only use orders that start on this specific date"
348
  )
349
 
@@ -357,7 +322,9 @@ def render_schedule_config():
357
  st.session_state.evening_shift_mode = st.selectbox(
358
  "Evening Shift Mode",
359
  options=['normal', 'activate_evening', 'always_available'],
360
- index=['normal', 'activate_evening', 'always_available'].index(st.session_state.evening_shift_mode),
 
 
361
  help="""
362
  - **Normal**: Only regular shift (1) and overtime shift (3)
363
  - **Activate Evening**: Allow evening shift (2) when demand is high
@@ -365,12 +332,12 @@ def render_schedule_config():
365
  """
366
  )
367
 
368
- if st.session_state.evening_shift_mode == 'activate_evening':
369
  st.session_state.evening_shift_threshold = st.slider(
370
  "Evening Shift Activation Threshold",
371
  min_value=0.1,
372
  max_value=1.0,
373
- value=st.session_state.evening_shift_threshold,
374
  step=0.1,
375
  help="Activate evening shift if regular+overtime capacity < threshold of demand"
376
  )
@@ -383,7 +350,9 @@ def render_workforce_config():
383
  st.session_state.fixed_staff_mode = st.selectbox(
384
  "Fixed Staff Constraint Mode",
385
  options=['mandatory', 'available', 'priority', 'none'],
386
- index=['mandatory', 'available', 'priority', 'none'].index(st.session_state.fixed_staff_mode),
 
 
387
  help="""
388
  - **Mandatory**: Forces all fixed staff to work full hours every day
389
  - **Available**: Staff available up to limits but not forced
@@ -402,7 +371,7 @@ def render_workforce_config():
402
  "Max UNICEF Fixed Term per Day",
403
  min_value=1,
404
  max_value=50,
405
- value=st.session_state.max_unicef_per_day,
406
  help="Maximum number of UNICEF fixed term employees per day"
407
  )
408
 
@@ -411,7 +380,7 @@ def render_workforce_config():
411
  "Max Humanizer per Day",
412
  min_value=1,
413
  max_value=50,
414
- value=st.session_state.max_humanizer_per_day,
415
  help="Maximum number of Humanizer employees per day"
416
  )
417
 
@@ -422,7 +391,7 @@ def render_workforce_config():
422
  "Fixed Minimum UNICEF per Day",
423
  min_value=0,
424
  max_value=20,
425
- value=st.session_state.fixed_min_unicef_per_day,
426
  help="Minimum number of UNICEF Fixed term employees required every working day (constraint)"
427
  )
428
 
@@ -433,7 +402,7 @@ def render_workforce_config():
433
  "Max Hours per Person per Day",
434
  min_value=1,
435
  max_value=24,
436
- value=st.session_state.max_hour_per_person_per_day,
437
  help="Legal maximum working hours per person per day"
438
  )
439
 
@@ -444,7 +413,7 @@ def render_workforce_config():
444
  "Max Hours - Shift 1 (Regular)",
445
  min_value=1.0,
446
  max_value=12.0,
447
- value=float(st.session_state.max_hours_shift_1),
448
  step=0.5,
449
  help="Maximum hours per person for regular shift"
450
  )
@@ -454,7 +423,7 @@ def render_workforce_config():
454
  "Max Hours - Shift 2 (Evening)",
455
  min_value=1.0,
456
  max_value=12.0,
457
- value=float(st.session_state.max_hours_shift_2),
458
  step=0.5,
459
  help="Maximum hours per person for evening shift"
460
  )
@@ -464,7 +433,7 @@ def render_workforce_config():
464
  "Max Hours - Shift 3 (Overtime)",
465
  min_value=1.0,
466
  max_value=12.0,
467
- value=float(st.session_state.max_hours_shift_3),
468
  step=0.5,
469
  help="Maximum hours per person for overtime shift"
470
  )
@@ -483,7 +452,7 @@ def render_operations_config():
483
  "Number of Long Lines",
484
  min_value=1,
485
  max_value=20,
486
- value=st.session_state.line_count_long_line,
487
  help="Number of long line production lines available"
488
  )
489
 
@@ -491,7 +460,7 @@ def render_operations_config():
491
  "Max Workers per Long Line",
492
  min_value=1,
493
  max_value=50,
494
- value=st.session_state.max_parallel_workers_long_line,
495
  help="Maximum number of workers that can work simultaneously on a long line"
496
  )
497
 
@@ -500,7 +469,7 @@ def render_operations_config():
500
  "Number of Mini Load Lines",
501
  min_value=1,
502
  max_value=20,
503
- value=st.session_state.line_count_mini_load,
504
  help="Number of mini load production lines available"
505
  )
506
 
@@ -508,7 +477,7 @@ def render_operations_config():
508
  "Max Workers per Mini Load Line",
509
  min_value=1,
510
  max_value=50,
511
- value=st.session_state.max_parallel_workers_mini_load,
512
  help="Maximum number of workers that can work simultaneously on a mini load line"
513
  )
514
 
@@ -531,7 +500,7 @@ def render_cost_config():
531
  st.session_state.payment_mode_shift_1 = st.selectbox(
532
  "Shift 1 (Regular) Payment",
533
  options=['bulk', 'partial'],
534
- index=0 if st.session_state.payment_mode_shift_1 == 'bulk' else 1,
535
  help="Payment mode for regular shift"
536
  )
537
 
@@ -539,7 +508,7 @@ def render_cost_config():
539
  st.session_state.payment_mode_shift_2 = st.selectbox(
540
  "Shift 2 (Evening) Payment",
541
  options=['bulk', 'partial'],
542
- index=0 if st.session_state.payment_mode_shift_2 == 'bulk' else 1,
543
  help="Payment mode for evening shift"
544
  )
545
 
@@ -547,7 +516,7 @@ def render_cost_config():
547
  st.session_state.payment_mode_shift_3 = st.selectbox(
548
  "Shift 3 (Overtime) Payment",
549
  options=['bulk', 'partial'],
550
- index=0 if st.session_state.payment_mode_shift_3 == 'bulk' else 1,
551
  help="Payment mode for overtime shift"
552
  )
553
 
@@ -562,7 +531,7 @@ def render_cost_config():
562
  "Shift 1 (Regular) - UNICEF",
563
  min_value=0.0,
564
  max_value=200.0,
565
- value=float(st.session_state.unicef_rate_shift_1),
566
  step=0.01,
567
  format="%.2f",
568
  help="Hourly rate for UNICEF Fixed Term staff during regular shift"
@@ -573,7 +542,7 @@ def render_cost_config():
573
  "Shift 2 (Evening) - UNICEF",
574
  min_value=0.0,
575
  max_value=200.0,
576
- value=float(st.session_state.unicef_rate_shift_2),
577
  step=0.01,
578
  format="%.2f",
579
  help="Hourly rate for UNICEF Fixed Term staff during evening shift"
@@ -584,7 +553,7 @@ def render_cost_config():
584
  "Shift 3 (Overtime) - UNICEF",
585
  min_value=0.0,
586
  max_value=200.0,
587
- value=float(st.session_state.unicef_rate_shift_3),
588
  step=0.01,
589
  format="%.2f",
590
  help="Hourly rate for UNICEF Fixed Term staff during overtime shift"
@@ -598,7 +567,7 @@ def render_cost_config():
598
  "Shift 1 (Regular) - Humanizer",
599
  min_value=0.0,
600
  max_value=200.0,
601
- value=float(st.session_state.humanizer_rate_shift_1),
602
  step=0.01,
603
  format="%.2f",
604
  help="Hourly rate for Humanizer staff during regular shift"
@@ -609,7 +578,7 @@ def render_cost_config():
609
  "Shift 2 (Evening) - Humanizer",
610
  min_value=0.0,
611
  max_value=200.0,
612
- value=float(st.session_state.humanizer_rate_shift_2),
613
  step=0.01,
614
  format="%.2f",
615
  help="Hourly rate for Humanizer staff during evening shift"
@@ -620,7 +589,7 @@ def render_cost_config():
620
  "Shift 3 (Overtime) - Humanizer",
621
  min_value=0.0,
622
  max_value=200.0,
623
- value=float(st.session_state.humanizer_rate_shift_3),
624
  step=0.01,
625
  format="%.2f",
626
  help="Hourly rate for Humanizer staff during overtime shift"
 
248
  display_optimization_results(st.session_state.optimization_results)
249
 
250
  def initialize_session_state():
251
+ """Initialize session state with simple default values using Streamlit's standard pattern"""
252
 
253
+ # Simple default values - no complex imports or function calls
254
+ # Use setdefault to avoid overwriting existing values
255
+
256
+ # Schedule defaults
257
+ st.session_state.setdefault('start_date', datetime.date(2025, 7, 7))
258
+ st.session_state.setdefault('schedule_type', 'weekly')
259
+
260
+ # Shift defaults
261
+ st.session_state.setdefault('evening_shift_mode', 'normal')
262
+ st.session_state.setdefault('evening_shift_threshold', 0.9)
263
+
264
+ # Staff defaults
265
+ st.session_state.setdefault('fixed_staff_mode', 'priority')
266
+ st.session_state.setdefault('fixed_min_unicef_per_day', 5)
267
+
268
+ # Payment modes
269
+ st.session_state.setdefault('payment_mode_shift_1', 'bulk')
270
+ st.session_state.setdefault('payment_mode_shift_2', 'bulk')
271
+ st.session_state.setdefault('payment_mode_shift_3', 'partial')
272
+
273
+ # Working hours
274
+ st.session_state.setdefault('max_hour_per_person_per_day', 14)
275
+ st.session_state.setdefault('max_hours_shift_1', 9.0)
276
+ st.session_state.setdefault('max_hours_shift_2', 7.0)
277
+ st.session_state.setdefault('max_hours_shift_3', 4.0)
278
+
279
+ # Workforce limits
280
+ st.session_state.setdefault('max_unicef_per_day', 8)
281
+ st.session_state.setdefault('max_humanizer_per_day', 10)
282
+
283
+ # Operations
284
+ st.session_state.setdefault('line_count_long_line', 3)
285
+ st.session_state.setdefault('line_count_mini_load', 2)
286
+ st.session_state.setdefault('max_parallel_workers_long_line', 7)
287
+ st.session_state.setdefault('max_parallel_workers_mini_load', 5)
288
+
289
+ # Cost rates
290
+ st.session_state.setdefault('unicef_rate_shift_1', 12.5)
291
+ st.session_state.setdefault('unicef_rate_shift_2', 15.0)
292
+ st.session_state.setdefault('unicef_rate_shift_3', 18.75)
293
+ st.session_state.setdefault('humanizer_rate_shift_1', 10.0)
294
+ st.session_state.setdefault('humanizer_rate_shift_2', 12.0)
295
+ st.session_state.setdefault('humanizer_rate_shift_3', 15.0)
296
+
297
+ # Data selection
298
+ st.session_state.setdefault('selected_employee_types', ["UNICEF Fixed term", "Humanizer"])
299
+ st.session_state.setdefault('selected_shifts', [1, 3])
300
+ st.session_state.setdefault('selected_lines', [6, 7])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
 
302
  def render_schedule_config():
303
  """Render schedule configuration section"""
 
308
  with col1:
309
  st.session_state.start_date = st.date_input(
310
  "Start Date",
311
+ value=st.session_state.get('start_date', datetime.date(2025, 7, 7)),
312
  help="Exact start date to filter demand data - will only use orders that start on this specific date"
313
  )
314
 
 
322
  st.session_state.evening_shift_mode = st.selectbox(
323
  "Evening Shift Mode",
324
  options=['normal', 'activate_evening', 'always_available'],
325
+ index=['normal', 'activate_evening', 'always_available'].index(
326
+ st.session_state.get('evening_shift_mode', 'normal')
327
+ ),
328
  help="""
329
  - **Normal**: Only regular shift (1) and overtime shift (3)
330
  - **Activate Evening**: Allow evening shift (2) when demand is high
 
332
  """
333
  )
334
 
335
+ if st.session_state.get('evening_shift_mode') == 'activate_evening':
336
  st.session_state.evening_shift_threshold = st.slider(
337
  "Evening Shift Activation Threshold",
338
  min_value=0.1,
339
  max_value=1.0,
340
+ value=st.session_state.get('evening_shift_threshold', 0.9),
341
  step=0.1,
342
  help="Activate evening shift if regular+overtime capacity < threshold of demand"
343
  )
 
350
  st.session_state.fixed_staff_mode = st.selectbox(
351
  "Fixed Staff Constraint Mode",
352
  options=['mandatory', 'available', 'priority', 'none'],
353
+ index=['mandatory', 'available', 'priority', 'none'].index(
354
+ st.session_state.get('fixed_staff_mode', 'priority')
355
+ ),
356
  help="""
357
  - **Mandatory**: Forces all fixed staff to work full hours every day
358
  - **Available**: Staff available up to limits but not forced
 
371
  "Max UNICEF Fixed Term per Day",
372
  min_value=1,
373
  max_value=50,
374
+ value=st.session_state.get('max_unicef_per_day', 8),
375
  help="Maximum number of UNICEF fixed term employees per day"
376
  )
377
 
 
380
  "Max Humanizer per Day",
381
  min_value=1,
382
  max_value=50,
383
+ value=st.session_state.get('max_humanizer_per_day', 10),
384
  help="Maximum number of Humanizer employees per day"
385
  )
386
 
 
391
  "Fixed Minimum UNICEF per Day",
392
  min_value=0,
393
  max_value=20,
394
+ value=st.session_state.get('fixed_min_unicef_per_day', 5),
395
  help="Minimum number of UNICEF Fixed term employees required every working day (constraint)"
396
  )
397
 
 
402
  "Max Hours per Person per Day",
403
  min_value=1,
404
  max_value=24,
405
+ value=st.session_state.get('max_hour_per_person_per_day', 14),
406
  help="Legal maximum working hours per person per day"
407
  )
408
 
 
413
  "Max Hours - Shift 1 (Regular)",
414
  min_value=1.0,
415
  max_value=12.0,
416
+ value=float(st.session_state.get('max_hours_shift_1', 9.0)),
417
  step=0.5,
418
  help="Maximum hours per person for regular shift"
419
  )
 
423
  "Max Hours - Shift 2 (Evening)",
424
  min_value=1.0,
425
  max_value=12.0,
426
+ value=float(st.session_state.get('max_hours_shift_2', 7.0)),
427
  step=0.5,
428
  help="Maximum hours per person for evening shift"
429
  )
 
433
  "Max Hours - Shift 3 (Overtime)",
434
  min_value=1.0,
435
  max_value=12.0,
436
+ value=float(st.session_state.get('max_hours_shift_3', 4.0)),
437
  step=0.5,
438
  help="Maximum hours per person for overtime shift"
439
  )
 
452
  "Number of Long Lines",
453
  min_value=1,
454
  max_value=20,
455
+ value=st.session_state.get('line_count_long_line', 3),
456
  help="Number of long line production lines available"
457
  )
458
 
 
460
  "Max Workers per Long Line",
461
  min_value=1,
462
  max_value=50,
463
+ value=st.session_state.get('max_parallel_workers_long_line', 7),
464
  help="Maximum number of workers that can work simultaneously on a long line"
465
  )
466
 
 
469
  "Number of Mini Load Lines",
470
  min_value=1,
471
  max_value=20,
472
+ value=st.session_state.get('line_count_mini_load', 2),
473
  help="Number of mini load production lines available"
474
  )
475
 
 
477
  "Max Workers per Mini Load Line",
478
  min_value=1,
479
  max_value=50,
480
+ value=st.session_state.get('max_parallel_workers_mini_load', 5),
481
  help="Maximum number of workers that can work simultaneously on a mini load line"
482
  )
483
 
 
500
  st.session_state.payment_mode_shift_1 = st.selectbox(
501
  "Shift 1 (Regular) Payment",
502
  options=['bulk', 'partial'],
503
+ index=0 if st.session_state.get('payment_mode_shift_1', 'bulk') == 'bulk' else 1,
504
  help="Payment mode for regular shift"
505
  )
506
 
 
508
  st.session_state.payment_mode_shift_2 = st.selectbox(
509
  "Shift 2 (Evening) Payment",
510
  options=['bulk', 'partial'],
511
+ index=0 if st.session_state.get('payment_mode_shift_2', 'bulk') == 'bulk' else 1,
512
  help="Payment mode for evening shift"
513
  )
514
 
 
516
  st.session_state.payment_mode_shift_3 = st.selectbox(
517
  "Shift 3 (Overtime) Payment",
518
  options=['bulk', 'partial'],
519
+ index=0 if st.session_state.get('payment_mode_shift_3', 'partial') == 'bulk' else 1,
520
  help="Payment mode for overtime shift"
521
  )
522
 
 
531
  "Shift 1 (Regular) - UNICEF",
532
  min_value=0.0,
533
  max_value=200.0,
534
+ value=float(st.session_state.get('unicef_rate_shift_1', 12.5)),
535
  step=0.01,
536
  format="%.2f",
537
  help="Hourly rate for UNICEF Fixed Term staff during regular shift"
 
542
  "Shift 2 (Evening) - UNICEF",
543
  min_value=0.0,
544
  max_value=200.0,
545
+ value=float(st.session_state.get('unicef_rate_shift_2', 15.0)),
546
  step=0.01,
547
  format="%.2f",
548
  help="Hourly rate for UNICEF Fixed Term staff during evening shift"
 
553
  "Shift 3 (Overtime) - UNICEF",
554
  min_value=0.0,
555
  max_value=200.0,
556
+ value=float(st.session_state.get('unicef_rate_shift_3', 18.75)),
557
  step=0.01,
558
  format="%.2f",
559
  help="Hourly rate for UNICEF Fixed Term staff during overtime shift"
 
567
  "Shift 1 (Regular) - Humanizer",
568
  min_value=0.0,
569
  max_value=200.0,
570
+ value=float(st.session_state.get('humanizer_rate_shift_1', 10.0)),
571
  step=0.01,
572
  format="%.2f",
573
  help="Hourly rate for Humanizer staff during regular shift"
 
578
  "Shift 2 (Evening) - Humanizer",
579
  min_value=0.0,
580
  max_value=200.0,
581
+ value=float(st.session_state.get('humanizer_rate_shift_2', 12.0)),
582
  step=0.01,
583
  format="%.2f",
584
  help="Hourly rate for Humanizer staff during evening shift"
 
589
  "Shift 3 (Overtime) - Humanizer",
590
  min_value=0.0,
591
  max_value=200.0,
592
+ value=float(st.session_state.get('humanizer_rate_shift_3', 15.0)),
593
  step=0.01,
594
  format="%.2f",
595
  help="Hourly rate for Humanizer staff during overtime shift"