From 82ae9e39ba18cd2b01e15c73386f27ef298d8cc7 Mon Sep 17 00:00:00 2001 From: kwanok Date: Mon, 18 Mar 2024 18:05:18 +0900 Subject: [PATCH] wip --- web/league_entry.py | 22 +-- ...nt_id_summoner_profile_icon_id_and_more.py | 38 ++++++ web/project/app/models.py | 18 ++- web/project/app/riot_client.py | 7 + web/project/app/serializers.py | 3 +- web/project/app/urls.py | 15 ++- web/project/app/views.py | 100 ++++++++++---- web/project/project/settings.py | 8 +- web/project/templates/recommend/ai.html | 4 +- web/project/templates/recommend/result.html | 2 +- web/project/templates/users/profile.html | 125 +++++++++++++++--- web/project/users/admin.py | 4 +- ...summoner_info_appuser_summoner_and_more.py | 32 +++++ web/project/users/models.py | 15 +-- 14 files changed, 303 insertions(+), 90 deletions(-) create mode 100644 web/project/app/migrations/0007_summoner_account_id_summoner_profile_icon_id_and_more.py create mode 100644 web/project/users/migrations/0003_remove_appuser_summoner_info_appuser_summoner_and_more.py diff --git a/web/league_entry.py b/web/league_entry.py index c37566d..2f8a061 100644 --- a/web/league_entry.py +++ b/web/league_entry.py @@ -15,15 +15,15 @@ def __init__(self): self.api_key = os.environ.get("RIOT_API_KEY") self.tiers = ["IRON", "BRONZE", "SILVER", "GOLD", "PLATINUM", "DIAMOND"] self.divisions = ["I", "II", "III", "IV"] - + self.db_user = os.environ.get("POSTGRES_USER") self.db_password = os.environ.get("POSTGRES_PASSWORD") self.db_host = "localhost" self.db_port = os.environ.get("POSTGRES_PORT") self.db_name = os.environ.get("POSTGRES_WEB_DB") - + self.pool = None - + async def create_db_pool(self): self.pool = await asyncpg.create_pool( user=self.db_user, @@ -31,7 +31,7 @@ async def create_db_pool(self): host=self.db_host, port=self.db_port, database=self.db_name, - ) + ) async def run(self): await self.create_db_pool() @@ -50,7 +50,9 @@ async def get_league_entries(self, tier: str, division: str): league_entries = await self._fetch(url + f"&page={page}") while league_entries: - print(f"Fetching {tier} {division} page {page}... entries: {len(league_entries)}") + print( + f"Fetching {tier} {division} page {page}... entries: {len(league_entries)}" + ) summoners = [ ( league_entry["summonerId"], @@ -58,10 +60,9 @@ async def get_league_entries(self, tier: str, division: str): datetime.datetime.now(), datetime.datetime.now(), ) - for league_entry - in league_entries + for league_entry in league_entries ] - + async with self.pool.acquire() as connection: async with connection.transaction(): await connection.executemany( @@ -93,8 +94,7 @@ async def get_league_entries(self, tier: str, division: str): datetime.datetime.now(), datetime.datetime.now(), ) - for league_entry - in league_entries + for league_entry in league_entries ] async with self.pool.acquire() as connection: async with connection.transaction(): @@ -123,7 +123,7 @@ async def get_league_entries(self, tier: str, division: str): page += 1 league_entries = await self._fetch(url + f"&page={page}") - + return [] async def _fetch(self, url: str) -> list: diff --git a/web/project/app/migrations/0007_summoner_account_id_summoner_profile_icon_id_and_more.py b/web/project/app/migrations/0007_summoner_account_id_summoner_profile_icon_id_and_more.py new file mode 100644 index 0000000..292c239 --- /dev/null +++ b/web/project/app/migrations/0007_summoner_account_id_summoner_profile_icon_id_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.0.2 on 2024-03-15 06:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("app", "0006_summoner_app_summone_name_9a15eb_idx"), + ] + + operations = [ + migrations.AddField( + model_name="summoner", + name="account_id", + field=models.CharField(max_length=100, null=True), + ), + migrations.AddField( + model_name="summoner", + name="profile_icon_id", + field=models.IntegerField(null=True), + ), + migrations.AddField( + model_name="summoner", + name="puuid", + field=models.CharField(max_length=100, null=True), + ), + migrations.AddField( + model_name="summoner", + name="revision_date", + field=models.BigIntegerField(null=True), + ), + migrations.AddField( + model_name="summoner", + name="summoner_level", + field=models.IntegerField(null=True), + ), + ] diff --git a/web/project/app/models.py b/web/project/app/models.py index 64591a2..a374022 100644 --- a/web/project/app/models.py +++ b/web/project/app/models.py @@ -1,6 +1,5 @@ # Create your models here. -from django.contrib.postgres.search import SearchVector, SearchVectorField from users.models import AppUser as User from django.db import models @@ -31,16 +30,19 @@ class Champion(models.Model): class Summoner(models.Model): id = models.CharField(max_length=100, blank=False, null=False, primary_key=True) name = models.CharField(max_length=100, blank=False, null=False) + puuid = models.CharField(max_length=100, blank=False, null=True) + account_id = models.CharField(max_length=100, blank=False, null=True) + profile_icon_id = models.IntegerField(blank=False, null=True) + revision_date = models.BigIntegerField(blank=False, null=True) + summoner_level = models.IntegerField(blank=False, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.name - + class Meta: - indexes = [ - models.Index(fields=['name']) - ] + indexes = [models.Index(fields=["name"])] class LeagueEntry(models.Model): @@ -61,8 +63,10 @@ class LeagueEntry(models.Model): def __str__(self): return f"{self.summoner.name} - {self.tier} {self.rank}" - + class Meta: constraints = [ - models.UniqueConstraint(fields=['summoner', 'queue_type'], name='unique_league_entry') + models.UniqueConstraint( + fields=["summoner", "queue_type"], name="unique_league_entry" + ) ] diff --git a/web/project/app/riot_client.py b/web/project/app/riot_client.py index 9b63e0a..47ee2e9 100644 --- a/web/project/app/riot_client.py +++ b/web/project/app/riot_client.py @@ -30,6 +30,13 @@ def get_champion_masteries_by_puuid_top(self, puuid: str) -> requests.Response: response = requests.get(url, headers={"X-Riot-Token": self.api_key}) return response + def get_summoner_by_encrypted_summoner_id( + self, encrypted_summoner_id: str + ) -> requests.Response: + url = f"{KR_API_HOST}/lol/summoner/v4/summoners/{encrypted_summoner_id}" + response = requests.get(url, headers={"X-Riot-Token": self.api_key}) + return response + client = RiotClient() diff --git a/web/project/app/serializers.py b/web/project/app/serializers.py index a597750..ae825db 100644 --- a/web/project/app/serializers.py +++ b/web/project/app/serializers.py @@ -1,6 +1,7 @@ from app.models import Summoner, LeagueEntry from rest_framework import serializers + class SummonerSerializer(serializers.ModelSerializer): class Meta: model = Summoner @@ -10,4 +11,4 @@ class Meta: class LeagueEntrySerializer(serializers.ModelSerializer): class Meta: model = LeagueEntry - fields = "__all__" \ No newline at end of file + fields = "__all__" diff --git a/web/project/app/urls.py b/web/project/app/urls.py index ee39366..3bff6e2 100644 --- a/web/project/app/urls.py +++ b/web/project/app/urls.py @@ -1,5 +1,6 @@ from django.urls import path, include -from rest_framework import routers + +from project import settings from .views import ( index, get_account_by_summoner_name, @@ -7,6 +8,7 @@ recommend_result, riot_txt, search_summoners_by_name, + save_summoner, ) urlpatterns = [ @@ -15,6 +17,13 @@ path("summoner/", get_account_by_summoner_name, name="summoner"), path("recommend-ai/", recommend_ai, name="recommend-ai"), path("recommend-result/", recommend_result, name="recommend-result"), - path("summoners/search/", search_summoners_by_name, name="search-summoners-by-name"), - path("__debug__/", include("debug_toolbar.urls")), + path( + "summoners/search/", search_summoners_by_name, name="search-summoners-by-name" + ), + path("profile/summoner", save_summoner, name="save-summoner-info"), ] + +if settings.DEBUG: + import debug_toolbar + + urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns diff --git a/web/project/app/views.py b/web/project/app/views.py index 5e79c89..01e7adf 100644 --- a/web/project/app/views.py +++ b/web/project/app/views.py @@ -1,18 +1,14 @@ -import os -import uuid from django.http import JsonResponse from django.shortcuts import redirect, render from django.core.handlers.wsgi import WSGIRequest -import requests from django.contrib.auth.decorators import login_required -from users.models import SummonerInfo +from app.models import Summoner from rest_framework.decorators import api_view from app.riot_client import get_client -from app.riot_assets import get_riot_assets -from rest_framework import viewsets from app.serializers import SummonerSerializer, LeagueEntrySerializer -from app.models import Summoner, LeagueEntry +from app.models import LeagueEntry +from users.models import AppUser def riot_txt(request: WSGIRequest): @@ -42,10 +38,10 @@ def authorize(request: WSGIRequest): def get_account_by_summoner_name(request: WSGIRequest): summoner_name = request.GET.get("summoner_name") - if SummonerInfo.objects.filter(name=summoner_name).exists(): - summoner_info = SummonerInfo.objects.get(name=summoner_name) + if Summoner.objects.filter(name=summoner_name).exists(): + summoner = Summoner.objects.get(name=summoner_name) - return render(request, "summoner/index.html", {"summoner": summoner_info}) + return render(request, "summoner/index.html", {"summoner": summoner}) client = get_client() @@ -54,7 +50,7 @@ def get_account_by_summoner_name(request: WSGIRequest): if response.status_code == 200: data = response.json() - summoner_info = SummonerInfo( + summoner = Summoner( id=data["id"], account_id=data["accountId"], profile_icon_id=data["profileIconId"], @@ -64,9 +60,9 @@ def get_account_by_summoner_name(request: WSGIRequest): summoner_level=data["summonerLevel"], ) - summoner_info.save() + summoner.save() - return render(request, "summoner/index.html", {"summoner": summoner_info}) + return render(request, "summoner/index.html", {"summoner": summoner}) return render(request, "summoner/index.html", {"error": response.json()}) @@ -90,30 +86,50 @@ def search_summoners_by_name(request: WSGIRequest): count = request.GET.get("count") if not name or not count: return JsonResponse({"error": "name, count are required"}, status=400) - + try: count = int(count) except ValueError: return JsonResponse({"error": "count must be an integer"}, status=400) - + if count > 10: - return JsonResponse({"error": "count must be less than or equal to 10"}, status=400) - + return JsonResponse( + {"error": "count must be less than or equal to 10"}, status=400 + ) + queryset = Summoner.objects.filter(name__startswith=name)[:count] serializer = SummonerSerializer(queryset, many=True) - + summoners = serializer.data - - summoner_ids = [ - summoner["id"] - for summoner in serializer.data - ] - + + summoner_ids = [summoner["id"] for summoner in serializer.data] + queryset = LeagueEntry.objects.filter(summoner_id__in=summoner_ids) serializer = LeagueEntrySerializer(queryset, many=True) - + league_entries = serializer.data - + + for summoner in summoners: + if summoner["puuid"] is None: + new_summoner = get_client().get_summoner_by_encrypted_summoner_id( + summoner["id"] + ) + + if new_summoner.status_code == 200: + summoner["puuid"] = new_summoner.json()["puuid"] + summoner["account_id"] = new_summoner.json()["accountId"] + summoner["profile_icon_id"] = new_summoner.json()["profileIconId"] + summoner["revision_date"] = new_summoner.json()["revisionDate"] + summoner["summoner_level"] = new_summoner.json()["summonerLevel"] + + Summoner.objects.filter(id=summoner["id"]).update( + puuid=summoner["puuid"], + account_id=summoner["account_id"], + profile_icon_id=summoner["profile_icon_id"], + revision_date=summoner["revision_date"], + summoner_level=summoner["summoner_level"], + ) + result = [ { "summoner": summoner, @@ -121,9 +137,35 @@ def search_summoners_by_name(request: WSGIRequest): league_entry for league_entry in league_entries if league_entry["summoner"] == summoner["id"] - ] + ], } for summoner in summoners ] - - return JsonResponse(result, safe=False) \ No newline at end of file + + return JsonResponse(result, safe=False) + + +@api_view(["POST"]) +def save_summoner(request: WSGIRequest): + me = request.user + + summoner_id = request.data["summoner_id"] + + if not summoner_id: + return JsonResponse({"error": "id is required"}, status=400) + + summoner = Summoner.objects.get(id=summoner_id) + + AppUser.objects.filter(id=me.id).update(summoner=summoner) + + return JsonResponse( + { + "name": summoner.name, + "puuid": summoner.puuid, + "level": summoner.summoner_level, + "account_id": summoner.account_id, + "profile_icon_id": summoner.profile_icon_id, + "revision_date": summoner.revision_date, + }, + safe=False, + ) diff --git a/web/project/project/settings.py b/web/project/project/settings.py index 465fc52..e9dc697 100644 --- a/web/project/project/settings.py +++ b/web/project/project/settings.py @@ -28,7 +28,7 @@ # SECURITY WARNING: don't run with debug turned on in production! if ENV == "production": - DEBUG = True + DEBUG = False SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_HSTS_SECONDS = 31536000 # 1 year SESSION_COOKIE_SECURE = True @@ -177,10 +177,10 @@ AUTH_USER_MODEL = "users.AppUser" REST_FRAMEWORK = { - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', - 'PAGE_SIZE': 10 + "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", + "PAGE_SIZE": 10, } INTERNAL_IPS = [ "127.0.0.1", -] \ No newline at end of file +] diff --git a/web/project/templates/recommend/ai.html b/web/project/templates/recommend/ai.html index 1faa87a..9c9e9c4 100644 --- a/web/project/templates/recommend/ai.html +++ b/web/project/templates/recommend/ai.html @@ -10,13 +10,13 @@

안녕하세요, 방문자님

로그인을 하시면 AI 추천을 받아보실 수 있습니다.

- {% elif not user.summoner_info %} + {% elif not user.summoner %}

안녕하세요, {{ user.username }}님

라이엇 계정 연동을 통해 AI 추천을 받아보실 수 있습니다.

{% else %} -

안녕하세요, {{ user.summoner_info.name }}님

+

안녕하세요, {{ user.summoner.name }}님

diff --git a/web/project/templates/recommend/result.html b/web/project/templates/recommend/result.html index 54e36a3..ce6b358 100644 --- a/web/project/templates/recommend/result.html +++ b/web/project/templates/recommend/result.html @@ -8,7 +8,7 @@
추천 결과

- {{ user.summoner_info.name }}님의 추천 결과입니다. + {{ user.summoner.name }}님의 추천 결과입니다.

diff --git a/web/project/templates/users/profile.html b/web/project/templates/users/profile.html index fcd7556..f5a74ac 100644 --- a/web/project/templates/users/profile.html +++ b/web/project/templates/users/profile.html @@ -26,30 +26,28 @@

👾 마이페이지

- +
-
- {% if user.summoner_info %} +
+ {% if user.summoner %} + value="{{ user.summoner.name }}"> {% else %} + name="summoner_name" + id="summoner_name" + class="block border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" + placeholder="소환사 이름으로 라이엇 계정을 연동해보세요."> {% endif %}
+
- {% if user.summoner_info %} + {% if user.summoner %}
@@ -61,7 +59,7 @@

👾 마이페이지

autocomplete="summoner_level" class="block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" disabled="disabled" - value="{{ user.summoner_info.summoner_level }}"> + value="{{ user.summoner.summoner_level }}">
@@ -69,11 +67,11 @@

👾 마이페이지

- {% if user.summoner_info %} + {% if user.summoner %} {% else %} 👾 마이페이지
{% endblock content %} +{% block js %} + +{% endblock js %} \ No newline at end of file diff --git a/web/project/users/admin.py b/web/project/users/admin.py index 6a48d48..8aa232d 100644 --- a/web/project/users/admin.py +++ b/web/project/users/admin.py @@ -6,8 +6,8 @@ class CustomUserAdmin(UserAdmin): model = AppUser # 추가된 필드를 관리자 페이지에서 보이도록 설정 - fieldsets = UserAdmin.fieldsets + ((None, {"fields": ("summoner_info",)}),) - add_fieldsets = UserAdmin.add_fieldsets + ((None, {"fields": ("summoner_info",)}),) + fieldsets = UserAdmin.fieldsets + ((None, {"fields": ("summoner",)}),) + add_fieldsets = UserAdmin.add_fieldsets + ((None, {"fields": ("summoner",)}),) admin.site.register(AppUser, CustomUserAdmin) diff --git a/web/project/users/migrations/0003_remove_appuser_summoner_info_appuser_summoner_and_more.py b/web/project/users/migrations/0003_remove_appuser_summoner_info_appuser_summoner_and_more.py new file mode 100644 index 0000000..43cc517 --- /dev/null +++ b/web/project/users/migrations/0003_remove_appuser_summoner_info_appuser_summoner_and_more.py @@ -0,0 +1,32 @@ +# Generated by Django 5.0.2 on 2024-03-18 06:20 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("app", "0007_summoner_account_id_summoner_profile_icon_id_and_more"), + ("users", "0002_summonerinfo_remove_appuser_riot_id_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="appuser", + name="summoner_info", + ), + migrations.AddField( + model_name="appuser", + name="summoner", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="app.summoner", + ), + ), + migrations.DeleteModel( + name="SummonerInfo", + ), + ] diff --git a/web/project/users/models.py b/web/project/users/models.py index 8a9671f..a603334 100644 --- a/web/project/users/models.py +++ b/web/project/users/models.py @@ -3,16 +3,7 @@ class AppUser(AbstractUser): - summoner_info = models.OneToOneField( - "SummonerInfo", on_delete=models.CASCADE, blank=True, null=True - ) - -class SummonerInfo(models.Model): - id = models.CharField(max_length=100, blank=False, null=False, primary_key=True) - account_id = models.CharField(max_length=100, blank=False, null=False) - profile_icon_id = models.IntegerField(blank=False, null=False) - revision_date = models.BigIntegerField(blank=False, null=False) - name = models.CharField(max_length=100, blank=False, null=False) - puuid = models.CharField(max_length=100, blank=False, null=False) - summoner_level = models.IntegerField(blank=False, null=False) + summoner = models.OneToOneField( + "app.Summoner", on_delete=models.CASCADE, blank=True, null=True + )