James McCool commited on
Commit
b7931c0
·
1 Parent(s): b31b495

Add update-pm-files.ps1 script for EC2 Instance Connect file updates and enhance exposure_spread.py with salary eligibility checks for player replacements

Browse files
Files changed (2) hide show
  1. global_func/exposure_spread.py +42 -11
  2. update-pm-files.ps1 +112 -0
global_func/exposure_spread.py CHANGED
@@ -148,6 +148,31 @@ def check_position_eligibility(sport, column_name, player_positions):
148
  # Default fallback - assume exact position match
149
  return column_name in player_positions
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary_below, comp_salary_above, ignore_stacks, remove_teams, specific_replacements, specific_columns, projections_df, sport_var, type_var, salary_max, stacking_sports):
152
  comparable_players = projections_df[projections_df['player_names'] == exposure_player]
153
 
@@ -295,7 +320,10 @@ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary
295
  if check_position_eligibility(sport_var, col, replacement_player_positions):
296
  suitable_replacements.append(candidate)
297
  else:
298
- suitable_replacements.append(candidate)
 
 
 
299
 
300
  if suitable_replacements:
301
  insert_player = random.choice(suitable_replacements)
@@ -317,9 +345,12 @@ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary
317
  replacement_made = True
318
  break
319
  else:
320
- working_frame.at[row, col] = insert_player
321
- replacement_made = True
322
- break
 
 
 
323
 
324
  # Only increment counter if we actually made a replacement
325
  if replacement_made:
@@ -378,22 +409,22 @@ def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary
378
 
379
  for col in working_columns:
380
  if row_data[col] in comparable_player_list:
381
- if working_frame.iloc[row]['salary'] - projections_df[projections_df['player_names'] == row_data[col]]['salary'].iloc[0] + projections_df[projections_df['player_names'] == exposure_player]['salary'].iloc[0] <= salary_max:
 
 
 
 
382
  if type_var == 'Classic':
383
- replacement_player_positions = projections_df[projections_df['player_names'] == row_data[col]]['position'].iloc[0].split('/')
384
  exposure_player_positions = projections_df[projections_df['player_names'] == exposure_player]['position'].iloc[0].split('/')
385
-
386
- # Check if the replacement player is eligible for this column
387
-
388
  if check_position_eligibility(sport_var, col, exposure_player_positions):
389
  working_frame.at[row, col] = exposure_player
390
  change_counter += 1
391
  break
392
  else:
 
393
  working_frame.at[row, col] = exposure_player
394
  change_counter += 1
395
  break
396
- else:
397
- continue
398
  return working_frame
399
 
 
148
  # Default fallback - assume exact position match
149
  return column_name in player_positions
150
 
151
+ def get_effective_salary(player_name, column_name, projections_df, type_var):
152
+ """Calculate the effective salary for a player in a specific column"""
153
+ base_salary = projections_df[projections_df['player_names'] == player_name]['salary'].iloc[0]
154
+ if type_var != 'Classic' and column_name == 'CPT':
155
+ return base_salary * 1.5
156
+ return base_salary
157
+
158
+ def find_player_column(player_name, row_data, working_columns):
159
+ """Find which column contains the specified player"""
160
+ for col in working_columns:
161
+ if row_data[col] == player_name:
162
+ return col
163
+ return None
164
+
165
+ def calculate_salary_difference(current_player, new_player, column_name, projections_df, type_var):
166
+ """Calculate the salary difference when replacing current_player with new_player in a specific column"""
167
+ current_salary = get_effective_salary(current_player, column_name, projections_df, type_var)
168
+ new_salary = get_effective_salary(new_player, column_name, projections_df, type_var)
169
+ return new_salary - current_salary
170
+
171
+ def check_salary_eligibility(current_lineup_salary, current_player, new_player, column_name, projections_df, type_var, salary_max):
172
+ """Check if replacing current_player with new_player in the specified column stays within salary cap"""
173
+ salary_diff = calculate_salary_difference(current_player, new_player, column_name, projections_df, type_var)
174
+ return current_lineup_salary + salary_diff <= salary_max
175
+
176
  def exposure_spread(working_frame, exposure_player, exposure_target, comp_salary_below, comp_salary_above, ignore_stacks, remove_teams, specific_replacements, specific_columns, projections_df, sport_var, type_var, salary_max, stacking_sports):
177
  comparable_players = projections_df[projections_df['player_names'] == exposure_player]
178
 
 
320
  if check_position_eligibility(sport_var, col, replacement_player_positions):
321
  suitable_replacements.append(candidate)
322
  else:
323
+ # For non-Classic types, check salary eligibility using helper function
324
+ current_lineup_salary = working_frame.iloc[row]['Salary']
325
+ if check_salary_eligibility(current_lineup_salary, exposure_player, candidate, col, projections_df, type_var, salary_max):
326
+ suitable_replacements.append(candidate)
327
 
328
  if suitable_replacements:
329
  insert_player = random.choice(suitable_replacements)
 
345
  replacement_made = True
346
  break
347
  else:
348
+ # For non-Classic types, check salary eligibility using helper function
349
+ current_lineup_salary = working_frame.iloc[row]['Salary']
350
+ if check_salary_eligibility(current_lineup_salary, exposure_player, insert_player, col, projections_df, type_var, salary_max):
351
+ working_frame.at[row, col] = insert_player
352
+ replacement_made = True
353
+ break
354
 
355
  # Only increment counter if we actually made a replacement
356
  if replacement_made:
 
409
 
410
  for col in working_columns:
411
  if row_data[col] in comparable_player_list:
412
+ current_lineup_salary = working_frame.iloc[row]['Salary']
413
+ current_player = row_data[col]
414
+
415
+ # Check salary eligibility using helper function
416
+ if check_salary_eligibility(current_lineup_salary, current_player, exposure_player, col, projections_df, type_var, salary_max):
417
  if type_var == 'Classic':
418
+ # For Classic types, also check position eligibility
419
  exposure_player_positions = projections_df[projections_df['player_names'] == exposure_player]['position'].iloc[0].split('/')
 
 
 
420
  if check_position_eligibility(sport_var, col, exposure_player_positions):
421
  working_frame.at[row, col] = exposure_player
422
  change_counter += 1
423
  break
424
  else:
425
+ # For non-Classic types, salary check is sufficient (position eligibility handled elsewhere)
426
  working_frame.at[row, col] = exposure_player
427
  change_counter += 1
428
  break
 
 
429
  return working_frame
430
 
update-pm-files.ps1 ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Simple script to update files on all instances using EC2 Instance Connect
2
+ #
3
+ # Usage Examples:
4
+ # .\update-exposure-spread.ps1 # Updates exposure_spread.py (default)
5
+ # .\update-exposure-spread.ps1 -FileName "predict_dupes.py" # Updates predict_dupes.py
6
+ # .\update-exposure-spread.ps1 -FileName "app.py" -TargetPath "/home/ec2-user/AWS_Portfolio_Manager/" # Updates app.py in root directory
7
+ #
8
+ param(
9
+ [Parameter(Mandatory=$false)]
10
+ [string]$FileName = "exposure_spread.py",
11
+
12
+ [Parameter(Mandatory=$false)]
13
+ [string]$TargetPath = "/home/ec2-user/AWS_Portfolio_Manager/global_func/"
14
+ )
15
+
16
+ # Handle special case: app.py maps to application.py on the remote instances
17
+ $remoteFileName = $FileName
18
+ if ($FileName -eq "app.py") {
19
+ $remoteFileName = "application.py"
20
+ Write-Host "Note: app.py will be uploaded as application.py on remote instances" -ForegroundColor Yellow
21
+ }
22
+
23
+ # Determine local file path - check global_func first, then root directory
24
+ $localFilePath = ""
25
+ if (Test-Path ".\global_func\$FileName") {
26
+ $localFilePath = ".\global_func\$FileName"
27
+ } elseif (Test-Path ".\$FileName") {
28
+ $localFilePath = ".\$FileName"
29
+ # If uploading from root and no target path specified, default to root on remote
30
+ if ($TargetPath -eq "/home/ec2-user/AWS_Portfolio_Manager/global_func/") {
31
+ $TargetPath = "/home/ec2-user/AWS_Portfolio_Manager/"
32
+ }
33
+ } else {
34
+ Write-Host "Error: File '$FileName' not found!" -ForegroundColor Red
35
+ Write-Host "Checked locations:" -ForegroundColor Yellow
36
+ Write-Host " .\global_func\$FileName" -ForegroundColor Cyan
37
+ Write-Host " .\$FileName" -ForegroundColor Cyan
38
+ Write-Host "`nAvailable files in global_func:" -ForegroundColor Yellow
39
+ Get-ChildItem ".\global_func\*.py" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host " $($_.Name)" -ForegroundColor Cyan }
40
+ Write-Host "`nAvailable files in root:" -ForegroundColor Yellow
41
+ Get-ChildItem ".\*.py" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host " $($_.Name)" -ForegroundColor Cyan }
42
+ exit 1
43
+ }
44
+
45
+ Write-Host "Updating file: $FileName -> $remoteFileName" -ForegroundColor Green
46
+ Write-Host "Local path: $localFilePath" -ForegroundColor Cyan
47
+ Write-Host "Target path: $TargetPath$remoteFileName" -ForegroundColor Cyan
48
+
49
+ # Get instance IPs and IDs
50
+ . .\get-ips.ps1
51
+ $instanceIds = aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names portfolio-manager-asg --query 'AutoScalingGroups[0].Instances[?HealthStatus==`Healthy`].InstanceId' --output text
52
+
53
+ if ($global:instances.Count -eq 0) {
54
+ Write-Host "No instances found" -ForegroundColor Red
55
+ exit 1
56
+ }
57
+
58
+ Write-Host "Found $($global:instances.Count) instances: $($global:instances -join ', ')" -ForegroundColor Green
59
+
60
+ # Generate a temporary SSH key for this session
61
+ Write-Host "Generating temporary SSH key..." -ForegroundColor Cyan
62
+ ssh-keygen -t rsa -f temp_update_key -N '""' -q
63
+
64
+ $instanceIdArray = $instanceIds -split "`t"
65
+
66
+ # Update each instance one at a time to maintain uptime
67
+ for ($i = 0; $i -lt $global:instances.Count; $i++) {
68
+ $ip = $global:instances[$i]
69
+ $instanceId = $instanceIdArray[$i]
70
+
71
+ Write-Host "Updating instance $instanceId ($ip)..." -ForegroundColor Yellow
72
+
73
+ # Send SSH public key via EC2 Instance Connect
74
+ Write-Host "Sending SSH key to $instanceId..."
75
+ aws ec2-instance-connect send-ssh-public-key --instance-id $instanceId --instance-os-user ec2-user --ssh-public-key file://temp_update_key.pub
76
+
77
+ if ($LASTEXITCODE -eq 0) {
78
+ # Copy the file using SCP
79
+ Write-Host "Copying $FileName -> $remoteFileName to $ip..."
80
+ scp -i temp_update_key -o StrictHostKeyChecking=no "$localFilePath" ec2-user@${ip}:$TargetPath$remoteFileName
81
+
82
+ if ($LASTEXITCODE -eq 0) {
83
+ Write-Host "$remoteFileName copied successfully to $ip" -ForegroundColor Green
84
+
85
+ # Only restart Streamlit if we're updating a Python file
86
+ if ($remoteFileName -like "*.py") {
87
+ Write-Host "Restarting Streamlit service on $ip..."
88
+ ssh -i temp_update_key -o StrictHostKeyChecking=no ec2-user@$ip "pkill -f 'streamlit run' && cd /home/ec2-user/AWS_Portfolio_Manager && nohup ./venv/bin/streamlit run application.py --server.port 5000 --server.address 0.0.0.0 > /dev/null 2>&1 &"
89
+
90
+ if ($LASTEXITCODE -eq 0) {
91
+ Write-Host "Service restarted successfully on $ip" -ForegroundColor Green
92
+ } else {
93
+ Write-Host "Failed to restart service on $ip" -ForegroundColor Red
94
+ }
95
+ } else {
96
+ Write-Host "Non-Python file - skipping service restart" -ForegroundColor Yellow
97
+ }
98
+ } else {
99
+ Write-Host "Failed to copy $remoteFileName to $ip" -ForegroundColor Red
100
+ }
101
+ } else {
102
+ Write-Host "Failed to send SSH key to $instanceId" -ForegroundColor Red
103
+ }
104
+
105
+ Write-Host "Waiting 15 seconds before next instance..." -ForegroundColor Cyan
106
+ Start-Sleep -Seconds 15
107
+ }
108
+
109
+ # Clean up temporary key files
110
+ Remove-Item temp_update_key, temp_update_key.pub -ErrorAction SilentlyContinue
111
+
112
+ Write-Host "All instances updated with rolling restart!" -ForegroundColor Green