Skip to content

Commit

Permalink
Merge pull request #15 from cesaregarza/feature/weapon_leaderboard
Browse files Browse the repository at this point in the history
Feature/weapon leaderboard
  • Loading branch information
cesaregarza authored Jun 27, 2024
2 parents ef1c9d4 + 5912403 commit 75c2bf8
Show file tree
Hide file tree
Showing 54 changed files with 2,165 additions and 207 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/update_i18n.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ jobs:
- name: Check if push is from GitHub Actions
id: check_ci_step
run: |
if [ "$GITHUB_ACTOR" = "github-actions" ]; then
echo "Prevent infinite loop by CI"
LAST_COMMITTER=$(git log -1 --pretty=format:'%an')
if [ "$LAST_COMMITTER" = "GitHub Actions" ]; then
exit 0
fi
Expand Down
15 changes: 13 additions & 2 deletions i18n/EUen.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,26 @@
"column_total_x_power_title": "Total X Power",
"column_peak_xpower_title": "Peak X Power",
"column_percent_games_played_title": "% Games Played",
"column_season_number_title": "Season"
"column_season_number_title": "Season",
"weapon_select_main": "Weapon Select",
"weapon_select_alt": "Weapon Select (Alt)",
"weapon_title": "Top Weapon Wielders",
"threshold_select": "Minimum Percent Usage",
"weapon_leaderboard.peak_x_power": "Peak X Power",
"weapon_leaderboard.final_x_power": "Season End X Power",
"select_weapon": "Select Weapon",
"null_selection": "No Alt Weapon",
"no_data": "No data available",
"errors.503": "Service Unavailable, please try again later."
},
"navigation": {
"top500": "Top 500",
"footer.about": "About",
"footer.contact": "Contact",
"footer.rights": "%DATE% splat.top. All rights reserved.",
"navbar.faq": "FAQ",
"navbar.analytics": "Analytics"
"navbar.analytics": "Analytics",
"navbar.top_weapons": "Top Weapons"
},
"player": {
"data_lang_key": "USen",
Expand Down
17 changes: 14 additions & 3 deletions i18n/USen.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,31 @@
"column_weapon_not_supported": "Weapon name not yet supported",
"column_xpower_title": "X Power",
"column_peak_xpower_title": "Peak X Power",
"column_percent_games_played_title": "% Games Played",
"column_percent_games_played_title": "Est. % Usage",
"column_season_number_title": "Season",
"select_columns": "Select columns",
"modes": "Modes",
"region": "Region",
"column_total_x_power_title": "Total X Power"
"column_total_x_power_title": "Total X Power",
"weapon_select_main": "Weapon Select",
"weapon_select_alt": "Weapon Select (Alt)",
"weapon_title": "Top Weapon Wielders",
"threshold_select": "Minimum Percent Usage",
"weapon_leaderboard.peak_x_power": "Peak X Power",
"weapon_leaderboard.final_x_power": "Season End X Power",
"select_weapon": "Select Weapon",
"null_selection": "No Alt Weapon",
"no_data": "No data available",
"errors.503": "Service Unavailable, please try again later."
},
"navigation": {
"top500": "Top 500",
"footer.about": "About",
"footer.contact": "Contact",
"footer.rights": "%DATE% splat.top. All rights reserved.",
"navbar.faq": "FAQ",
"navbar.analytics": "Analytics"
"navbar.analytics": "Analytics",
"navbar.top_weapons": "Top Weapons"
},
"player": {
"data_lang_key": "USen",
Expand Down
15 changes: 13 additions & 2 deletions i18n/USes.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,26 @@
"column_total_x_power_title": "Fuerza X Total",
"column_peak_xpower_title": "Peak X Power",
"column_percent_games_played_title": "% Games Played",
"column_season_number_title": "Season"
"column_season_number_title": "Season",
"weapon_select_main": "Weapon Select",
"weapon_select_alt": "Weapon Select (Alt)",
"weapon_title": "Top Weapon Wielders",
"threshold_select": "Minimum Percent Usage",
"weapon_leaderboard.peak_x_power": "Peak X Power",
"weapon_leaderboard.final_x_power": "Season End X Power",
"select_weapon": "Select Weapon",
"null_selection": "No Alt Weapon",
"no_data": "No data available",
"errors.503": "Service Unavailable, please try again later."
},
"navigation": {
"top500": "Top 500",
"footer.about": "Acerca de",
"footer.contact": "Contacto",
"footer.rights": "%DATE% splat.top. Todos los derechos reservados.",
"navbar.faq": "FAQ",
"navbar.analytics": "Analítica"
"navbar.analytics": "Analítica",
"navbar.top_weapons": "Top Weapons"
},
"player": {
"data_lang_key": "USes",
Expand Down
14 changes: 13 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ orjson = "^3.10.1"
slowapi = "^0.1.9"
gunicorn = "^22.0.0"
scipy = "^1.13.1"
cachetools = "^5.3.3"


[tool.poetry.group.dev.dependencies]
Expand Down
6 changes: 6 additions & 0 deletions src/celery_app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
from celery_app.tasks.analytics.lorenz import compute_lorenz_and_gini
from celery_app.tasks.analytics.skill_offset import compute_skill_offset
from celery_app.tasks.front_page import pull_data
from celery_app.tasks.leaderboard import (
fetch_season_results,
fetch_weapon_leaderboard,
)
from celery_app.tasks.misc import pull_aliases, update_weapon_info
from celery_app.tasks.player_detail import fetch_player_data
from shared_lib.constants import REDIS_URI
Expand All @@ -25,3 +29,5 @@
celery.task(name="tasks.pull_aliases")(pull_aliases)
celery.task(name="tasks.update_skill_offset")(compute_skill_offset)
celery.task(name="tasks.update_lorenz_and_gini")(compute_lorenz_and_gini)
celery.task(name="tasks.fetch_weapon_leaderboard")(fetch_weapon_leaderboard)
celery.task(name="tasks.fetch_season_results")(fetch_season_results)
8 changes: 8 additions & 0 deletions src/celery_app/beat.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,12 @@
"task": "tasks.update_lorenz_and_gini",
"schedule": crontab(minute="*/10"),
},
"fetch-weapon-leaderboard-every-ten-minutes": {
"task": "tasks.fetch_weapon_leaderboard",
"schedule": crontab(minute="5-59/10"),
},
"fetch-season-results-every-hour": {
"task": "tasks.fetch_season_results",
"schedule": crontab(minute=30, hour="*"),
},
}
6 changes: 4 additions & 2 deletions src/celery_app/tasks/analytics/skill_offset.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ def compute_probability_map(sorted_xp_scaled: pd.Series) -> pd.DataFrame:
prob_df.columns = [int(x) * 2 + 1 for x in range(prob_df.shape[1])]
prob_df["y"] = sorted_xp_scaled.values
prob_df["y_bin"] = pd.cut(prob_df["y"], bins=NUM_BINS)
prob_df_binned = prob_df.groupby("y_bin").sum().drop(columns="y")
prob_df_binned = (
prob_df.groupby("y_bin", observed=False).sum().drop(columns="y")
)
df_sums = prob_df_binned.sum(axis=0)
prob_df_logbin: pd.DataFrame = (
prob_df_binned.div(df_sums, axis=1)
Expand Down Expand Up @@ -144,7 +146,7 @@ def subcompute_skill_offset(
input_df[label]
.sub(input_df["mode_bin_center"])
.gt(0)
.replace({True: 1, False: -1})
.map({True: 1, False: -1})
.mul(diff)
)

Expand Down
117 changes: 117 additions & 0 deletions src/celery_app/tasks/leaderboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import logging

import orjson
import pandas as pd
from sqlalchemy import text

from celery_app.connections import Session, redis_conn
from shared_lib.constants import (
SEASON_RESULTS_REDIS_KEY,
WEAPON_LEADERBOARD_PEAK_REDIS_KEY,
)
from shared_lib.queries.leaderboard_queries import (
LIVE_WEAPON_LEADERBOARD_QUERY,
SEASON_RESULTS_QUERY,
WEAPON_LEADERBOARD_QUERY,
)
from shared_lib.utils import get_all_alt_kits

logger = logging.getLogger(__name__)

idx_columns = ["player_id", "season_number", "mode", "region"]


def fetch_past_weapon_leaderboard_data() -> pd.DataFrame:
"""Fetches past weapon leaderboard data from the database.
Returns:
pd.DataFrame: A DataFrame of weapon leaderboard data.
"""
logger.info("Fetching past weapon leaderboard data")
query = text(WEAPON_LEADERBOARD_QUERY)
with Session() as session:
result = session.execute(query).fetchall()
weapon_leaderboard = pd.DataFrame(
[{**row._asdict()} for row in result]
).set_index(idx_columns)

return weapon_leaderboard


def fetch_live_weapon_leaderboard_data() -> pd.DataFrame:
"""Fetches live weapon leaderboard data from the database.
Returns:
pd.DataFrame: A DataFrame of weapon leaderboard data.
"""
logger.info("Fetching live weapon leaderboard data")
query = text(LIVE_WEAPON_LEADERBOARD_QUERY)
with Session() as session:
result = session.execute(query).fetchall()
weapon_leaderboard = pd.DataFrame(
[{**row._asdict()} for row in result]
).set_index(idx_columns)

weapon_leaderboard["weapon_id"] = (
weapon_leaderboard["weapon_id"]
.astype(str)
.map(get_all_alt_kits())
.fillna(weapon_leaderboard["weapon_id"])
.astype(str)
)

total_games_df = (
weapon_leaderboard.reset_index()
.groupby(idx_columns)["games_played"]
.sum()
.rename("total_games_played")
)
weapon_leaderboard = weapon_leaderboard.merge(
total_games_df, left_index=True, right_index=True, how="left"
)
weapon_leaderboard["percent_games_played"] = weapon_leaderboard[
"games_played"
].div(weapon_leaderboard["total_games_played"])

return (
weapon_leaderboard.groupby(idx_columns + ["weapon_id"])
.agg(
{
"max_x_power": "max",
"games_played": "sum",
"percent_games_played": "sum",
}
)
.reset_index()
.set_index(idx_columns)
)


def fetch_weapon_leaderboard() -> pd.DataFrame:
logger.info("Fetching weapon data")
past_weapon_leaderboard = fetch_past_weapon_leaderboard_data()
live_weapon_leaderboard = fetch_live_weapon_leaderboard_data()
weapon_leaderboard = pd.concat(
[past_weapon_leaderboard, live_weapon_leaderboard]
).sort_index()
del past_weapon_leaderboard, live_weapon_leaderboard

redis_conn.set(
WEAPON_LEADERBOARD_PEAK_REDIS_KEY,
orjson.dumps(
weapon_leaderboard.reset_index().to_dict(orient="records")
),
)


def fetch_season_results() -> pd.DataFrame:
logger.info("Fetching season results")
query = text(SEASON_RESULTS_QUERY)
with Session() as session:
result = session.execute(query).fetchall()
season_results = pd.DataFrame([{**row._asdict()} for row in result])

redis_conn.set(
SEASON_RESULTS_REDIS_KEY,
orjson.dumps(season_results.to_dict(orient="records")),
)
4 changes: 4 additions & 0 deletions src/fast_api_app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
player_detail_router,
search_router,
weapon_info_router,
weapon_leaderboard_router,
)

# Setup basic logging
Expand All @@ -34,6 +35,8 @@ async def lifespan(app: FastAPI):
celery.send_task("tasks.pull_aliases")
celery.send_task("tasks.update_skill_offset")
celery.send_task("tasks.update_lorenz_and_gini")
celery.send_task("tasks.fetch_weapon_leaderboard")
celery.send_task("tasks.fetch_season_results")

start_pubsub_listener()
asyncio.create_task(background_runner.run())
Expand Down Expand Up @@ -63,6 +66,7 @@ async def lifespan(app: FastAPI):
app.include_router(player_detail_router)
app.include_router(search_router)
app.include_router(weapon_info_router)
app.include_router(weapon_leaderboard_router)


# Base route that lists all available routes
Expand Down
Loading

0 comments on commit 75c2bf8

Please sign in to comment.