Re: Player Eval Tool
by
AlabamaTide
@
3/16/2026 10:18 am
hoping for plug n play
|
|
Re: Player Eval Tool
by
martinwarnett
@
3/16/2026 11:58 am
I've taken Seth's single script on an done a lot of work breaking things down, re-structuring; would be a bit much to post it all, given a lot still in progress.
If I can get some time - and prob not looking till Friday - will take Seth's original script and apply the changes there |
|
|
|
|
Re: Player Eval Tool
by
dfisherman
@
3/16/2026 12:15 pm
Here is a new version that runs in your browser at port 8765:
import http.server import json import io import cgi import webbrowser import pandas as pd HTML_PAGE = '''<!DOCTYPE html> <html> <head> <title>Player Evaluation by Position</title> <style> * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #1a1a2e; color: #eee; padding: 20px; } h1 { margin-bottom: 15px; color: #e94560; } .controls { margin-bottom: 15px; display: flex; gap: 10px; align-items: center; } .controls button { background: #e94560; color: #fff; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 14px; } .controls button:hover { background: #c73550; } .tabs { display: flex; flex-wrap: wrap; gap: 2px; margin-bottom: 0; border-bottom: 2px solid #e94560; } .tab { padding: 8px 16px; background: #16213e; cursor: pointer; border-radius: 5px 5px 0 0; font-size: 13px; } .tab.active { background: #e94560; color: #fff; } .tab-content { display: none; } .tab-content.active { display: block; } table { width: 100%; border-collapse: collapse; margin-top: 0; font-size: 13px; } th { background: #16213e; padding: 8px 10px; text-align: left; cursor: pointer; user-select: none; white-space: nowrap; position: sticky; top: 0; } th:hover { background: #1a1a4e; } th .arrow { font-size: 10px; margin-left: 4px; } td { padding: 6px 10px; border-bottom: 1px solid #2a2a4a; white-space: nowrap; } tr:hover td { background: #16213e; } .table-wrap { max-height: 75vh; overflow: auto; border: 1px solid #2a2a4a; border-radius: 0 0 5px 5px; } .empty { padding: 40px; text-align: center; color: #888; } #file-input { display: none; } </style> </head> <body> <h1>Player Evaluation by Position</h1> <div class="controls"> <input type="file" id="file-input" accept=".xlsx"> <button onclick="uploadFile('all')">Evaluate All Positions</button> <button onclick="uploadFile('current')">Evaluate Current Position</button> </div> <div id="tabs" class="tabs"></div> <div id="content"></div> <div class="empty" id="placeholder">Upload an Excel file to begin evaluation.</div> <script> let allData = {}; let sortState = {}; function uploadFile(mode) { const input = document.getElementById('file-input'); input.onchange = function() { if (!input.files.length) return; const form = new FormData(); form.append('file', input.files[0]); form.append('mode', mode); fetch('/upload', { method: 'POST', body: form }) .then(r => r.json()) .then(data => { if (data.error) { alert(data.error); return; } allData = data; sortState = {}; renderTabs(Object.keys(data)); const firstPos = Object.keys(data)[0]; if (firstPos) showTab(firstPos); }) .catch(e => alert('Error: ' + e)); input.value = ''; }; input.click(); } function renderTabs(positions) { document.getElementById('placeholder').style.display = 'none'; const tabsEl = document.getElementById('tabs'); tabsEl.innerHTML = positions.map(p => '<div class="tab" onclick="showTab(\\''+p+'\\')" id="tab-'+p+'">'+p+'</div>' ).join(''); } function showTab(pos) { document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); const tab = document.getElementById('tab-' + pos); if (tab) tab.classList.add('active'); renderTable(pos); } function renderTable(pos) { const players = allData[pos] || []; const contentEl = document.getElementById('content'); if (!players.length) { contentEl.innerHTML = '<div class="empty">No qualifying players for ' + pos + '</div>'; return; } const baseCols = ['Player', 'Composite Score', 'Current Position', 'Current Weight', 'Estimated Speed', 'Estimated Acceleration']; const skillCols = new Set(); players.forEach(p => (p['Primary Skills'] || []).forEach(s => { if (s[0] !== 'MaxSpeedMax' && s[0] !== 'AccelerationMax') skillCols.add(s[0]); })); const allCols = baseCols.concat([...skillCols].sort()); const sortKey = sortState[pos] || { col: 'Composite Score', desc: true }; const sorted = [...players].sort((a, b) => { let va = getVal(a, sortKey.col, baseCols); let vb = getVal(b, sortKey.col, baseCols); let na = parseFloat(va), nb = parseFloat(vb); if (!isNaN(na) && !isNaN(nb)) return sortKey.desc ? nb - na : na - nb; return sortKey.desc ? String(vb).localeCompare(String(va)) : String(va).localeCompare(String(vb)); }); let html = '<div class="table-wrap"><table><thead><tr>'; allCols.forEach(col => { const arrow = sortKey.col === col ? (sortKey.desc ? ' ▼' : ' ▲') : ''; html += '<th onclick="sortBy(\\''+pos+'\\',\\''+col.replace(/'/g,"\\\\'")+'\\')">' + col + '<span class="arrow">' + arrow + '</span></th>'; }); html += '</tr></thead><tbody>'; sorted.forEach(p => { html += '<tr>'; allCols.forEach(col => { let v = getVal(p, col, baseCols); if (typeof v === 'number') v = Math.round(v * 10) / 10; html += '<td>' + (v !== undefined && v !== null ? v : 'N/A') + '</td>'; }); html += '</tr>'; }); html += '</tbody></table></div>'; contentEl.innerHTML = html; } function getVal(player, col, baseCols) { if (baseCols.includes(col)) return player[col]; const skill = (player['Primary Skills'] || []).find(s => s[0] === col); return skill ? skill[1] : 'N/A'; } function sortBy(pos, col) { const prev = sortState[pos] || { col: 'Composite Score', desc: true }; if (prev.col === col) { sortState[pos] = { col, desc: !prev.desc }; } else { sortState[pos] = { col, desc: true }; } renderTable(pos); } </script> </body> </html>''' class PlayerEvaluationApp: def __init__(self): self.allowed_positions = { "QB": ("QB"), "RB": ("QB", "RB", "FB", "WR", "TE"), "WR": ("RB", "WR", "FB", "TE"), "FB": ("RB", "FB", "TE"), "TE": ("RB", "FB", "TE"), "LT": ("LT", "RT", "LG", "RG", "C", "TE"), "LG": ("LT", "RT", "LG", "RG", "C", "TE"), "C": ("LT", "RT", "LG", "RG", "C", "TE"), "RDE": ("RDE", "LDE", "DT", "WLB", "SLB", "MLB"), "DT": ("RDE", "LDE", "DT", "WLB", "SLB", "MLB"), "SLB": ("RDE", "LDE", "DT", "WLB", "SLB", "MLB", "CB", "SS", "FS"), "MLB": ("RDE", "LDE", "DT", "WLB", "SLB", "MLB", "CB", "SS", "FS"), "WLB": ("RDE", "LDE", "DT", "WLB", "SLB", "MLB", "CB", "SS", "FS"), "CB": ("WLB", "SLB", "MLB", "CB", "SS", "FS"), "SS": ("WLB", "SLB", "MLB", "CB", "SS", "FS") } self.current_positions = { "QB": ("QB"), "RB": ("RB"), "WR": ("WR"), "FB": ("FB"), "TE": ("TE"), "LT": ("LT", "RT", "LG", "RG", "C"), "LG": ("LT", "RT", "LG", "RG", "C"), "C": ("LT", "RT", "LG", "RG", "C"), "RDE": ("RDE", "LDE", "DT"), "DT": ("RDE", "LDE", "DT"), "SLB": ("WLB", "SLB", "MLB"), "MLB": ("WLB", "SLB", "MLB"), "WLB": ("WLB", "SLB", "MLB"), "CB": ("CB", "SS", "FS"), "SS": ("CB", "SS", "FS") } self.position_weight = { "CB": (191, 96), "WR": (197, 95), "SS": (206, 94), "RB": (217, 92), "QB": (220, 91), "WLB": (237, 89), "SLB": (241, 88), "FB": (243, 88), "MLB": (245, 88), "TE": (257, 86), "RDE": (276, 84), "C": (283, 83), "DT": (300, 80), "LT": (308, 79), "LG": (312, 78) } self.primary_skills = { "QB": [ [("PassAccuracyMax", 50), ("ArmStrengthMax", 50), ("IntelligenceMax", 50)], ], "RB": [ [("MaxSpeedMax", 68), ("AccelerationMax", 52), ("BallCarryingMax", 50), ("BreakTackleMax", 0), ("AvoidFumbleMax", 50)], [("MaxSpeedMax", 68), ("AccelerationMax", 52), ("BallCarryingMax", 0), ("BreakTackleMax", 70), ("AvoidFumbleMax", 50)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("BallCarryingMax", 50), ("BreakTackleMax", 0), ("AvoidFumbleMax", 50)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("BallCarryingMax", 0), ("BreakTackleMax", 70), ("AvoidFumbleMax", 50)], [("MaxSpeedMax", 86), ("AccelerationMax", 0), ("BallCarryingMax", 0), ("BreakTackleMax", 0), ("AvoidFumbleMax", 0)] ], "WR": [ [("MaxSpeedMax", 68), ("AccelerationMax", 78), ("B&RAvoidanceMax", 50), ("PassRecCourageMax", 50), ("PassCatchingMax", 50), ("RouteRunningMax", 50)], [("MaxSpeedMax", 78), ("AccelerationMax", 78), ("B&RAvoidanceMax", 0), ("PassRecCourageMax", 80), ("PassCatchingMax", 50), ("RouteRunningMax", 50)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("B&RAvoidanceMax", 80), ("PassRecCourageMax", 50), ("PassCatchingMax", 50), ("RouteRunningMax", 0)], [("MaxSpeedMax", 86), ("AccelerationMax", 0), ("B&RAvoidanceMax", 50), ("PassRecCourageMax", 50), ("PassCatchingMax", 50), ("RouteRunningMax", 0)], [("MaxSpeedMax", 90), ("AccelerationMax", 0), ("B&RAvoidanceMax", 0), ("PassRecCourageMax", 0), ("PassCatchingMax", 0), ("RouteRunningMax", 0)] ], "FB": [ [("MaxSpeedMax", 68), ("AccelerationMax", 58), ("RunBlockingMax", 50), ("PassBlockingMax", 50), ("StrengthMax", 0)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("RunBlockingMax", 50), ("PassBlockingMax", 50), ("StrengthMax", 0)], [("MaxSpeedMax", 86), ("AccelerationMax", 0), ("RunBlockingMax", 0), ("PassBlockingMax", 0), ("StrengthMax", 0)] ], "TE": [ [("MaxSpeedMax", 68), ("AccelerationMax", 77), ("B&RAvoidanceMax", 50), ("PassRecCourageMax", 50), ("PassCatchingMax", 50), ("RouteRunningMax", 0)], [("MaxSpeedMax", 73), ("AccelerationMax", 0), ("B&RAvoidanceMax", 80), ("PassRecCourageMax", 50), ("PassCatchingMax", 50), ("RouteRunningMax", 0)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("B&RAvoidanceMax", 50), ("PassRecCourageMax", 50), ("PassCatchingMax", 50), ("RouteRunningMax", 0)], [("MaxSpeedMax", 80), ("AccelerationMax", 0), ("B&RAvoidanceMax", 0), ("PassRecCourageMax", 0), ("PassCatchingMax", 0), ("RouteRunningMax", 0)] ], "C": [ [("MaxSpeedMax", 58), ("AccelerationMax", 55), ("PassBlockingMax", 50), ("StrengthMax", 50), ("ShortSnappingMax", 50)], [("MaxSpeedMax", 68), ("AccelerationMax", 0), ("PassBlockingMax", 50), ("StrengthMax", 50), ("ShortSnappingMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 75), ("PassBlockingMax", 50), ("StrengthMax", 50), ("ShortSnappingMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 0), ("PassBlockingMax", 90), ("StrengthMax", 50), ("ShortSnappingMax", 90)] ], "LG": [ [("MaxSpeedMax", 65), ("AccelerationMax", 61), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 68), ("AccelerationMax", 0), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 75), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 0), ("PassBlockingMax", 90), ("StrengthMax", 50)] ], "LT": [ [("MaxSpeedMax", 65), ("AccelerationMax", 61), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 68), ("AccelerationMax", 0), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 75), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 0), ("PassBlockingMax", 90), ("StrengthMax", 50)] ], "RG": [ [("MaxSpeedMax", 65), ("AccelerationMax", 61), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 68), ("AccelerationMax", 0), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 75), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 0), ("PassBlockingMax", 90), ("StrengthMax", 50)] ], "RT": [ [("MaxSpeedMax", 65), ("AccelerationMax", 61), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 68), ("AccelerationMax", 0), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 75), ("PassBlockingMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 0), ("PassBlockingMax", 90), ("StrengthMax", 50)] ], "RDE": [ [("MaxSpeedMax", 60), ("AccelerationMax", 50), ("PassRushMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 70), ("PassRushMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("PassRushMax", 0), ("StrengthMax", 0)] ], "LDE": [ [("MaxSpeedMax", 60), ("AccelerationMax", 50), ("PassRushMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 70), ("PassRushMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("PassRushMax", 0), ("StrengthMax", 0)] ], "DT": [ [("MaxSpeedMax", 60), ("AccelerationMax", 50), ("PassRushMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 0), ("AccelerationMax", 75), ("PassRushMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("PassRushMax", 0), ("StrengthMax", 0)] ], "SLB": [ [("MaxSpeedMax", 68), ("AccelerationMax", 60), ("PassRushMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("PassRushMax", 50), ("StrengthMax", 50)], [("MaxSpeedMax", 86), ("AccelerationMax", 0), ("PassRushMax", 0), ("StrengthMax", 0)] ], "MLB": [ [("MaxSpeedMax", 68), ("AccelerationMax", 60), ("TackleAbilityMax", 0), ("M2MCoverageMax", 50)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("TackleAbilityMax", 0), ("M2MCoverageMax", 50)], [("MaxSpeedMax", 86), ("AccelerationMax", 0), ("TackleAbilityMax", 0), ("M2MCoverageMax", 0)] ], "WLB": [ [("MaxSpeedMax", 68), ("AccelerationMax", 60), ("TackleAbilityMax", 0), ("M2MCoverageMax", 50)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("TackleAbilityMax", 0), ("M2MCoverageMax", 50)], [("MaxSpeedMax", 86), ("AccelerationMax", 0), ("TackleAbilityMax", 0), ("M2MCoverageMax", 0)] ], "CB": [ [("MaxSpeedMax", 68), ("AccelerationMax", 74), ("B&RCoverageMax", 50), ("PunishReceiverMax", 50), ("M2MCoverageMax", 50)], [("MaxSpeedMax", 68), ("AccelerationMax", 74), ("B&RCoverageMax", 90), ("PunishReceiverMax", 0), ("M2MCoverageMax", 0)], [("MaxSpeedMax", 60), ("AccelerationMax", 0), ("B&RCoverageMax", 90), ("PunishReceiverMax", 0), ("M2MCoverageMax", 90)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("B&RCoverageMax", 50), ("PunishReceiverMax", 50), ("M2MCoverageMax", 50)], [("MaxSpeedMax", 86), ("AccelerationMax", 0), ("B&RCoverageMax", 0), ("PunishReceiverMax", 0), ("M2MCoverageMax", 0)] ], "SS": [ [("MaxSpeedMax", 68), ("AccelerationMax", 72), ("TackleAbilityMax", 0), ("M2MCoverageMax", 50), ("ZoneCoverageMax", 50), ("PunishReceiverMax", 50)], [("MaxSpeedMax", 78), ("AccelerationMax", 0), ("TackleAbilityMax", 0), ("M2MCoverageMax", 50), ("ZoneCoverageMax", 50), ("PunishReceiverMax", 50)], [("MaxSpeedMax", 86), ("AccelerationMax", 0), ("TackleAbilityMax", 0), ("M2MCoverageMax", 0), ("ZoneCoverageMax", 0), ("PunishReceiverMax", 0)] ] } self.secondary_skills = { "QB": [ [("LookOffDefMax", 50), ("FieldOfVisionMax", 50), ("MaxSpeedMax", 50), ("AccelerationMax", 50), ("ScramblingSkillMax", 0)], [("LookOffDefMax", 50), ("FieldOfVisionMax", 50), ("MaxSpeedMax", 50), ("AccelerationMax", 0), ("ScramblingSkillMax", 50)], [("LookOffDefMax", 50), ("FieldOfVisionMax", 50), ("MaxSpeedMax", 0), ("AccelerationMax", 80), ("ScramblingSkillMax", 50)], ], "RB": [("IntelligenceMax", 0), ("RouteRunningMax", 0), ("PassCatchingMax", 0), ("PassRecCourageMax", 0)], "WR": [("IntelligenceMax", 0), ("BallCarryingMax", 0), ("AvoidFumbleMax", 0)], "FB": [("IntelligenceMax", 0), ("DisciplineMax", 0), ("RouteRunningMax", 0), ("PassCatchingMax", 0), ("PassRecCourageMax", 0)], "TE": [("IntelligenceMax", 0), ("DisciplineMax", 0), ("StrengthMax", 0), ("RunBlockingMax", 0)], "C": [("RunBlockingMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0)], "LT": [("RunBlockingMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0)], "LG": [("RunBlockingMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0)], "RG": [("RunBlockingMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0)], "RT": [("RunBlockingMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0)], "LDE": [("TackleAbilityMax", 0), ("RunDefenseMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0)], "RDE": [("TackleAbilityMax", 0), ("RunDefenseMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0)], "DT": [("TackleAbilityMax", 0), ("RunDefenseMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0)], "MLB": [("PunishReceiverMax", 0), ("ZoneCoverageMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0), ("StripBallMax", 0)], "SLB": [("RunDefenseMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0), ("StripBallMax", 0)], "WLB": [("PunishReceiverMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0), ("StripBallMax", 0)], "CB": [("TackleAbilityMax", 0), ("IntelligenceMax", 0), ("DisciplineMax", 0)], "SS": [("IntelligenceMax", 0), ("DisciplineMax", 0), ("StripBallMax", 0)] } def adjust_speed(self, player_weight, position_weight): weight_diff = position_weight - player_weight if abs(weight_diff) > 50: return -7 if weight_diff > 0 else 7 return round(weight_diff * -0.15, 0) def adjust_acceleration(self, player_weight, position_weight): weight_diff = position_weight - player_weight if abs(weight_diff) > 50: return -5 if weight_diff > 0 else 5 return round(weight_diff * -0.10, 0) def process_skills(self, skills_list, skill_type, available_columns, row, player, pos, player_weight): skill_entries = set() meets_criteria = False for criteria_set in skills_list: current_set_meets_criteria = True for sublist in criteria_set if isinstance(criteria_set, list) else [criteria_set]: column, min_value = sublist if column in available_columns: if column == 'MaxSpeedMax': adjusted_value = row[column] + self.adjust_speed(player_weight, self.position_weight[pos][0]) percent_value = (adjusted_value / self.position_weight[pos][1]) * 100 skill_entries.add((column, percent_value)) if adjusted_value < min_value: current_set_meets_criteria = False elif column == 'AccelerationMax': adjusted_value = row[column] + self.adjust_acceleration(player_weight, self.position_weight[pos][0]) percent_value = (adjusted_value / self.position_weight[pos][1]) * 100 skill_entries.add((column, percent_value)) if adjusted_value < min_value: current_set_meets_criteria = False else: skill_entries.add((column, row[column])) if row[column] < min_value: current_set_meets_criteria = False else: current_set_meets_criteria = False if current_set_meets_criteria: meets_criteria = True return list(skill_entries), meets_criteria def filter_players_by_position(self, df, allowed_positions): players_by_pos = {pos: [] for pos in allowed_positions.keys()} for _, row in df.iterrows(): player = row['FirstName'] + ' ' + row["LastName"] player_pos = row['Pos'] player_weight = row['Weight'] estimated_speed = row['MaxSpeedMax'] estimated_acceleration = row['AccelerationMax'] player_data = { 'Player': player, 'Pos': player_pos, 'Weight': player_weight, 'Estimated Speed': estimated_speed, 'Estimated Acceleration': estimated_acceleration } for pos, allowed_pos in allowed_positions.items(): if player_pos in allowed_pos: entry = player_data.copy() entry['Estimated Speed'] += self.adjust_speed(player_weight, self.position_weight[pos][0]) entry['Estimated Acceleration'] += + self.adjust_acceleration(player_weight, self.position_weight[pos][0]) available_columns = set(row.keys()) entry['Primary Skills'] = [] primary_meets_criteria = True if pos in self.primary_skills: primary_skills_list, primary_meets_criteria = self.process_skills(self.primary_skills[pos], "Primary", available_columns, row, player, pos, player_weight) entry['Primary Skills'] = primary_skills_list if pos in ("CB", "SS", "MLB", "WLB"): ballhawk = (((row["PassCatchingMax"] / 50) * 100) + ((row["RouteRunningMax"] / 50) * 100)) if ballhawk < 100: entry['Primary Skills'].append( ("Ballhawk", ballhawk) ) else: entry['Primary Skills'].append( ("Ballhawk", 100) ) entry['Secondary Skills'] = [] secondary_meets_criteria = True if pos in self.secondary_skills: secondary_skills_list, secondary_meets_criteria = self.process_skills(self.secondary_skills[pos], "Secondary", available_columns, row, player, pos, player_weight) entry['Secondary Skills'] = secondary_skills_list if (entry['Primary Skills'] or entry['Secondary Skills']) and primary_meets_criteria and secondary_meets_criteria: players_by_pos[pos].append(entry) return players_by_pos def evaluate_player_skills(self, primary_skills, secondary_skills, players_by_pos, allowed_positions): evaluated_players_by_pos = {pos: [] for pos in allowed_positions.keys()} for pos, players in players_by_pos.items(): for player in players: fullname = player['Player'] position = player['Pos'] weight = player["Weight"] estimated_speed = player['Estimated Speed'] estimated_acceleration = player['Estimated Acceleration'] skills = {skill: value for skill, value in player['Primary Skills'] + player['Secondary Skills']} primary_values = [value for skill, value in player['Primary Skills']] secondary_values = [value for skill, value in player['Secondary Skills']] primary_avg = sum(primary_values) / len(primary_values) if primary_values else 0 secondary_avg = sum(secondary_values) / len(secondary_values) if secondary_values else 0 composite_score = round(((primary_avg * 2) + secondary_avg) / 3, 1) if primary_values and secondary_values else 0 evaluated_players_by_pos[pos].append({ "Player": fullname, "Current Position": position, "Current Weight": int(weight), "Composite Score": composite_score, "Estimated Speed": float(estimated_speed), "Estimated Acceleration": float(estimated_acceleration), "Primary Skills": [[s, float(v)] for s, v in player['Primary Skills']] }) return evaluated_players_by_pos def process_upload(self, file_data, mode): player_df = pd.read_excel(io.BytesIO(file_data)) player_df.columns = [col[0].upper() + col[1:] if col[0].islower() else col for col in player_df.columns] if mode == 'current': positions = self.current_positions else: positions = self.allowed_positions filtered_players = self.filter_players_by_position(player_df, positions) evaluated_players = self.evaluate_player_skills(self.primary_skills, self.secondary_skills, filtered_players, positions) return evaluated_players app = PlayerEvaluationApp() class Handler(http.server.BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header('Content-Type', 'text/html') self.end_headers() self.wfile.write(HTML_PAGE.encode()) def do_POST(self): if self.path == '/upload': content_type = self.headers.get('Content-Type', '') if 'multipart/form-data' not in content_type: self._json_response({"error": "Invalid content type"}) return form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': content_type} ) file_item = form['file'] mode = form.getvalue('mode', 'all') file_data = file_item.file.read() try: result = app.process_upload(file_data, mode) self._json_response(result) except Exception as e: self._json_response({"error": str(e)}) else: self.send_response(404) self.end_headers() def _json_response(self, data): self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(data).encode()) def log_message(self, format, *args): pass # Suppress request logging if __name__ == "__main__": PORT = 8765 server = http.server.HTTPServer(('127.0.0.1', PORT), Handler) print(f"Player Evaluation running at http://127.0.0.1:{PORT}") print("Press Ctrl+C to stop.") webbrowser.open(f'http://127.0.0.1:{PORT}') try: server.serve_forever() except KeyboardInterrupt: print("\nServer stopped.") server.server_close() |
|
Re: Player Eval Tool
by
martinwarnett
@
3/16/2026 12:42 pm
Interesting - not a python developer, might try it later.
Though the obvious issue is with indentations being blatted by the forum server. I suspect that's why Seth put his on google drive - not sure why people don't use gitlab.
Last edited 3/16/2026 5:49 pm
|
|