Skip to content

Commit

Permalink
migrate the logic for determining clear status to model
Browse files Browse the repository at this point in the history
  • Loading branch information
sapuri committed Mar 3, 2024
1 parent 512a7bc commit d0c99c9
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 123 deletions.
2 changes: 1 addition & 1 deletion main/management/commands/change_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.core.management.base import BaseCommand

from main.models import Sran_Level, Music
from main.models import Music, Sran_Level


class Command(BaseCommand):
Expand Down
37 changes: 28 additions & 9 deletions main/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime, timedelta

from django.db import models
from django.utils import timezone
from datetime import datetime, timedelta

from users.models import CustomUser

Expand Down Expand Up @@ -31,7 +32,6 @@ class Meta:
class Level(models.Model):
level = models.IntegerField('レベル')

# 整数型で返す
def int(self):
return self.level

Expand Down Expand Up @@ -79,16 +79,40 @@ class Meta:


class Medal(models.Model):
class ClearStatus(models.TextChoices):
NO_PLAY = 'no-play', 'No Play'
PERFECT = 'perfect', 'Perfect'
FULL_COMBO = 'fullcombo', 'Full Combo'
HARD_CLEARED = 'hard-cleared', 'Hard Cleared'
CLEARED = 'cleared', 'Cleared'
FAILED = 'failed', 'Failed'
EASY_CLEARED = 'easy-cleared', 'Easy Cleared'

medal = models.IntegerField('クリアメダル')
music = models.ForeignKey(Music, verbose_name='曲', on_delete=models.CASCADE)
user = models.ForeignKey(CustomUser, verbose_name='ユーザー', on_delete=models.PROTECT)
updated_at = models.DateTimeField('更新日時', default=datetime.now)

# int型で返す
def get_clear_status(self, bad_count: 'Bad_Count', extra_option: 'Extra_Option') -> ClearStatus:
if self.medal == 1 and bad_count.bad_count == 0:
return self.ClearStatus.PERFECT
elif 2 <= self.medal <= 4 and bad_count.bad_count == 0:
return self.ClearStatus.FULL_COMBO
elif 5 <= self.medal <= 7:
if extra_option and extra_option.is_hard():
return self.ClearStatus.HARD_CLEARED
else:
return self.ClearStatus.CLEARED
elif 8 <= self.medal <= 10:
return self.ClearStatus.FAILED
elif self.medal == 11:
return self.ClearStatus.EASY_CLEARED
else:
return self.ClearStatus.NO_PLAY

def int(self):
return self.medal

# 文字列で返す
def output_str(self) -> str:
if self.medal == 1:
return '金'
Expand Down Expand Up @@ -117,7 +141,6 @@ def output_str(self) -> str:
else:
return ''

# 更新日時をJSTで返す
def updated_at_jst(self):
return self.updated_at + timedelta(hours=9)

Expand All @@ -140,14 +163,12 @@ class Bad_Count(models.Model):
user = models.ForeignKey(CustomUser, verbose_name='ユーザー', on_delete=models.PROTECT)
updated_at = models.DateTimeField('更新日時', default=datetime.now)

# 整数型で返す
def int(self):
return self.bad_count

def __str__(self):
return str(self.bad_count)

# 更新日時をJSTで返す
def updated_at_jst(self):
return self.updated_at + timedelta(hours=9)

Expand All @@ -167,13 +188,11 @@ class Extra_Option(models.Model):
user = models.ForeignKey(CustomUser, verbose_name='ユーザー', on_delete=models.PROTECT)
updated_at = models.DateTimeField('更新日時', default=datetime.now)

# ハードしているかを判定
def is_hard(self) -> bool:
if self.hard:
return True
return False

# 更新日時をJSTで返す
def updated_at_jst(self):
return self.updated_at + timedelta(hours=9)

Expand Down
18 changes: 1 addition & 17 deletions main/templates/main/ranking_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,7 @@ <h2 class="page-header">ランキング</h2>
</thead>
<tbody>
{% for result in results %}
{% if result.medal.medal == 1 and result.bad_count.bad_count == 0 %}
<tr class="perfect">
{% elif result.bad_count.bad_count == 0 %}
<tr class="fullcombo">
{% elif result.extra_option.hard %}
<tr class="hard-cleared">
{% else %}
{% if result.medal.medal == 7 or result.medal.medal == 6 or result.medal.medal == 5 %}
<tr class="cleared">
{% elif result.medal.medal == 10 or result.medal.medal == 9 or result.medal.medal == 8 %}
<tr class="failed">
{% elif result.medal.medal == 11 %}
<tr class="easy-cleared">
{% else %}
<tr class="no-play">
{% endif %}
{% endif %}
<tr class="{{ result.status }}">
<td class="rank">
{% if result.user == user %}
<b>{{ result.rank|default:'-' }}</b>
Expand Down
24 changes: 6 additions & 18 deletions main/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,12 @@ def get_clear_status(request: HttpRequest, music_id: int) -> JsonResponse:
raise PermissionDenied

bad_count = Bad_Count.objects.filter(music_id=music_id, user_id=user_id).first()
medal_obj = Medal.objects.filter(music_id=music_id, user_id=user_id).first()
medal = medal_obj.medal if medal_obj else None
extra_option_exists = Extra_Option.objects.filter(music_id=music_id, user_id=user_id, hard=True).exists()

clear_status = 'no-play'
if medal is not None:
if medal == 1 and bad_count == 0:
clear_status = 'perfect'
elif medal in [2, 3, 4] and bad_count == 0:
clear_status = 'fullcombo'
elif 1 <= medal <= 7 and extra_option_exists:
clear_status = 'hard-cleared'
elif medal in [5, 6, 7]:
clear_status = 'cleared'
elif medal in [8, 9, 10]:
clear_status = 'failed'
elif medal == 11:
clear_status = 'easy-cleared'
medal = Medal.objects.filter(music_id=music_id, user_id=user_id).first()
extra_option = Extra_Option.objects.filter(music_id=music_id, user_id=user_id, hard=True).first()

clear_status = Medal.ClearStatus.NO_PLAY.value
if medal:
clear_status = medal.get_clear_status(bad_count=bad_count, extra_option=extra_option).value

return JsonResponse({'clear_status': clear_status})

Expand Down
2 changes: 1 addition & 1 deletion main/views/ranking.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.http import Http404, HttpRequest, HttpResponse
from django.shortcuts import render

Expand Down
94 changes: 21 additions & 73 deletions main/views/ranking_detail.py
Original file line number Diff line number Diff line change
@@ -1,98 +1,46 @@
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, render

from main.models import Music, Medal, Bad_Count, Extra_Option
from users.models import CustomUser
from main.models import Bad_Count, Extra_Option, Medal, Music


def ranking_detail(request: HttpRequest, music_id: int) -> HttpResponse:
""" ランキング: 詳細 """

def bad_count_rank(bad_count_list_ordered: list[Bad_Count], user: CustomUser) -> int | None:
"""
指定されたユーザーの順位を返す
@param bad_count_list_ordered: 曲で絞込済みのBAD数リスト(昇順)
@param user: 指定されたユーザー
@return rank: 順位
"""
if not bad_count_list_ordered:
return None

bad_count_num = 0 # BAD数の個数
bad_count_now = -1 # 現在のBAD数
rank = -1 # ランク
found = False # BAD数を登録済であればTrueを返す
tmp_rank = 0

for bad_count in bad_count_list_ordered:
bad_count_before = bad_count_now
bad_count_now = bad_count.bad_count
music = get_object_or_404(Music, pk=music_id)

# BAD数が前後で重複した場合
if bad_count_now == bad_count_before:
# 指定されたユーザーの記録が見つかれば rank にランクを格納
if bad_count.user.id == user.id:
found = True
rank = tmp_rank
medals = Medal.objects.filter(music=music).select_related('user')
extra_options = Extra_Option.objects.filter(music=music, hard=True).select_related('user')

bad_count_num += 1
medals_dict = {medal.user.id: medal for medal in medals}
extra_options_dict = {option.user.id: option for option in extra_options}

# BAD数が重複しなかった場合
else:
bad_count_num += 1
bad_count_list = list(Bad_Count.objects.filter(music=music).order_by('bad_count', 'updated_at'))
rank, last_bad_count, results = 1, None, []

# 一時ランクを更新
tmp_rank = bad_count_num
for i, bad_count in enumerate(bad_count_list, start=1):
if bad_count.bad_count != last_bad_count:
rank = i
last_bad_count = bad_count.bad_count

# 自分の記録が見つかれば rank にランクを格納
if bad_count.user.id == user.id:
found = True
rank = bad_count_num
medal = medals_dict.get(bad_count.user.id)
extra_option = extra_options_dict.get(bad_count.user.id)

if found:
return rank
if medal is None:
status = Medal.ClearStatus.NO_PLAY.value
else:
return None

# 曲を取得
music = get_object_or_404(Music, pk=music_id)

medal_list = Medal.objects.filter(music=music)
bad_count_list = Bad_Count.objects.filter(music=music).order_by('bad_count', 'updated_at')
extra_option_list = Extra_Option.objects.filter(music=music)

# 対象曲を記録しているユーザーを取得
user_id_list = list(bad_count_list.values_list('user', flat=True))
users = CustomUser.objects.filter(pk__in=user_id_list, is_active=True)
status = medal.get_clear_status(bad_count, extra_option).value

# ランキングを生成
results = []
for bad_count in bad_count_list:
selected_user = users.get(pk=bad_count.user.id)
try:
medal = medal_list.get(user=selected_user)
if medal.medal == 12:
medal = None
except ObjectDoesNotExist:
medal = None
try:
extra_option = extra_option_list.get(user=selected_user)
except ObjectDoesNotExist:
extra_option = None
rank = bad_count_rank(bad_count_list, selected_user)
results.append({
'rank': rank,
'user': selected_user,
'user': bad_count.user,
'medal': medal,
'bad_count': bad_count,
'extra_option': extra_option
'extra_option': extra_option,
'status': status,
})

context = {
'music': music,
'bad_count_list': bad_count_list,
'results': results
'results': results,
}

return render(request, 'main/ranking_detail.html', context)
11 changes: 7 additions & 4 deletions users/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.db import models


class Location(models.Model):
Expand All @@ -25,13 +25,16 @@ class Meta:


class CustomUser(AbstractUser):
player_name = models.CharField('プレイヤー名', max_length=6, null=True, blank=True, help_text='全角ひらがなカタカナ英数字6文字以内')
player_name = models.CharField('プレイヤー名', max_length=6, null=True, blank=True,
help_text='全角ひらがなカタカナ英数字6文字以内')
poputomo_id = models.CharField('ポプともID', max_length=12, null=True, blank=True, help_text='半角数字12文字')
location = models.ForeignKey(Location, verbose_name='都道府県', null=True, blank=True, on_delete=models.PROTECT)
profile = models.TextField('プロフィール', null=True, blank=True)
player_name_privacy = models.IntegerField('プレイヤー名', default=1, help_text='ランキングにプレイヤー名とプロフィールページへのリンクが掲載されます。(非公開の場合は"匿名希望さん"と表示されます)')
player_name_privacy = models.IntegerField('プレイヤー名', default=1,
help_text='ランキングにプレイヤー名とプロフィールページへのリンクが掲載されます。(非公開の場合は"匿名希望さん"と表示されます)')
cleardata_privacy = models.IntegerField('クリアデータ', default=1, help_text='プロフィールページにクリアデータを表示します。')
updated_recently_privacy = models.IntegerField('最近更新した曲', default=1, help_text='プロフィールページに最近更新した曲を表示します。')
updated_recently_privacy = models.IntegerField('最近更新した曲', default=1,
help_text='プロフィールページに最近更新した曲を表示します。')
theme = models.ForeignKey(Theme, verbose_name='テーマ', default=1, on_delete=models.PROTECT)
premium = models.BooleanField('プレミアムユーザー', default=False, help_text='Amazonギフト券を買ってくれた人')

Expand Down

0 comments on commit d0c99c9

Please sign in to comment.