MVC 완료 및 CSS 추가

자훈·2023년 9월 25일
0

개인프로젝트

목록 보기
6/10

📌 디렉토리 정리완료

📌 정리 후기

애 먹었던 디렉토리 별 모듈정리를 드디어 끝냈다. 정리하면서 느낀 과정은, 확실히 만들기 전부터 분리해놓고 구상하는 것이 훨씬 더 유지보수에 좋다고 느낀 것이다. 처음에 한 코드에 다 때려박았을 때는, 에러가 났을 때 고치기가 너무 힘들었다. 지금도 보면 토나온다... 근데 정리하고 나서는, 에러가 무섭지 않다 이게 가장 크게 느낀 점 같다. 에러가 나오면 어디서 났지를 너무 쉽게 찾고 접근하게 된다. 유지보수가 쉽다는 말이 괜히 있는 것이 아니다.. 그러다 보니 기능추가가 자연스레 쉬워지고, 루프하는 함수를 정리하게 되는 습관이 생겼다. CSS에도 눈을 돌리는 중이다. 어쨌든 좀 이쁘게 사이트를 만들고 싶기에 ㅎ..

db구현도 천천히 해나가며, 후에는 db와 소통하며 편리하게 만들 예정이다. 추가하고 싶은 기능이라 함은, 회원가입제, 게시판, 분석기능 및 관련 자료 업데이트 등등이 있다. 진짜 별거 아닌 것 같은데 학교다니면서 구현하려면 한 세월일 것 같다. 그래도 개인프로젝트 시작한거 되게 깔끔하게 마무리하고 싶은 생각이 강하다. 쓰다보니 일기가 되긴 했는데, 이제부터 정리본을 업로드 해보자.

(기본적인 디렉토리 구성이다.)

RiotProject
├── app
│   ├── controller
│   │   ├── controller.py
│   │   └── roop.py
│   ├── model
│   │   └── model.py
│   ├── env
│   │   ├── dict.py
│   │   └── URL.py
│   ├── view
│   │   └── view.py
│   └── order.py
├── run.py
└── prototype.py

📌 각 모듈별 코드

📌 run.py


from flask import Flask, url_for, redirect
from RiotApiProject.app.view import view

# import os / 디렉토리 절대 경로 이게 뭔지 공부해보기
# 템플릿 분리해서 사용해보기
# 데이터베이스 구현하기(controller model)
# view에서 데이터 입력받음 >> 컨트롤러로 모듈함수 호출 >> 컨트롤러에서 데이터 처리 및 조회 리턴 >> 값을 저장 >>  템플릿 랜더링

app = Flask(__name__)
app.register_blueprint(view.bp)


@app.route('/')
def create():
    print('hello site')
    return redirect(url_for('main.input'))
    #bp함수로 호출할 수 있다 이렇게하면.


if __name__ == '__main__':
    app.run(debug=True)

📌 order.py


query1 = "SELECT id, puuid FROM summoners WHERE nickname =?"

📌 controller.py

#controller.py
from RiotApiProject.app.env.URL import *
from RiotApiProject.app.env.dict import rank
from RiotApiProject.app.model import model as m
from RiotApiProject.app.controller import roop as r
import requests

# APIS  spectator-v4 banned champ / gamelength /summonerspell / championid /teamid
# match v-5 participantDTO 분석할 수 있는 데이터 진짜 많음. . banDTO / objectiveDTO


api_key = Api


def search(summoner_name):
    # db 연결
    conn, cursor = m.get_db_cursor()
    m.create_database(conn, cursor)
    # db에서 데이터 확인
    id, puuid = m.db_check(cursor, summoner_name)

    if id and puuid:
        summoner_info = r.get_info(summoner_name)
        summoner_puuid = puuid
        summoner_rank = r.get_rank(id) #랭크정보 조회
        r.roop(summoner_rank)
        return summoner_info, rank, summoner_puuid
    else:
        summoner_info = r.get_info(summoner_name)  # summoner_info 저장
        summoner_puuid = summoner_info['puuid']
        id = summoner_info['id']
        summoner_rank = r.get_rank(id)
        r.roop(summoner_rank)
        m.save_info(conn, cursor, summoner_info)
        return summoner_info, rank, summoner_puuid


def ingame(puuid, match_type):
    if match_type == '솔로랭크':
        match_type = 420  # 솔로랭크 엔드포인트
    else:
        match_type = 440  # 자유랭크 엔드포인트

    match_url = f'{matchnumL}{puuid}/ids?queue={match_type}&type=ranked&start=0&count=10&api_key={api_key}'
    response = requests.get(match_url)
    match_id = response.json()

    match_data = []
    for m in match_id:
        base_url = f'{matchL}{m}?api_key={api_key}'
        r = requests.get(base_url)
        Match_Dto = r.json()
        Match_info = Match_Dto['info']
        Participants = Match_info['participants']

        for participant in Participants:
            if participant['puuid'] == puuid:
                w = participant['win']
                k = participant['kills']
                d = participant['deaths']
                a = participant['assists']
                mdtoc = participant['magicDamageDealtToChampions']
                pdtoc = participant['physicalDamageDealtToChampions']
                win = 'Win' if w else 'Loss'
                # zero divisionerror
                try:
                    kda = (k + a) / d
                    kda = round(kda, 2)
                except ZeroDivisionError:
                    kda = 999
                match_data.append({
                    'Match_id': m,
                    'Win': win,
                    'Kills': k,
                    'Deaths': d,
                    'Assists': a,
                    'Kda': kda,
                    'PhysicalDtoC': pdtoc,
                    'MagicDtoC': mdtoc
                })
    return match_data

📌 roop.py


import requests
from RiotApiProject.app.env.dict import rank
from RiotApiProject.app.env.URL import *

api_key = Api

def roop(summoner_rank):
    print(summoner_rank)
    for entry in summoner_rank:
        print(entry)
        rank['queue_type'] = entry["queueType"]
        rank['tier'] = entry["tier"]
        rank['match'] = entry["rank"]
        rank['leaguepoints'] = entry["leaguePoints"]
        rank['wins'] = entry["wins"]
        rank['losses'] = entry["losses"]
    pass

def get_info(summoner_name):
    summonerV4_url = summonerV4
    url = f'{summonerV4_url}{summoner_name}?api_key={api_key}'
    response = requests.get(url)
    summoner_info = response.json()
    return summoner_info

def get_rank(id):
    league = leagueV4
    leagueV4_url = f'{league}{id}?api_key={api_key}'
    response = requests.get(leagueV4_url)
    summoner_rank = response.json()
    return summoner_rank

📌 model.py

import sqlite3
from RiotApiProject.app import order as o


# 데이터베이스 구축
# 조회 및 업데이트 기능 구현

def get_db_cursor():
    try:
        conn = sqlite3.connect('lol.db')
        cursor = conn.cursor()
        return conn, cursor

    except Exception as e:
        print(f"Database connection error: {e}")
        return None, None

def db_check(cursor, name):
    cursor.execute(o.query1, (name,))
    existing = cursor.fetchone()
    if existing:
        id, puuid = existing
        return id, puuid
    else:
        return None, None

def create_database(conn, cursor):
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS summoners (
            id TEXT PRIMARY KEY,
            puuid TEXT,
            nickname TEXT
        )
    ''')
    conn.commit()
    pass

## db 안닫고있음 id primary key라 중복되게 업데이트하면 안됨
def save_info(conn, cursor, summoner_info):
    id = summoner_info['id']
    puuid = summoner_info['puuid']
    name = summoner_info['name']
    cursor.execute('''
        INSERT INTO summoners(
            id, 
            puuid, 
            nickname
        ) VALUES (?, ?, ?)
    ''',(id, puuid, name))
    conn.commit()
    conn.close()
    pass

📌 view.py

from flask import render_template, request, Blueprint
from RiotApiProject.app.controller import controller as c
from RiotApiProject.app.env.URL import profile

# controller 각 엔드포인트의 요청에 대한 응답만 정의함.
# 변수명을 반드시 확인하도록
# 플라스크 동적 템플릿 생성방법에 대해 공부
# 초기화면 검색화면
# 렌더링 화면 데이터 출력 템플릿


bp = Blueprint('main',__name__, template_folder="../templates", url_prefix='/kr')



#url_prefix에 의해 endpoit시작은 고정. 그렇기에 충돌없이 실행한다. 
@bp.route('/', methods=['POST', 'GET'])
def input():
    if request.method == 'POST':
        summoner_name = request.form['summoner_name']
        match_type = request.form['match_type']
        #소환사 프로필 정보
        summoner_info, rank,  summoner_puuid = c.search(summoner_name)
        url = f"{profile}{summoner_info['profileIconId']}.png"
        #랭크 경기 정보
        match_data = c.ingame(summoner_puuid, match_type)


        return render_template('index.html', summoner_info=summoner_info, rank=rank, url=url, match_data=match_data)
    return render_template('input.html')
# 뷰를 나눴는데 방법은 간단했다. 함수리턴 템플릿을 인풋으로 해놓고
# 조건부 post에 대한 부분의 리턴 템플릿은 인덱스로 해놓으니 문제없이 작동한다.

📌 URL.py, dict.py

#변수저장 리스트

Api = 'yout_api'
profile = 'https://ddragon.leagueoflegends.com/cdn/11.11.1/img/profileicon/'
summonerV4 = 'https://kr.api.riotgames.com/lol/summoner/v4/summoners/by-name/'
leagueV4 = 'https://kr.api.riotgames.com/lol/league/v4/entries/by-summoner/'
matchnumL = 'https://asia.api.riotgames.com/lol/match/v5/matches/by-puuid/'
matchL = 'https://asia.api.riotgames.com/lol/match/v5/matches/'

rank = {
    "queue_type": "",
    "tier": "",
    "match": "",
    "leaguepoints": "",
    "wins": "",
    "losses": ""
}

📌 프로젝트의 처리 과정

run에서 실행에 관한 선언과, 뷰의 블루프린트를 불러와 실행시키고, viewinput 함수를 실행시킨다.

view에서는 controller를 모듈로 사용해, 기본적인 POST 요청으로 수집한 데이터에 대해 API와 함께 정보를 서칭하여 가져온다. 수집된 데이터의 형식은 모두가 딕셔너리 형식으로 반환될 수 있도록 하였다.

controllermodel을 이어주는 통로로도 사용을 해야하기 때문에, modeldatabase에 관한 처리를 다룬다. 그래서 database의 커넥션, 커서는 컨트롤러에서 담당하여 처리하고 table 생성과 데이터 삽입에 관한 처리만 모델에 정리해 놓았다. controller를 하나의 연결다리로 사용해야 하는 점에서, 처리를 하는데 조금 시간이 걸렸다.

loop는 반복문 폴더이다. 반복적으로 접근해야되는 로직의 경우, 일일이 입력하는데 번거로움이 있기 때문에 묶어냈다.

URL, dict, order는 링크 변수와 저장해야하는 딕셔너리를 미리 선언한 변수를 담았다. 변수는 미리 처리하여 정리해두는 것이 코드를 보는데 있어 깔끔하기 때문에, 중복되는 변수들은 최대한 미리 정리하였다.

📌 추가적인 기능구현

  • 기본적으로 조금 씩 최적화가 필요

  • 회원가입 기능과 게시판 기능 구현. 데이터에 관한 처리는 db로.

  • 관련된 영상 추천

  • 데이터 처리 속도 향상(로직의 접근을 달리 해야할듯)

  • 데이터 UI 개선하여 가독성있도록 만들기

  • 이미지 파일들도 로드하여 띄우기

0개의 댓글