전적검색 - 수정 및 향후계획

자훈·2024년 1월 4일
0

개인프로젝트

목록 보기
10/10
post-thumbnail

📌 라이엇 닉네임 정책 변경

기존에는 고유한 닉네임으로 중복이 불가능하게 만들어져 있던 닉네임 정책이, 이번에 태그라인을 추가하며 중복이 가능하도록 바뀌었다. 그래서 핵심은 소환사명이 아니라, 그 뒤에 붙은 태그로 구분하여야 된다는 점이다. 그렇기에 기존에 만들었던 코드도 어느정도 수정이 필요하였고, 그에 맞춰 추가적인 기능을 구현하였다. 물론 원래 소환사닉네임으로 확인하던 기능을 삭제하진 않고, 추가된 기능은 새로운 router로 만들었다.

📌 router.py

from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session

from ..database import get_db
from ..Search.search_crud import *
from ..Search.search_schema import *



router = APIRouter(
    prefix="/kr/search"
)

# 기존 닉네임 검색방법
@router.post("/{summoner_name}")
async def summoner_search(summoner_name: str,
                          db: Session = Depends(get_db)):
    summoner_name = summoner_name
    match_type = "솔로랭크"

    user = get_existing_user(db, summoner_name)

    if user:
        print(1)
        id = user.id
        puuid = user.puuid
        basic_info = await get_summoner_info(summoner_name)
        rank_info = await get_rank_data(id)
        match_data = await ingame_data(puuid)
        return basic_info, rank_info, match_data
    else:
        print(2)
        basic_info = await get_summoner_info(summoner_name)
        id = basic_info['id']
        puuid = basic_info['puuid']
        await save_info(id, puuid, summoner_name)
        rank_info = await get_rank_data(id)
        match_data = await ingame_data(puuid)
        return basic_info, rank_info, match_data

# 바뀐 태그라인 검색방법, 매치 데이터는 불러오기 가능.
# db 저장기능은 아직 미구현. 
@router.post("/{gamename}/{tagline}")
async def new_summoner_search(gamename:str, tagline:str):
    puuid = await get_summoner_prototype(gamename, tagline)
    id = await get_gamename_id(puuid)
    rank_info = await get_rank_data(id)
    match_data = await ingame_data(puuid)
    return rank_info, match_data

router코드이고, 밑의 바뀐 태그라인 검색방법을 중점적으로 확인하면 된다. 태그라인의 추가로 새롭게 db에 저장하는 방식을 구현해야하지만 아직 그 부분을 만들지는 않았다. router의 경우 tagline이 추가된 것 말고는 별 다른 것이 없다.

태그라인의 서치를 통해 얻을 수 있는 것은 소환사의 고유 id인 puuid이다.
해당 내용은
https://asia.api.riotgames.com/riot/account/v1/accounts/by-riot-id' 에서 확인 가능하다. 참고하길 바란다.

📌 crud.py

import time
from ..config import *
from ..models import User
import aiohttp
import asyncio

headers = {
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
    "Accept-Language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7",
    "Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8",
    "Origin": "https://developer.riotgames.com",
    "X-Riot-Token": Api
}


def save_info(db, summoner_id, summoner_puuid, summoner_name):
    db_user = User(id=summoner_id,
                   puuid=summoner_puuid,
                   name=summoner_name)
    db.add(db_user)
    db.commit()


def get_existing_user(db, user_name):
    return db.query(User).filter(User.name == user_name).first()

# 기존 데이터 서치 코드
async def get_summoner_info(summoner_name):
    base_url = f"{summonerV4}{summoner_name}"

    async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False)) as session:
        async with session.get(base_url, headers=headers) as response:
            if response.status == 200:
                data = await response.json()
                return data
            else:
                return None

# 태그라인 데이터 서치 코드
async def get_gamename_id(puuid):
    base_url = f"{summonerV4_puuid}{puuid}"
    async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False)) as session:
        async with session.get(base_url, headers=headers) as response:
            if response.status == 200:
                data = (await response.json())['id']
                return data
            else:
                return None

async def get_summoner_prototype(gamename, tagline):
    base_url = f"{puuid}/{gamename}/{tagline}"
    # print(base_url)
    async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False)) as session:
        async with session.get(base_url, headers=headers) as response:
            if response.status == 200:
                data = (await response.json())['puuid']
                return data
            else:
                return None


async def get_rank_data(id):
    leagueV4_url = f'{leagueV4}{id}'
    async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False)) as session:
        async with session.get(leagueV4_url, headers=headers) as response:
            summoner_rank = (await response.json())
            for test in summoner_rank:
                if test["queueType"] == "RANKED_SOLO_5x5":
                    rank = {
                        'queue_type': test.get("queueType"),
                        'tier': test.get("tier"),
                        'rank': test.get("rank"),
                        'leaguepoints': test.get("leaguePoints"),
                        'wins': test.get("wins"),
                        'losses': test.get("losses")
                    }
            return rank


async def match_data(match_id, session, puuid):
    match_datas = []
    match_url = f'{matchData_Url}{match_id}'
    win = 0
    loss = 0


    async with session.get(match_url, headers=headers) as response:
        Match_Dto = (await response.json())['info']['participants']
        match_data = next((participant for participant in Match_Dto if participant['puuid'] == puuid), None)
        if match_data:
            match_datas.append({
                'champname': match_data['championName'],
                'Kills': match_data['kills'],
                'Deaths': match_data['deaths'],
                'Assists': match_data['assists'],
                'Kda': round(match_data['challenges']['kda'], 2),
                'Result': 'Win' if match_data['win'] else 'Loss',
                'PhysicalDtoC': match_data['physicalDamageDealtToChampions'],
                'MagicalDtoC': match_data['magicDamageDealtToChampions'],
            })

    return match_datas


async def ingame_data(puuid):
    win = 0
    loss = 0
    start = time.time()
    match_id = await url(puuid)
    async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False)) as session:
        data = [match_data(match, session, puuid) for match in match_id]
        datas = await asyncio.gather(*data)

        # 함수화 필요
        await most_picked_champion(datas)

        # 함수화 필요
        for test in datas:
            result = test[0]['Result']
            if result == 'Win':
                win += 1
            else:
                loss += 1
        win_rate = (win / (win + loss)) * 100
        print(f"승률: {win_rate}%")

        end = time.time()
        print(end - start)
        return datas


async def url(puuid):
    match_url = f'{matchList_Url}{puuid}/ids?queue=420&type=ranked&start=0&count=10' #default match 10
    match_id = await fetch_match_id(match_url)
    return match_id


async def fetch_match_id(url):
    async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False)) as session:
        async with session.get(url, headers=headers) as response:
            return await response.json()


# 모스트 챔피언 찾기 공사중..
async def most_picked_champion(datas):
    champion_counts = {}  # 빈 딕셔너리에서 반복문을 이용해가지고, 횟수랑 챔프이름 리턴하기.
    for test in datas:
        championname = test[0]['champname']
        if championname in champion_counts:
            champion_counts[championname] += 1  # 이미 존재하는 챔피언 이름이면 픽 수를 증가
        else:
            champion_counts[championname] = 1  # 처음 발견되는 챔피언 이름이면 1로 초기화
    most_picked = max(champion_counts, key=champion_counts.get)  # 가장 많이 픽된 챔피언 찾기
    print(most_picked, champion_counts.get(most_picked))
    # return most_picked, champion_counts[most_picked]

태그라인의 변경과 더불어 데이터가 주어지는 방식 또한 변경이 있어, 그 부분을 약간 수정할 필요가 있었다. puuid를 통해, encrypted id를 얻어 이를 바탕으로 소환사의 정보를 검색하는 기존의 방식을 유지하였다. 작동은 정상적으로 잘 되며, swagger에서 확인해보면 된다.

추가적인 기능 구현으로는 모스트 챔피언을 찾는 함수와 승률을 만들어내는 반복문을 임시로 만들었다. 그러나 검색 속도에 지장을 주고 있기 때문에 마음에 들지는 않는다. 비동기적으로 데이터를 가져오는 부분에서 해당 부분들을 같이 정리하였다가 출력과정에서 한번에 처리하는 것이 속도를 높이는데 도움이 될 것이라고 예상중이다. 기존에 해당 기능을 추가하지 않았을 때는, 검색속도가 0.6초 정도로 나왔으며 현재의 출력 결과는

모스트 챔피언 (경기수) / 승률 / 경과시간 순서로 나온 데이터이다. 반복문을 효율적으로 만들거나, 데이터를 통합하는 과정에서 비동기로 처리된 데이터를 한번에 정리하는 기존의 방법을 사용하여 속도를 높일 필요성은 있다. 이런 것들이 정리되고 나면, 추가적으로 match_id를 통해 접근할 수 있는 다양한 timestamp 데이터 접근하여, 시각화와 분석을 방학동안에 구현할 예정이다.

이런식으로 상황복잡도라는 것을 정의해서 제공할 예정이다.

2개의 댓글

comment-user-thumbnail
2024년 6월 17일

궁굼한게 있는데요,

새롭게 바뀐 tagline검색 에서는
riot ID + tagline 인데

이 tagline은 어떻게 가져올수 있는 방법이 없는건가요?
검색시마다 그냥 DB에 넣어두고 쓰는건가용?

1개의 답글