쿠버네티스,쿠버플로우

던전앤파이터 시세 예측[6]

세용용용용 2023. 9. 12. 11:23

장고에서 던파 아바타 정보를 input으로 받아 모델predict를 하여 예측값을 출력해보자!!


input으로 받을 컬러 목록을 테이블에 저장해보자

테이블 생성

CREATE TABLE input_list (
	title TEXT,
    jobname TEXT,
    emblem TEXT
);

 

테이블에 input_list값 넣는 코드

title_data = df['title'].drop_duplicates()
jobname_data = df['jobname'].drop_duplicates()
emblem_data = df['emblem'].drop_duplicates()

import pymysql

# 연결 정보 설정
host = "10.103.132.127"
port = 3306
database = "donpa_item"
user = "root"
password = "1234"

# 연결
connection = pymysql.connect(
    host=host,
    port=port,
    user=user,
    password=password,
    database=database
)

try:
    # 커서 생성
    cursor = connection.cursor()

    # 데이터 입력 (존재하지 않는 데이터만 입력)
    for item in title_data:
        sql = f"INSERT INTO input_list (title) SELECT '{item}' FROM DUAL WHERE NOT EXISTS (SELECT * FROM input_list WHERE title = '{item}')"
        cursor.execute(sql)
        
    for item in jobname_data:
        sql = f"INSERT INTO input_list (jobname) SELECT '{item}' FROM DUAL WHERE NOT EXISTS (SELECT * FROM input_list WHERE jobname = '{item}')"
        cursor.execute(sql)
        
    for item in emblem_data:
        sql = f"INSERT INTO input_list (emblem) SELECT '{item}' FROM DUAL WHERE NOT EXISTS (SELECT * FROM input_list WHERE emblem = '{item}')"
        cursor.execute(sql)

    # 커밋
    connection.commit()

    print(f"{len(title_data)}개의 레코드를 입력했습니다.")
    print(f"{len(jobname_data)}개의 레코드를 입력했습니다.")
    print(f"{len(emblem_data)}개의 레코드를 입력했습니다.")
except Exception as e:
    # 에러 발생 시 롤백
    connection.rollback()
    print(f"데이터 입력 중 오류 발생: {str(e)}")
finally:
    # 연결 종료
    connection.close()

 

장고 models.py 해당 테이블을 추가해준다

class InputList(models.Model):
    title = models.TextField(blank=True, null=False, primary_key=True)
    jobname = models.TextField(blank=True, null=True)
    emblem = models.TextField(blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'input_list'

 

 

views.py도 수정해주자

aabata.html 에서 해당 테이블을 사용할것이기에

from .models import InputList
def aabata_view(request):
    # aabata.html 템플릿 렌더링
    items = InputList.objects.all()
    price = Goldprice.objects.latest('date')
    return render(request, 'aabata.html', {'items': items, 'price': price})

 

 

최종 aabata.html

<!DOCTYPE html>
<html>
<head>
    <title>Item List</title>
    <style>
        
        /* 제목 */
        h1 {
            font-size: 46px; /* 큰 제목 크기로 변경 */
            color: #f7e706; /* 게임 색상으로 변경 */
            text-align: center;/* 제목 가운데 정렬*/
        }
        /* 메뉴 스타일링 */
        ul.menu {
            list-style-type: none;
            margin: 0;
            padding: 0;
            text-align: center;
            
            padding: 10px 0; /* 메뉴 항목 위아래로 간격 추가 */
        }
        ul.menu li {
            display: inline;
            margin-right: 20px;
            font-size: 40px;
        }
        ul.menu li a {
            color: rgb(255, 255, 255);
            text-decoration: none;
            padding: 10px 20px; /* 각 메뉴 항목의 내부 여백 추가 */
            border-radius: 5px; /* 각 메뉴 항목을 둥글게 꾸미기 */
            /* background-color: #534747; /* 배경색 추가 */
            box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* 그림자 효과 추가 */
            transition: background-color 0.3s, transform 0.2s; /* 호버 효과를 위한 트랜지션 설정 */
        }
        ul.menu li a:hover {
            background-color: #4e2626; /* 호버 시 배경색 변경 */
            transform: translateY(-2px); /* 호버 시 약간 위로 이동 효과 */
        }
        

        /* 배경 이미지 추가 */
        body {
            background-image: url('https://bbscdn.df.nexon.com/data7/commu/201712/205032_5a44da887e6a8.jpg');
            background-size: cover;
            background-repeat: no-repeat;
            background-attachment: fixed;
            background-position: center;
            position: relative;
            z-index: 0;
        }

        /* select 스타일링 */
        select, input {
            padding: 10px;
            font-size: 16px;
            margin: 5px 0;
        }
        
        label {
            display: inline-block;
            width: 80px; /* 라벨 너비 조절 */
        }
        
        /* 결과 표시 스타일링 */
        #resultDiv {
            font-size: 20px;
            text-align: center;
            margin-top: 20px;
        }

        /* 확인 버튼 스타일링 */
        #confirmButton {
            display: block;
            margin: 20px auto; /* 가운데 정렬 */
            padding: 10px 20px;
            font-size: 24px; /* 버튼 크기 키우기 */
            background-color: #4e2626; /* 배경색 변경 */
            color: #fff; /* 텍스트 색상 변경 */
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }
        
        /* 추가된 텍스트 스타일링 */
        .input-label {
            display: block;
            font-size: 20px;
            text-align: center;
            margin-top: 10px;
        }

        /* select 요소 스타일링 */
        .select-container {
            text-align: center;
        }
        
        .select-container select {
            padding: 10px;
            font-size: 16px;
            margin: 5px;
        }

        /* 금일 예상 가격 스타일링 */
        #resultDiv {
            font-size: 30px; /* 글꼴 크기 키우기 */
            text-align: center;
            margin-top: 20px;
            color: #12e612; /* 색상 변경 */
            opacity: 0; /* 초기에는 투명하게 설정 */
            transform: translateY(20px); /* 초기 위치를 아래로 설정 */
            transition: opacity 0.5s, transform 0.5s; /* 나타날 때 효과를 위한 트랜지션 설정 */
        }

        /* 추가된 텍스트 스타일링 */
        .input-label {
            display: block;
            font-size: 20px;
            text-align: center;
            margin-top: 10px;
            color: #12e612; /* 글씨색을 원하는 색상으로 변경 */
        }
    </style>
    
</head>
<body>
    <ul class="menu">
        <li><a href="/">아이템 시세</a></li>
        <li><a href="/aabata/">아바타 시세</a></li>
        <li><a href="/event/">진행 중 이벤트</a></li>
        <li><a href="/news/">던파 소식</a></li>
    </ul>
    <h1>던전앤파이터 아바타</h1>
    
    <!-- select 요소 추가 -->
    <div class="input-label">Title: 
        <select id="select1">
            {% for item in items %}
                {% if item.title %}
                    <option value="{{ item.title }}">{{ item.title }}</option>
                {% endif %}
            {% endfor %}
        </select>
    </div>

    <div class="input-label">Jobname: 
        <select id="select2">
            {% for item in items %}
                {% if item.jobname %}
                    <option value="{{ item.jobname }}">{{ item.jobname }}</option>
                {% endif %}
            {% endfor %}
        </select>
    </div>

    <div class="input-label">Emblem: 
        <select id="select3">
            {% for item in items %}
                {% if item.emblem %}
                    <option value="{{ item.emblem }}">{{ item.emblem }}</option>
                {% endif %}
            {% endfor %}
        </select>
    </div>

    <!-- 두 번째 행: Day of the week, Year, Month, Day -->
    <div class="input-label">Day of the week: 
        <select id="select4">
            <!-- JavaScript를 사용하여 요일 옵션 추가 -->
        </select>

        <script>
            // 요일 배열 생성
            var daysOfWeek = ["Sunday", "Saturday", "Friday", "Monday", "Thursday", "Tuesday", "Wednesday"];

            // select 요소 가져오기
            var selectElement = document.getElementById("select4");

            // 요일 배열을 반복하면서 옵션 요소 추가
            for (var i = 0; i < daysOfWeek.length; i++) {
                var option = document.createElement("option");
                option.value = daysOfWeek[i];
                option.text = daysOfWeek[i];
                selectElement.appendChild(option);
            }
        </script>
    </div>

        <!-- year, month, day 입력 필드 추가 -->
    <div class="input-label">Year: 
    <input type="number" id="year" name="year" min="2000" max="2099" step="1" placeholder="Enter year">
    </div>

    <div class="input-label">Month: 
    <input type="number" id="month" name="month" min="1" max="12" step="1" placeholder="Enter month">
    </div>

    <div class="input-label">Day: 
    <input type="number" id="day" name="day" min="1" max="31" step="1" placeholder="Enter day">
    </div>
    <!-- 확인 버튼 추가 -->
    <button id="confirmButton">확인</button>

    <!-- 결과를 표시할 div 추가 -->
    <div id="resultDiv"></div>

    <script>
    var resultDiv = document.getElementById("resultDiv");

    document.getElementById("confirmButton").addEventListener("click", function () {
        var selectedTitle = document.getElementById("select1").value;
        var selectedJobname = document.getElementById("select2").value;
        var selectedEmblem = document.getElementById("select3").value;
        var sell = parseFloat("{{ price.sell }}");
        var buy = parseFloat("{{ price.buy }}");
        var year = parseInt(document.getElementById("year").value);
        var month = parseInt(document.getElementById("month").value);
        var day = parseInt(document.getElementById("day").value);
        var day_name = document.getElementById("select4").value;

        // JavaScript 객체로 데이터 준비
        var data = {
            'selectedTitle': selectedTitle,
            'selectedJobname': selectedJobname,
            'selectedEmblem': selectedEmblem,
            'sell': sell,
            'buy': buy,
            'year': year,
            'month': month,
            'day': day,
            'day_name': day_name,
        };

        // AJAX를 사용하여 데이터를 서버로 전송
        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/process_data/', true); // URL을 수정해야 합니다.
        xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
        xhr.send(JSON.stringify(data));

        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    // 서버에서의 응답 처리
                    var response = JSON.parse(xhr.responseText);
                    // 결과를 표시할 위치에 표시
                    resultDiv.innerHTML = "금일 예상 가격:" + response.message;
                    resultDiv.style.opacity = 1;
                    resultDiv.style.transform = "translateY(0)";
                } else {
                    // 오류 처리
                    resultDiv.innerHTML = 'Error: ' + xhr.status;
                }
            }
        };
    });
</script>

</body>
</html>

 

select, input으로 입력받은 값을 처리할

 

views.py

import pandas as pd
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json

@csrf_exempt
def process_data(request):
    if request.method == 'POST':
        try:
            # POST 요청에서 데이터 추출
            data = json.loads(request.body.decode('utf-8'))
            selectedTitle = data['selectedTitle']
            selectedJobname = data['selectedJobname']
            selectedEmblem = data['selectedEmblem']
            sell = data['sell']
            buy = data['buy']
            year = data['year']
            month = data['month']
            day = data['day']
            day_name = data['day_name']

            # 데이터 프레임 생성
            df = pd.DataFrame({
                'title': [selectedTitle],
                'jobname': [selectedJobname],
                'emblem': [selectedEmblem],
                'sell': [float(sell)],
                'buy': [float(buy)],
                'year': [int(year)],
                'month': [int(month)],
                'day': [int(day)],
                'day_name': [day_name]
            })

            # 여기에서 데이터프레임을 원하는 방식으로 처리
            df['sell'] = df['sell'].astype(float)
            df['buy'] = df['buy'].astype(float)
            df['year'] = df['year'].astype(int)
            df['month'] = df['month'].astype(int)
            df['day'] = df['day'].astype(int)

            # 데이터프레임 스케일링
            obj_col = df.select_dtypes(include='object').columns
            from sklearn.preprocessing import StandardScaler

            # 스케일러 불러오기
            import joblib
            sds = joblib.load('/home/user/donpa/pybo/scaler.pkl')

            df_sc = sds.transform(df.drop(columns = obj_col))
            df_sc = pd.DataFrame(df_sc, columns = df.drop(columns = obj_col).columns)

            # object 타입 컬럼 붙여주기
            for i in obj_col:
                df_sc[i] = df[i]

            # 원핫 인코딩 돌려주자
            from sklearn.preprocessing import OneHotEncoder
            encoder = joblib.load('/home/user/donpa/pybo/encoder.pkl')
            
            # 범주형 열만 선택
            obj_df = df_sc.select_dtypes(include='object')
            # 숫자형 열만 선택
            no_obj_df = df_sc.select_dtypes(exclude='object')
            # 범주형 열을 원핫 인코딩
            encoded_features = encoder.transform(obj_df)

            # 인코딩된 결과를 데이터프레임으로 변환
            encoded_df = pd.DataFrame(encoded_features.toarray(), columns=encoder.get_feature_names(obj_df.columns))
            # 인코딩된 범주형 열과 숫자형 열을 합침
            df_sc_encoding = pd.concat([no_obj_df[:len(df_sc)] , encoded_df[:len(df_sc)]], axis = 1)
            

            # 컬럼 특수문자 제거
            import re
            # 데이터프레임의 컬럼 이름에서 특수 문자를 제거하고 변경할 새로운 컬럼 이름 리스트 생성
            new_columns = []
            for old_column in df_sc_encoding.columns:
                new_column = re.sub(r'[^\w\s]', '', old_column)  # 특수 문자 제거
                new_columns.append(new_column)

            # 컬럼 이름을 새로운 이름으로 설정
            df_sc_encoding.columns = new_columns
            

            # 모델을 가져와 predict하자
            import xgboost as xgb
            xgb = joblib.load('/home/user/donpa/pybo/xg_model.pkl')
            pred_result = xgb.predict(df_sc_encoding)[0]

            pred_result = float(pred_result)
            
            # 결과를 JSON 형식으로 반환
            result = {'message': pred_result}
            return JsonResponse(result)
        except Exception as e:
            return JsonResponse({'message': 'Error: {}'.format(str(e))})
    else:
        return JsonResponse({'message': 'Invalid request method'})

 

 

urls.py

path('process_data/', views.process_data, name='process_data'),  # "/process_data/" URL 패턴 추가

 

 

장고 실행 화면

예상가격이 리턴되는것을 확인할수 있다!!!

 

내일 최종적으로 쿠버플로우 파이프라인을 구축하고 장고를 올려보자!!!

 

파이프라인

모델을 돌리는 이미지( 스케일러 객체, 인코딩 객체 저장) >>> 카팁을 돌려 최적의 파라미터를 찾고 파싱해 모델에 적용시켜 모델을 저장시키는 이미지

 

총 2개의 모델이 돌아가야됨 레어아바타, 상급 아바타