diff options
| author | Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> | 2024-10-31 00:29:37 +0300 |
|---|---|---|
| committer | Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> | 2024-10-31 00:29:37 +0300 |
| commit | 7e63ef2fd6ce429d63ef73ee54199bc530da84fe (patch) | |
| tree | 6f23d82c0cb113ce21728ce0fdf789e5b763de11 /backend/handlers/user.go | |
| parent | frontend: fix coop map categories (diff) | |
| download | lphub-7e63ef2fd6ce429d63ef73ee54199bc530da84fe.tar.gz lphub-7e63ef2fd6ce429d63ef73ee54199bc530da84fe.tar.bz2 lphub-7e63ef2fd6ce429d63ef73ee54199bc530da84fe.zip | |
backend: fix all rankings and placements, use sql funcs
Diffstat (limited to '')
| -rw-r--r-- | backend/handlers/user.go | 493 |
1 files changed, 193 insertions, 300 deletions
diff --git a/backend/handlers/user.go b/backend/handlers/user.go index dc058c8..021a47f 100644 --- a/backend/handlers/user.go +++ b/backend/handlers/user.go | |||
| @@ -134,162 +134,118 @@ func Profile(c *gin.Context) { | |||
| 134 | } | 134 | } |
| 135 | } | 135 | } |
| 136 | rankings.Overall.CompletionCount = rankings.Singleplayer.CompletionCount + rankings.Cooperative.CompletionCount | 136 | rankings.Overall.CompletionCount = rankings.Singleplayer.CompletionCount + rankings.Cooperative.CompletionCount |
| 137 | // Get user ranking placement for singleplayer | 137 | // Get user rankings. We are basically doing the same thing in RankingsLPHUB endpoint lol. |
| 138 | sql = `SELECT u.steam_id, COUNT(DISTINCT map_id), | 138 | rankingsList := RankingsResponse{ |
| 139 | (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g."name" = 'Portal 2 - Singleplayer' AND maps.is_disabled = false), | 139 | Singleplayer: []models.UserRanking{}, |
| 140 | (SELECT SUM(min_score_count) AS total_min_score_count FROM ( | 140 | Multiplayer: []models.UserRanking{}, |
| 141 | SELECT user_id, MIN(score_count) AS min_score_count FROM records_sp WHERE is_deleted = false GROUP BY user_id, map_id) AS subquery WHERE user_id = u.steam_id) | 141 | Overall: []models.UserRanking{}, |
| 142 | FROM records_sp sp JOIN users u ON u.steam_id = sp.user_id WHERE is_deleted = false GROUP BY u.steam_id, u.user_name | 142 | } |
| 143 | HAVING COUNT(DISTINCT map_id) = (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false) | 143 | // Singleplayer rankings |
| 144 | ORDER BY total_min_score_count ASC` | 144 | rows, err = database.DB.Query(`SELECT * FROM get_rankings_singleplayer();`) |
| 145 | rows, err = database.DB.Query(sql) | ||
| 146 | if err != nil { | 145 | if err != nil { |
| 147 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 146 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 148 | return | 147 | return |
| 149 | } | 148 | } |
| 150 | placement := 1 | ||
| 151 | for rows.Next() { | 149 | for rows.Next() { |
| 152 | var steamID string | 150 | ranking := models.UserRanking{} |
| 153 | var completionCount int | 151 | err = rows.Scan(&ranking.User.SteamID, &ranking.User.UserName, &ranking.User.AvatarLink, &ranking.TotalScore) |
| 154 | var totalCount int | ||
| 155 | var userPortalCount int | ||
| 156 | err = rows.Scan(&steamID, &completionCount, &totalCount, &userPortalCount) | ||
| 157 | if err != nil { | 152 | if err != nil { |
| 158 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 153 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 159 | return | 154 | return |
| 160 | } | 155 | } |
| 161 | if completionCount != totalCount { | 156 | rankingsList.Singleplayer = append(rankingsList.Singleplayer, ranking) |
| 162 | placement++ | 157 | } |
| 163 | continue | 158 | // Multiplayer rankings |
| 164 | } | 159 | rows, err = database.DB.Query(`SELECT * FROM get_rankings_multiplayer();`) |
| 165 | if steamID != user.(models.User).SteamID { | ||
| 166 | placement++ | ||
| 167 | continue | ||
| 168 | } | ||
| 169 | rankings.Singleplayer.Rank = placement | ||
| 170 | } | ||
| 171 | // Get user ranking placement for multiplayer | ||
| 172 | sql = `SELECT u.steam_id, COUNT(DISTINCT map_id), | ||
| 173 | (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g."name" = 'Portal 2 - Cooperative' AND is_disabled = false), | ||
| 174 | (SELECT SUM(min_score_count) AS total_min_score_count FROM ( | ||
| 175 | SELECT host_id, partner_id, MIN(score_count) AS min_score_count FROM records_mp WHERE is_deleted = false GROUP BY host_id, partner_id, map_id) AS subquery WHERE host_id = u.steam_id OR partner_id = u.steam_id) | ||
| 176 | FROM records_mp mp JOIN users u ON u.steam_id = mp.host_id OR u.steam_id = mp.partner_id WHERE mp.is_deleted = false GROUP BY u.steam_id, u.user_name | ||
| 177 | HAVING COUNT(DISTINCT map_id) = (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = true AND is_disabled = false) | ||
| 178 | ORDER BY total_min_score_count ASC` | ||
| 179 | rows, err = database.DB.Query(sql) | ||
| 180 | if err != nil { | 160 | if err != nil { |
| 181 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 161 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 182 | return | 162 | return |
| 183 | } | 163 | } |
| 184 | placement = 1 | ||
| 185 | for rows.Next() { | 164 | for rows.Next() { |
| 186 | var steamID string | 165 | ranking := models.UserRanking{} |
| 187 | var completionCount int | 166 | err = rows.Scan(&ranking.User.SteamID, &ranking.User.UserName, &ranking.User.AvatarLink, &ranking.TotalScore) |
| 188 | var totalCount int | ||
| 189 | var userPortalCount int | ||
| 190 | err = rows.Scan(&steamID, &completionCount, &totalCount, &userPortalCount) | ||
| 191 | if err != nil { | 167 | if err != nil { |
| 192 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 168 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 193 | return | 169 | return |
| 194 | } | 170 | } |
| 195 | if completionCount != totalCount { | 171 | rankingsList.Multiplayer = append(rankingsList.Multiplayer, ranking) |
| 196 | placement++ | 172 | } |
| 197 | continue | 173 | // Overall rankings |
| 174 | var hasOverallPlacement bool | ||
| 175 | for _, spRanking := range rankingsList.Singleplayer { | ||
| 176 | for _, mpRanking := range rankingsList.Multiplayer { | ||
| 177 | if spRanking.User.SteamID == mpRanking.User.SteamID { | ||
| 178 | if spRanking.User.SteamID == user.(models.User).SteamID { | ||
| 179 | hasOverallPlacement = true | ||
| 180 | } | ||
| 181 | totalScore := spRanking.TotalScore + mpRanking.TotalScore | ||
| 182 | overallRanking := models.UserRanking{ | ||
| 183 | User: spRanking.User, | ||
| 184 | TotalScore: totalScore, | ||
| 185 | } | ||
| 186 | rankingsList.Overall = append(rankingsList.Overall, overallRanking) | ||
| 187 | break | ||
| 188 | } | ||
| 198 | } | 189 | } |
| 199 | if steamID != user.(models.User).SteamID { | 190 | } |
| 200 | placement++ | 191 | |
| 201 | continue | 192 | placement := 1 |
| 193 | ties := 0 | ||
| 194 | for index := 0; index < len(rankingsList.Singleplayer); index++ { | ||
| 195 | if index != 0 && rankingsList.Singleplayer[index-1].TotalScore == rankingsList.Singleplayer[index].TotalScore { | ||
| 196 | ties++ | ||
| 197 | rankingsList.Singleplayer[index].Placement = placement - ties | ||
| 198 | } else { | ||
| 199 | ties = 0 | ||
| 200 | rankingsList.Singleplayer[index].Placement = placement | ||
| 202 | } | 201 | } |
| 203 | rankings.Cooperative.Rank = placement | 202 | placement++ |
| 204 | } | 203 | } |
| 205 | // Get user ranking placement for overall if they qualify | 204 | |
| 206 | if rankings.Singleplayer.Rank != 0 && rankings.Cooperative.Rank != 0 { | 205 | placement = 1 |
| 207 | sql = `WITH user_sp AS ( | 206 | ties = 0 |
| 208 | SELECT u.steam_id, | 207 | for index := 0; index < len(rankingsList.Multiplayer); index++ { |
| 209 | SUM(subquery.min_score_count) AS total_min_score_count | 208 | if index != 0 && rankingsList.Multiplayer[index-1].TotalScore == rankingsList.Multiplayer[index].TotalScore { |
| 210 | FROM users u | 209 | ties++ |
| 211 | LEFT JOIN ( | 210 | rankingsList.Multiplayer[index].Placement = placement - ties |
| 212 | SELECT user_id, map_id, MIN(score_count) AS min_score_count | 211 | } else { |
| 213 | FROM records_sp WHERE is_deleted = false | 212 | ties = 0 |
| 214 | GROUP BY user_id, map_id | 213 | rankingsList.Multiplayer[index].Placement = placement |
| 215 | ) AS subquery ON subquery.user_id = u.steam_id | ||
| 216 | WHERE u.steam_id IN ( | ||
| 217 | SELECT user_id | ||
| 218 | FROM records_sp sp | ||
| 219 | JOIN maps m ON sp.map_id = m.id | ||
| 220 | JOIN games g ON m.game_id = g.id | ||
| 221 | WHERE g.is_coop = FALSE AND m.is_disabled = FALSE AND sp.is_deleted = false | ||
| 222 | GROUP BY user_id | ||
| 223 | HAVING COUNT(DISTINCT sp.map_id) = ( | ||
| 224 | SELECT COUNT(maps.name) | ||
| 225 | FROM maps | ||
| 226 | INNER JOIN games g ON maps.game_id = g.id | ||
| 227 | WHERE g.is_coop = FALSE AND maps.is_disabled = FALSE | ||
| 228 | ) | ||
| 229 | ) | ||
| 230 | GROUP BY u.steam_id | ||
| 231 | ), user_mp AS ( | ||
| 232 | SELECT u.steam_id, | ||
| 233 | SUM(subquery.min_score_count) AS total_min_score_count | ||
| 234 | FROM users u | ||
| 235 | LEFT JOIN ( | ||
| 236 | SELECT host_id, partner_id, map_id, MIN(score_count) AS min_score_count | ||
| 237 | FROM records_mp WHERE is_deleted = false | ||
| 238 | GROUP BY host_id, partner_id, map_id | ||
| 239 | ) AS subquery ON subquery.host_id = u.steam_id OR subquery.partner_id = u.steam_id | ||
| 240 | WHERE u.steam_id IN ( | ||
| 241 | SELECT host_id | ||
| 242 | FROM records_mp mp | ||
| 243 | JOIN maps m ON mp.map_id = m.id | ||
| 244 | JOIN games g ON m.game_id = g.id | ||
| 245 | WHERE g.is_coop = TRUE AND m.is_disabled = FALSE AND mp.is_deleted = false | ||
| 246 | GROUP BY host_id | ||
| 247 | HAVING COUNT(DISTINCT mp.map_id) = ( | ||
| 248 | SELECT COUNT(maps.name) | ||
| 249 | FROM maps | ||
| 250 | INNER JOIN games g ON maps.game_id = g.id | ||
| 251 | WHERE g.is_coop = TRUE AND maps.is_disabled = FALSE | ||
| 252 | ) | ||
| 253 | UNION | ||
| 254 | SELECT partner_id | ||
| 255 | FROM records_mp mp | ||
| 256 | JOIN maps m ON mp.map_id = m.id | ||
| 257 | JOIN games g ON m.game_id = g.id | ||
| 258 | WHERE g.is_coop = TRUE AND m.is_disabled = FALSE AND mp.is_deleted = false | ||
| 259 | GROUP BY partner_id | ||
| 260 | HAVING COUNT(DISTINCT mp.map_id) = ( | ||
| 261 | SELECT COUNT(maps.name) | ||
| 262 | FROM maps | ||
| 263 | INNER JOIN games g ON maps.game_id = g.id | ||
| 264 | WHERE g.is_coop = TRUE AND maps.is_disabled = FALSE | ||
| 265 | ) | ||
| 266 | ) | ||
| 267 | GROUP BY u.steam_id | ||
| 268 | ) | ||
| 269 | SELECT COALESCE(sp.steam_id, mp.steam_id) AS steam_id, | ||
| 270 | sp.total_min_score_count + mp.total_min_score_count AS overall_total_min_score_count | ||
| 271 | FROM user_sp sp | ||
| 272 | INNER JOIN user_mp mp ON sp.steam_id = mp.steam_id | ||
| 273 | ORDER BY overall_total_min_score_count ASC` | ||
| 274 | rows, err = database.DB.Query(sql) | ||
| 275 | if err != nil { | ||
| 276 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | ||
| 277 | return | ||
| 278 | } | 214 | } |
| 279 | placement = 1 | 215 | placement++ |
| 280 | for rows.Next() { | 216 | } |
| 281 | var steamID string | 217 | |
| 282 | var userPortalCount int | 218 | placement = 1 |
| 283 | err = rows.Scan(&steamID, &userPortalCount) | 219 | ties = 0 |
| 284 | if err != nil { | 220 | for index := 0; index < len(rankingsList.Overall); index++ { |
| 285 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 221 | if index != 0 && rankingsList.Overall[index-1].TotalScore == rankingsList.Overall[index].TotalScore { |
| 286 | return | 222 | ties++ |
| 287 | } | 223 | rankingsList.Overall[index].Placement = placement - ties |
| 288 | if steamID != user.(models.User).SteamID { | 224 | } else { |
| 289 | placement++ | 225 | ties = 0 |
| 290 | continue | 226 | rankingsList.Overall[index].Placement = placement |
| 227 | } | ||
| 228 | placement++ | ||
| 229 | } | ||
| 230 | // After we did that heavy calculation and got the rankings of ALL players, let's see if our user exists and grab the placements if they do. | ||
| 231 | for _, singleplayer := range rankingsList.Singleplayer { | ||
| 232 | if singleplayer.User.SteamID == user.(models.User).SteamID { | ||
| 233 | rankings.Singleplayer.Rank = singleplayer.Placement | ||
| 234 | break | ||
| 235 | } | ||
| 236 | } | ||
| 237 | for _, multiplayer := range rankingsList.Multiplayer { | ||
| 238 | if multiplayer.User.SteamID == user.(models.User).SteamID { | ||
| 239 | rankings.Cooperative.Rank = multiplayer.Placement | ||
| 240 | break | ||
| 241 | } | ||
| 242 | } | ||
| 243 | if hasOverallPlacement { | ||
| 244 | for _, overall := range rankingsList.Overall { | ||
| 245 | if overall.User.SteamID == user.(models.User).SteamID { | ||
| 246 | rankings.Overall.Rank = overall.Placement | ||
| 247 | break | ||
| 291 | } | 248 | } |
| 292 | rankings.Overall.Rank = placement | ||
| 293 | } | 249 | } |
| 294 | } | 250 | } |
| 295 | records := []ProfileRecords{} | 251 | records := []ProfileRecords{} |
| @@ -301,13 +257,7 @@ func Profile(c *gin.Context) { | |||
| 301 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 257 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 302 | return | 258 | return |
| 303 | } | 259 | } |
| 304 | sql = `WITH best_scores AS (WITH RankedScores AS (SELECT sp.user_id, sp.map_id, sp.score_count, sp.score_time, ROW_NUMBER() OVER ( | 260 | placementsRows, err := database.DB.Query(`SELECT * FROM get_placements_singleplayer($1);`, user.(models.User).SteamID) |
| 305 | PARTITION BY sp.user_id, sp.map_id ORDER BY sp.score_count ASC, sp.score_time ASC) AS rank FROM records_sp sp WHERE sp.is_deleted = false) | ||
| 306 | SELECT user_id, map_id, score_count AS best_score_count, score_time AS best_score_time FROM RankedScores WHERE rank = 1) | ||
| 307 | SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id | ||
| 308 | AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count | ||
| 309 | AND inner_scores.best_score_time < bs.best_score_time))) AS placement FROM best_scores AS bs WHERE bs.user_id = $1 ORDER BY map_id, placement` | ||
| 310 | placementsRows, err := database.DB.Query(sql, user.(models.User).SteamID) | ||
| 311 | if err != nil { | 261 | if err != nil { |
| 312 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 262 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 313 | return | 263 | return |
| @@ -315,8 +265,9 @@ func Profile(c *gin.Context) { | |||
| 315 | placements := []int{} | 265 | placements := []int{} |
| 316 | placementIndex := 0 | 266 | placementIndex := 0 |
| 317 | for placementsRows.Next() { | 267 | for placementsRows.Next() { |
| 268 | var mapID int | ||
| 318 | var placement int | 269 | var placement int |
| 319 | placementsRows.Scan(&placement) | 270 | placementsRows.Scan(&mapID, &placement) |
| 320 | placements = append(placements, placement) | 271 | placements = append(placements, placement) |
| 321 | } | 272 | } |
| 322 | for rows.Next() { | 273 | for rows.Next() { |
| @@ -357,13 +308,7 @@ func Profile(c *gin.Context) { | |||
| 357 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 308 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 358 | return | 309 | return |
| 359 | } | 310 | } |
| 360 | sql = `WITH best_scores AS (WITH RankedScores AS (SELECT mp.host_id, mp.partner_id, mp.map_id, mp.score_count, mp.score_time, ROW_NUMBER() OVER ( | 311 | placementsRows, err = database.DB.Query(`SELECT * FROM get_placements_multiplayer($1);`, user.(models.User).SteamID) |
| 361 | PARTITION BY mp.host_id, mp.partner_id, mp.map_id ORDER BY mp.score_count ASC, mp.score_time ASC) AS rank FROM records_mp mp WHERE mp.is_deleted = false) | ||
| 362 | SELECT host_id, partner_id, map_id, score_count AS best_score_count, score_time AS best_score_time FROM RankedScores WHERE rank = 1) | ||
| 363 | SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id | ||
| 364 | AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count | ||
| 365 | AND inner_scores.best_score_time < bs.best_score_time))) AS placement FROM best_scores AS bs WHERE bs.host_id = $1 or bs.partner_id = $1 ORDER BY map_id, placement` | ||
| 366 | placementsRows, err = database.DB.Query(sql, user.(models.User).SteamID) | ||
| 367 | if err != nil { | 312 | if err != nil { |
| 368 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 313 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 369 | return | 314 | return |
| @@ -371,8 +316,9 @@ func Profile(c *gin.Context) { | |||
| 371 | placements = []int{} | 316 | placements = []int{} |
| 372 | placementIndex = 0 | 317 | placementIndex = 0 |
| 373 | for placementsRows.Next() { | 318 | for placementsRows.Next() { |
| 319 | var mapID int | ||
| 374 | var placement int | 320 | var placement int |
| 375 | placementsRows.Scan(&placement) | 321 | placementsRows.Scan(&mapID, &placement) |
| 376 | placements = append(placements, placement) | 322 | placements = append(placements, placement) |
| 377 | } | 323 | } |
| 378 | for rows.Next() { | 324 | for rows.Next() { |
| @@ -512,162 +458,119 @@ func FetchUser(c *gin.Context) { | |||
| 512 | } | 458 | } |
| 513 | } | 459 | } |
| 514 | rankings.Overall.CompletionCount = rankings.Singleplayer.CompletionCount + rankings.Cooperative.CompletionCount | 460 | rankings.Overall.CompletionCount = rankings.Singleplayer.CompletionCount + rankings.Cooperative.CompletionCount |
| 515 | // Get user ranking placement for singleplayer | 461 | // Get user rankings. We are basically doing the same thing in RankingsLPHUB endpoint lol. |
| 516 | sql = `SELECT u.steam_id, COUNT(DISTINCT map_id), | 462 | rankingsList := RankingsResponse{ |
| 517 | (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g."name" = 'Portal 2 - Singleplayer' AND maps.is_disabled = false), | 463 | Singleplayer: []models.UserRanking{}, |
| 518 | (SELECT SUM(min_score_count) AS total_min_score_count FROM ( | 464 | Multiplayer: []models.UserRanking{}, |
| 519 | SELECT user_id, MIN(score_count) AS min_score_count FROM records_sp WHERE is_deleted = false GROUP BY user_id, map_id) AS subquery WHERE user_id = u.steam_id) | 465 | Overall: []models.UserRanking{}, |
| 520 | FROM records_sp sp JOIN users u ON u.steam_id = sp.user_id WHERE is_deleted = false GROUP BY u.steam_id, u.user_name | 466 | } |
| 521 | HAVING COUNT(DISTINCT map_id) = (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false) | 467 | // Singleplayer rankings |
| 522 | ORDER BY total_min_score_count ASC` | 468 | rows, err = database.DB.Query(`SELECT * FROM get_rankings_singleplayer();`) |
| 523 | rows, err = database.DB.Query(sql) | ||
| 524 | if err != nil { | 469 | if err != nil { |
| 525 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 470 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 526 | return | 471 | return |
| 527 | } | 472 | } |
| 528 | placement := 1 | ||
| 529 | for rows.Next() { | 473 | for rows.Next() { |
| 530 | var steamID string | 474 | ranking := models.UserRanking{} |
| 531 | var completionCount int | 475 | err = rows.Scan(&ranking.User.SteamID, &ranking.User.UserName, &ranking.User.AvatarLink, &ranking.TotalScore) |
| 532 | var totalCount int | ||
| 533 | var userPortalCount int | ||
| 534 | err = rows.Scan(&steamID, &completionCount, &totalCount, &userPortalCount) | ||
| 535 | if err != nil { | 476 | if err != nil { |
| 536 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 477 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 537 | return | 478 | return |
| 538 | } | 479 | } |
| 539 | if completionCount != totalCount { | 480 | rankingsList.Singleplayer = append(rankingsList.Singleplayer, ranking) |
| 540 | placement++ | 481 | } |
| 541 | continue | 482 | // Multiplayer rankings |
| 542 | } | 483 | rows, err = database.DB.Query(`SELECT * FROM get_rankings_multiplayer();`) |
| 543 | if steamID != user.SteamID { | ||
| 544 | placement++ | ||
| 545 | continue | ||
| 546 | } | ||
| 547 | rankings.Singleplayer.Rank = placement | ||
| 548 | } | ||
| 549 | // Get user ranking placement for multiplayer | ||
| 550 | sql = `SELECT u.steam_id, COUNT(DISTINCT map_id), | ||
| 551 | (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g."name" = 'Portal 2 - Cooperative' AND is_disabled = false), | ||
| 552 | (SELECT SUM(min_score_count) AS total_min_score_count FROM ( | ||
| 553 | SELECT host_id, partner_id, MIN(score_count) AS min_score_count FROM records_mp WHERE is_deleted = false GROUP BY host_id, partner_id, map_id) AS subquery WHERE host_id = u.steam_id OR partner_id = u.steam_id) | ||
| 554 | FROM records_mp mp JOIN users u ON u.steam_id = mp.host_id OR u.steam_id = mp.partner_id WHERE mp.is_deleted = false GROUP BY u.steam_id, u.user_name | ||
| 555 | HAVING COUNT(DISTINCT map_id) = (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = true AND is_disabled = false) | ||
| 556 | ORDER BY total_min_score_count ASC` | ||
| 557 | rows, err = database.DB.Query(sql) | ||
| 558 | if err != nil { | 484 | if err != nil { |
| 559 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 485 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 560 | return | 486 | return |
| 561 | } | 487 | } |
| 562 | placement = 1 | ||
| 563 | for rows.Next() { | 488 | for rows.Next() { |
| 564 | var steamID string | 489 | ranking := models.UserRanking{} |
| 565 | var completionCount int | 490 | err = rows.Scan(&ranking.User.SteamID, &ranking.User.UserName, &ranking.User.AvatarLink, &ranking.TotalScore) |
| 566 | var totalCount int | ||
| 567 | var userPortalCount int | ||
| 568 | err = rows.Scan(&steamID, &completionCount, &totalCount, &userPortalCount) | ||
| 569 | if err != nil { | 491 | if err != nil { |
| 570 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 492 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 571 | return | 493 | return |
| 572 | } | 494 | } |
| 573 | if completionCount != totalCount { | 495 | rankingsList.Multiplayer = append(rankingsList.Multiplayer, ranking) |
| 574 | placement++ | 496 | } |
| 575 | continue | 497 | // Overall rankings |
| 498 | var hasOverallPlacement bool | ||
| 499 | for _, spRanking := range rankingsList.Singleplayer { | ||
| 500 | for _, mpRanking := range rankingsList.Multiplayer { | ||
| 501 | if spRanking.User.SteamID == mpRanking.User.SteamID { | ||
| 502 | if spRanking.User.SteamID == user.SteamID { | ||
| 503 | hasOverallPlacement = true | ||
| 504 | } | ||
| 505 | totalScore := spRanking.TotalScore + mpRanking.TotalScore | ||
| 506 | overallRanking := models.UserRanking{ | ||
| 507 | User: spRanking.User, | ||
| 508 | TotalScore: totalScore, | ||
| 509 | } | ||
| 510 | rankingsList.Overall = append(rankingsList.Overall, overallRanking) | ||
| 511 | break | ||
| 512 | } | ||
| 576 | } | 513 | } |
| 577 | if steamID != user.SteamID { | 514 | } |
| 578 | placement++ | 515 | |
| 579 | continue | 516 | placement := 1 |
| 517 | ties := 0 | ||
| 518 | for index := 0; index < len(rankingsList.Singleplayer); index++ { | ||
| 519 | if index != 0 && rankingsList.Singleplayer[index-1].TotalScore == rankingsList.Singleplayer[index].TotalScore { | ||
| 520 | ties++ | ||
| 521 | rankingsList.Singleplayer[index].Placement = placement - ties | ||
| 522 | } else { | ||
| 523 | ties = 0 | ||
| 524 | rankingsList.Singleplayer[index].Placement = placement | ||
| 580 | } | 525 | } |
| 581 | rankings.Cooperative.Rank = placement | 526 | placement++ |
| 582 | } | 527 | } |
| 583 | // Get user ranking placement for overall if they qualify | 528 | |
| 584 | if rankings.Singleplayer.Rank != 0 && rankings.Cooperative.Rank != 0 { | 529 | placement = 1 |
| 585 | sql = `WITH user_sp AS ( | 530 | ties = 0 |
| 586 | SELECT u.steam_id, | 531 | for index := 0; index < len(rankingsList.Multiplayer); index++ { |
| 587 | SUM(subquery.min_score_count) AS total_min_score_count | 532 | if index != 0 && rankingsList.Multiplayer[index-1].TotalScore == rankingsList.Multiplayer[index].TotalScore { |
| 588 | FROM users u | 533 | ties++ |
| 589 | LEFT JOIN ( | 534 | rankingsList.Multiplayer[index].Placement = placement - ties |
| 590 | SELECT user_id, map_id, MIN(score_count) AS min_score_count | 535 | } else { |
| 591 | FROM records_sp WHERE is_deleted = false | 536 | ties = 0 |
| 592 | GROUP BY user_id, map_id | 537 | rankingsList.Multiplayer[index].Placement = placement |
| 593 | ) AS subquery ON subquery.user_id = u.steam_id | ||
| 594 | WHERE u.steam_id IN ( | ||
| 595 | SELECT user_id | ||
| 596 | FROM records_sp sp | ||
| 597 | JOIN maps m ON sp.map_id = m.id | ||
| 598 | JOIN games g ON m.game_id = g.id | ||
| 599 | WHERE g.is_coop = FALSE AND m.is_disabled = FALSE AND sp.is_deleted = false | ||
| 600 | GROUP BY user_id | ||
| 601 | HAVING COUNT(DISTINCT sp.map_id) = ( | ||
| 602 | SELECT COUNT(maps.name) | ||
| 603 | FROM maps | ||
| 604 | INNER JOIN games g ON maps.game_id = g.id | ||
| 605 | WHERE g.is_coop = FALSE AND maps.is_disabled = FALSE | ||
| 606 | ) | ||
| 607 | ) | ||
| 608 | GROUP BY u.steam_id | ||
| 609 | ), user_mp AS ( | ||
| 610 | SELECT u.steam_id, | ||
| 611 | SUM(subquery.min_score_count) AS total_min_score_count | ||
| 612 | FROM users u | ||
| 613 | LEFT JOIN ( | ||
| 614 | SELECT host_id, partner_id, map_id, MIN(score_count) AS min_score_count | ||
| 615 | FROM records_mp WHERE is_deleted = false | ||
| 616 | GROUP BY host_id, partner_id, map_id | ||
| 617 | ) AS subquery ON subquery.host_id = u.steam_id OR subquery.partner_id = u.steam_id | ||
| 618 | WHERE u.steam_id IN ( | ||
| 619 | SELECT host_id | ||
| 620 | FROM records_mp mp | ||
| 621 | JOIN maps m ON mp.map_id = m.id | ||
| 622 | JOIN games g ON m.game_id = g.id | ||
| 623 | WHERE g.is_coop = TRUE AND m.is_disabled = FALSE AND mp.is_deleted = false | ||
| 624 | GROUP BY host_id | ||
| 625 | HAVING COUNT(DISTINCT mp.map_id) = ( | ||
| 626 | SELECT COUNT(maps.name) | ||
| 627 | FROM maps | ||
| 628 | INNER JOIN games g ON maps.game_id = g.id | ||
| 629 | WHERE g.is_coop = TRUE AND maps.is_disabled = FALSE | ||
| 630 | ) | ||
| 631 | UNION | ||
| 632 | SELECT partner_id | ||
| 633 | FROM records_mp mp | ||
| 634 | JOIN maps m ON mp.map_id = m.id | ||
| 635 | JOIN games g ON m.game_id = g.id | ||
| 636 | WHERE g.is_coop = TRUE AND m.is_disabled = FALSE AND mp.is_deleted = false | ||
| 637 | GROUP BY partner_id | ||
| 638 | HAVING COUNT(DISTINCT mp.map_id) = ( | ||
| 639 | SELECT COUNT(maps.name) | ||
| 640 | FROM maps | ||
| 641 | INNER JOIN games g ON maps.game_id = g.id | ||
| 642 | WHERE g.is_coop = TRUE AND maps.is_disabled = FALSE | ||
| 643 | ) | ||
| 644 | ) | ||
| 645 | GROUP BY u.steam_id | ||
| 646 | ) | ||
| 647 | SELECT COALESCE(sp.steam_id, mp.steam_id) AS steam_id, | ||
| 648 | sp.total_min_score_count + mp.total_min_score_count AS overall_total_min_score_count | ||
| 649 | FROM user_sp sp | ||
| 650 | INNER JOIN user_mp mp ON sp.steam_id = mp.steam_id | ||
| 651 | ORDER BY overall_total_min_score_count ASC` | ||
| 652 | rows, err = database.DB.Query(sql) | ||
| 653 | if err != nil { | ||
| 654 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | ||
| 655 | return | ||
| 656 | } | 538 | } |
| 657 | placement = 1 | 539 | placement++ |
| 658 | for rows.Next() { | 540 | } |
| 659 | var steamID string | 541 | |
| 660 | var userPortalCount int | 542 | placement = 1 |
| 661 | err = rows.Scan(&steamID, &userPortalCount) | 543 | ties = 0 |
| 662 | if err != nil { | 544 | for index := 0; index < len(rankingsList.Overall); index++ { |
| 663 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 545 | if index != 0 && rankingsList.Overall[index-1].TotalScore == rankingsList.Overall[index].TotalScore { |
| 664 | return | 546 | ties++ |
| 665 | } | 547 | rankingsList.Overall[index].Placement = placement - ties |
| 666 | if steamID != user.SteamID { | 548 | } else { |
| 667 | placement++ | 549 | ties = 0 |
| 668 | continue | 550 | rankingsList.Overall[index].Placement = placement |
| 551 | } | ||
| 552 | placement++ | ||
| 553 | } | ||
| 554 | // After we did that heavy calculation and got the rankings of ALL players, let's see if our user exists | ||
| 555 | // and grab the placements if they do. | ||
| 556 | for _, singleplayer := range rankingsList.Singleplayer { | ||
| 557 | if singleplayer.User.SteamID == user.SteamID { | ||
| 558 | rankings.Singleplayer.Rank = singleplayer.Placement | ||
| 559 | break | ||
| 560 | } | ||
| 561 | } | ||
| 562 | for _, multiplayer := range rankingsList.Multiplayer { | ||
| 563 | if multiplayer.User.SteamID == user.SteamID { | ||
| 564 | rankings.Cooperative.Rank = multiplayer.Placement | ||
| 565 | break | ||
| 566 | } | ||
| 567 | } | ||
| 568 | if hasOverallPlacement { | ||
| 569 | for _, overall := range rankingsList.Overall { | ||
| 570 | if overall.User.SteamID == user.SteamID { | ||
| 571 | rankings.Overall.Rank = overall.Placement | ||
| 572 | break | ||
| 669 | } | 573 | } |
| 670 | rankings.Overall.Rank = placement | ||
| 671 | } | 574 | } |
| 672 | } | 575 | } |
| 673 | records := []ProfileRecords{} | 576 | records := []ProfileRecords{} |
| @@ -679,13 +582,7 @@ func FetchUser(c *gin.Context) { | |||
| 679 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 582 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 680 | return | 583 | return |
| 681 | } | 584 | } |
| 682 | sql = `WITH best_scores AS (WITH RankedScores AS (SELECT sp.user_id, sp.map_id, sp.score_count, sp.score_time, ROW_NUMBER() OVER ( | 585 | placementsRows, err := database.DB.Query(`SELECT * FROM get_placements_singleplayer($1);`, user.SteamID) |
| 683 | PARTITION BY sp.user_id, sp.map_id ORDER BY sp.score_count ASC, sp.score_time ASC) AS rank FROM records_sp sp WHERE sp.is_deleted = false) | ||
| 684 | SELECT user_id, map_id, score_count AS best_score_count, score_time AS best_score_time FROM RankedScores WHERE rank = 1) | ||
| 685 | SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id | ||
| 686 | AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count | ||
| 687 | AND inner_scores.best_score_time < bs.best_score_time))) AS placement FROM best_scores AS bs WHERE bs.user_id = $1 ORDER BY map_id, placement` | ||
| 688 | placementsRows, err := database.DB.Query(sql, user.SteamID) | ||
| 689 | if err != nil { | 586 | if err != nil { |
| 690 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 587 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 691 | return | 588 | return |
| @@ -693,8 +590,9 @@ func FetchUser(c *gin.Context) { | |||
| 693 | placements := []int{} | 590 | placements := []int{} |
| 694 | placementIndex := 0 | 591 | placementIndex := 0 |
| 695 | for placementsRows.Next() { | 592 | for placementsRows.Next() { |
| 593 | var mapID int | ||
| 696 | var placement int | 594 | var placement int |
| 697 | placementsRows.Scan(&placement) | 595 | placementsRows.Scan(&mapID, &placement) |
| 698 | placements = append(placements, placement) | 596 | placements = append(placements, placement) |
| 699 | } | 597 | } |
| 700 | for rows.Next() { | 598 | for rows.Next() { |
| @@ -735,13 +633,7 @@ func FetchUser(c *gin.Context) { | |||
| 735 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 633 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 736 | return | 634 | return |
| 737 | } | 635 | } |
| 738 | sql = `WITH best_scores AS (WITH RankedScores AS (SELECT mp.host_id, mp.partner_id, mp.map_id, mp.score_count, mp.score_time, ROW_NUMBER() OVER ( | 636 | placementsRows, err = database.DB.Query(`SELECT * FROM get_placements_multiplayer($1);`, user.SteamID) |
| 739 | PARTITION BY mp.host_id, mp.partner_id, mp.map_id ORDER BY mp.score_count ASC, mp.score_time ASC) AS rank FROM records_mp mp WHERE mp.is_deleted = false) | ||
| 740 | SELECT host_id, partner_id, map_id, score_count AS best_score_count, score_time AS best_score_time FROM RankedScores WHERE rank = 1) | ||
| 741 | SELECT (SELECT COUNT(*) + 1 FROM best_scores AS inner_scores WHERE inner_scores.map_id = bs.map_id | ||
| 742 | AND (inner_scores.best_score_count < bs.best_score_count OR (inner_scores.best_score_count = bs.best_score_count | ||
| 743 | AND inner_scores.best_score_time < bs.best_score_time))) AS placement FROM best_scores AS bs WHERE bs.host_id = $1 or bs.partner_id = $1 ORDER BY map_id, placement` | ||
| 744 | placementsRows, err = database.DB.Query(sql, user.SteamID) | ||
| 745 | if err != nil { | 637 | if err != nil { |
| 746 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 638 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| 747 | return | 639 | return |
| @@ -749,8 +641,9 @@ func FetchUser(c *gin.Context) { | |||
| 749 | placements = []int{} | 641 | placements = []int{} |
| 750 | placementIndex = 0 | 642 | placementIndex = 0 |
| 751 | for placementsRows.Next() { | 643 | for placementsRows.Next() { |
| 644 | var mapID int | ||
| 752 | var placement int | 645 | var placement int |
| 753 | placementsRows.Scan(&placement) | 646 | placementsRows.Scan(&mapID, &placement) |
| 754 | placements = append(placements, placement) | 647 | placements = append(placements, placement) |
| 755 | } | 648 | } |
| 756 | for rows.Next() { | 649 | for rows.Next() { |