Skip to content

Commit a640161

Browse files
feat: Add ML predictions and redesign team management UI
- Add predicted_points column to player table (V4 migration) - Create train_model.py for model training with historical data - Create predict_current.py for generating and persisting predictions - Update PlayerDTO with predictedPoints field (camelCase JSON) - Update Player entity with predictedPoints field - Update all services to map predictedPoints to DTOs - Add .gitignore for large model files (regenerate via train_model.py) - Redesign TeamManagement UI: - Remove emojis from all UI elements - Update header to show only username - Hide import section after team import - Add tab navigation (My Team / Optimized Predictions) - Convert player display to table layout - Update CSS with tab and table styles
1 parent 8bfc0a6 commit a640161

File tree

12 files changed

+1170
-321
lines changed

12 files changed

+1170
-321
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,4 @@ temp-elo-data/
8181
*.swp
8282
*.swo
8383
*~
84+
flask-api/models/*.joblib

backend/src/main/java/com/example/demo/dto/PlayerDTO.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ public class PlayerDTO {
1111
private String position; // Ensure consistency with the Player model
1212
private double value; // Player's current value (cost)
1313

14-
@JsonProperty("total_points")
14+
@JsonProperty("totalPoints")
1515
private int totalPoints; // Total points for the season
1616

17-
@JsonProperty("event_points")
17+
@JsonProperty("weeklyPoints")
1818
private int weeklyPoints; // Points for the most recent gameweek
1919

2020
@JsonProperty("fplId")
@@ -84,4 +84,15 @@ public Long getFplId() {
8484
public void setFplId(Long fplId) {
8585
this.fplId = fplId;
8686
}
87+
88+
@JsonProperty("predictedPoints")
89+
private double predictedPoints;
90+
91+
public double getPredictedPoints() {
92+
return predictedPoints;
93+
}
94+
95+
public void setPredictedPoints(double predictedPoints) {
96+
this.predictedPoints = predictedPoints;
97+
}
8798
}

backend/src/main/java/com/example/demo/model/Player.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ public Player(Long id, String name, String position, String team, double value,
4545
this.totalPoints = totalPoints;
4646
this.weeklyPoints = weeklyPoints;
4747
}
48-
49-
public Player(Long id, String name, String position, String team, Long fplId, double value, int totalPoints, int weeklyPoints) {
48+
49+
public Player(Long id, String name, String position, String team, Long fplId, double value, int totalPoints,
50+
int weeklyPoints) {
5051
this.id = id;
5152
this.name = name;
5253
this.position = position;
@@ -121,12 +122,23 @@ public List<Team> getTeams() {
121122
public void setTeams(List<Team> teams) {
122123
this.teams = teams;
123124
}
124-
125+
125126
public Long getFplId() {
126127
return fplId;
127128
}
128-
129+
129130
public void setFplId(Long fplId) {
130131
this.fplId = fplId;
131132
}
133+
134+
// Predicted Points
135+
private double predictedPoints = 0.0;
136+
137+
public double getPredictedPoints() {
138+
return predictedPoints;
139+
}
140+
141+
public void setPredictedPoints(double predictedPoints) {
142+
this.predictedPoints = predictedPoints;
143+
}
132144
}

backend/src/main/java/com/example/demo/service/FplImportService.java

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class FplImportService {
3131

3232
private final RestTemplate restTemplate;
3333
private final String FPL_BASE_URL = "https://fantasy.premierleague.com/api";
34-
34+
3535
public FplImportService() {
3636
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
3737
factory.setConnectTimeout(10000); // 10 seconds
@@ -42,34 +42,35 @@ public FplImportService() {
4242
public Team importTeamFromFpl(Long userId, Long entryId) {
4343
try {
4444
System.out.println("Starting FPL import for user " + userId + ", entry " + entryId);
45-
45+
4646
// Try to get the most recent team data by checking multiple gameweeks
4747
// Start from a high gameweek and work backwards until we find data
4848
Map<String, Object> teamData = null;
4949
Integer gameweekUsed = null;
50-
50+
5151
// First, try to get the current gameweek from the API
5252
Integer currentGameweek = getCurrentGameweek();
5353
System.out.println("API reports current gameweek: " + currentGameweek);
54-
54+
5555
// Try current gameweek first, then work backwards
5656
if (teamData == null) {
5757
System.out.println("Trying current gameweek (" + currentGameweek + ") and working backwards...");
5858
for (int gw = currentGameweek; gw >= 1; gw--) {
5959
try {
6060
String picksUrl = String.format("%s/entry/%d/event/%d/picks/", FPL_BASE_URL, entryId, gw);
6161
System.out.println("Trying gameweek " + gw + ": " + picksUrl);
62-
62+
6363
ResponseEntity<Map> response = restTemplate.getForEntity(picksUrl, Map.class);
64-
64+
6565
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
6666
Map<String, Object> data = response.getBody();
6767
List<Map<String, Object>> picks = (List<Map<String, Object>>) data.get("picks");
68-
68+
6969
if (picks != null && !picks.isEmpty()) {
7070
teamData = data;
7171
gameweekUsed = gw;
72-
System.out.println("Found team data for gameweek " + gw + " with " + picks.size() + " players");
72+
System.out.println(
73+
"Found team data for gameweek " + gw + " with " + picks.size() + " players");
7374
break;
7475
}
7576
}
@@ -79,25 +80,26 @@ public Team importTeamFromFpl(Long userId, Long entryId) {
7980
}
8081
}
8182
}
82-
83+
8384
// If we still haven't found data, try from gameweek 20 down to current+1
8485
if (teamData == null) {
8586
System.out.println("No data found from current gameweek down, trying higher gameweeks...");
8687
for (int gw = 20; gw > currentGameweek; gw--) {
8788
try {
8889
String picksUrl = String.format("%s/entry/%d/event/%d/picks/", FPL_BASE_URL, entryId, gw);
8990
System.out.println("Trying higher gameweek " + gw + ": " + picksUrl);
90-
91+
9192
ResponseEntity<Map> response = restTemplate.getForEntity(picksUrl, Map.class);
92-
93+
9394
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
9495
Map<String, Object> data = response.getBody();
9596
List<Map<String, Object>> picks = (List<Map<String, Object>>) data.get("picks");
96-
97+
9798
if (picks != null && !picks.isEmpty()) {
9899
teamData = data;
99100
gameweekUsed = gw;
100-
System.out.println("Found team data for gameweek " + gw + " with " + picks.size() + " players");
101+
System.out.println(
102+
"Found team data for gameweek " + gw + " with " + picks.size() + " players");
101103
break;
102104
}
103105
}
@@ -107,26 +109,27 @@ public Team importTeamFromFpl(Long userId, Long entryId) {
107109
}
108110
}
109111
}
110-
112+
111113
if (teamData == null) {
112114
throw new RuntimeException("Could not find team data for any gameweek");
113115
}
114-
116+
115117
// Extract picks
116118
List<Map<String, Object>> picks = (List<Map<String, Object>>) teamData.get("picks");
117119
System.out.println("Using team data from gameweek " + gameweekUsed + " with " + picks.size() + " players");
118-
120+
119121
// Get user first
120122
System.out.println("Looking for user with ID: " + userId);
121123
Optional<User> userOpt = userRepository.findById(userId);
122124
if (!userOpt.isPresent()) {
123125
System.out.println("User not found in database. Available users:");
124-
userRepository.findAll().forEach(u -> System.out.println(" - User ID: " + u.getId() + ", Email: " + u.getEmail()));
126+
userRepository.findAll()
127+
.forEach(u -> System.out.println(" - User ID: " + u.getId() + ", Email: " + u.getEmail()));
125128
throw new RuntimeException("User not found with ID: " + userId + ". Please register or log in first.");
126129
}
127130
User user = userOpt.get();
128131
System.out.println("Found user: " + user.getEmail() + " (ID: " + user.getId() + ")");
129-
132+
130133
// Get or create team for user
131134
Team team = teamRepository.findByUserId(userId);
132135
if (team == null) {
@@ -141,16 +144,16 @@ public Team importTeamFromFpl(Long userId, Long entryId) {
141144
// Clear existing players
142145
team.getPlayers().clear();
143146
}
144-
147+
145148
// Process each pick and add players to team
146149
List<Player> teamPlayers = new ArrayList<>();
147150
long totalPlayersInDb = playerRepository.count();
148151
System.out.println("Total players in database: " + totalPlayersInDb);
149-
152+
150153
for (Map<String, Object> pick : picks) {
151154
Integer fplPlayerId = (Integer) pick.get("element");
152155
Optional<Player> playerOpt = playerRepository.findByFplId(fplPlayerId.longValue());
153-
156+
154157
if (playerOpt.isPresent()) {
155158
teamPlayers.add(playerOpt.get());
156159
System.out.println("Added player: " + playerOpt.get().getName() + " (FPL ID: " + fplPlayerId + ")");
@@ -160,32 +163,34 @@ public Team importTeamFromFpl(Long userId, Long entryId) {
160163
System.out.println(" Checking if database has any players...");
161164
}
162165
}
163-
164-
System.out.println("Successfully matched " + teamPlayers.size() + " out of " + picks.size() + " players from FPL");
165-
166+
167+
System.out.println(
168+
"Successfully matched " + teamPlayers.size() + " out of " + picks.size() + " players from FPL");
169+
166170
team.setPlayers(teamPlayers);
167-
171+
168172
// Calculate remaining budget
169173
double totalValue = teamPlayers.stream()
170-
.mapToDouble(Player::getValue)
171-
.sum();
174+
.mapToDouble(Player::getValue)
175+
.sum();
172176
team.setBudget(100.0 - totalValue);
173-
177+
174178
team = teamRepository.save(team);
175-
System.out.println("Team saved with " + team.getPlayers().size() + " players from gameweek " + gameweekUsed);
176-
179+
System.out
180+
.println("Team saved with " + team.getPlayers().size() + " players from gameweek " + gameweekUsed);
181+
177182
return team;
178-
183+
179184
} catch (Exception e) {
180185
throw new RuntimeException("Failed to import team from FPL: " + e.getMessage(), e);
181186
}
182187
}
183-
188+
184189
private Integer getCurrentGameweek() {
185190
try {
186191
String bootstrapUrl = FPL_BASE_URL + "/bootstrap-static/";
187192
ResponseEntity<Map> response = restTemplate.getForEntity(bootstrapUrl, Map.class);
188-
193+
189194
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
190195
Map<String, Object> data = response.getBody();
191196
Map<String, Object> currentEvent = (Map<String, Object>) data.get("current-event");
@@ -198,7 +203,7 @@ private Integer getCurrentGameweek() {
198203
} catch (Exception e) {
199204
System.out.println("Warning: Could not fetch current gameweek: " + e.getMessage());
200205
}
201-
206+
202207
// If we can't get it from API, try to determine by testing recent gameweeks
203208
System.out.println("API failed, trying to determine current gameweek by testing recent weeks...");
204209
for (int gw = 10; gw >= 1; gw--) {
@@ -213,21 +218,21 @@ private Integer getCurrentGameweek() {
213218
continue;
214219
}
215220
}
216-
221+
217222
return 7; // Default fallback - we know gameweek 7 exists
218223
}
219-
224+
220225
public List<PlayerDTO> getTeamPlayers(Long userId) {
221226
Team team = teamRepository.findByUserId(userId);
222227
if (team == null) {
223228
return new ArrayList<>();
224229
}
225-
230+
226231
return team.getPlayers().stream()
227-
.map(this::convertToDTO)
228-
.collect(Collectors.toList());
232+
.map(this::convertToDTO)
233+
.collect(Collectors.toList());
229234
}
230-
235+
231236
private PlayerDTO convertToDTO(Player player) {
232237
PlayerDTO dto = new PlayerDTO();
233238
dto.setId(player.getId());
@@ -237,6 +242,7 @@ private PlayerDTO convertToDTO(Player player) {
237242
dto.setValue(player.getValue());
238243
dto.setTotalPoints(player.getTotalPoints());
239244
dto.setWeeklyPoints(player.getWeeklyPoints());
245+
dto.setPredictedPoints(player.getPredictedPoints());
240246
return dto;
241247
}
242248
}

backend/src/main/java/com/example/demo/service/PlayerDataService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public PlayerDTO convertToDTO(Player player) {
3838
dto.setTotalPoints(player.getTotalPoints());
3939
dto.setWeeklyPoints(player.getWeeklyPoints());
4040
dto.setFplId(player.getFplId());
41+
dto.setPredictedPoints(player.getPredictedPoints());
4142
return dto;
4243
}
4344
}

0 commit comments

Comments
 (0)