diff --git a/server/fishtest/__init__.py b/server/fishtest/__init__.py index eae2801f3..71ff5dbf3 100644 --- a/server/fishtest/__init__.py +++ b/server/fishtest/__init__.py @@ -118,6 +118,7 @@ def group_finder(username, request): config.add_route("api_download_nn", "/api/nn/{id}") config.add_route("api_get_elo", "/api/get_elo/{id}") config.add_route("api_actions", "/api/actions") + config.add_route("api_calc_elo", "/api/calc_elo") config.scan() return config.make_wsgi_app() diff --git a/server/fishtest/api.py b/server/fishtest/api.py index cbd132c3f..ae3d822ec 100644 --- a/server/fishtest/api.py +++ b/server/fishtest/api.py @@ -347,6 +347,91 @@ def get_elo(self): run["elo"] = a return run + @view_config(route_name="api_calc_elo") + def calc_elo(self): + W = self.request.params.get("W") + D = self.request.params.get("D") + L = self.request.params.get("L") + LL = self.request.params.get("LL") + LD = self.request.params.get("LD") + DD = self.request.params.get("DD") + WD = self.request.params.get("WD") + WW = self.request.params.get("WW") + elo0 = self.request.params.get("elo0") + elo1 = self.request.params.get("elo1") + elo_model = self.request.params.get("elo_model", "normalized") + + if elo_model not in ["BayesElo", "logistic", "normalized"]: + self.handle_error( + "Valid Elo models are: BayesElo, logistic, and normalized." + ) + + isPtnml = all( + value is not None + and ( + ( + isinstance(value, (int, float)) + or isinstance(value, str) + and value.replace(".", "").replace("-", "").isdigit() + ) + and float(value) >= 0 + ) + for value in [LL, LD, DD, WD, WW, elo0, elo1] + ) + + isWdl = not isPtnml and all( + value is not None + and ( + ( + isinstance(value, (int, float)) + or isinstance(value, str) + and value.replace(".", "").replace("-", "").isdigit() + ) + and float(value) >= 0 + ) + for value in [W, D, L, elo0, elo1] + ) + + if not isPtnml and not isWdl: + self.handle_error( + "Invalid or missing parameters. Please provide all values as valid numbers." + ) + + if isPtnml: + LL = int(LL) + LD = int(LD) + DD = int(DD) + WD = int(WD) + WW = int(WW) + if (LL + LD + DD + WD + WW) * 2 > 2**32: + self.handle_error("Number of games exceeds the limit.") + if LL + LD + DD + WD + WW == 0: + self.handle_error("No games to calculate Elo.") + results = { + "pentanomial": [LL, LD, DD, WD, WW], + } + if isWdl: + W = int(W) + D = int(D) + L = int(L) + if W + D + L > 2**32: + self.handle_error("Number of games exceeds the limit.") + if W + D + L == 0: + self.handle_error("No games to calculate Elo.") + results = { + "wins": W, + "draws": D, + "losses": L, + } + elo0 = float(elo0) + elo1 = float(elo1) + alpha = 0.05 + beta = 0.05 + a = SPRT_elo( + results, alpha=alpha, beta=beta, elo0=elo0, elo1=elo1, elo_model=elo_model + ) + return a + @view_config(route_name="api_request_task") def request_task(self): self.validate_request("/api/request_task")