haileyhalimj@gmail.com commited on
Commit
28016d1
ยท
1 Parent(s): 4d36152

Trying to fix infinite reloading

Browse files
src/config/optimization_config.py CHANGED
@@ -130,7 +130,17 @@ def get_line_list():
130
  # LINE_LIST = get_line_list() # REMOVED - was causing stale data!
131
 
132
 
 
 
 
133
  def get_kit_line_match():
 
 
 
 
 
 
 
134
  kit_line_match = extract.read_kit_line_match_data()
135
  kit_line_match_dict = kit_line_match.set_index("kit_name")["line_type"].to_dict()
136
 
@@ -152,7 +162,6 @@ def get_kit_line_match():
152
  if line_id is not None:
153
  converted_dict[kit] = line_id
154
  else:
155
- print(f"Warning: Unknown line type '{line_name}' for kit {kit}")
156
  # Default to long line if unknown
157
  converted_dict[kit] = LineType.LONG_LINE
158
  elif isinstance(line_name, (int, float)) and not pd.isna(line_name):
@@ -161,26 +170,29 @@ def get_kit_line_match():
161
  else:
162
  # Missing or empty line type - skip (no production needed for non-standalone masters)
163
  pass # Don't add to converted_dict - these kits won't have line assignments
164
-
 
 
165
  return converted_dict
166
 
167
- KIT_LINE_MATCH_DICT = get_kit_line_match()
 
 
 
168
 
169
 
170
  def get_line_cnt_per_type():
 
171
  try:
172
- # Try to get from streamlit session state (from config page)
173
  import streamlit as st
174
  if hasattr(st, 'session_state') and 'line_counts' in st.session_state:
175
- print(f"Using line counts from config page: {st.session_state.line_counts}")
176
  return st.session_state.line_counts
177
- except Exception as e:
178
- print(f"Could not get line counts from streamlit session: {e}")
179
 
180
- print(f"Loading default line count values from data files")
181
  line_df = extract.read_packaging_line_data()
182
  line_cnt_per_type = line_df.set_index("id")["line_count"].to_dict()
183
- print("line cnt per type", line_cnt_per_type)
184
  return line_cnt_per_type
185
 
186
  # DO NOT load at import time - always call get_line_cnt_per_type() dynamically
@@ -200,28 +212,23 @@ def get_demand_dictionary(force_reload=False):
200
  filter_instance.load_data(force_reload=True)
201
 
202
  demand_dictionary = filter_instance.get_filtered_demand_dictionary()
203
- print(f"๐Ÿ“ˆ FRESH FILTERED DEMAND: {len(demand_dictionary)} products with total demand {sum(demand_dictionary.values())}")
204
- print(f"๐Ÿ”„ LOADED DYNAMICALLY: Reflects current Streamlit configs")
205
  return demand_dictionary
206
  except Exception as e:
207
- print(f"Error loading dynamic demand dictionary: {e}")
208
  raise Exception("Demand dictionary not found with error:"+str(e))
209
 
210
  # DO NOT load at import time - always call get_demand_dictionary() dynamically
211
  # DEMAND_DICTIONARY = get_demand_dictionary() # REMOVED - was causing stale data!
212
 
213
  def get_cost_list_per_emp_shift():
 
214
  try:
215
- # Try to get from streamlit session state (from config page)
216
  import streamlit as st
217
  if hasattr(st, 'session_state') and 'cost_list_per_emp_shift' in st.session_state:
218
- print(f"Using cost list from config page: {st.session_state.cost_list_per_emp_shift}")
219
  return st.session_state.cost_list_per_emp_shift
220
- except Exception as e:
221
- print(f"Could not get cost list from streamlit session: {e}")
222
 
223
- print(f"Loading default cost values")
224
- # Default hourly rates - Important: multiple employment types with different costs
225
  return DefaultConfig.DEFAULT_COST_RATES
226
 
227
  def shift_code_to_name():
@@ -233,18 +240,6 @@ def line_code_to_name():
233
 
234
  # DO NOT load at import time - always call get_cost_list_per_emp_shift() dynamically
235
  # COST_LIST_PER_EMP_SHIFT = get_cost_list_per_emp_shift() # REMOVED - was causing stale data!
236
-
237
-
238
-
239
- # COST_LIST_PER_EMP_SHIFT = { # WH_Workforce_Hourly_Pay_Scale
240
- # "Fixed": {1: 0, 2: 22, 3: 18},
241
- # "Humanizer": {1: 10, 2: 10, 3: 10},
242
- # }
243
-
244
-
245
-
246
-
247
-
248
 
249
 
250
  def get_team_requirements(product_list=None):
@@ -255,20 +250,9 @@ def get_team_requirements(product_list=None):
255
  if product_list is None:
256
  product_list = get_product_list() # Get fresh product list
257
 
258
- try:
259
- # Check if streamlit has this data (for future extension)
260
- # streamlit_team_req = dashboard.team_requirements
261
- # return streamlit_team_req
262
- pass
263
- except Exception as e:
264
- print(f"Using default value for team requirements, extracting from CSV: {e}")
265
-
266
  # Read the kits calculation data directly
267
  kits_df = extract.read_personnel_requirement_data()
268
- # kits_path = "data/real_data_excel/converted_csv/Kits__Calculation.csv"
269
- # kits_df = pd.read_csv(kits_path)
270
- print("kits_df columns:", kits_df.columns.tolist())
271
- print("kits_df head:", kits_df.head())
272
  # Initialize the team requirements dictionary
273
  team_req_dict = {
274
  "UNICEF Fixed term": {},
@@ -277,10 +261,7 @@ def get_team_requirements(product_list=None):
277
 
278
  # Process each product in the product list
279
  for product in product_list:
280
- print("product",product)
281
- print(f"Processing team requirements for product: {product}")
282
  product_data = kits_df[kits_df['Kit'] == product]
283
- print("product_data",product_data)
284
  if not product_data.empty:
285
  # Extract Humanizer and UNICEF staff requirements
286
  humanizer_req = product_data["Humanizer"].iloc[0]
@@ -289,9 +270,6 @@ def get_team_requirements(product_list=None):
289
  # Convert to int (data is already cleaned in extract function)
290
  team_req_dict["Humanizer"][product] = int(humanizer_req)
291
  team_req_dict["UNICEF Fixed term"][product] = int(unicef_req)
292
- else:
293
- print(f"Warning: Product {product} not found in Kits Calculation data, setting requirements to 0")
294
-
295
 
296
  return team_req_dict
297
 
@@ -300,22 +278,22 @@ def get_team_requirements(product_list=None):
300
 
301
 
302
  def get_max_employee_per_type_on_day():
 
303
  try:
304
- # Try to get from streamlit session state (from config page)
305
  import streamlit as st
306
  if hasattr(st, 'session_state') and 'max_employee_per_type_on_day' in st.session_state:
307
- print(f"Using max employee counts from config page: {st.session_state.max_employee_per_type_on_day}")
308
  return st.session_state.max_employee_per_type_on_day
309
- except Exception as e:
310
- print(f"Could not get max employee counts from streamlit session: {e}")
311
 
312
- print(f"Loading default max employee values")
 
313
  max_employee_per_type_on_day = {
314
  "UNICEF Fixed term": {
315
- t: 8 for t in DATE_SPAN
316
  },
317
  "Humanizer": {
318
- t: 10 for t in DATE_SPAN
319
  }
320
  }
321
  return max_employee_per_type_on_day
@@ -332,8 +310,8 @@ def get_max_hour_per_shift_per_person():
332
  import streamlit as st
333
  if hasattr(st, 'session_state') and 'max_hour_per_shift_per_person' in st.session_state:
334
  return st.session_state.max_hour_per_shift_per_person
335
- except Exception as e:
336
- print(f"Could not get max hours per shift from session: {e}")
337
 
338
  # Fallback to default only if not configured by user
339
  return DefaultConfig.MAX_HOUR_PER_SHIFT_PER_PERSON
@@ -341,13 +319,6 @@ def get_max_hour_per_shift_per_person():
341
  # DO NOT load at import time - always call get_max_hour_per_shift_per_person() dynamically
342
  # MAX_HOUR_PER_SHIFT_PER_PERSON = get_max_hour_per_shift_per_person() # REMOVED - was causing stale data!
343
 
344
- # Removed unnecessary getter functions - use direct imports instead:
345
- # - MAX_HOUR_PER_PERSON_PER_DAY
346
- # - MAX_HOUR_PER_SHIFT_PER_PERSON
347
- # - KIT_LINE_MATCH_DICT
348
- # - MAX_PARALLEL_WORKERS
349
- # - EVENING_SHIFT_MODE
350
-
351
  # Keep these complex getters that access DefaultConfig or have complex logic:
352
  def get_evening_shift_demand_threshold():
353
  """Get evening shift demand threshold - checks Streamlit session state first"""
@@ -355,8 +326,8 @@ def get_evening_shift_demand_threshold():
355
  import streamlit as st
356
  if hasattr(st, 'session_state') and 'evening_shift_demand_threshold' in st.session_state:
357
  return st.session_state.evening_shift_demand_threshold
358
- except Exception as e:
359
- print(f"Could not get evening shift threshold from session: {e}")
360
 
361
  # Fallback to default only if not configured by user
362
  return getattr(DefaultConfig, 'EVENING_SHIFT_DEMAND_THRESHOLD', 10000)
@@ -367,8 +338,8 @@ def get_fixed_min_unicef_per_day():
367
  import streamlit as st
368
  if hasattr(st, 'session_state') and 'fixed_min_unicef_per_day' in st.session_state:
369
  return st.session_state.fixed_min_unicef_per_day
370
- except Exception as e:
371
- print(f"Could not get fixed min UNICEF from session: {e}")
372
 
373
  # Fallback to default only if not configured by user
374
  return getattr(DefaultConfig, 'FIXED_MIN_UNICEF_PER_DAY', {1: 1, 2: 1, 3: 1, 4: 1, 5: 1})
@@ -406,22 +377,45 @@ def _ensure_fresh_config():
406
  # after all functions are defined. This ensures all getter functions are available.
407
 
408
  # ---- Kit Hierarchy for Production Ordering ----
 
 
 
409
  def get_kit_hierarchy_data():
410
- try:
411
- # Try to get from streamlit first (future extension)
412
- # streamlit_hierarchy = dashboard.kit_hierarchy_data
413
- # return streamlit_hierarchy
414
- pass
415
- except Exception as e:
416
- print(f"Using default hierarchy data from extract: {e}")
417
-
418
  # Get hierarchy data from extract functions
419
  kit_levels, dependencies, priority_order = extract.get_production_order_data()
420
 
 
 
421
  return kit_levels, dependencies, priority_order
422
 
423
- KIT_LEVELS, KIT_DEPENDENCIES, PRODUCTION_PRIORITY_ORDER = get_kit_hierarchy_data()
424
- print(f"Kit Hierarchy loaded: {len(KIT_LEVELS)} kits, Priority order: {len(PRODUCTION_PRIORITY_ORDER)} items")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
 
426
  def get_max_parallel_workers():
427
  """Get max parallel workers - checks Streamlit session state first"""
@@ -429,8 +423,8 @@ def get_max_parallel_workers():
429
  import streamlit as st
430
  if hasattr(st, 'session_state') and 'max_parallel_workers' in st.session_state:
431
  return st.session_state.max_parallel_workers
432
- except Exception as e:
433
- print(f"Could not get max parallel workers from session: {e}")
434
 
435
  # Fallback to default only if not configured by user
436
  return DefaultConfig.MAX_PARALLEL_WORKERS
@@ -456,10 +450,9 @@ def get_fixed_min_unicef_per_day():
456
  try:
457
  import streamlit as st
458
  if hasattr(st, 'session_state') and 'fixed_min_unicef_per_day' in st.session_state:
459
- print(f"Using fixed minimum UNICEF per day from config page: {st.session_state.fixed_min_unicef_per_day}")
460
  return st.session_state.fixed_min_unicef_per_day
461
- except ImportError:
462
- pass # Streamlit not available in CLI mode
463
 
464
  # Default value - minimum UNICEF Fixed term employees required per day
465
  return 2
@@ -477,19 +470,14 @@ def get_payment_mode_config():
477
  - "partial": Pay only for actual hours worked
478
  """
479
  try:
480
- # Try to get from streamlit session state (from Dataset Metadata page)
481
  import streamlit as st
482
  if hasattr(st, 'session_state') and 'payment_mode_config' in st.session_state:
483
- print(f"Using payment mode config from streamlit session: {st.session_state.payment_mode_config}")
484
  return st.session_state.payment_mode_config
485
- except Exception as e:
486
- print(f"Could not get payment mode config from streamlit session: {e}")
487
 
488
  # Default payment mode configuration
489
- print(f"Loading default payment mode configuration")
490
- payment_mode_config = DefaultConfig.PAYMENT_MODE_CONFIG
491
-
492
- return payment_mode_config
493
 
494
  # DO NOT load at import time - always call get_payment_mode_config() dynamically
495
 
@@ -503,24 +491,8 @@ def get_payment_mode_config():
503
  # This prevents the infinite loop where importing this module triggers Streamlit session access
504
  # which causes the app to reload, which imports this module again, etc.
505
 
506
- # Initialize with default values (will use fallback data when no Streamlit session)
507
- # PER_PRODUCT_SPEED = extract.read_package_speed_data()
508
- # LINE_LIST = get_line_list()
509
- # EMPLOYEE_TYPE_LIST = get_employee_type_list()
510
- # SHIFT_LIST = get_active_shift_list()
511
- # LINE_CNT_PER_TYPE = get_line_cnt_per_type()
512
- # COST_LIST_PER_EMP_SHIFT = get_cost_list_per_emp_shift()
513
- # MAX_EMPLOYEE_PER_TYPE_ON_DAY = get_max_employee_per_type_on_day()
514
- # MAX_HOUR_PER_SHIFT_PER_PERSON = get_max_hour_per_shift_per_person()
515
- # MAX_PARALLEL_WORKERS = get_max_parallel_workers()
516
- # FIXED_MIN_UNICEF_PER_DAY = get_fixed_min_unicef_per_day()
517
- # PAYMENT_MODE_CONFIG = get_payment_mode_config()
518
-
519
- print("โœ… Module-level configuration functions defined (variables initialized dynamically)")
520
-
521
  # Note: These variables are initialized once at import time with default/fallback values.
522
  # To get fresh values after changing Streamlit configuration, either:
523
  # 1. Call the get_*() functions directly (RECOMMENDED for dynamic use)
524
  # 2. Call _ensure_fresh_config() to refresh all module-level variables
525
  # 3. Use importlib.reload() to reload the entire module
526
-
 
130
  # LINE_LIST = get_line_list() # REMOVED - was causing stale data!
131
 
132
 
133
+ # Module-level cache for kit line match - prevents reloading on every access
134
+ _KIT_LINE_MATCH_DICT_CACHE = None
135
+
136
  def get_kit_line_match():
137
+ """Get kit line match dictionary with caching to prevent reload on every import"""
138
+ global _KIT_LINE_MATCH_DICT_CACHE
139
+
140
+ # Return cached value if available
141
+ if _KIT_LINE_MATCH_DICT_CACHE is not None:
142
+ return _KIT_LINE_MATCH_DICT_CACHE
143
+
144
  kit_line_match = extract.read_kit_line_match_data()
145
  kit_line_match_dict = kit_line_match.set_index("kit_name")["line_type"].to_dict()
146
 
 
162
  if line_id is not None:
163
  converted_dict[kit] = line_id
164
  else:
 
165
  # Default to long line if unknown
166
  converted_dict[kit] = LineType.LONG_LINE
167
  elif isinstance(line_name, (int, float)) and not pd.isna(line_name):
 
170
  else:
171
  # Missing or empty line type - skip (no production needed for non-standalone masters)
172
  pass # Don't add to converted_dict - these kits won't have line assignments
173
+
174
+ # Cache the result
175
+ _KIT_LINE_MATCH_DICT_CACHE = converted_dict
176
  return converted_dict
177
 
178
+ # Lazy property - will be initialized on first access
179
+ @property
180
+ def KIT_LINE_MATCH_DICT():
181
+ return get_kit_line_match()
182
 
183
 
184
  def get_line_cnt_per_type():
185
+ """Get line counts from session state or default"""
186
  try:
 
187
  import streamlit as st
188
  if hasattr(st, 'session_state') and 'line_counts' in st.session_state:
 
189
  return st.session_state.line_counts
190
+ except:
191
+ pass
192
 
193
+ # Default: load from data files
194
  line_df = extract.read_packaging_line_data()
195
  line_cnt_per_type = line_df.set_index("id")["line_count"].to_dict()
 
196
  return line_cnt_per_type
197
 
198
  # DO NOT load at import time - always call get_line_cnt_per_type() dynamically
 
212
  filter_instance.load_data(force_reload=True)
213
 
214
  demand_dictionary = filter_instance.get_filtered_demand_dictionary()
 
 
215
  return demand_dictionary
216
  except Exception as e:
 
217
  raise Exception("Demand dictionary not found with error:"+str(e))
218
 
219
  # DO NOT load at import time - always call get_demand_dictionary() dynamically
220
  # DEMAND_DICTIONARY = get_demand_dictionary() # REMOVED - was causing stale data!
221
 
222
  def get_cost_list_per_emp_shift():
223
+ """Get cost list from session state or default"""
224
  try:
 
225
  import streamlit as st
226
  if hasattr(st, 'session_state') and 'cost_list_per_emp_shift' in st.session_state:
 
227
  return st.session_state.cost_list_per_emp_shift
228
+ except:
229
+ pass
230
 
231
+ # Default hourly rates
 
232
  return DefaultConfig.DEFAULT_COST_RATES
233
 
234
  def shift_code_to_name():
 
240
 
241
  # DO NOT load at import time - always call get_cost_list_per_emp_shift() dynamically
242
  # COST_LIST_PER_EMP_SHIFT = get_cost_list_per_emp_shift() # REMOVED - was causing stale data!
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
 
245
  def get_team_requirements(product_list=None):
 
250
  if product_list is None:
251
  product_list = get_product_list() # Get fresh product list
252
 
 
 
 
 
 
 
 
 
253
  # Read the kits calculation data directly
254
  kits_df = extract.read_personnel_requirement_data()
255
+
 
 
 
256
  # Initialize the team requirements dictionary
257
  team_req_dict = {
258
  "UNICEF Fixed term": {},
 
261
 
262
  # Process each product in the product list
263
  for product in product_list:
 
 
264
  product_data = kits_df[kits_df['Kit'] == product]
 
265
  if not product_data.empty:
266
  # Extract Humanizer and UNICEF staff requirements
267
  humanizer_req = product_data["Humanizer"].iloc[0]
 
270
  # Convert to int (data is already cleaned in extract function)
271
  team_req_dict["Humanizer"][product] = int(humanizer_req)
272
  team_req_dict["UNICEF Fixed term"][product] = int(unicef_req)
 
 
 
273
 
274
  return team_req_dict
275
 
 
278
 
279
 
280
  def get_max_employee_per_type_on_day():
281
+ """Get max employee counts from session state or default"""
282
  try:
 
283
  import streamlit as st
284
  if hasattr(st, 'session_state') and 'max_employee_per_type_on_day' in st.session_state:
 
285
  return st.session_state.max_employee_per_type_on_day
286
+ except:
287
+ pass
288
 
289
+ # Default: get date span dynamically
290
+ date_span, _, _ = get_date_span()
291
  max_employee_per_type_on_day = {
292
  "UNICEF Fixed term": {
293
+ t: 8 for t in date_span
294
  },
295
  "Humanizer": {
296
+ t: 10 for t in date_span
297
  }
298
  }
299
  return max_employee_per_type_on_day
 
310
  import streamlit as st
311
  if hasattr(st, 'session_state') and 'max_hour_per_shift_per_person' in st.session_state:
312
  return st.session_state.max_hour_per_shift_per_person
313
+ except:
314
+ pass
315
 
316
  # Fallback to default only if not configured by user
317
  return DefaultConfig.MAX_HOUR_PER_SHIFT_PER_PERSON
 
319
  # DO NOT load at import time - always call get_max_hour_per_shift_per_person() dynamically
320
  # MAX_HOUR_PER_SHIFT_PER_PERSON = get_max_hour_per_shift_per_person() # REMOVED - was causing stale data!
321
 
 
 
 
 
 
 
 
322
  # Keep these complex getters that access DefaultConfig or have complex logic:
323
  def get_evening_shift_demand_threshold():
324
  """Get evening shift demand threshold - checks Streamlit session state first"""
 
326
  import streamlit as st
327
  if hasattr(st, 'session_state') and 'evening_shift_demand_threshold' in st.session_state:
328
  return st.session_state.evening_shift_demand_threshold
329
+ except:
330
+ pass
331
 
332
  # Fallback to default only if not configured by user
333
  return getattr(DefaultConfig, 'EVENING_SHIFT_DEMAND_THRESHOLD', 10000)
 
338
  import streamlit as st
339
  if hasattr(st, 'session_state') and 'fixed_min_unicef_per_day' in st.session_state:
340
  return st.session_state.fixed_min_unicef_per_day
341
+ except:
342
+ pass
343
 
344
  # Fallback to default only if not configured by user
345
  return getattr(DefaultConfig, 'FIXED_MIN_UNICEF_PER_DAY', {1: 1, 2: 1, 3: 1, 4: 1, 5: 1})
 
377
  # after all functions are defined. This ensures all getter functions are available.
378
 
379
  # ---- Kit Hierarchy for Production Ordering ----
380
+ # Module-level cache for hierarchy - prevents reloading on every import
381
+ _KIT_HIERARCHY_CACHE = None
382
+
383
  def get_kit_hierarchy_data():
384
+ """Load kit hierarchy data from extract functions with caching"""
385
+ global _KIT_HIERARCHY_CACHE
386
+
387
+ # Return cached value if available
388
+ if _KIT_HIERARCHY_CACHE is not None:
389
+ return _KIT_HIERARCHY_CACHE
390
+
 
391
  # Get hierarchy data from extract functions
392
  kit_levels, dependencies, priority_order = extract.get_production_order_data()
393
 
394
+ # Cache the result
395
+ _KIT_HIERARCHY_CACHE = (kit_levels, dependencies, priority_order)
396
  return kit_levels, dependencies, priority_order
397
 
398
+ # Lazy properties - will be initialized on first access
399
+ def get_kit_levels():
400
+ """Get kit levels lazily"""
401
+ kit_levels, _, _ = get_kit_hierarchy_data()
402
+ return kit_levels
403
+
404
+ def get_kit_dependencies():
405
+ """Get kit dependencies lazily"""
406
+ _, dependencies, _ = get_kit_hierarchy_data()
407
+ return dependencies
408
+
409
+ def get_production_priority_order():
410
+ """Get production priority order lazily"""
411
+ _, _, priority_order = get_kit_hierarchy_data()
412
+ return priority_order
413
+
414
+ # Initialize only on first access - use properties
415
+ KIT_LEVELS = None
416
+ KIT_DEPENDENCIES = None
417
+ PRODUCTION_PRIORITY_ORDER = None
418
+ KIT_LINE_MATCH_DICT = None
419
 
420
  def get_max_parallel_workers():
421
  """Get max parallel workers - checks Streamlit session state first"""
 
423
  import streamlit as st
424
  if hasattr(st, 'session_state') and 'max_parallel_workers' in st.session_state:
425
  return st.session_state.max_parallel_workers
426
+ except:
427
+ pass
428
 
429
  # Fallback to default only if not configured by user
430
  return DefaultConfig.MAX_PARALLEL_WORKERS
 
450
  try:
451
  import streamlit as st
452
  if hasattr(st, 'session_state') and 'fixed_min_unicef_per_day' in st.session_state:
 
453
  return st.session_state.fixed_min_unicef_per_day
454
+ except:
455
+ pass
456
 
457
  # Default value - minimum UNICEF Fixed term employees required per day
458
  return 2
 
470
  - "partial": Pay only for actual hours worked
471
  """
472
  try:
 
473
  import streamlit as st
474
  if hasattr(st, 'session_state') and 'payment_mode_config' in st.session_state:
 
475
  return st.session_state.payment_mode_config
476
+ except:
477
+ pass
478
 
479
  # Default payment mode configuration
480
+ return DefaultConfig.PAYMENT_MODE_CONFIG
 
 
 
481
 
482
  # DO NOT load at import time - always call get_payment_mode_config() dynamically
483
 
 
491
  # This prevents the infinite loop where importing this module triggers Streamlit session access
492
  # which causes the app to reload, which imports this module again, etc.
493
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494
  # Note: These variables are initialized once at import time with default/fallback values.
495
  # To get fresh values after changing Streamlit configuration, either:
496
  # 1. Call the get_*() functions directly (RECOMMENDED for dynamic use)
497
  # 2. Call _ensure_fresh_config() to refresh all module-level variables
498
  # 3. Use importlib.reload() to reload the entire module
 
src/demand_filtering.py CHANGED
@@ -140,7 +140,7 @@ class DemandFilter:
140
 
141
  # Calculate available capacity hours
142
  # Available hours = line_count ร— total_hours_per_day ร— days_in_period
143
- available_hours = line_count * total_hours_per_day * len(DATE_SPAN)
144
 
145
  return available_hours
146
 
 
140
 
141
  # Calculate available capacity hours
142
  # Available hours = line_count ร— total_hours_per_day ร— days_in_period
143
+ available_hours = line_count * total_hours_per_day * len(date_span)
144
 
145
  return available_hours
146
 
src/models/optimizer_real.py CHANGED
@@ -28,13 +28,13 @@ from src.config.optimization_config import (
28
  FIXED_STAFF_CONSTRAINT_MODE, # not used in fixed-team model (๋™์‹œ ํˆฌ์ž…์ด๋ผ ๋ฌด์˜๋ฏธ)
29
  get_team_requirements, # DYNAMIC: {emp_type: {product: team_size}} from Kits_Calculation.csv
30
  get_payment_mode_config, # DYNAMIC: {shift: 'bulk'/'partial'} payment mode configuration
31
- KIT_LINE_MATCH_DICT,
32
  EVENING_SHIFT_MODE,
33
  EVENING_SHIFT_DEMAND_THRESHOLD,
34
  # Hierarchy variables for production ordering
35
- KIT_LEVELS, # {kit_id: level} where 0=prepack, 1=subkit, 2=master
36
- KIT_DEPENDENCIES, # {kit_id: [dependency_list]}
37
- PRODUCTION_PRIORITY_ORDER, # [kit_ids] sorted by production priority
38
  # Fixed staffing requirements
39
  get_fixed_min_unicef_per_day, # DYNAMIC: Minimum UNICEF employees required per day
40
  )
@@ -42,7 +42,7 @@ from src.config.optimization_config import (
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
@@ -171,6 +171,13 @@ def sort_products_by_hierarchy(product_list):
171
  # Dependency ordering is now handled by topological sorting in sort_products_by_hierarchy()
172
 
173
  def run_optimization_for_week():
 
 
 
 
 
 
 
174
  # *** CRITICAL: Load fresh data to reflect current Streamlit configs ***
175
  print("\n" + "="*60)
176
  print("๐Ÿ”„ LOADING FRESH DATA FOR OPTIMIZATION")
 
28
  FIXED_STAFF_CONSTRAINT_MODE, # not used in fixed-team model (๋™์‹œ ํˆฌ์ž…์ด๋ผ ๋ฌด์˜๋ฏธ)
29
  get_team_requirements, # DYNAMIC: {emp_type: {product: team_size}} from Kits_Calculation.csv
30
  get_payment_mode_config, # DYNAMIC: {shift: 'bulk'/'partial'} payment mode configuration
31
+ get_kit_line_match, # DYNAMIC: Get kit line match lazily
32
  EVENING_SHIFT_MODE,
33
  EVENING_SHIFT_DEMAND_THRESHOLD,
34
  # Hierarchy variables for production ordering
35
+ get_kit_levels, # DYNAMIC: {kit_id: level} where 0=prepack, 1=subkit, 2=master
36
+ get_kit_dependencies, # DYNAMIC: {kit_id: [dependency_list]}
37
+ get_production_priority_order, # DYNAMIC: [kit_ids] sorted by production priority
38
  # Fixed staffing requirements
39
  get_fixed_min_unicef_per_day, # DYNAMIC: Minimum UNICEF employees required per day
40
  )
 
42
 
43
 
44
  # 2) kit_line_match - no printing to avoid spam
45
+ # KIT_LINE_MATCH_DICT loaded lazily in functions that need it
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
 
171
  # Dependency ordering is now handled by topological sorting in sort_products_by_hierarchy()
172
 
173
  def run_optimization_for_week():
174
+ # Load hierarchy data lazily at the start of optimization
175
+ global KIT_LEVELS, KIT_DEPENDENCIES, PRODUCTION_PRIORITY_ORDER, KIT_LINE_MATCH_DICT
176
+ KIT_LEVELS = get_kit_levels()
177
+ KIT_DEPENDENCIES = get_kit_dependencies()
178
+ PRODUCTION_PRIORITY_ORDER = get_production_priority_order()
179
+ KIT_LINE_MATCH_DICT = get_kit_line_match()
180
+
181
  # *** CRITICAL: Load fresh data to reflect current Streamlit configs ***
182
  print("\n" + "="*60)
183
  print("๐Ÿ”„ LOADING FRESH DATA FOR OPTIMIZATION")
src/models/optimizer_real_import_fix.txt ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ---- config import (ํ”„๋กœ์ ํŠธ ๊ฒฝ๋กœ์— ๋งž์ถฐ ์กฐ์ •) ----
2
+ from src.config.optimization_config import (
3
+ get_date_span, # DYNAMIC: Get date span dynamically
4
+ get_product_list, # DYNAMIC: list of products (e.g., ['A','B',...])
5
+ get_employee_type_list, # DYNAMIC: e.g., ['UNICEF Fixed term','Humanizer']
6
+ get_active_shift_list, # DYNAMIC: e.g., [1,2,3]
7
+ get_line_list, # DYNAMIC: e.g., [6,7] (line type ids)
8
+ get_line_cnt_per_type, # DYNAMIC: {6: count_of_long_lines, 7: count_of_short_lines}
9
+ get_demand_dictionary, # DYNAMIC: {product: total_units_over_period}
10
+ get_cost_list_per_emp_shift, # DYNAMIC: {emp_type: {shift: cost_per_hour}}
11
+ get_max_employee_per_type_on_day, # DYNAMIC: {emp_type: {t: headcount}}
12
+ MAX_HOUR_PER_PERSON_PER_DAY, # e.g., 14
13
+ get_max_hour_per_shift_per_person, # DYNAMIC: {1: hours, 2: hours, 3: hours}
14
+ get_max_parallel_workers, # DYNAMIC: {6: max_workers, 7: max_workers}
15
+ FIXED_STAFF_CONSTRAINT_MODE, # not used in fixed-team model (๋™์‹œ ํˆฌ์ž…์ด๋ผ ๋ฌด์˜๋ฏธ)
16
+ get_team_requirements, # DYNAMIC: {emp_type: {product: team_size}} from Kits_Calculation.csv
17
+ get_payment_mode_config, # DYNAMIC: {shift: 'bulk'/'partial'} payment mode configuration
18
+ get_kit_line_match, # DYNAMIC: Get kit line match lazily
19
+ EVENING_SHIFT_MODE,
20
+ EVENING_SHIFT_DEMAND_THRESHOLD,
21
+ # Hierarchy variables for production ordering - now using getter functions
22
+ get_kit_levels, # DYNAMIC: {kit_id: level} where 0=prepack, 1=subkit, 2=master
23
+ get_kit_dependencies, # DYNAMIC: {kit_id: [dependency_list]}
24
+ get_production_priority_order, # DYNAMIC: [kit_ids] sorted by production priority
25
+ # Fixed staffing requirements
26
+ get_fixed_min_unicef_per_day, # DYNAMIC: Minimum UNICEF employees required per day
27
+ )
28
+
29
+
30
+
31
+ # 2) kit_line_match - lazy load on first use
32
+ KIT_LINE_MATCH_DICT = get_kit_line_match()
33
+
34
+
src/preprocess/kit_composition_cleaner.py CHANGED
@@ -109,8 +109,7 @@ class KitCompositionCleaner:
109
  })
110
 
111
  self.master_df = pd.DataFrame(master_data)
112
- print(f"Created {len(self.master_df)} master kit records")
113
- print(f"Standalone masters with 'long line': {sum(self.master_df['line_type'] == 'long line')}")
114
 
115
  return self.master_df
116
 
 
109
  })
110
 
111
  self.master_df = pd.DataFrame(master_data)
112
+
 
113
 
114
  return self.master_df
115