Skip to content

Commit

Permalink
Enable diffcalc for all scores on all gamemodes
Browse files Browse the repository at this point in the history
  • Loading branch information
Syriiin committed Jun 7, 2024
1 parent fdfbcb7 commit 77250ec
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 140 deletions.
23 changes: 18 additions & 5 deletions common/osu/difficultycalculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,16 +518,29 @@ def gamemode():
return Gamemode.MANIA


difficulty_calculators: dict[str, type[AbstractDifficultyCalculator]] = {
difficulty_calculators_classes: dict[str, type[AbstractDifficultyCalculator]] = {
name: import_string(calculator_class)
for name, calculator_class in settings.DIFFICULTY_CALCULATOR_CLASSES.items()
}


def get_difficulty_calculator_class(name: str) -> Type[AbstractDifficultyCalculator]:
return difficulty_calculators[name]
return difficulty_calculators_classes[name]


DifficultyCalculator: Type[AbstractDifficultyCalculator] = import_string(
settings.DIFFICULTY_CALCULATOR_CLASS
)
def get_default_difficulty_calculator_class(
gamemode: Gamemode,
) -> Type[AbstractDifficultyCalculator]:
return get_difficulty_calculator_class(
settings.DEFAULT_DIFFICULTY_CALCULATORS[gamemode]
)


def get_difficulty_calculators_for_gamemode(
gamemode: Gamemode,
) -> list[Type[AbstractDifficultyCalculator]]:
return [
calculator_class
for calculator_class in difficulty_calculators_classes.values()
if calculator_class.gamemode() == gamemode
]
13 changes: 9 additions & 4 deletions osuchan/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from celery.schedules import crontab
from pydantic_settings import BaseSettings

from common.osu.enums import Gamemode


class EnvSettings(BaseSettings):
SECRET_KEY: str
Expand Down Expand Up @@ -324,10 +326,6 @@ class EnvSettings(BaseSettings):

# Difficulty calculation

DIFFICULTY_CALCULATOR_CLASS = (
"common.osu.difficultycalculator.RosuppDifficultyCalculator"
)

DIFFICULTY_CALCULATOR_CLASSES = {
"oppai": "common.osu.difficultycalculator.OppaiDifficultyCalculator",
"rosupp": "common.osu.difficultycalculator.RosuppDifficultyCalculator",
Expand All @@ -337,6 +335,13 @@ class EnvSettings(BaseSettings):
"difficalcy-mania": "common.osu.difficultycalculator.DifficalcyManiaDifficultyCalculator",
}

DEFAULT_DIFFICULTY_CALCULATORS = {
Gamemode.STANDARD: "rosupp",
Gamemode.TAIKO: "difficalcy-taiko",
Gamemode.CATCH: "difficalcy-catch",
Gamemode.MANIA: "difficalcy-mania",
}

DIFFICALCY_OSU_URL = f"http://{env_settings.DIFFICALCY_OSU_HOST}"
DIFFICALCY_TAIKO_URL = f"http://{env_settings.DIFFICALCY_TAIKO_HOST}"
DIFFICALCY_CATCH_URL = f"http://{env_settings.DIFFICALCY_CATCH_HOST}"
Expand Down
4 changes: 2 additions & 2 deletions profiles/management/commands/calculationbreakdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from common.osu.difficultycalculator import (
AbstractDifficultyCalculator,
difficulty_calculators,
difficulty_calculators_classes,
)
from common.osu.enums import Gamemode
from profiles.models import (
Expand All @@ -18,7 +18,7 @@ class Command(BaseCommand):
help = "Displays current db calculation breakdown (new models)"

def handle(self, *args, **options):
for name, difficulty_calculator_class in difficulty_calculators.items():
for name, difficulty_calculator_class in difficulty_calculators_classes.items():
difficulty_calculator = difficulty_calculator_class()
gamemode = difficulty_calculator.gamemode()

Expand Down
4 changes: 2 additions & 2 deletions profiles/management/commands/calculationstatus.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from common.osu.difficultycalculator import (
AbstractDifficultyCalculator,
difficulty_calculators,
difficulty_calculators_classes,
)
from common.osu.enums import Gamemode
from profiles.models import Beatmap, Score
Expand All @@ -15,7 +15,7 @@ class Command(BaseCommand):
help = "Displays current db calculation status (old models)"

def handle(self, *args, **options):
for name, difficulty_calculator_class in difficulty_calculators.items():
for name, difficulty_calculator_class in difficulty_calculators_classes.items():
gamemode = difficulty_calculator_class.gamemode()

self.stdout.write(
Expand Down
73 changes: 37 additions & 36 deletions profiles/management/commands/recalculate.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
from common.osu.difficultycalculator import (
AbstractDifficultyCalculator,
CalculationException,
DifficultyCalculator,
get_difficulty_calculator_class,
get_difficulty_calculators_for_gamemode,
)
from common.osu.enums import Gamemode
from leaderboards.models import Membership
Expand Down Expand Up @@ -49,44 +49,45 @@ def handle(self, *args, **options):
if diffcalc_name:
difficulty_calculator_class = get_difficulty_calculator_class(diffcalc_name)
else:
difficulty_calculator_class = DifficultyCalculator
difficulty_calculator_class = get_difficulty_calculators_for_gamemode(
Gamemode.STANDARD
)[0]

difficulty_calculator = difficulty_calculator_class()
with difficulty_calculator_class() as difficulty_calculator:

gamemode = difficulty_calculator.gamemode()

self.stdout.write(
f"Gamemode: {Gamemode(gamemode).name}\n"
f"Difficulty Calculator Engine: {difficulty_calculator.engine()}\n"
f"Difficulty Calculator Version: {difficulty_calculator.version()}\n"
)

if v2:
# Recalculate beatmaps
beatmaps = Beatmap.objects.filter(gamemode=gamemode)
self.recalculate_beatmaps_v2(difficulty_calculator, beatmaps, force)

# Recalculate scores
scores = Score.objects.filter(gamemode=gamemode)
self.recalculate_scores_v2(difficulty_calculator, scores, force)
else:
# Recalculate beatmaps
beatmaps = Beatmap.objects.filter(gamemode=gamemode)
self.recalculate_beatmaps(difficulty_calculator_class, beatmaps, force)

# Recalculate scores
scores = Score.objects.filter(gamemode=gamemode)
self.recalculate_scores(difficulty_calculator_class, scores, force)

# Recalculate user stats
all_user_stats = UserStats.objects.filter(gamemode=gamemode)
self.recalculate_user_stats(all_user_stats)
gamemode = difficulty_calculator.gamemode()
self.stdout.write(
f"Gamemode: {Gamemode(gamemode).name}\n"
f"Difficulty Calculator Engine: {difficulty_calculator.engine()}\n"
f"Difficulty Calculator Version: {difficulty_calculator.version()}\n"
)

# Recalculate memberships
memberships = Membership.objects.select_related("leaderboard").filter(
leaderboard__gamemode=gamemode
)
self.recalculate_memberships(memberships)
if v2:
# Recalculate beatmaps
beatmaps = Beatmap.objects.filter(gamemode=gamemode)
self.recalculate_beatmaps_v2(difficulty_calculator, beatmaps, force)

# Recalculate scores
scores = Score.objects.filter(gamemode=gamemode)
self.recalculate_scores_v2(difficulty_calculator, scores, force)
else:
# Recalculate beatmaps
beatmaps = Beatmap.objects.filter(gamemode=gamemode)
self.recalculate_beatmaps(difficulty_calculator_class, beatmaps, force)

# Recalculate scores
scores = Score.objects.filter(gamemode=gamemode)
self.recalculate_scores(difficulty_calculator_class, scores, force)

# Recalculate user stats
all_user_stats = UserStats.objects.filter(gamemode=gamemode)
self.recalculate_user_stats(all_user_stats)

# Recalculate memberships
memberships = Membership.objects.select_related("leaderboard").filter(
leaderboard__gamemode=gamemode
)
self.recalculate_memberships(memberships)

def recalculate_beatmap_page(
self,
Expand Down
13 changes: 2 additions & 11 deletions profiles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
from common.osu import utils
from common.osu.difficultycalculator import (
AbstractDifficultyCalculator,
DifficultyCalculator,
DifficultyCalculatorException,
)
from common.osu.difficultycalculator import Score as DifficultyCalculatorScore
from common.osu.difficultycalculator import get_difficulty_calculator_class
from common.osu.enums import BeatmapStatus, Gamemode, Mods
from profiles.enums import AllowedBeatmapStatus, ScoreResult, ScoreSet

Expand Down Expand Up @@ -260,10 +260,6 @@ def from_data(cls, beatmap_data):
beatmap_data["last_update"], "%Y-%m-%d %H:%M:%S"
).replace(tzinfo=timezone.utc)

beatmap.difficulty_total = float(beatmap_data["difficultyrating"])
beatmap.difficulty_calculator_engine = "legacy"
beatmap.difficulty_calculator_version = "legacy"

# Update foreign key ids
beatmap.creator_id = int(beatmap_data["creator_id"])

Expand Down Expand Up @@ -541,7 +537,7 @@ def process(self):
self.__process_score_result()
try:
# only need to pass beatmap_id, 100s, 50s, and mods since all other options default to best possible
with DifficultyCalculator() as calc:
with get_difficulty_calculator_class("rosupp")() as calc:
nochoke_calculation = calc.calculate_score(
DifficultyCalculatorScore(
beatmap_id=self.beatmap_id,
Expand All @@ -553,11 +549,6 @@ def process(self):
self.nochoke_performance_total = (
nochoke_calculation.performance_values["total"]
)
self.difficulty_total = nochoke_calculation.difficulty_values[
"total"
]
self.difficulty_calculator_engine = "legacy" # legacy because performance_total is still coming from the api response
self.difficulty_calculator_version = "legacy"
except DifficultyCalculatorException as e:
error_reporter = ErrorReporter()
error_reporter.report_error(e)
Expand Down
110 changes: 65 additions & 45 deletions profiles/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
from common.osu.apiv1 import OsuApiV1
from common.osu.difficultycalculator import (
AbstractDifficultyCalculator,
DifficultyCalculator,
DifficultyCalculatorException,
)
from common.osu.difficultycalculator import Score as DifficultyCalculatorScore
from common.osu.difficultycalculator import (
get_default_difficulty_calculator_class,
get_difficulty_calculators_for_gamemode,
)
from common.osu.enums import BeatmapStatus, Gamemode, Mods
from leaderboards.models import Leaderboard, Membership
from profiles.models import (
Expand Down Expand Up @@ -217,24 +220,22 @@ def refresh_user_from_api(
score_data_list.extend(
osu_api_v1.get_user_best_scores(user_stats.user_id, gamemode)
)
if gamemode == Gamemode.STANDARD:
# If standard, check user recent because we will be able to calculate pp for those scores
score_data_list.extend(
score
for score in osu_api_v1.get_user_recent_scores(user_stats.user_id, gamemode)
if score["rank"] != "F"
)
score_data_list.extend(
score
for score in osu_api_v1.get_user_recent_scores(user_stats.user_id, gamemode)
if score["rank"] != "F"
)

user_stats.save()

# Process and add scores
created_scores = add_scores_from_data(user_stats, score_data_list)

# TODO: iterate all registered difficulty calculators for gamemode
if gamemode == Gamemode.STANDARD:
difficulty_calculator = DifficultyCalculator()
for score in created_scores:
update_performance_calculation(score, difficulty_calculator)
difficulty_calculators = get_difficulty_calculators_for_gamemode(gamemode)
for difficulty_calculator in difficulty_calculators:
with difficulty_calculator() as calc:
for score in created_scores:
update_performance_calculation(score, calc)

return user_stats

Expand All @@ -258,8 +259,27 @@ def refresh_beatmap_from_api(beatmap_id: int):
]:
return None

gamemode = Gamemode(beatmap.gamemode)

with get_default_difficulty_calculator_class(gamemode)() as calc:
calculation = calc.calculate_score(
DifficultyCalculatorScore(
mods=Mods.NONE.value,
beatmap_id=str(beatmap.id),
)
)
beatmap.difficulty_total = calculation.difficulty_values["total"]
beatmap.difficulty_calculator_engine = calc.engine()
beatmap.difficulty_calculator_version = calc.version()

beatmap.save()

for difficulty_calculator_class in get_difficulty_calculators_for_gamemode(
gamemode
):
with difficulty_calculator_class() as difficulty_calculator:
update_difficulty_calculations([beatmap], difficulty_calculator)

return beatmap


Expand Down Expand Up @@ -293,10 +313,12 @@ def fetch_scores(user_id, beatmap_ids, gamemode):
# Process add scores
created_scores = add_scores_from_data(user_stats, full_score_data_list)

# TODO: iterate all registered difficulty calculators for gamemode
difficulty_calculator = DifficultyCalculator()
for score in created_scores:
update_performance_calculation(score, difficulty_calculator)
for difficulty_calculator_class in get_difficulty_calculators_for_gamemode(
gamemode
):
with difficulty_calculator_class() as difficulty_calculator:
for score in created_scores:
update_performance_calculation(score, difficulty_calculator)

return created_scores

Expand Down Expand Up @@ -382,36 +404,34 @@ def add_scores_from_data(user_stats: UserStats, score_data_list: list[dict]):
score.beatmap = beatmap
score.user_stats = user_stats

# Update pp
if "pp" in score_data and score_data["pp"] is not None:
score.performance_total = float(score_data["pp"])
score.difficulty_calculator_engine = "legacy"
score.difficulty_calculator_version = "legacy"
else:
# Check for gamemode
if user_stats.gamemode != Gamemode.STANDARD:
# We cant calculate pp for this mode yet so we need to disregard this score
continue
gamemode = Gamemode(user_stats.gamemode)

try:
with DifficultyCalculator() as calc:
calculation = calc.calculate_score(
DifficultyCalculatorScore(
mods=score.mods,
beatmap_id=beatmap_id,
count_100=score.count_100,
count_50=score.count_50,
count_miss=score.count_miss,
combo=score.best_combo,
)
# Calculate performance total
try:
with get_default_difficulty_calculator_class(gamemode)() as calc:
calculation = calc.calculate_score(
DifficultyCalculatorScore(
mods=score.mods,
beatmap_id=str(beatmap_id),
count_katu=score.count_katu,
count_300=score.count_300,
count_100=score.count_100,
count_50=score.count_50,
count_miss=score.count_miss,
combo=score.best_combo,
)
score.performance_total = calculation.performance_values["total"]
score.difficulty_calculator_engine = DifficultyCalculator.engine()
score.difficulty_calculator_version = DifficultyCalculator.version()
except DifficultyCalculatorException as e:
error_reporter = ErrorReporter()
error_reporter.report_error(e)
continue
)
score.performance_total = calculation.performance_values["total"]
score.difficulty_total = calculation.difficulty_values["total"]
score.difficulty_calculator_engine = calc.engine()
score.difficulty_calculator_version = calc.version()
except DifficultyCalculatorException as e:
error_reporter = ErrorReporter()
error_reporter.report_error(e)
score.performance_total = 0
score.difficulty_total = 0
score.difficulty_calculator_engine = "error"
score.difficulty_calculator_version = "error"

# Update convenience fields
score.gamemode = user_stats.gamemode
Expand Down
Loading

0 comments on commit 77250ec

Please sign in to comment.