This repository has been archived on 2022-02-23. You can view files and clone it, but cannot push or open issues or pull requests.
lets/handlers/apiPPHandler.py

177 lines
5.1 KiB
Python

import json
import sys
import traceback
import tornado.gen
import tornado.web
from raven.contrib.tornado import SentryMixin
from objects import beatmap
from common.constants import gameModes
from common.log import logUtils as log
from common.web import requestsManager
from constants import exceptions
from helpers import osuapiHelper
from objects import glob
from pp import rippoppai
from pp import rxoppai
from common.sentry import sentry
MODULE_NAME = "api/pp"
class handler(requestsManager.asyncRequestHandler):
"""
Handler for /api/v1/pp
"""
@tornado.web.asynchronous
@tornado.gen.engine
@sentry.captureTornado
def asyncGet(self):
statusCode = 400
data = {"message": "unknown error"}
try:
# Check arguments
if not requestsManager.checkArguments(self.request.arguments, ["b"]):
raise exceptions.invalidArgumentsException(MODULE_NAME)
# Get beatmap ID and make sure it's a valid number
beatmapID = self.get_argument("b")
if not beatmapID.isdigit():
raise exceptions.invalidArgumentsException(MODULE_NAME)
# Get mods
if "m" in self.request.arguments:
modsEnum = self.get_argument("m")
if not modsEnum.isdigit():
raise exceptions.invalidArgumentsException(MODULE_NAME)
modsEnum = int(modsEnum)
else:
modsEnum = 0
# Get game mode
if "g" in self.request.arguments:
gameMode = self.get_argument("g")
if not gameMode.isdigit():
raise exceptions.invalidArgumentsException(MODULE_NAME)
gameMode = int(gameMode)
else:
gameMode = 0
# Get acc
if "a" in self.request.arguments:
accuracy = self.get_argument("a")
try:
accuracy = float(accuracy)
except ValueError:
raise exceptions.invalidArgumentsException(MODULE_NAME)
else:
accuracy = -1.0
# Print message
log.info("Requested pp for beatmap {}".format(beatmapID))
# Get beatmap md5 from osuapi
# TODO: Move this to beatmap object
osuapiData = osuapiHelper.osuApiRequest("get_beatmaps", "b={}".format(beatmapID))
if osuapiData is None or "file_md5" not in osuapiData or "beatmapset_id" not in osuapiData:
raise exceptions.invalidBeatmapException(MODULE_NAME)
beatmapMd5 = osuapiData["file_md5"]
beatmapSetID = osuapiData["beatmapset_id"]
# Create beatmap object
bmap = beatmap.beatmap(beatmapMd5, beatmapSetID)
# Check beatmap length
if bmap.hitLength > 900:
raise exceptions.beatmapTooLongException(MODULE_NAME)
returnPP = []
if gameMode == gameModes.STD and bmap.starsStd == 0:
# Mode Specific beatmap, auto detect game mode
if bmap.starsTaiko > 0:
gameMode = gameModes.TAIKO
if bmap.starsCtb > 0:
gameMode = gameModes.CTB
if bmap.starsMania > 0:
gameMode = gameModes.MANIA
# Calculate pp
if gameMode == gameModes.STD or gameMode == gameModes.TAIKO:
# Std pp
if accuracy < 0 and modsEnum == 0:
# Generic acc
# Get cached pp values
cachedPP = bmap.getCachedTillerinoPP()
if cachedPP != [0,0,0,0]:
log.debug("Got cached pp.")
returnPP = cachedPP
else:
log.debug("Cached pp not found. Calculating pp with oppai...")
# Cached pp not found, calculate them
oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=True)
returnPP = oppai.pp
bmap.starsStd = oppai.stars
# Cache values in DB
log.debug("Saving cached pp...")
if type(returnPP) == list and len(returnPP) == 4:
bmap.saveCachedTillerinoPP(returnPP)
else:
# Specific accuracy, calculate
# Create oppai instance
log.debug("Specific request ({}%/{}). Calculating pp with oppai...".format(accuracy, modsEnum))
if modsEnum & 128:
oppai = rxoppai.oppai(bmap, mods=modsEnum, tillerino=True)
else:
oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=True)
bmap.starsStd = oppai.stars
if accuracy > 0:
returnPP.append(calculatePPFromAcc(oppai, accuracy))
else:
returnPP = oppai.pp
else:
raise exceptions.unsupportedGameModeException()
# Data to return
data = {
"song_name": bmap.songName,
"pp": [round(x, 2) for x in returnPP] if type(returnPP) == list else returnPP,
"length": bmap.hitLength,
"stars": bmap.starsStd,
"ar": bmap.AR,
"bpm": bmap.bpm,
}
# Set status code and message
statusCode = 200
data["message"] = "ok"
except exceptions.invalidArgumentsException:
# Set error and message
statusCode = 400
data["message"] = "missing required arguments"
except exceptions.invalidBeatmapException:
statusCode = 400
data["message"] = "beatmap not found"
except exceptions.beatmapTooLongException:
statusCode = 400
data["message"] = "requested beatmap is too long"
except exceptions.unsupportedGameModeException:
statusCode = 400
data["message"] = "Unsupported gamemode"
finally:
# Add status code to data
data["status"] = statusCode
# Debug output
log.debug(str(data))
# Send response
#self.clear()
self.write(json.dumps(data))
self.set_header("Content-Type", "application/json")
self.set_status(statusCode)
def calculatePPFromAcc(ppcalc, acc):
ppcalc.acc = acc
ppcalc.calculatePP()
return ppcalc.pp