Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add models to support multiple difficulty calculators #31

Merged
merged 7 commits into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@
"isort.args": ["--profile", "black"],
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
"python.formatting.provider": "none"
}
}
14 changes: 11 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ COMPOSE_RUN_TOOLING = UID=${UID} GID=${GID} docker compose -f docker-compose.yml
COMPOSE_APP_DEV = docker compose -f docker-compose.yml -f docker-compose.override.yml

help: ## Show this help
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
@printf "\nUSAGE: make [command] \n\n"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
@printf '\n'

env: ## Switch to an environment config
env: ## Switch to an environment config
@mkdir -p config/active
rm -rf config/active/*
cp -r config/${ENV}/* config/active/
Expand Down Expand Up @@ -57,5 +59,11 @@ build-dev: ## Builds development docker images
start-dev: ## Starts development environment
$(COMPOSE_APP_DEV) up -d

clean-dev: ## Cleans development environment
clean-dev: ## Cleans development environment containers
$(COMPOSE_APP_DEV) down --remove-orphans

reset-dev: ## Resets config, data and containers to default states
make env ENV=dev
$(COMPOSE_RUN_TOOLING) python manage.py flush --no-input
$(COMPOSE_APP_DEV) down --remove-orphans --volumes
make start-dev
39 changes: 38 additions & 1 deletion profiles/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
from django.contrib import admin

from profiles.models import Beatmap, OsuUser, Score, ScoreFilter, UserStats
from profiles.models import (
Beatmap,
DifficultyCalculation,
DifficultyValue,
OsuUser,
PerformanceCalculation,
PerformanceValue,
Score,
ScoreFilter,
UserStats,
)


class UserStatsAdmin(admin.ModelAdmin):
Expand All @@ -13,6 +23,16 @@ class BeatmapAdmin(admin.ModelAdmin):
raw_id_fields = ("creator",)


class DifficultyCalculationAdmin(admin.ModelAdmin):
model = DifficultyCalculation
raw_id_fields = ("beatmap",)


class DifficultyValueAdmin(admin.ModelAdmin):
model = DifficultyCalculation
raw_id_fields = ("calculation",)


class ScoreAdmin(admin.ModelAdmin):
model = Score
raw_id_fields = (
Expand All @@ -21,8 +41,25 @@ class ScoreAdmin(admin.ModelAdmin):
)


class PerformanceCalculationAdmin(admin.ModelAdmin):
model = PerformanceCalculation
raw_id_fields = (
"score",
"difficulty_calculation",
)


class PerformanceValueAdmin(admin.ModelAdmin):
model = PerformanceCalculation
raw_id_fields = ("calculation",)


admin.site.register(OsuUser)
admin.site.register(UserStats, UserStatsAdmin)
admin.site.register(Beatmap, BeatmapAdmin)
admin.site.register(DifficultyCalculation, DifficultyCalculationAdmin)
admin.site.register(DifficultyValue, DifficultyValueAdmin)
admin.site.register(Score, ScoreAdmin)
admin.site.register(PerformanceCalculation, PerformanceCalculationAdmin)
admin.site.register(PerformanceValue, PerformanceValueAdmin)
admin.site.register(ScoreFilter)
106 changes: 106 additions & 0 deletions profiles/management/commands/calculationstatus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from django.core.management.base import BaseCommand
from django.db.models import QuerySet

from common.osu.difficultycalculator import DifficultyCalculator
from common.osu.enums import Gamemode
from profiles.models import Beatmap, Score


class Command(BaseCommand):
help = "Displays current db calculation status"

def add_arguments(self, parser):
parser.add_argument(
"--v2",
action="store_true",
help="Use new difficulty and performance models",
)

def handle(self, *args, **options):
# the v2 flag is used to determine whether to use the new difficulty and performance models
v2 = options["v2"]

# TODO: iterate over supported gamemodes
gamemode = Gamemode.STANDARD

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

if v2:
beatmaps = Beatmap.objects.filter(gamemode=gamemode)
outdated_beatmap_count = self.get_outdated_beatmap_count_v2(beatmaps)

scores = Score.objects.filter(gamemode=gamemode)
outdated_score_count = self.get_outdated_score_count_v2(scores)
else:
beatmaps = Beatmap.objects.filter(gamemode=gamemode)
outdated_beatmap_count = self.get_outdated_beatmap_count(beatmaps)

scores = Score.objects.filter(gamemode=gamemode)
outdated_score_count = self.get_outdated_score_count(scores)

beatmap_count = beatmaps.count()
up_to_date_beatmap_count = beatmap_count - outdated_beatmap_count

score_count = scores.count()
up_to_date_score_count = score_count - outdated_score_count

if up_to_date_beatmap_count == 0:
beatmap_output_style = self.style.ERROR
elif up_to_date_beatmap_count == beatmap_count:
beatmap_output_style = self.style.SUCCESS
else:
beatmap_output_style = self.style.WARNING

if up_to_date_score_count == 0:
score_output_style = self.style.ERROR
elif up_to_date_score_count == score_count:
score_output_style = self.style.SUCCESS
else:
score_output_style = self.style.WARNING

self.stdout.write(
beatmap_output_style(
f"Up-to-date Beatmaps: {up_to_date_beatmap_count} / {beatmap_count} ({(up_to_date_beatmap_count / beatmap_count) * 100:.2f}%)"
)
)
self.stdout.write(
score_output_style(
f"Up-to-date Scores: {up_to_date_score_count} / {score_count} ({(up_to_date_score_count / score_count) * 100:.2f}%)"
)
)

def get_outdated_beatmap_count(self, beatmaps: QuerySet[Beatmap]):
beatmaps_to_recalculate = beatmaps.exclude(
difficulty_calculator_engine=DifficultyCalculator.engine(),
difficulty_calculator_version=DifficultyCalculator.version(),
).order_by("pk")

return beatmaps_to_recalculate.count()

def get_outdated_score_count(self, scores: QuerySet[Score]):
scores_to_recalculate = scores.exclude(
difficulty_calculator_engine=DifficultyCalculator.engine(),
difficulty_calculator_version=DifficultyCalculator.version(),
).order_by("pk")

return scores_to_recalculate.count()

def get_outdated_beatmap_count_v2(self, beatmaps: QuerySet[Beatmap]):
beatmaps_to_recalculate = beatmaps.exclude(
difficulty_calculations__calculator_engine=DifficultyCalculator.engine(),
difficulty_calculations__calculator_version=DifficultyCalculator.version(),
)

return beatmaps_to_recalculate.count()

def get_outdated_score_count_v2(self, scores: QuerySet[Score]):
scores_to_recalculate = scores.exclude(
performance_calculations__calculator_engine=DifficultyCalculator.engine(),
performance_calculations__calculator_version=DifficultyCalculator.version(),
)

return scores_to_recalculate.count()
Loading
Loading