def updateSet(beatmapSetID): apiResponse = osuapiHelper.osuApiRequest("get_beatmaps", "s={}".format(beatmapSetID), False) if len(apiResponse) == 0: return for i in apiResponse: beatmap.beatmap(i["file_md5"], int(i["beatmapset_id"]), refresh=True)
def recalc_score(score_data: Dict, no_download=False) -> Optional[score.score]: """ Recalculates pp for a score :param score_data: dict containing score and beatmap information about a score. :param no_download: if True, raise FileNotFoundError() if the map should be re-downloaded. this ensures no requests are made to osu! :return: new `score` object, with `pp` attribute set to the new value """ # Create score object and set its data s: score.score = score.score() s.setDataFromDict(score_data) s.passed = True # Create beatmap object and set its data b: beatmap.beatmap = beatmap.beatmap() b.setDataFromDict(score_data) # Abort if we are running in no_download mode and the map should be re-downloaded if no_download and mapsHelper.shouldDownloadMap( mapsHelper.cachedMapPath(b.beatmapID), b): raise FileNotFoundError("no_download mode and local map not found") # Calculate score pp s.calculatePP(b) del b return s
def asyncPost(self): statusCode = 400 data = {"message": "unknown error"} try: # Check arguments if not requestsManager.checkArguments(self.request.arguments, ["sid", "refresh"]): raise exceptions.invalidArgumentsException(MODULE_NAME) # Get beatmap set data from osu api beatmapSetID = self.get_argument("sid") refresh = int(self.get_argument("refresh")) if refresh == 1: log.debug("Forced refresh") apiResponse = osuapiHelper.osuApiRequest( "get_beatmaps", "s={}".format(beatmapSetID), False) if len(apiResponse) == 0: raise exceptions.invalidBeatmapException # Loop through all beatmaps in this set and save them in db data["maps"] = [] for i in apiResponse: log.debug("Saving beatmap {} in db".format(i["file_md5"])) bmap = beatmap.beatmap(i["file_md5"], int(i["beatmapset_id"]), refresh=refresh) pp = glob.db.fetch( "SELECT pp_100 FROM beatmaps WHERE beatmap_id = %s LIMIT 1", [bmap.beatmapID]) if pp is None: pp = 0 else: pp = pp["pp_100"] data["maps"].append({ "id": bmap.beatmapID, "name": bmap.songName, "status": bmap.rankedStatus, "frozen": bmap.rankedStatusFrozen, "pp": pp, }) # 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 from osu!api." finally: # Add status code to data data["status"] = statusCode # Send response self.write(json.dumps(data)) self.set_header("Content-Type", "application/json") #self.add_header("Access-Control-Allow-Origin", "*") self.set_status(statusCode)
def UpdateWrongDiff(self): bm = glob.db.fetch("SELECT beatmap_id,ranked, latest_update, beatmapset_id, beatmap_md5 FROM beatmaps WHERE mode = 0 AND difficulty_std = 0 AND hit_length>30 ORDER BY latest_update DESC LIMIT 1") if bm is not None: bmap = beatmap.beatmap(bm["beatmap_md5"], bm["beatmapset_id"], refresh=True) if(bmap.rankedStatus == rankedStatuses.NEED_UPDATE or bmap.rankedStatus < rankedStatuses.PENDING): self.stats["updated"] += 1 self.stats["total"] += 1 threading.Timer(15, self.UpdateWrongDiff).start() return
def UpdateQualif(self): bm = glob.db.fetch("SELECT beatmap_id,ranked, latest_update, beatmapset_id, beatmap_md5 FROM beatmaps WHERE ranked = 4 ORDER BY latest_update ASC LIMIT 1") if bm is not None: bmap = beatmap.beatmap(bm["beatmap_md5"], bm["beatmapset_id"], refresh=True) if(bmap.rankedStatus == rankedStatuses.NEED_UPDATE or bmap.rankedStatus < rankedStatuses.PENDING): self.stats["updated"] += 1 self.stats["total"] += 1 threading.Timer(self.time, self.UpdateQualif).start() return
def UpdateBeatmap(self): bm = glob.db.fetch("SELECT beatmap_id,ranked, latest_update, beatmapset_id, beatmap_md5 FROM beatmaps WHERE ((ranked > 3 or ranked_status_freezed > 0) AND latest_update < %s) ORDER BY latest_update ASC LIMIT 1",[time.time() - 36400]) if bm is not None: bmap = beatmap.beatmap(bm["beatmap_md5"], bm["beatmapset_id"], refresh=True) if(bmap.rankedStatus == rankedStatuses.NEED_UPDATE or bmap.rankedStatus < rankedStatuses.PENDING): self.stats["updated"] += 1 self.stats["total"] += 1 threading.Timer(self.time, self.UpdateBeatmap).start() return
def setCompletedStatus(self, b=None): """ Set this score completed status and rankedScoreIncrease """ self.completed = 0 # Create beatmap object if b is None: b = beatmap.beatmap(self.fileMd5, 0) if self.passed == True and scoreUtils.isRankable(self.mods): # Get userID userID = userUtils.getID(self.playerName) # Make sure we don't have another score identical to this one duplicate = glob.db.fetch( "SELECT id FROM scores_relax WHERE userid = %s AND beatmap_md5 = %s AND play_mode = %s AND time = %s AND score = %s LIMIT 1", [userID, self.fileMd5, self.gameMode, self.date, self.score]) if duplicate is not None: # Found same score in db. Don't save this score. self.completed = -1 return # No duplicates found. # Get right "completed" value personalBest = glob.db.fetch( "SELECT id, pp, score FROM scores_relax WHERE userid = %s AND beatmap_md5 = %s AND play_mode = %s AND completed = 3 LIMIT 1", [userID, self.fileMd5, self.gameMode]) if personalBest is None: # This is our first score on this map, so it's our best score self.completed = 3 self.rankedScoreIncrease = self.score self.oldPersonalBest = 0 else: # Compare personal best's score with current score if b.rankedStatus == rankedStatuses.RANKED or b.rankedStatus == rankedStatuses.APPROVED: if self.pp > personalBest["pp"]: # New best score self.completed = 3 self.rankedScoreIncrease = self.score - personalBest[ "score"] self.oldPersonalBest = personalBest["id"] else: self.completed = 2 self.rankedScoreIncrease = 0 self.oldPersonalBest = 0 elif b.rankedStatus == rankedStatuses.LOVED: if self.score > personalBest["score"]: # New best score self.completed = 3 self.rankedScoreIncrease = self.score - personalBest[ "score"] self.oldPersonalBest = personalBest["id"] else: self.completed = 2 self.rankedScoreIncrease = 0 self.oldPersonalBest = 0
def update(userID): achievement_ids = [] entries = glob.db.fetchAll( "SELECT beatmap_md5, play_mode FROM scores WHERE completed = 3 AND userid = %s", [userID]) for entry in entries: current_beatmap = beatmap.beatmap() current_beatmap.setDataFromDB(entry["beatmap_md5"]) achievement_ids += check(entry["play_mode"], current_beatmap) return achievement_ids
def calculatePP(self, b=None): """ Calculate this score's pp value if completed == 3 """ # Create beatmap object if b is None: b = beatmap.beatmap(self.fileMd5, 0) # Calculate pp if b.rankedStatus >= rankedStatuses.RANKED and b.rankedStatus != rankedStatuses.LOVED and b.rankedStatus != rankedStatuses.UNKNOWN \ and scoreUtils.isRankable(self.mods) and self.passed and self.gameMode in score.PP_CALCULATORS: calculator = score.PP_CALCULATORS[self.gameMode](b, self) self.pp = calculator.pp else: self.pp = 0
def recalcFromScoreData(scoreData): """ Recalculate pp value for a score. Does every check, output and queries needed. score -- score+beatmap dictionary (returned from db with JOIN) of score to recalc return -- calculated pp value or None """ # Create score object and set its data s = score.score() s.setDataFromDict(scoreData) if s.scoreID == 0: # Make sure the score exists if glob.debug: consoleHelper.printColored( "[!] No score with id {}".format(scoreData["id"]), bcolors.RED) # Create beatmap object b = beatmap.beatmap() # Check if we have data for this song if scoreData["song_name"] is None or args.apirefresh == True: # If we don't have song data in scoreData, get with get_scores method (mysql, osuapi blabla) b.setData(scoreData["beatmap_md5"], 0) else: # If we have data, set data from dict b.setDataFromDict(scoreData) # Make sure the beatmap is ranked if b.rankedStatus < rankedStatuses.RANKED: if glob.debug: consoleHelper.printColored( "[!] Beatmap {} is not ranked ().".format(s.fileMd5), bcolors.RED) # Don't calculate pp if the beatmap is not ranked return False # Calculate score pp s.calculatePP(b) # Update score pp in dictionary scoreData["pp"] = s.pp return True
def recalc_score(self, score_data: Dict) -> scoreRelax2: """ Recalculates pp for a score :param score_data: dict containing score and beatmap information about a score. :return: new `score` object, with `pp` attribute set to the new value """ # Create score object and set its data s: scoreRelax2.score = scoreRelax2.score() s.setDataFromDict(score_data) s.passed = True # Create beatmap object and set its data b: beatmap.beatmap = beatmap.beatmap() b.setDataFromDict(score_data) # Calculate score pp s.calculatePP(b) del b return s
def recalc_score(self, score_data: Dict) -> score.baseScore: """ Recalculates pp for a score :param score_data: dict containing score and beatmap information about a score. :return: new `score` object, with `pp` attribute set to the new value """ # Create score object and set its data s: score.baseScore = score.standardScore() s.setDataFromDict(score_data) s.passed = True # Create beatmap object and set its data b: beatmap.beatmap = beatmap.beatmap() b.setDataFromDict(score_data) # Calculate score pp s.calculatePP(b) if s.pp > 0: stat = b.stats.get(s.gameMode, s.mods) # print(s.scoreID, stat.length_drain, stat.actual_length_drain, stat.pp_scale) del b return s
def asyncGet(self, fileName=None): try: # Check arguments if fileName is None: raise exceptions.invalidArgumentsException(MODULE_NAME) if fileName == "": raise exceptions.invalidArgumentsException(MODULE_NAME) fileNameShort = fileName[:32] + \ "..." if len(fileName) > 32 else fileName[:-4] log.info("Requested .osu file {}".format(fileNameShort)) re = MAPFILE_REGEX.match(fileName) if not re: raise exceptions.invalidArgumentsException(MODULE_NAME) # Get .osu file from osu! server searchingBeatmap = beatmap.beatmap() searchingBeatmap.setData(None, None, unquote(fileName)) if not searchingBeatmap.fileMD5: self.set_status(404) return self.write(b"") req = requests.get( f'{glob.conf.config["osuapi"]["apiurl"]}/osu/{searchingBeatmap.beatmapID}', timeout=20) req.encoding = "utf-8" response = req.content self.write(response) glob.dog.increment(glob.DATADOG_PREFIX + ".osu_api.osu_file_requests") except exceptions.invalidArgumentsException: self.set_status(500) except exceptions.osuApiFailException: self.set_status(500)
def asyncPost(self): statusCode = 400 data = {"message": "unknown error"} try: # Check arguments if not requestsManager.checkArguments(self.request.arguments, ["refresh"]): raise exceptions.invalidArgumentsException(MODULE_NAME) userID = 0 gameMode = 0 outputType = 'dap_rank' if 'u' in self.request.arguments: userID = int(self.get_argument('u')) if 'm' in self.request.arguments: gameMode = int(self.get_argument('m')) if 'out' in self.request.arguments: if self.get_argument('out').lower() in ('page', ): outputType = self.get_argument('out') # Get beatmap set data from osu api data["maps"] = [] refresh = int(self.get_argument("refresh")) apiResponse = [] if refresh == 1: log.debug("Forced refresh") if 'sid' in self.request.arguments: beatmapSetID = self.get_argument("sid") apiResponse = osuapiHelper.osuApiRequest( "get_beatmaps", "s={}".format(beatmapSetID), False) elif 'bid' in self.request.arguments: beatmapID = self.get_argument("bid") apiResponse = osuapiHelper.osuApiRequest( "get_beatmaps", "b={}".format(beatmapID), False) else: raise exceptions.invalidArgumentsException(MODULE_NAME) if len(apiResponse) == 0: raise exceptions.invalidBeatmapException forcefulStop = False for b in apiResponse: bm = glob.db.fetch( 'select beatmap_id id, beatmap_md5 md5 from beatmaps where beatmap_id = %s', [b['beatmap_id']]) if bm is None and not eligible.tryLoadBeatmap(userID): forcefulStop = 'beatmapNotice2' if bm is not None and bm['md5'] != b[ 'file_md5'] and not eligible.tryLoadBeatmap(userID): forcefulStop = 'beatmapNotice3' if forcefulStop: break if forcefulStop: params = { "k": glob.conf.config["server"]["apikey"], "to": userID, "code": forcefulStop, 'force': 1 } requests.get("{}/api/v1/specialNotice?{}".format( glob.conf.config["server"]["banchourl"], urlencode(params))) raise exceptions.invalidBeatmapException def processOutput(t, b, **x): if outputType == 'page': if b.creatorSrc == 'datenshi_id': ownerLink = "https://osu.troke.id/u/{}" else: ownerLink = "https://osu.ppy.sh/users/{}" if b.mapperSrc == 'datenshi_id': mapperLink = "https://osu.troke.id/u/{}" else: mapperLink = "https://osu.ppy.sh/users/{}" mapStat = b.stats.get(b.gameMode or gameMode, 0) mapDiff = { 'hp': mapStat.hp, 'od': mapStat.od, 'cs': mapStat.cs, 'ar': mapStat.ar, 'star': mapStat.rating, } if b.gameMode == 1: del mapDiff['cs'], mapDiff['ar'] elif b.gameMode == 2: del mapDiff['cs'] elif b.gameMode == 3: del mapDiff['ar'] return { 'artist': b.artist, 'title': b.title, 'owner': b.creatorName, 'owner_link': ownerLink.format(b.creatorID), 'mapper': b.mapperName or b.creatorName, 'mapper_link': mapperLink.format(b.mapperID) if b.mapperName else ownerLink.format(b.creatorID), 'source': b.source, 'version': b.difficultyName, 'difficulty': mapDiff, 'length': { 'drain': mapStat.actual_length_drain, 'total': mapStat.actual_length_total, }, 'bpm': b.bpm, } else: return { "id": b.beatmapID, "mode": b.mode, "baseName": "{} - {}".format(b.artist, b.title), "diffName": b.difficultyName, "status": b.rankedStatus, "frozen": b.rankedStatusFrozen, "pp": x['pp'], "request": x['request'] } # Loop through all beatmaps in this set and save them in db for i in apiResponse: log.debug("Saving beatmap {} in db".format(i["file_md5"])) bmap = beatmap.beatmap(i["file_md5"], int(i["beatmapset_id"]), refresh=refresh) if outputType == 'out' and bmap.takendown and not bmap.userHaveAccess( userID): continue pp = glob.db.fetch( "SELECT pp_100 FROM beatmaps WHERE beatmap_id = %s LIMIT 1", [bmap.beatmapID]) if pp is None: pp = 0 else: pp = pp["pp_100"] reqData = glob.db.fetch( "SELECT userid as user_id, blacklisted as bad, hidden as done FROM rank_requests WHERE (`type` = 'b' and bid = %s) or (`type` = 's' and bid = %s)", [bmap.beatmapID, bmap.beatmapSetID]) data["maps"].append( processOutput(outputType, bmap, pp=pp, request=reqData)) # 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 from osu!api." finally: # Add status code to data data["status"] = statusCode # Send response self.write(json.dumps(data)) self.set_header("Content-Type", "application/json") #self.add_header("Access-Control-Allow-Origin", "*") self.set_status(statusCode)
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) if math.isnan(accuracy): accuracy = -1.0 except ValueError: raise exceptions.invalidArgumentsException(MODULE_NAME) else: accuracy = -1.0 if "x" in self.request.arguments: misses = self.get_argument("x") try: misses = int(misses) except ValueError: raise exceptions.invalidArgumentsException(MODULE_NAME) else: misses = 0 if "c" in self.request.arguments: combo = self.get_argument("c") if not combo.isdigit(): raise exceptions.invalidArgumentsException(MODULE_NAME) combo = int(combo) else: combo = 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"]""" dbData = glob.db.fetch("SELECT beatmap_md5, beatmapset_id, mode FROM beatmaps WHERE beatmap_id = {}".format(beatmapID)) if dbData is None: raise exceptions.invalidBeatmapException(MODULE_NAME) beatmapMd5 = dbData["beatmap_md5"] beatmapSetID = dbData["beatmapset_id"] # Create beatmap object bmap = beatmap.beatmap(beatmapMd5, beatmapSetID) stars = 0 # Check beatmap length if bmap.hitLength > 900: raise exceptions.beatmapTooLongException(MODULE_NAME) returnPP = [] if gameMode == gameModes.STD and bmap.starsStd == 0: gameMode = dbData["mode"] if(gameMode == 2): raise exceptions.unsupportedGameModeException if accuracy > 100 or combo > bmap.maxCombo or misses < 0 or math.isnan(accuracy): raise exceptions.invalidArgumentsException(MODULE_NAME) # Calculate pp if gameMode != 2: # Std pp if accuracy < 0 and combo == 0 and misses == 0 and gameMode == dbData["mode"]: # Generic acc # Get cached pp values cachedPP = bmap.getCachedTillerinoPP() if(modsEnum == 0 and cachedPP != [0,0,0,0]): log.debug("Got cached pp.") returnPP = cachedPP stars = bmap.starsStd else: log.debug("Cached pp not found. Calculating pp with oppai...") # Cached pp not found, calculate them if(gameMode == 1 or gameMode == 0): oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=True, stars=True) returnPP = oppai.pp stars = oppai.stars else: xeno = omppcPy.piano(bmap, mods=modsEnum, tillerino=True, stars=True) returnPP = xeno.pp stars = xeno.stars # Cache values in DB log.debug("Saving cached pp...") if(modsEnum == 0 and type(returnPP) != int and len(returnPP) == 4): bmap.saveCachedTillerinoPP(returnPP) else: # Specific accuracy, calculate # Create oppai instance if(gameMode == 3): log.info("Specific request ({}%/{}). Calculating pp with omppc...".format(accuracy, modsEnum)) xeno = omppcPy.piano(bmap, acc=accuracy, mods=modsEnum, tillerino=False, stars=True) returnPP.append(xeno.pp) stars = xeno.stars else: if(gameMode != 0 and gameMode != 1): raise exceptions.unsupportedGameModeException log.debug("Specific request ({}%/{}). Calculating pp with oppai...".format(accuracy, modsEnum)) oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=False, stars=True) stars = oppai.stars if accuracy > 0: oppai.acc = accuracy if combo > 0: oppai.combo = combo if misses > 0: oppai.misses = misses oppai.getPP() returnPP.append(oppai.pp) else: raise exceptions.unsupportedGameModeException # Data to return data = { "song_name": bmap.songName, "pp": returnPP, "length": round(bmap.hitLength*0.75) if modsEnum & mods.DOUBLETIME > 0 else bmap.hitLength if modsEnum & mods.HALFTIME < 1 else round(bmap.hitLength*1.50), "stars": stars, "ar": round(min(10, bmap.AR * 1.4),1) if modsEnum & mods.HARDROCK > 0 else bmap.AR if modsEnum & mods.EASY < 1 else round(max(0, bmap.AR / 2),1), "od": round(min(10, bmap.OD * 1.4),1) if modsEnum & mods.HARDROCK > 0 else bmap.OD if modsEnum & mods.EASY < 1 else round(max(0, bmap.OD / 2),1), "bpm": round(bmap.bpm*1.5) if modsEnum & mods.DOUBLETIME > 0 else bmap.bpm if modsEnum & mods.HALFTIME < 1 else round(bmap.bpm*0.75), } # 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 autorankCheck(beatmap, recursive=True): # No autorank check for frozen maps if beatmap.rankedStatusFrozen not in (0, 3): return # No autorank check for already ranked beatmap # Only handle this if the map is ranked by autorank. if beatmap.rankedStatusFrozen != 3 and beatmap.rankedStatus >= 2: return # Only check if the map is autorankable. if not autorankFlagOK(beatmap.beatmapID): return # Check recursiveness if recursive: beatmap_id_query = glob.db.fetchAll( 'select beatmap_md5, beatmap_id from beatmaps where beatmapset_id = %s', [beatmap.beatmapSetID]) for beatmap_id_data in beatmap_id_query: # Skip self. if int(beatmap_id_data['beatmap_id']) == beatmap.beatmapID: continue siblingmap = bm.beatmap(None, beatmap.beatmapSetID) siblingmap.setData(beatmap_id_data['beatmap_md5'], beatmap.beatmapSetID, True) # Not checking maps with non updated date. #if recursive: #log.info(f"Checking {beatmap.artist} - {beatmap.title} for autorank eligiblity.") # log.info(f"Checking {beatmap.songName} for autorank eligiblity.") obtainDateTime = lambda t: datetime.datetime.strptime( t, "%Y-%m-%d %H:%M:%S") obtainUnixClock = lambda t: int(time.mktime(t.timetuple())) if beatmap.updateDate == 0: log.info(f"Updating {beatmap.fileMD5} data") data = osuapiHelper.osuApiRequest('get_beatmaps', 'h={}'.format(beatmap.fileMD5)) if not data: return dateTouch = obtainDateTime(data['last_update']) beatmap.updateDate = obtainUnixClock(dateTouch) else: dateTouch = datetime.datetime.fromtimestamp(beatmap.updateDate) dateNow = datetime.datetime.today() dateQualify = dateTouch + datetime.timedelta(days=GRAVEYARD_DAYS - QUALIFIED_DAYS) dateRanked = dateTouch + datetime.timedelta(days=GRAVEYARD_DAYS) forLove = autorankFlagForLove(beatmap.beatmapID) rankStatus = beatmap.rankedStatus needWipe = False if dateNow >= dateRanked: needWipe = rankStatus == rankedStatuses.QUALIFIED if forLove: log.debug(f"Considering {beatmap.fileMD5} to be loved") beatmap.rankedStatus = rankedStatuses.LOVED else: log.debug(f"Considering {beatmap.fileMD5} on ranking") beatmap.rankedStatus = rankedStatuses.RANKED beatmap.rankedStatusFrozen = 3 elif dateNow >= dateQualify and not forLove: log.debug(f"Considering {beatmap.fileMD5} for qualified") beatmap.rankedStatus = rankedStatuses.QUALIFIED beatmap.rankedStatusFrozen = 3 else: needWipe = rankStatus >= rankedStatuses.RANKED beatmap.rankedStatus = rankedStatuses.PENDING beatmap.rankedStatusFrozen = 0 if rankStatus != beatmap.rankedStatus: dbBeatmap = glob.db.fetch( 'select ranked_status_freezed as f from beatmaps where beatmap_md5 = %s', [beatmap.fileMD5]) oldFreeze = 0 if dbBeatmap is not None: oldFreeze = dbBeatmap['f'] glob.db.execute( 'update beatmaps set ranked_status_freezed = 0 where beatmap_md5 = %s', [beatmap.fileMD5]) if oldFreeze != beatmap.rankedStatusFrozen and recursive: autorankAnnounce(beatmap) glob.db.execute( 'update beatmaps set rankedby = %s where beatmap_md5 = %s', [autorankUserID(beatmap.beatmapID), beatmap.fileMD5]) if needWipe: log.info(f"Wiping {beatmap.fileMD5} leaderboard") beatmap.clearLeaderboard() pass
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 = None # 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 int(beatmapID) > 100000000: raise exceptions.ppCustomBeatmap(MODULE_NAME) 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 in (gameModes.STD, gameModes.TAIKO): # Std pp if accuracy is None and modsEnum == 0: # Generic acc/nomod # Get cached pp values cachedPP = bmap.getCachedTillerinoPP() if (modsEnum & mods.RELAX): cachedPP = [0, 0, 0, 0] elif (modsEnum & mods.RELAX2): cachedPP = [0, 0, 0, 0] 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 if gameMode == gameModes.STD and (modsEnum & mods.RELAX): oppai = relaxoppai.oppai(bmap, mods=modsEnum, tillerino=True) elif gameMode == gameModes.STD and (modsEnum & mods.RELAX2): oppai = relax2oppai.oppai(bmap, mods=modsEnum, tillerino=True) else: oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=True) returnPP = oppai.pp bmap.starsStd = oppai.stars if not (modsEnum & mods.RELAX) or (modsEnum & mods.RELAX2): # Cache values in DB log.debug("Saving cached pp...") if type(returnPP) == list and len(returnPP) == 4: bmap.saveCachedTillerinoPP(returnPP) else: # Specific accuracy/mods, calculate pp # Create oppai instance log.debug( "Specific request ({}%/{}). Calculating pp with oppai..." .format(accuracy, modsEnum)) if gameMode == gameModes.STD and (modsEnum & mods.RELAX): oppai = relaxoppai.oppai(bmap, mods=modsEnum, tillerino=True) elif gameMode == gameModes.STD and (modsEnum & mods.RELAX2): oppai = relax2oppai.oppai(bmap, mods=modsEnum, tillerino=True) else: oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=True) bmap.starsStd = oppai.stars if accuracy is not None: returnPP = calculatePPFromAcc(oppai, accuracy) else: returnPP = oppai.pp else: raise exceptions.unsupportedGameModeException() # Data to return data = { "song_name": bmap.songName, "pp": [x for x in returnPP] if type(returnPP) is 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.ppCustomBeatmap: statusCode = 400 data[ "message"] = "Custom map does not supported pp calculating yet" 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 asyncGet(self): statusCode = 400 data = {"message": "unknown error"} try: # Check arguments if not requestsManager.checkArguments(self.request.arguments, ["b"]): raise exceptions.invalidArgumentsException(self.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(self.MODULE_NAME) # Get mods if "m" in self.request.arguments: modsEnum = self.get_argument("m") if not modsEnum.isdigit(): raise exceptions.invalidArgumentsException( self.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( self.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( self.MODULE_NAME) else: accuracy = None # 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(self.MODULE_NAME) beatmapMd5 = osuapiData["file_md5"] beatmapSetID = osuapiData["beatmapset_id"] # Create beatmap object bmap = beatmap.beatmap(beatmapMd5, beatmapSetID) # Check beatmap length # TODO: Why do we do this? if bmap.hit_length > 900: raise exceptions.beatmapTooLongException(self.MODULE_NAME) 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 in (gameModes.STD, gameModes.TAIKO): # osu!standard and osu!taiko oppai = ez.Ez(bmap, mods_=modsEnum, tillerino=accuracy is None, acc=accuracy, gameMode=gameMode) bmap.starsStd = oppai.stars returnPP = oppai.pp elif gameMode == gameModes.CTB: # osu!catch ciccio = cicciobello.Cicciobello(bmap, mods_=modsEnum, tillerino=accuracy is None, accuracy=accuracy) bmap.starsStd = ciccio.stars returnPP = ciccio.pp else: raise exceptions.unsupportedGameModeException() # Data to return data = { "song_name": bmap.songName, "pp": [x for x in returnPP] if type(returnPP) is list else returnPP, "game_mode": gameMode, "length": bmap.hit_length, "stars": bmap.starsStd if bmap.starsStd is not None else bmap.difficultyrating, "ar": bmap.diff_approach, "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.write(json.dumps(data)) self.set_header("Content-Type", "application/json") self.set_status(statusCode)
def update_beatmaps(args): beatmaps = glob.db.fetchAll( 'select beatmapset_id as sid, beatmap_id as id, beatmap_md5 as md5 from beatmaps' ) for bm in beatmaps: b = beatmap.beatmap() b.beatmapID, b.fileMD5 = bm['id'], bm['md5'] if bm['id'] > 100000000: continue fn = mapsHelper.cachedMapPath(bm['id']) if mapsHelper.os.path.isfile(fn) and mapsHelper.generalUtils.fileMd5( fn) == bm['md5']: continue try: if args.download_update: if mapsHelper.os.path.isfile(fn): if mapsHelper.generalUtils.fileMd5(fn) != bm['md5']: log.info("BID {}: Hash difference ({}) expected ({})". format( bm['id'], mapsHelper.generalUtils.fileMd5(fn)[:8], bm['md5'][:8])) else: log.info("BID {}: Not exists, downloading...".format( bm['id'])) mapsHelper.cacheMap(fn, b) if mapsHelper.os.path.isfile( fn) and mapsHelper.generalUtils.fileMd5(fn) != bm['md5']: bm['update.md5'] = mapsHelper.generalUtils.fileMd5(fn) else: bm['delete.file'] = True except Exception as e: log.warning('BID {}: ({}) {}'.format(bm['id'], type(e).__name__, e)) pass if args.download_update: time.sleep(2.5) stmts = [] bmgroup = [] def destructor(query): nonlocal bmgroup query += " end where beatmap_id in ({})".format(','.join( str(bm) for bm in bmgroup)) del bmgroup[:] return query stmt = QueryBuilder("update beatmaps set beatmap_md5=case ", '', " ") stmt.finisher = destructor for bm in beatmaps: if 'delete.file' in bm: continue if 'update.md5' not in bm: continue bmgroup.append(bm['id']) stmtline = "when beatmap_id={} then '{}'".format( bm['id'], bm['update.md5']) newQuery = stmt.appendAndBuild(stmtline) if newQuery: stmts.append(newQuery) if stmt.query: stmts.append(stmt.build()) for s in stmts: # print(s) glob.db.execute(s)