hualinxin8615 commited on
Commit
8c1efcb
·
verified ·
1 Parent(s): cefcd1e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +207 -211
app.py CHANGED
@@ -13,14 +13,111 @@ DATA_STORAGE_REPO = "CIV3283/Data_Storage"
13
  DATA_BRANCH_NAME = "data_branch"
14
  LOCAL_DATA_DIR = "temp_data_storage"
15
  MIN_IDLE_MINUTES = 10 # Minimum idle time required for space assignment
16
- ALLOCATION_RECORD_FILE = "allocation_records.csv" # New file for tracking allocations
17
  ALLOCATION_LOCK_DURATION = 5 # Lock duration in minutes
18
 
19
- # 缓存配置
20
- ACTIVITY_CACHE_FILE = "space_activity_cache.json"
21
  CACHE_UPDATE_INTERVAL = 600 # 10分钟 = 600秒
22
  DATA_SYNC_INTERVAL = 600 # 数据同步间隔,10分钟 = 600秒
23
  CACHE_LOCK = threading.Lock()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
  # 全局数据管理器
26
  class DataManager:
@@ -153,9 +250,9 @@ class DataManager:
153
  print("[DataManager] Background sync thread stopping...")
154
 
155
  class SpaceActivityCache:
 
156
  def __init__(self, data_manager):
157
  self.data_manager = data_manager
158
- self.cache_file_path = os.path.join(LOCAL_DATA_DIR, ACTIVITY_CACHE_FILE)
159
  self._cache_data = {}
160
  self._last_update = None
161
  self._update_thread = None
@@ -233,9 +330,6 @@ class SpaceActivityCache:
233
  with CACHE_LOCK:
234
  self._cache_data = new_cache_data
235
  self._last_update = datetime.now()
236
-
237
- # 保存到文件
238
- self._save_cache_to_file()
239
 
240
  elapsed_time = time.time() - start_time
241
  print(f"[SpaceActivityCache] Cache updated in {elapsed_time:.2f}s, {len(available_spaces)} spaces processed")
@@ -296,37 +390,12 @@ class SpaceActivityCache:
296
  print(f"[SpaceActivityCache] Error reading {csv_file_path}: {e}")
297
  return datetime.min, "read_error"
298
 
299
- def _save_cache_to_file(self):
300
- """保存缓存到文件"""
301
- try:
302
- with open(self.cache_file_path, 'w', encoding='utf-8') as f:
303
- json.dump(self._cache_data, f, indent=2, ensure_ascii=False)
304
- print(f"[SpaceActivityCache] Cache saved to {self.cache_file_path}")
305
- except Exception as e:
306
- print(f"[SpaceActivityCache] Error saving cache to file: {e}")
307
-
308
- def _load_cache_from_file(self):
309
- """从文件加载缓存"""
310
- try:
311
- if os.path.exists(self.cache_file_path):
312
- with open(self.cache_file_path, 'r', encoding='utf-8') as f:
313
- data = json.load(f)
314
- print(f"[SpaceActivityCache] Cache loaded from file")
315
- return data
316
- except Exception as e:
317
- print(f"[SpaceActivityCache] Error loading cache from file: {e}")
318
- return {}
319
-
320
  def get_space_activity(self, space_name):
321
  """获取指定空间的活动信息"""
322
  with CACHE_LOCK:
323
  if not self._cache_data or 'spaces' not in self._cache_data:
324
- # 缓存为空,尝试从文件加载
325
- self._cache_data = self._load_cache_from_file()
326
- if not self._cache_data:
327
- # 文件也没有,立即更新一次
328
- print("[SpaceActivityCache] No cache available, updating immediately...")
329
- self._update_cache()
330
 
331
  spaces_data = self._cache_data.get('spaces', {})
332
  space_info = spaces_data.get(space_name, {})
@@ -351,10 +420,8 @@ class SpaceActivityCache:
351
  """获取所有空间的活动信息"""
352
  with CACHE_LOCK:
353
  if not self._cache_data or 'spaces' not in self._cache_data:
354
- self._cache_data = self._load_cache_from_file()
355
- if not self._cache_data:
356
- print("[SpaceActivityCache] No cache available, updating immediately...")
357
- self._update_cache()
358
 
359
  result = {}
360
  spaces_data = self._cache_data.get('spaces', {})
@@ -439,7 +506,8 @@ class LocalAllocationTracker:
439
 
440
  for space_name in expired_spaces:
441
  del self._recent_allocations[space_name]
442
- print(f"[LocalAllocationTracker] Cleaned up expired allocation: {space_name}")
 
443
 
444
  def is_recently_allocated_locally(self, space_name):
445
  """检查空间是否在本地被最近分配过"""
@@ -483,6 +551,7 @@ class LocalAllocationTracker:
483
  data_manager = DataManager()
484
  activity_cache = None
485
  local_tracker = LocalAllocationTracker()
 
486
 
487
  def init_system():
488
  """初始化整个系统 - 只在启动时调用一次"""
@@ -500,7 +569,7 @@ def init_system():
500
  print("[init_system] System initialization completed")
501
 
502
  def analyze_space_activity_cached():
503
- """使用缓存的空间活动分析 - 不再需要传入参数"""
504
  global activity_cache
505
 
506
  if activity_cache is None:
@@ -508,7 +577,6 @@ def analyze_space_activity_cached():
508
 
509
  # 从数据管理器获取可用空间列表(不触发数据同步)
510
  available_spaces = data_manager.get_available_spaces()
511
- repo_dir = data_manager.get_repo_dir()
512
 
513
  space_activity = []
514
  current_time = datetime.now()
@@ -520,10 +588,11 @@ def analyze_space_activity_cached():
520
  # 获取所有空间的缓存活动数据
521
  all_spaces_activity = activity_cache.get_all_spaces_activity()
522
 
523
- # Read allocation records to filter out recently allocated spaces
524
- active_allocations = read_allocation_records(repo_dir)
525
 
526
  print(f"[analyze_space_activity_cached] Analyzing {len(available_spaces)} spaces using cached data...")
 
527
 
528
  for space_name in available_spaces:
529
  # 从缓存获取活动信息
@@ -547,12 +616,12 @@ def analyze_space_activity_cached():
547
  status = f"Idle for {idle_minutes:.1f} minutes (cached)"
548
  last_activity_str = last_activity.strftime('%Y-%m-%d %H:%M:%S')
549
 
550
- # Check if space is recently allocated (remote)
551
- is_recently_allocated_remote = space_name in active_allocations
552
- if is_recently_allocated_remote:
553
  alloc_info = active_allocations[space_name]
554
  minutes_until_free = (alloc_info['expires_at'] - current_time).total_seconds() / 60
555
- status += f" (Recently allocated remotely, free in {minutes_until_free:.1f} min)"
556
 
557
  # Check if space is recently allocated (local)
558
  is_recently_allocated_local, local_student = local_tracker.is_recently_allocated_locally(space_name)
@@ -565,7 +634,7 @@ def analyze_space_activity_cached():
565
  'last_activity_str': last_activity_str,
566
  'idle_minutes': idle_minutes,
567
  'status': status,
568
- 'is_recently_allocated_remote': is_recently_allocated_remote,
569
  'is_recently_allocated_local': is_recently_allocated_local,
570
  'cached_status': cached_status
571
  })
@@ -577,152 +646,18 @@ def analyze_space_activity_cached():
577
 
578
  return space_activity
579
 
580
- def read_allocation_records(repo_dir):
581
- """Read and parse allocation records, returning only non-expired allocations"""
582
- allocation_file = os.path.join(repo_dir, ALLOCATION_RECORD_FILE)
583
- current_time = datetime.now()
584
- active_allocations = {}
585
-
586
- try:
587
- if not os.path.exists(allocation_file):
588
- print(f"[read_allocation_records] Allocation file not found, returning empty")
589
- return active_allocations
590
-
591
- # Use the same CSV reading approach as query logs
592
- with open(allocation_file, 'r', encoding='utf-8') as f:
593
- csv_reader = csv.reader(f)
594
-
595
- # Skip header row
596
- try:
597
- header = next(csv_reader)
598
- print(f"[read_allocation_records] CSV header: {header}")
599
- except StopIteration:
600
- print(f"[read_allocation_records] Empty allocation file")
601
- return active_allocations
602
-
603
- # Read all data rows
604
- rows = []
605
- try:
606
- for row in csv_reader:
607
- if row and len(row) >= 4: # Skip empty rows and ensure minimum columns
608
- rows.append(row)
609
- except Exception as csv_error:
610
- print(f"[read_allocation_records] CSV parsing error: {csv_error}")
611
- return active_allocations
612
-
613
- if not rows:
614
- print(f"[read_allocation_records] No allocation data rows")
615
- return active_allocations
616
-
617
- # Process each allocation record
618
- for row in rows:
619
- try:
620
- space_name = row[0].strip()
621
- student_id = row[1].strip()
622
- allocated_time_str = row[2].strip()
623
- expires_at_str = row[3].strip()
624
-
625
- allocated_time = datetime.strptime(allocated_time_str, '%Y-%m-%d %H:%M:%S')
626
- expires_at = datetime.strptime(expires_at_str, '%Y-%m-%d %H:%M:%S')
627
-
628
- # Only keep non-expired allocations
629
- if expires_at > current_time:
630
- active_allocations[space_name] = {
631
- 'student_id': student_id,
632
- 'allocated_time': allocated_time,
633
- 'expires_at': expires_at
634
- }
635
- print(f"[read_allocation_records] Active allocation: {space_name} -> {student_id}")
636
- else:
637
- print(f"[read_allocation_records] Expired allocation: {space_name} (expired at {expires_at})")
638
-
639
- except (ValueError, IndexError) as ve:
640
- print(f"[read_allocation_records] Error parsing allocation row {row}: {ve}")
641
- continue
642
-
643
- except Exception as e:
644
- print(f"[read_allocation_records] Error reading allocation records: {e}")
645
- return {}
646
-
647
- print(f"[read_allocation_records] Found {len(active_allocations)} active allocations")
648
- return active_allocations
649
-
650
- def write_allocation_record(space_name, student_id, repo_dir):
651
- """Write a new allocation record to the file"""
652
- allocation_file = os.path.join(repo_dir, ALLOCATION_RECORD_FILE)
653
- current_time = datetime.now()
654
- expires_at = current_time + timedelta(minutes=ALLOCATION_LOCK_DURATION)
655
-
656
- try:
657
- # Read existing records to filter out expired ones
658
- existing_records = []
659
- current_time_check = datetime.now()
660
-
661
- if os.path.exists(allocation_file):
662
- with open(allocation_file, 'r', encoding='utf-8') as f:
663
- csv_reader = csv.reader(f)
664
- try:
665
- header = next(csv_reader) # Skip header
666
- for row in csv_reader:
667
- if len(row) >= 4: # Ensure row has all required columns
668
- try:
669
- row_expires_at = datetime.strptime(row[3], '%Y-%m-%d %H:%M:%S')
670
- # Only keep non-expired records
671
- if row_expires_at > current_time_check:
672
- existing_records.append(row)
673
- except ValueError:
674
- continue
675
- except StopIteration:
676
- pass # Empty file or no header
677
-
678
- # Append new allocation record
679
- with open(allocation_file, 'w', newline='', encoding='utf-8') as f:
680
- writer = csv.writer(f)
681
- # Write header
682
- writer.writerow(['space_name', 'student_id', 'allocated_time', 'expires_at'])
683
- # Write existing non-expired records
684
- writer.writerows(existing_records)
685
- # Write new allocation
686
- new_record = [
687
- space_name,
688
- student_id,
689
- current_time.strftime('%Y-%m-%d %H:%M:%S'),
690
- expires_at.strftime('%Y-%m-%d %H:%M:%S')
691
- ]
692
- writer.writerow(new_record)
693
-
694
- print(f"[write_allocation_record] Recorded allocation: {space_name} -> {student_id} (expires at {expires_at})")
695
- return True
696
-
697
- except Exception as e:
698
- print(f"[write_allocation_record] Error writing allocation record: {e}")
699
- return False
700
-
701
- def simple_push_allocation_record(repo, space_name, student_id):
702
- """Simple push allocation record using Repository API"""
703
- try:
704
- # Use Repository's push_to_hub method instead of git commands
705
- commit_message = f"Allocate {space_name} to student {student_id}"
706
-
707
- # Push the allocation file to hub
708
- repo.push_to_hub(
709
- commit_message=commit_message,
710
- blocking=True, # Wait for completion
711
- auto_lfs_prune=True
712
- )
713
-
714
- print(f"[simple_push_allocation_record] Successfully pushed allocation record")
715
- return True
716
-
717
- except Exception as e:
718
- print(f"[simple_push_allocation_record] Error pushing allocation record: {e}")
719
- print(f"[simple_push_allocation_record] Allocation recorded locally, sync will happen eventually")
720
- return False
721
-
722
  def create_status_display(space_activity):
723
  """Create formatted status display for all spaces with proper line breaks"""
724
  status_display = "📊 **Current Space Status (sorted by availability):**<br><br>"
725
 
 
 
 
 
 
 
 
 
726
  # 显示本地分配记录摘要
727
  local_summary = local_tracker.get_recent_allocations_summary()
728
  if local_summary:
@@ -739,16 +674,16 @@ def create_status_display(space_activity):
739
  return status_display
740
 
741
  def select_space_with_enhanced_collision_avoidance_cached(space_activity, student_id):
742
- """使用缓存的增强防撞空间选择函数"""
743
 
744
  print(f"[select_space_with_enhanced_collision_avoidance_cached] Starting selection for student: {student_id}")
745
 
746
  # 第一步:过滤掉不符合基本条件的空间
747
  basic_available_spaces = []
748
  for space in space_activity:
749
- # 检查基本条件
750
  if (space['idle_minutes'] >= MIN_IDLE_MINUTES and
751
- not space['is_recently_allocated_remote'] and
752
  not space['is_recently_allocated_local']):
753
  basic_available_spaces.append(space)
754
 
@@ -757,14 +692,14 @@ def select_space_with_enhanced_collision_avoidance_cached(space_activity, studen
757
  if not basic_available_spaces:
758
  # 生成详细的错误信息
759
  idle_spaces = [s for s in space_activity if s['idle_minutes'] >= MIN_IDLE_MINUTES]
760
- remote_allocated = [s for s in space_activity if s['is_recently_allocated_remote']]
761
  local_allocated = [s for s in space_activity if s['is_recently_allocated_local']]
762
 
763
  error_parts = []
764
  if not idle_spaces:
765
  error_parts.append(f"all spaces used within {MIN_IDLE_MINUTES} minutes")
766
- if remote_allocated:
767
- error_parts.append(f"{len(remote_allocated)} spaces remotely allocated")
768
  if local_allocated:
769
  error_parts.append(f"{len(local_allocated)} spaces locally allocated")
770
 
@@ -805,21 +740,13 @@ def select_space_with_enhanced_collision_avoidance_cached(space_activity, studen
805
  print(f"[select_space_with_enhanced_collision_avoidance_cached] No alternatives available")
806
  raise gr.Error(error_msg, duration=8)
807
 
808
- # 第四步:立即记录本地分配(在写入文件之前)
809
  local_tracker.record_local_allocation(space_name, student_id)
810
- print(f"[select_space_with_enhanced_collision_avoidance_cached] Local allocation recorded BEFORE file write")
811
 
812
- # 第五步:记录到文件远程
813
- repo = data_manager.get_repo()
814
- repo_dir = data_manager.get_repo_dir()
815
-
816
- write_success = write_allocation_record(space_name, student_id, repo_dir)
817
- if write_success:
818
- push_success = simple_push_allocation_record(repo, space_name, student_id)
819
- if not push_success:
820
- print(f"[select_space_with_enhanced_collision_avoidance_cached] Warning: Allocation recorded locally but not synced to remote")
821
- else:
822
- print(f"[select_space_with_enhanced_collision_avoidance_cached] Warning: Failed to record allocation to file")
823
 
824
  # 第六步:生成结果(使用带缓存信息的状态显示)
825
  status_display = create_status_display_with_cache_info(space_activity)
@@ -876,7 +803,7 @@ def redirect_to_space(redirect_url, selected_space, status_display):
876
  {status_display}
877
  </div>
878
  <p style="margin-bottom: 0; color: #666; font-size: 14px; margin-top: 15px;">
879
- <strong>Enhanced Selection Algorithm:</strong> Spaces idle for ≥{MIN_IDLE_MINUTES} minutes, not remotely allocated, and not locally allocated are eligible. The system includes real-time collision avoidance to prevent multiple students from being assigned to the same space.
880
  </p>
881
  </div>
882
 
@@ -891,7 +818,7 @@ def redirect_to_space(redirect_url, selected_space, status_display):
891
  return gr.HTML(redirect_html)
892
 
893
  def load_balance_user_cached(student_id):
894
- """使用缓存的负载均衡函数 - 不再初始化数据连接"""
895
  print(f"[load_balance_user_cached] Starting cached load balancing for student ID: {student_id}")
896
 
897
  # 检查系统是否已初始化
@@ -934,6 +861,14 @@ def create_status_display_with_cache_info(space_activity):
934
  status_display += f"🔄 **Cache Status:** {cache_status}<br>"
935
  status_display += f"📋 **Cached Spaces:** {cache_info['spaces_count']}<br><br>"
936
 
 
 
 
 
 
 
 
 
937
  # 显示本地分配记录摘要
938
  local_summary = local_tracker.get_recent_allocations_summary()
939
  if local_summary:
@@ -1024,6 +959,20 @@ def force_data_sync():
1024
  except Exception as e:
1025
  return {"error": str(e)}
1026
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1027
  # 添加可选的管理界面(用于调试)
1028
  def create_admin_interface():
1029
  """创建管理界面(可选)- 增强版本"""
@@ -1046,6 +995,14 @@ def create_admin_interface():
1046
  data_info_display = gr.JSON(label="Data Manager Information")
1047
  data_status_message = gr.Markdown("")
1048
 
 
 
 
 
 
 
 
 
1049
  def check_cache_status():
1050
  try:
1051
  status = get_cache_status()
@@ -1106,6 +1063,33 @@ def create_admin_interface():
1106
  except Exception as e:
1107
  return {"error": str(e)}, f"❌ Error syncing data: {str(e)}"
1108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1109
  # Cache tab event handlers
1110
  cache_status_btn.click(
1111
  fn=check_cache_status,
@@ -1127,6 +1111,17 @@ def create_admin_interface():
1127
  fn=force_sync,
1128
  outputs=[data_info_display, data_status_message]
1129
  )
 
 
 
 
 
 
 
 
 
 
 
1130
 
1131
  return admin_interface
1132
 
@@ -1154,6 +1149,7 @@ if __name__ == "__main__":
1154
  print("🚀 Initializing CIV3283 Load Distributor System...")
1155
  init_system()
1156
  print("✅ System initialization completed successfully")
 
1157
 
1158
  # 检查是否需要管理界面
1159
  enable_admin = "--admin" in sys.argv or os.environ.get("ENABLE_ADMIN", "false").lower() == "true"
 
13
  DATA_BRANCH_NAME = "data_branch"
14
  LOCAL_DATA_DIR = "temp_data_storage"
15
  MIN_IDLE_MINUTES = 10 # Minimum idle time required for space assignment
 
16
  ALLOCATION_LOCK_DURATION = 5 # Lock duration in minutes
17
 
18
+ # 缓存配置 - 全部改为内存存储
 
19
  CACHE_UPDATE_INTERVAL = 600 # 10分钟 = 600秒
20
  DATA_SYNC_INTERVAL = 600 # 数据同步间隔,10分钟 = 600秒
21
  CACHE_LOCK = threading.Lock()
22
+ ALLOCATION_LOCK = threading.Lock()
23
+
24
+ # 全局内存存储
25
+ class MemoryAllocationStore:
26
+ """内存中的分配记录存储"""
27
+ def __init__(self):
28
+ self._allocations = {} # {space_name: {'student_id': str, 'allocated_time': datetime, 'expires_at': datetime}}
29
+ self._lock = threading.Lock()
30
+ self._cleanup_interval = 60 # 每分钟清理一次过期记录
31
+
32
+ # 启动后台清理线程
33
+ self._start_cleanup_thread()
34
+
35
+ def _start_cleanup_thread(self):
36
+ """启动后台清理过期分配记录的线程"""
37
+ def cleanup_worker():
38
+ while True:
39
+ time.sleep(self._cleanup_interval)
40
+ self._cleanup_expired_allocations()
41
+
42
+ cleanup_thread = threading.Thread(target=cleanup_worker, daemon=True)
43
+ cleanup_thread.start()
44
+ print("[MemoryAllocationStore] Background cleanup thread started")
45
+
46
+ def _cleanup_expired_allocations(self):
47
+ """清理过期的分配记录"""
48
+ with self._lock:
49
+ current_time = datetime.now()
50
+ expired_spaces = []
51
+
52
+ for space_name, alloc_info in self._allocations.items():
53
+ if alloc_info['expires_at'] <= current_time:
54
+ expired_spaces.append(space_name)
55
+
56
+ for space_name in expired_spaces:
57
+ del self._allocations[space_name]
58
+ if expired_spaces: # 只在有过期记录时打印
59
+ print(f"[MemoryAllocationStore] Cleaned up expired allocation: {space_name}")
60
+
61
+ def add_allocation(self, space_name, student_id):
62
+ """添加新的分配记录"""
63
+ with self._lock:
64
+ current_time = datetime.now()
65
+ expires_at = current_time + timedelta(minutes=ALLOCATION_LOCK_DURATION)
66
+
67
+ self._allocations[space_name] = {
68
+ 'student_id': student_id,
69
+ 'allocated_time': current_time,
70
+ 'expires_at': expires_at
71
+ }
72
+
73
+ print(f"[MemoryAllocationStore] Added allocation: {space_name} -> {student_id} (expires at {expires_at.strftime('%H:%M:%S')})")
74
+
75
+ def get_active_allocations(self):
76
+ """获取所有有效的分配记录"""
77
+ with self._lock:
78
+ current_time = datetime.now()
79
+ active_allocations = {}
80
+
81
+ for space_name, alloc_info in self._allocations.items():
82
+ if alloc_info['expires_at'] > current_time:
83
+ active_allocations[space_name] = alloc_info.copy()
84
+
85
+ return active_allocations
86
+
87
+ def is_allocated(self, space_name):
88
+ """检查指定空间是否被分配"""
89
+ with self._lock:
90
+ if space_name not in self._allocations:
91
+ return False, None
92
+
93
+ alloc_info = self._allocations[space_name]
94
+ current_time = datetime.now()
95
+
96
+ if alloc_info['expires_at'] > current_time:
97
+ return True, alloc_info['student_id']
98
+ else:
99
+ # 过期了,删除记录
100
+ del self._allocations[space_name]
101
+ return False, None
102
+
103
+ def get_status_summary(self):
104
+ """获取分配状态摘要"""
105
+ with self._lock:
106
+ current_time = datetime.now()
107
+ active_count = 0
108
+ summary = []
109
+
110
+ for space_name, alloc_info in self._allocations.items():
111
+ if alloc_info['expires_at'] > current_time:
112
+ active_count += 1
113
+ remaining_minutes = (alloc_info['expires_at'] - current_time).total_seconds() / 60
114
+ summary.append(f"{space_name} -> {alloc_info['student_id']} ({remaining_minutes:.1f}min left)")
115
+
116
+ return {
117
+ 'active_count': active_count,
118
+ 'total_stored': len(self._allocations),
119
+ 'summary': summary
120
+ }
121
 
122
  # 全局数据管理器
123
  class DataManager:
 
250
  print("[DataManager] Background sync thread stopping...")
251
 
252
  class SpaceActivityCache:
253
+ """内存中的空间活动缓存"""
254
  def __init__(self, data_manager):
255
  self.data_manager = data_manager
 
256
  self._cache_data = {}
257
  self._last_update = None
258
  self._update_thread = None
 
330
  with CACHE_LOCK:
331
  self._cache_data = new_cache_data
332
  self._last_update = datetime.now()
 
 
 
333
 
334
  elapsed_time = time.time() - start_time
335
  print(f"[SpaceActivityCache] Cache updated in {elapsed_time:.2f}s, {len(available_spaces)} spaces processed")
 
390
  print(f"[SpaceActivityCache] Error reading {csv_file_path}: {e}")
391
  return datetime.min, "read_error"
392
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  def get_space_activity(self, space_name):
394
  """获取指定空间的活动信息"""
395
  with CACHE_LOCK:
396
  if not self._cache_data or 'spaces' not in self._cache_data:
397
+ print("[SpaceActivityCache] No cache available, updating immediately...")
398
+ self._update_cache()
 
 
 
 
399
 
400
  spaces_data = self._cache_data.get('spaces', {})
401
  space_info = spaces_data.get(space_name, {})
 
420
  """获取所有空间的活动信息"""
421
  with CACHE_LOCK:
422
  if not self._cache_data or 'spaces' not in self._cache_data:
423
+ print("[SpaceActivityCache] No cache available, updating immediately...")
424
+ self._update_cache()
 
 
425
 
426
  result = {}
427
  spaces_data = self._cache_data.get('spaces', {})
 
506
 
507
  for space_name in expired_spaces:
508
  del self._recent_allocations[space_name]
509
+ if expired_spaces: # 只在有过期记录时打印
510
+ print(f"[LocalAllocationTracker] Cleaned up expired allocation: {space_name}")
511
 
512
  def is_recently_allocated_locally(self, space_name):
513
  """检查空间是否在本地被最近分配过"""
 
551
  data_manager = DataManager()
552
  activity_cache = None
553
  local_tracker = LocalAllocationTracker()
554
+ memory_allocation_store = MemoryAllocationStore()
555
 
556
  def init_system():
557
  """初始化整个系统 - 只在启动时调用一次"""
 
569
  print("[init_system] System initialization completed")
570
 
571
  def analyze_space_activity_cached():
572
+ """使用缓存的空间活动分析 - 完全使用内存"""
573
  global activity_cache
574
 
575
  if activity_cache is None:
 
577
 
578
  # 从数据管理器获取可用空间列表(不触发数据同步)
579
  available_spaces = data_manager.get_available_spaces()
 
580
 
581
  space_activity = []
582
  current_time = datetime.now()
 
588
  # 获取所有空间的缓存活动数据
589
  all_spaces_activity = activity_cache.get_all_spaces_activity()
590
 
591
+ # 从内存获取分配记录 - 不再读取文件
592
+ active_allocations = memory_allocation_store.get_active_allocations()
593
 
594
  print(f"[analyze_space_activity_cached] Analyzing {len(available_spaces)} spaces using cached data...")
595
+ print(f"[analyze_space_activity_cached] Memory allocations: {len(active_allocations)} active")
596
 
597
  for space_name in available_spaces:
598
  # 从缓存获取活动信息
 
616
  status = f"Idle for {idle_minutes:.1f} minutes (cached)"
617
  last_activity_str = last_activity.strftime('%Y-%m-%d %H:%M:%S')
618
 
619
+ # 检查内存中的分配记录
620
+ is_recently_allocated_memory = space_name in active_allocations
621
+ if is_recently_allocated_memory:
622
  alloc_info = active_allocations[space_name]
623
  minutes_until_free = (alloc_info['expires_at'] - current_time).total_seconds() / 60
624
+ status += f" (Allocated in memory to {alloc_info['student_id']}, free in {minutes_until_free:.1f} min)"
625
 
626
  # Check if space is recently allocated (local)
627
  is_recently_allocated_local, local_student = local_tracker.is_recently_allocated_locally(space_name)
 
634
  'last_activity_str': last_activity_str,
635
  'idle_minutes': idle_minutes,
636
  'status': status,
637
+ 'is_recently_allocated_memory': is_recently_allocated_memory,
638
  'is_recently_allocated_local': is_recently_allocated_local,
639
  'cached_status': cached_status
640
  })
 
646
 
647
  return space_activity
648
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
649
  def create_status_display(space_activity):
650
  """Create formatted status display for all spaces with proper line breaks"""
651
  status_display = "📊 **Current Space Status (sorted by availability):**<br><br>"
652
 
653
+ # 显示内存分配记录摘要
654
+ memory_summary = memory_allocation_store.get_status_summary()
655
+ if memory_summary['active_count'] > 0:
656
+ status_display += f"🧠 **Memory Allocations ({memory_summary['active_count']} active):**<br>"
657
+ for alloc in memory_summary['summary']:
658
+ status_display += f"&nbsp;&nbsp;&nbsp;• {alloc}<br>"
659
+ status_display += "<br>"
660
+
661
  # 显示本地分配记录摘要
662
  local_summary = local_tracker.get_recent_allocations_summary()
663
  if local_summary:
 
674
  return status_display
675
 
676
  def select_space_with_enhanced_collision_avoidance_cached(space_activity, student_id):
677
+ """使用缓存的增强防撞空间选择函数 - 完全使用内存存储"""
678
 
679
  print(f"[select_space_with_enhanced_collision_avoidance_cached] Starting selection for student: {student_id}")
680
 
681
  # 第一步:过滤掉不符合基本条件的空间
682
  basic_available_spaces = []
683
  for space in space_activity:
684
+ # 检查基本条件 - 使用内存分配记录
685
  if (space['idle_minutes'] >= MIN_IDLE_MINUTES and
686
+ not space['is_recently_allocated_memory'] and
687
  not space['is_recently_allocated_local']):
688
  basic_available_spaces.append(space)
689
 
 
692
  if not basic_available_spaces:
693
  # 生成详细的错误信息
694
  idle_spaces = [s for s in space_activity if s['idle_minutes'] >= MIN_IDLE_MINUTES]
695
+ memory_allocated = [s for s in space_activity if s['is_recently_allocated_memory']]
696
  local_allocated = [s for s in space_activity if s['is_recently_allocated_local']]
697
 
698
  error_parts = []
699
  if not idle_spaces:
700
  error_parts.append(f"all spaces used within {MIN_IDLE_MINUTES} minutes")
701
+ if memory_allocated:
702
+ error_parts.append(f"{len(memory_allocated)} spaces allocated in memory")
703
  if local_allocated:
704
  error_parts.append(f"{len(local_allocated)} spaces locally allocated")
705
 
 
740
  print(f"[select_space_with_enhanced_collision_avoidance_cached] No alternatives available")
741
  raise gr.Error(error_msg, duration=8)
742
 
743
+ # 第四步:立即记录本地分配(在写入内存之前)
744
  local_tracker.record_local_allocation(space_name, student_id)
745
+ print(f"[select_space_with_enhanced_collision_avoidance_cached] Local allocation recorded BEFORE memory write")
746
 
747
+ # 第五步:记录到内存存储 - 不再写入文件或推送到远程
748
+ memory_allocation_store.add_allocation(space_name, student_id)
749
+ print(f"[select_space_with_enhanced_collision_avoidance_cached] Allocation recorded in memory only")
 
 
 
 
 
 
 
 
750
 
751
  # 第六步:生成结果(使用带缓存信息的状态显示)
752
  status_display = create_status_display_with_cache_info(space_activity)
 
803
  {status_display}
804
  </div>
805
  <p style="margin-bottom: 0; color: #666; font-size: 14px; margin-top: 15px;">
806
+ <strong>Enhanced Selection Algorithm:</strong> Spaces idle for ≥{MIN_IDLE_MINUTES} minutes, not allocated in memory, and not locally allocated are eligible. The system uses pure memory storage for real-time collision avoidance.
807
  </p>
808
  </div>
809
 
 
818
  return gr.HTML(redirect_html)
819
 
820
  def load_balance_user_cached(student_id):
821
+ """使用缓存的负载均衡函数 - 完全使用内存存储"""
822
  print(f"[load_balance_user_cached] Starting cached load balancing for student ID: {student_id}")
823
 
824
  # 检查系统是否已初始化
 
861
  status_display += f"🔄 **Cache Status:** {cache_status}<br>"
862
  status_display += f"📋 **Cached Spaces:** {cache_info['spaces_count']}<br><br>"
863
 
864
+ # 显示内存分配记录摘要
865
+ memory_summary = memory_allocation_store.get_status_summary()
866
+ if memory_summary['active_count'] > 0:
867
+ status_display += f"🧠 **Memory Allocations ({memory_summary['active_count']} active):**<br>"
868
+ for alloc in memory_summary['summary']:
869
+ status_display += f"&nbsp;&nbsp;&nbsp;• {alloc}<br>"
870
+ status_display += "<br>"
871
+
872
  # 显示本地分配记录摘要
873
  local_summary = local_tracker.get_recent_allocations_summary()
874
  if local_summary:
 
959
  except Exception as e:
960
  return {"error": str(e)}
961
 
962
+ def get_memory_allocation_status():
963
+ """获取内存分配状态"""
964
+ return memory_allocation_store.get_status_summary()
965
+
966
+ def clear_memory_allocations():
967
+ """清除所有内存分配记录(管理功能)"""
968
+ try:
969
+ with memory_allocation_store._lock:
970
+ cleared_count = len(memory_allocation_store._allocations)
971
+ memory_allocation_store._allocations.clear()
972
+ return {"status": f"Cleared {cleared_count} memory allocations"}
973
+ except Exception as e:
974
+ return {"error": str(e)}
975
+
976
  # 添加可选的管理界面(用于调试)
977
  def create_admin_interface():
978
  """创建管理界面(可选)- 增强版本"""
 
995
  data_info_display = gr.JSON(label="Data Manager Information")
996
  data_status_message = gr.Markdown("")
997
 
998
+ with gr.Tab("Memory Allocation"):
999
+ with gr.Row():
1000
+ memory_status_btn = gr.Button("🧠 Check Memory Allocations", variant="secondary")
1001
+ clear_memory_btn = gr.Button("🗑️ Clear All Memory Allocations", variant="stop")
1002
+
1003
+ memory_info_display = gr.JSON(label="Memory Allocation Information")
1004
+ memory_status_message = gr.Markdown("")
1005
+
1006
  def check_cache_status():
1007
  try:
1008
  status = get_cache_status()
 
1063
  except Exception as e:
1064
  return {"error": str(e)}, f"❌ Error syncing data: {str(e)}"
1065
 
1066
+ def check_memory_status():
1067
+ try:
1068
+ status = get_memory_allocation_status()
1069
+ active_count = status['active_count']
1070
+ total_stored = status['total_stored']
1071
+
1072
+ if active_count > 0:
1073
+ message = f"🧠 {active_count} active allocations ({total_stored} total in memory)"
1074
+ else:
1075
+ message = "✅ No active memory allocations"
1076
+
1077
+ return status, message
1078
+ except Exception as e:
1079
+ return {"error": str(e)}, f"❌ Error checking memory allocations: {str(e)}"
1080
+
1081
+ def clear_memory():
1082
+ try:
1083
+ result = clear_memory_allocations()
1084
+ if 'error' in result:
1085
+ return result, "❌ Failed to clear memory allocations"
1086
+ else:
1087
+ # 获取更新后的状态
1088
+ status = get_memory_allocation_status()
1089
+ return status, f"✅ {result['status']}"
1090
+ except Exception as e:
1091
+ return {"error": str(e)}, f"❌ Error clearing memory: {str(e)}"
1092
+
1093
  # Cache tab event handlers
1094
  cache_status_btn.click(
1095
  fn=check_cache_status,
 
1111
  fn=force_sync,
1112
  outputs=[data_info_display, data_status_message]
1113
  )
1114
+
1115
+ # Memory tab event handlers
1116
+ memory_status_btn.click(
1117
+ fn=check_memory_status,
1118
+ outputs=[memory_info_display, memory_status_message]
1119
+ )
1120
+
1121
+ clear_memory_btn.click(
1122
+ fn=clear_memory,
1123
+ outputs=[memory_info_display, memory_status_message]
1124
+ )
1125
 
1126
  return admin_interface
1127
 
 
1149
  print("🚀 Initializing CIV3283 Load Distributor System...")
1150
  init_system()
1151
  print("✅ System initialization completed successfully")
1152
+ print("🧠 Using pure memory storage for allocations (no file I/O during user access)")
1153
 
1154
  # 检查是否需要管理界面
1155
  enable_admin = "--admin" in sys.argv or os.environ.get("ENABLE_ADMIN", "false").lower() == "true"