def bl_parse_stats(parsed, mode="quickplay"): # Just a quick FYI # If future me or future anyone else is looking at this, I do not how this code works. # I'm really really hoping it doesn't break. # Good luck! try: # XPath for the `u-align-center` h6 which signifies there's no data. no_data = parsed.xpath( ".//div[@id='{}']//ul/h6[@class='u-align-center']".format(mode))[0] except IndexError: pass else: if no_data.text.strip( ) == "We don't have any data for this account in this mode yet.": return None # Start the dict. built_dict = {"game_stats": [], "overall_stats": {}, "average_stats": []} # Shortcut location for player level etc mast_head = parsed.xpath(".//div[@class='masthead-player']")[0] # Get the prestige. prestige = mast_head.xpath(".//div[@class='player-level']")[0] # Extract the background-image from the styles. try: bg_image = [x for x in prestige.values() if 'background-image' in x][0] except IndexError: # Cannot find background-image. # Yikes! # Don't set a prestige. built_dict["overall_stats"]["prestige"] = 0 else: for key, val in PRESTIGE.items(): if key in bg_image: prestige_num = val built_dict["overall_stats"]["rank_image"] = bg_image.split( "(")[1][:-1] break else: # Unknown. prestige_num = None built_dict["overall_stats"]["prestige"] = prestige_num # Parse out the HTML. level = int(prestige.findall(".//div")[0].text) built_dict["overall_stats"]["level"] = level try: tier = mast_head.xpath(".//div[@class='competitive-rank']/img")[0] img_src = [x for x in tier.values() if 'rank-icons' in x][0] except IndexError: built_dict['overall_stats']['tier'] = None else: for key, val in tier_data_img_src.items(): if key in img_src: tier_str = val break else: tier_str = None built_dict["overall_stats"]["tier"] = tier_str hasrank = mast_head.findall(".//div[@class='competitive-rank']/div") if hasrank: comprank = int(hasrank[0].text) else: comprank = None built_dict["overall_stats"]["comprank"] = comprank # Fetch Avatar built_dict["overall_stats"]["avatar"] = mast_head.find( ".//img[@class='player-portrait']").attrib['src'] if mode == "competitive": hascompstats = parsed.xpath( ".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" ) if len(hascompstats) != 2: return None stat_groups = hascompstats[1] elif mode == "quickplay": try: stat_groups = parsed.xpath( ".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" )[0] except IndexError: # User has no stats... return None else: # how else to handle fallthrough case? stat_groups = parsed.xpath( ".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" )[0] # Highlight specific stat groups. try: game_box = stat_groups[6] except IndexError: try: game_box = stat_groups[5] except IndexError: # edge cases... # we can't really extract any more stats # so we do an early return return {} # Calculate the wins, losses, and win rate. try: wins = int( game_box.xpath(".//text()[. = 'Games Won']/../..")[0] [1].text.replace(",", "")) except IndexError: # weird edge case wins = 0 g = game_box.xpath(".//text()[. = 'Games Played']/../..") if len(g) < 1: # Blizzard f****d up, temporary quick fix for #70 games, losses = None, None else: games = int(g[0][1].text.replace(",", "")) if mode == "competitive": try: misc_box = stat_groups[7] losses = int( misc_box.xpath(".//text()[. = 'Games Lost']/../..")[0] [1].text.replace(",", "")) ties = int( misc_box.xpath(".//text()[. = 'Games Tied']/../..")[0] [1].text.replace(",", "")) except IndexError: # Sometimes the losses and ties don't exist. # I'm not 100% as to what causes this, but it might be because there are no ties. # In this case, just set ties to 0, and calculate losses manually. ties = 0 # Quickplay shit. # Goddamnit blizzard. if games is None: losses = 0 games = 0 wins = 0 else: # Competitive stats do have these values (for now...) losses = games - wins if games == 0 or games == ties: wr = 0 else: wr = round((wins / (games - ties)) * 100, 2) built_dict["overall_stats"]["ties"] = ties built_dict["overall_stats"]["games"] = games built_dict["overall_stats"]["losses"] = losses built_dict["overall_stats"]["win_rate"] = wr # Update the dictionary. built_dict["overall_stats"]["wins"] = wins # Build a dict using the stats. _t_d = {} _a_d = {} for subbox in stat_groups: trs = subbox.findall(".//tbody/tr") # Update the dict with [0]: [1] for subval in trs: name, value = util.sanitize_string(subval[0].text), subval[1].text # Try and parse out the value. It might be a time! # If so, try and extract the time. nvl = util.try_extract(value) if 'average' in name.lower(): _a_d[name.replace("_average", "_avg")] = nvl else: _t_d[name] = nvl # Manually add the KPD. try: _t_d["kpd"] = round(_t_d["eliminations"] / _t_d["deaths"], 2) except KeyError: # They don't have any eliminations/deaths. # Set the KPD to 0.0. # See: #106 _t_d["kpd"] = 0 built_dict["game_stats"] = _t_d built_dict["average_stats"] = _a_d built_dict["competitive"] = mode == "competitive" if not "games" in built_dict["overall_stats"]: # manually calculate it dmg_done = built_dict["game_stats"]["damage_done"] avg_dmgd = built_dict["average_stats"]["damage_done_avg"] # IT RETURNS games = int(dmg_done // avg_dmgd) losses = games - built_dict["overall_stats"]["wins"] built_dict["overall_stats"]["games"] = games built_dict["overall_stats"]["losses"] = losses built_dict["overall_stats"]["win_rate"] = round( (built_dict["overall_stats"]["wins"] / games) * 100, 2) return built_dict
async def bl_get_stats(mode, ctx, battletag): data = await bz.region_helper_v2(ctx, battletag, region=ctx.request.args.get("region", None), platform=ctx.request.args.get("platform", "pc")) if data == (None, None): raise HTTPException(404) parsed, region = data # Start the dict. built_dict = {"region": region, "battletag": battletag, "game_stats": [], "overall_stats": {}, "average_stats": []} # Get the prestige. prestige = parsed.xpath(".//div[@class='player-level']")[0] # Extract the background-image from the styles. try: bg_image = [x for x in prestige.values() if 'background-image' in x][0] except IndexError: # Cannot find background-image. # Yikes! # Don't set a prestige. built_dict["overall_stats"]["prestige"] = 0 else: for key, val in PRESTIGE.items(): if key in bg_image: prestige_num = val break else: # Unknown. prestige_num = None built_dict["overall_stats"]["prestige"] = prestige_num # Parse out the HTML. level = int(parsed.findall(".//div[@class='player-level']/div")[0].text) built_dict["overall_stats"]["level"] = level hasrank = parsed.findall(".//div[@class='competitive-rank']/div") if hasrank: comprank = int(hasrank[0].text) else: comprank = None built_dict["overall_stats"]["comprank"] = comprank # Fetch Avatar built_dict["overall_stats"]["avatar"] = parsed.find(".//img[@class='player-portrait']").attrib['src'] if mode == "competitive": hascompstats = parsed.xpath(".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']") if len(hascompstats) != 2: return {"error": 404, "msg": "competitive stats not found", "region": region}, 404 stat_groups = hascompstats[1] elif mode == "quickplay": stat_groups = parsed.xpath(".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']")[0] else: # how else to handle fallthrough case? stat_groups = parsed.xpath(".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']")[0] # Highlight specific stat groups. death_box = stat_groups[4] try: game_box = stat_groups[6] except IndexError: game_box = stat_groups[5] # Calculate the wins, losses, and win rate. try: wins = int(game_box.xpath(".//text()[. = 'Games Won']/../..")[0][1].text.replace(",", "")) except IndexError: # weird edge case wins = 0 g = game_box.xpath(".//text()[. = 'Games Played']/../..") if len(g) < 1: # Blizzard f****d up, temporary quick fix for #70 games, losses = 0, 0 wr = 0 else: games = int(g[0][1].text.replace(",", "")) losses = games - wins wr = floor((wins / games) * 100) # Update the dictionary. built_dict["overall_stats"]["games"] = games built_dict["overall_stats"]["losses"] = losses built_dict["overall_stats"]["wins"] = wins built_dict["overall_stats"]["win_rate"] = wr # Build a dict using the stats. _t_d = {} _a_d = {} for subbox in stat_groups: trs = subbox.findall(".//tbody/tr") # Update the dict with [0]: [1] for subval in trs: name, value = subval[0].text.lower().replace(" ", "_").replace("_-_", "_"), subval[1].text # Try and parse out the value. It might be a time! # If so, try and extract the time. nvl = util.try_extract(value) if 'average' in name.lower(): _a_d[name.replace("_average", "_avg")] = nvl else: _t_d[name] = nvl # Manually add the KPD. _t_d["kpd"] = round(_t_d["eliminations"] / _t_d["deaths"], 2) built_dict["game_stats"] = _t_d built_dict["average_stats"] = _a_d built_dict["competitive"] = mode == "competitive" return built_dict
def bl_parse_stats(parsed, mode="quickplay"): # Start the dict. built_dict = {"game_stats": [], "overall_stats": {}, "average_stats": []} # Get the prestige. prestige = parsed.xpath(".//div[@class='player-level']")[0] # Extract the background-image from the styles. try: bg_image = [x for x in prestige.values() if 'background-image' in x][0] except IndexError: # Cannot find background-image. # Yikes! # Don't set a prestige. built_dict["overall_stats"]["prestige"] = 0 else: for key, val in PRESTIGE.items(): if key in bg_image: prestige_num = val break else: # Unknown. prestige_num = None built_dict["overall_stats"]["prestige"] = prestige_num # Parse out the HTML. level = int(parsed.findall(".//div[@class='player-level']/div")[0].text) built_dict["overall_stats"]["level"] = level hasrank = parsed.findall(".//div[@class='competitive-rank']/div") if hasrank: comprank = int(hasrank[0].text) else: comprank = None built_dict["overall_stats"]["comprank"] = comprank # Fetch Avatar built_dict["overall_stats"]["avatar"] = parsed.find( ".//img[@class='player-portrait']").attrib['src'] if mode == "competitive": hascompstats = parsed.xpath( ".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" ) if len(hascompstats) != 2: return {} stat_groups = hascompstats[1] elif mode == "quickplay": stat_groups = parsed.xpath( ".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" )[0] else: # how else to handle fallthrough case? stat_groups = parsed.xpath( ".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" )[0] # Highlight specific stat groups. try: game_box = stat_groups[6] except IndexError: try: game_box = stat_groups[5] except IndexError: # edge cases... # we can't really extract any more stats # so we do an early return return {} # Calculate the wins, losses, and win rate. try: wins = int( game_box.xpath(".//text()[. = 'Games Won']/../..")[0] [1].text.replace(",", "")) except IndexError: # weird edge case wins = 0 g = game_box.xpath(".//text()[. = 'Games Played']/../..") if len(g) < 1: # Blizzard f****d up, temporary quick fix for #70 games, losses = None, None wr = 0 else: games = int(g[0][1].text.replace(",", "")) losses = games - wins wr = floor((wins / games) * 100) # Update the dictionary. built_dict["overall_stats"]["games"] = games built_dict["overall_stats"]["losses"] = losses built_dict["overall_stats"]["wins"] = wins built_dict["overall_stats"]["win_rate"] = wr # Build a dict using the stats. _t_d = {} _a_d = {} for subbox in stat_groups: trs = subbox.findall(".//tbody/tr") # Update the dict with [0]: [1] for subval in trs: name, value = util.sanitize_string(subval[0].text), subval[1].text # Try and parse out the value. It might be a time! # If so, try and extract the time. nvl = util.try_extract(value) if 'average' in name.lower(): _a_d[name.replace("_average", "_avg")] = nvl else: _t_d[name] = nvl # Manually add the KPD. _t_d["kpd"] = round(_t_d["eliminations"] / _t_d["deaths"], 2) built_dict["game_stats"] = _t_d built_dict["average_stats"] = _a_d built_dict["competitive"] = mode == "competitive" return built_dict
def bl_parse_stats(parsed, mode="quickplay"): # Just a quick FYI # If future me or future anyone else is looking at this, I do not how this code works. # I'm really really hoping it doesn't break. # Good luck! try: # XPath for the `u-align-center` h6 which signifies there's no data. no_data = parsed.xpath(".//div[@id='{}']//ul/h6[@class='u-align-center']".format(mode))[0] except IndexError: pass else: if no_data.text.strip() == "We don't have any data for this account in this mode yet.": return None # Start the dict. built_dict = {"game_stats": [], "overall_stats": {}, "average_stats": []} # Shortcut location for player level etc mast_head = parsed.xpath(".//div[@class='masthead-player']")[0] # Get the prestige. prestige = mast_head.xpath(".//div[@class='player-level']")[0] # Extract the background-image from the styles. try: bg_image = [x for x in prestige.values() if 'background-image' in x][0] except IndexError: # Cannot find background-image. # Yikes! # Don't set a prestige. built_dict["overall_stats"]["prestige"] = 0 else: for key, val in PRESTIGE.items(): if key in bg_image: prestige_num = val built_dict["overall_stats"]["rank_image"] = bg_image.split("(")[1][:-1] break else: # Unknown. prestige_num = None built_dict["overall_stats"]["prestige"] = prestige_num # Parse out the HTML. level = int(prestige.findall(".//div")[0].text) built_dict["overall_stats"]["level"] = level try: tier = mast_head.xpath(".//div[@class='competitive-rank']/img")[0] img_src = [x for x in tier.values() if 'rank-icons' in x][0] except IndexError: built_dict['overall_stats']['tier'] = None else: for key, val in tier_data_img_src.items(): if key in img_src: tier_str = val break else: tier_str = None built_dict["overall_stats"]["tier"] = tier_str hasrank = mast_head.findall(".//div[@class='competitive-rank']/div") if hasrank: comprank = int(hasrank[0].text) else: comprank = None built_dict["overall_stats"]["comprank"] = comprank # Fetch Avatar built_dict["overall_stats"]["avatar"] = mast_head.find( ".//img[@class='player-portrait']" ).attrib['src'] if mode == "competitive": # the competitive overview is under a div with id='competitive' # and the right category try: stat_groups = parsed.xpath( ".//div[@id='competitive']" "//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" )[0] except IndexError: # No stats return None elif mode == "quickplay": try: stat_groups = parsed.xpath( ".//div[@id='quickplay']" "//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" )[0] except IndexError: # User has no stats... return None else: # how else to handle fallthrough case? stat_groups = parsed.xpath( ".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" )[0] # Highlight specific stat groups. try: game_box = stat_groups[6] except IndexError: try: game_box = stat_groups[5] except IndexError: # edge cases... # we can't really extract any more stats # so we do an early return return {} # Calculate the wins, losses, and win rate. try: wins = int(game_box.xpath(".//text()[. = 'Games Won']/../..")[0][1].text.replace(",", "")) except IndexError: # weird edge case wins = 0 g = game_box.xpath(".//text()[. = 'Games Played']/../..") if len(g) < 1: # Blizzard f****d up, temporary quick fix for #70 games, losses = None, None else: games = int(g[0][1].text.replace(",", "")) if mode == "competitive": try: misc_box = stat_groups[7] losses = int(misc_box.xpath(".//text()[. = 'Games Lost']/../..")[0][1].text .replace(",", "")) ties = int(misc_box.xpath(".//text()[. = 'Games Tied']/../..")[0][1].text .replace(",", "")) except IndexError: # Sometimes the losses and ties don't exist. # I'm not 100% as to what causes this, but it might be because there are no ties. # In this case, just set ties to 0, and calculate losses manually. ties = 0 # Quickplay shit. # Goddamnit blizzard. if games is None: losses = 0 games = 0 wins = 0 else: # Competitive stats do have these values (for now...) losses = games - wins if games == 0 or games == ties: wr = 0 else: wr = round((wins / (games - ties)) * 100, 2) built_dict["overall_stats"]["ties"] = ties built_dict["overall_stats"]["games"] = games built_dict["overall_stats"]["losses"] = losses built_dict["overall_stats"]["win_rate"] = wr # Update the dictionary. built_dict["overall_stats"]["wins"] = wins # Build a dict using the stats. _t_d = {} _a_d = {} for subbox in stat_groups: trs = subbox.findall(".//tbody/tr") # Update the dict with [0]: [1] for subval in trs: name, value = util.sanitize_string(subval[0].text), subval[1].text # Try and parse out the value. It might be a time! # If so, try and extract the time. nvl = util.try_extract(value) if 'average' in name.lower(): _a_d[name.replace("_average", "_avg")] = nvl else: _t_d[name] = nvl # Manually add the KPD. try: _t_d["kpd"] = round(_t_d["eliminations"] / _t_d["deaths"], 2) except KeyError: # They don't have any eliminations/deaths. # Set the KPD to 0.0. # See: #106 _t_d["kpd"] = 0 built_dict["game_stats"] = _t_d built_dict["average_stats"] = _a_d built_dict["competitive"] = mode == "competitive" if "games" not in built_dict["overall_stats"]: # manually calculate it # 2017-07-04 - changed to use eliminations # since damage done gave a bit of a stupid amount # 2017-07-11 - changed to cycle some averages average_keys = ("eliminations", "healing_done", "final_blows", "objective_kills") for key in average_keys: try: total = built_dict["game_stats"][key] avg = built_dict["average_stats"][key + "_avg"] except KeyError: continue else: got = True break else: got = False if got: games = int(total // avg) losses = games - built_dict["overall_stats"]["wins"] built_dict["overall_stats"]["games"] = games built_dict["overall_stats"]["losses"] = losses built_dict["overall_stats"]["win_rate"] = round( (built_dict["overall_stats"]["wins"] / games) * 100, 2 ) else: # lol make them up built_dict["overall_stats"]["games"] = 0 built_dict["overall_stats"]["losses"] = 0 built_dict["overall_stats"]["win_rate"] = 0 return built_dict
def bl_parse_stats(parsed, mode="quickplay", status=None): # Just a quick FYI # If future me or future anyone else is looking at this, I do not how this code works. # I'm really really hoping it doesn't break. # Good luck! try: # XPath for the `u-align-center` h6 which signifies there's no data. no_data = parsed.xpath( ".//div[@id='{}']//ul/h6[@class='u-align-center']".format(mode))[0] except IndexError: pass else: if no_data.text.strip( ) == "We don't have any data for this account in this mode yet.": return None # Start the dict. built_dict = {"game_stats": [], "overall_stats": {}, "average_stats": []} # Shortcut location for player level etc if not status or status.lower() != "public profile": hasrank = parsed.xpath( '//*[@id="overview-section"]/div/div/div/div/div[2]/div/div[3]/div' ) if hasrank: comprank = int(hasrank[0].text) else: comprank = None built_dict["overall_stats"]["comprank"] = comprank return built_dict mast_head = parsed.xpath(".//div[@class='masthead-player']")[0] # Get the prestige. prestige = mast_head.xpath(".//div[@class='player-level']")[0] # Extract the background-image from the styles. try: bg_image = [x for x in prestige.values() if 'background-image' in x][0] except IndexError: # Cannot find background-image. # Yikes! # Don't set a prestige. built_dict["overall_stats"]["prestige"] = 0 else: for key, val in PRESTIGE.items(): if key in bg_image: prestige_num = val built_dict["overall_stats"]["rank_image"] = bg_image.split( "(")[1][:-1] break else: # Unknown. prestige_num = None built_dict["overall_stats"]["prestige"] = prestige_num # Parse out the HTML. level = int(prestige.findall(".//div")[0].text) built_dict["overall_stats"]["level"] = level # Get and parse out endorsement level. endorsement = mast_head.xpath(".//div[@class='endorsement-level']")[0] built_dict["overall_stats"]["endorsement_level"] = int( endorsement.findall(".//div[@class='u-center']")[0].text) # Get endorsement circle. endorsement_icon_inner = mast_head.xpath( ".//div[@class='endorsement-level']/div[@class='EndorsementIcon']/div[@class='EndorsementIcon-inner']" )[0] # Get individual endorsement segments. try: endorsement_shotcaller_image = endorsement_icon_inner.findall( ".//svg[@class='EndorsementIcon-border EndorsementIcon-border--shotcaller']" )[0] endorsement_shotcaller_level = endorsement_shotcaller_image.get( 'data-value') except: endorsement_shotcaller_level = 0 try: endorsement_teammate_image = endorsement_icon_inner.findall( ".//svg[@class='EndorsementIcon-border EndorsementIcon-border--teammate']" )[0] endorsement_teammate_level = endorsement_teammate_image.get( 'data-value') except: endorsement_teammate_level = 0 try: endorsement_sportsmanship_image = endorsement_icon_inner.findall( ".//svg[@class='EndorsementIcon-border EndorsementIcon-border--sportsmanship']" )[0] endorsement_sportsmanship_level = endorsement_sportsmanship_image.get( 'data-value') except: endorsement_sportsmanship_level = 0 # Parse out endorsement segements. built_dict["overall_stats"][ "endorsement_shotcaller"] = endorsement_shotcaller_level built_dict["overall_stats"][ "endorsement_teammate"] = endorsement_teammate_level built_dict["overall_stats"][ "endorsement_sportsmanship"] = endorsement_sportsmanship_level # Get comp rank. try: tier = mast_head.xpath(".//div[@class='competitive-rank']/img")[0] img_src = [x for x in tier.values() if 'rank-icons' in x][0] built_dict["overall_stats"]["tier_image"] = img_src except IndexError: built_dict['overall_stats']['tier'] = None else: for key, val in tier_data_img_src.items(): if key in img_src: tier_str = val break else: tier_str = None built_dict["overall_stats"]["tier"] = tier_str hasrank = mast_head.findall(".//div[@class='competitive-rank']/div") if hasrank: comprank = int(hasrank[0].text) else: comprank = None built_dict["overall_stats"]["comprank"] = comprank # Fetch Avatar built_dict["overall_stats"]["avatar"] = mast_head.find( ".//img[@class='player-portrait']").attrib['src'] if mode == "competitive": # the competitive overview is under a div with id='competitive' # and the right category try: stat_groups = parsed.xpath( ".//div[@id='competitive']" "//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" )[0] except IndexError: # No stats return None elif mode == "quickplay": try: stat_groups = parsed.xpath( ".//div[@id='quickplay']" "//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" )[0] except IndexError: # User has no stats... return None else: # how else to handle fallthrough case? stat_groups = parsed.xpath( ".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']" )[0] # Highlight specific stat groups. try: game_box = stat_groups[5] except IndexError: try: game_box = stat_groups[4] except IndexError: # edge cases... # we can't really extract any more stats # so we do an early return return {} # Calculate the wins, losses, and win rate. try: wins = int( game_box.xpath(".//text()[. = 'Games Won']/../..")[0] [1].text.replace(",", "")) except IndexError: # weird edge case wins = 0 g = game_box.xpath(".//text()[. = 'Games Played']/../..") if len(g) < 1: # Blizzard f****d up, temporary quick fix for #70 games, losses = None, None else: games = int(g[0][1].text.replace(",", "")) if mode == "competitive": try: misc_box = stat_groups[7] losses = int( misc_box.xpath(".//text()[. = 'Games Lost']/../..")[0] [1].text.replace(",", "")) ties = int( misc_box.xpath(".//text()[. = 'Games Tied']/../..")[0] [1].text.replace(",", "")) except IndexError: # Sometimes the losses and ties don't exist. # I'm not 100% as to what causes this, but it might be because there are no ties. # In this case, just set ties to 0, and calculate losses manually. ties = 0 # Quickplay shit. # Goddamnit blizzard. if games is None: losses = 0 games = 0 wins = 0 else: # Competitive stats do have these values (for now...) losses = games - wins if games == 0 or games == ties: wr = 0 else: wr = round((wins / (games - ties)) * 100, 2) built_dict["overall_stats"]["ties"] = ties built_dict["overall_stats"]["games"] = games built_dict["overall_stats"]["losses"] = losses built_dict["overall_stats"]["win_rate"] = wr # Update the dictionary. built_dict["overall_stats"]["wins"] = wins # Build a dict using the stats. game_stats = {} average_stats = {} rolling_average_stats = {} for subbox in stat_groups: trs = subbox.findall(".//tbody/tr") # Update the dict with [0]: [1] for subval in trs: name, value = util.sanitize_string(subval[0].text), subval[1].text # Try and parse out the value. It might be a time! # If so, try and extract the time. nvl = util.try_extract(value) if 'average' in name.lower(): average_stats[name.replace("_average", "_avg")] = nvl elif '_avg_per_10_min' in name.lower(): # 2017-08-03 - calculate rolling averages. name = name.lower().replace("_avg_per_10_min", "") rolling_average_stats[name] = nvl else: game_stats[name] = nvl # Manually add the KPD. try: game_stats["kpd"] = round( game_stats["eliminations"] / game_stats["deaths"], 2) except KeyError: # They don't have any eliminations/deaths. # Set the KPD to 0.0. # See: #106 game_stats["kpd"] = 0 built_dict["game_stats"] = game_stats built_dict["average_stats"] = average_stats built_dict["rolling_average_stats"] = rolling_average_stats built_dict["competitive"] = mode == "competitive" if "games" not in built_dict["overall_stats"]: # manually calculate it # 2017-07-04 - changed to use eliminations # since damage done gave a bit of a stupid amount # 2017-07-11 - changed to cycle some averages average_keys = ("eliminations", "healing_done", "final_blows", "objective_kills") for key in average_keys: try: total = built_dict["game_stats"][key] avg = built_dict["average_stats"][key + "_avg"] except KeyError: continue else: got = True break else: got = False if got: games = int(total // avg) losses = games - built_dict["overall_stats"]["wins"] built_dict["overall_stats"]["games"] = games built_dict["overall_stats"]["losses"] = losses built_dict["overall_stats"]["win_rate"] = round( (built_dict["overall_stats"]["wins"] / games) * 100, 2) else: # lol make them up built_dict["overall_stats"]["games"] = 0 built_dict["overall_stats"]["losses"] = 0 built_dict["overall_stats"]["win_rate"] = 0 return built_dict
async def bl_get_compstats(ctx: HTTPRequestContext, battletag: str): """ Get stats for a user using the Blizzard sources. """ data = await bz.region_helper(ctx, battletag, region=ctx.request.values.get("region", None)) if data == (None, None): raise HTTPException(404) parsed, region = data # Start the dict. built_dict = {"region": region, "battletag": battletag, "game_stats": [], "overall_stats": {}, "average_stats": []} # Get the prestige. prestige = parsed.xpath(".//div[@class='player-level']")[0] # Extract the background-image from the styles. try: bg_image = [x for x in prestige.values() if 'background-image' in x][0] except IndexError: # Cannot find background-image. # Yikes! # Don't set a prestige. built_dict["overall_stats"]["prestige"] = 0 else: for key, val in PRESTIGE.items(): if key in bg_image: prestige_num = val break else: # Unknown. prestige_num = None built_dict["overall_stats"]["prestige"] = prestige_num # Parse out the HTML. level = int(parsed.findall(".//div[@class='player-level']/div")[0].text) built_dict["overall_stats"]["level"] = level hasrank = parsed.findall(".//div[@class='competitive-rank']/div") if hasrank: comprank = int(hasrank[0].text) else: comprank = None built_dict["overall_stats"]["comprank"] = comprank # Fetch Avatar built_dict["overall_stats"]["avatar"] = parsed.find(".//img[@class='player-portrait']").attrib['src'] hascompstats = parsed.xpath(".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']") if len(hascompstats) != 2: return {"error": 404, "msg": "competitive stats not found", "region": region}, 404 stat_groups = hascompstats[1] # Highlight specific stat groups. death_box = stat_groups[4] game_box = stat_groups[6] # Calculate the wins, losses, and win rate. wins = int(game_box.xpath(".//text()[. = 'Games Won']/../..")[0][1].text.replace(",", "")) g = game_box.xpath(".//text()[. = 'Games Played']/../..") games = int(g[0][1].text.replace(",", "")) losses = games - wins wr = floor((wins / games) * 100) # Update the dictionary. built_dict["overall_stats"]["games"] = games built_dict["overall_stats"]["losses"] = losses built_dict["overall_stats"]["wins"] = wins built_dict["overall_stats"]["win_rate"] = wr #built_dict["overall_stats"]["rank"] = None # We don't have a rank in Blizz data. #since it always returns Null, this should be disabled for now # Build a dict using the stats. _t_d = {} _a_d = {} for subbox in stat_groups: trs = subbox.findall(".//tbody/tr") # Update the dict with [0]: [1] for subval in trs: name, value = subval[0].text.lower().replace(" ", "_").replace("_-_", "_"), subval[1].text nvl = util.try_extract(value) if 'average' in name.lower(): _a_d[name.replace("_average", "_avg")] = nvl else: _t_d[name] = nvl # Manually add the KPD. _t_d["kpd"] = round(_t_d["eliminations"] / _t_d["deaths"], 2) built_dict["game_stats"] = _t_d built_dict["average_stats"] = _a_d return built_dict
def bl_parse_stats(parsed, mode="quickplay"): # Start the dict. built_dict = {"game_stats": [], "overall_stats": {}, "average_stats": []} # Get the prestige. prestige = parsed.xpath(".//div[@class='player-level']")[0] # Extract the background-image from the styles. try: bg_image = [x for x in prestige.values() if 'background-image' in x][0] except IndexError: # Cannot find background-image. # Yikes! # Don't set a prestige. built_dict["overall_stats"]["prestige"] = 0 else: for key, val in PRESTIGE.items(): if key in bg_image: prestige_num = val break else: # Unknown. prestige_num = None built_dict["overall_stats"]["prestige"] = prestige_num # Parse out the HTML. level = int(parsed.findall(".//div[@class='player-level']/div")[0].text) built_dict["overall_stats"]["level"] = level hasrank = parsed.findall(".//div[@class='competitive-rank']/div") if hasrank: comprank = int(hasrank[0].text) else: comprank = None built_dict["overall_stats"]["comprank"] = comprank # Fetch Avatar built_dict["overall_stats"]["avatar"] = parsed.find(".//img[@class='player-portrait']").attrib['src'] if mode == "competitive": hascompstats = parsed.xpath(".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']") if len(hascompstats) != 2: return {} stat_groups = hascompstats[1] elif mode == "quickplay": try: stat_groups = parsed.xpath(".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']")[0] except IndexError: # User has no stats... return {} else: # how else to handle fallthrough case? stat_groups = parsed.xpath(".//div[@data-group-id='stats' and @data-category-id='0x02E00000FFFFFFFF']")[0] # Highlight specific stat groups. try: game_box = stat_groups[6] except IndexError: try: game_box = stat_groups[5] except IndexError: # edge cases... # we can't really extract any more stats # so we do an early return return {} # Calculate the wins, losses, and win rate. try: wins = int(game_box.xpath(".//text()[. = 'Games Won']/../..")[0][1].text.replace(",", "")) except IndexError: # weird edge case wins = 0 g = game_box.xpath(".//text()[. = 'Games Played']/../..") if len(g) < 1: # Blizzard f****d up, temporary quick fix for #70 games, losses = None, None else: games = int(g[0][1].text.replace(",", "")) if mode == "competitive": try: misc_box = stat_groups[7] losses = int(misc_box.xpath(".//text()[. = 'Games Lost']/../..")[0][1].text.replace(",", "")) ties = int(misc_box.xpath(".//text()[. = 'Games Tied']/../..")[0][1].text.replace(",", "")) except IndexError: # Sometimes the losses and ties don't exist. # I'm not 100% as to what causes this, but it might be because there are no ties. # In this case, just set ties to 0, and calculate losses manually. ties = 0 # Quickplay shit. # Goddamnit blizzard. if games is None: losses = 0 games = 0 wins = 0 else: # Competitive stats do have these values (for now...) losses = games - wins if games == 0 or games == ties: wr = 0 else: wr = floor((wins / (games - ties)) * 100) built_dict["overall_stats"]["ties"] = ties built_dict["overall_stats"]["games"] = games built_dict["overall_stats"]["losses"] = losses built_dict["overall_stats"]["win_rate"] = wr # Update the dictionary. built_dict["overall_stats"]["wins"] = wins # Build a dict using the stats. _t_d = {} _a_d = {} for subbox in stat_groups: trs = subbox.findall(".//tbody/tr") # Update the dict with [0]: [1] for subval in trs: name, value = util.sanitize_string(subval[0].text), subval[1].text # Try and parse out the value. It might be a time! # If so, try and extract the time. nvl = util.try_extract(value) if 'average' in name.lower(): _a_d[name.replace("_average", "_avg")] = nvl else: _t_d[name] = nvl # Manually add the KPD. try: _t_d["kpd"] = round(_t_d["eliminations"] / _t_d["deaths"], 2) except KeyError: # They don't have any eliminations/deaths. # Set the KPD to 0.0. # See: #106 _t_d["kpd"] = 0 built_dict["game_stats"] = _t_d built_dict["average_stats"] = _a_d built_dict["competitive"] = mode == "competitive" return built_dict